{"version":3,"file":"ngl.js","sources":["../node_modules/three/src/constants.js","../node_modules/three/src/core/EventDispatcher.js","../node_modules/three/src/math/MathUtils.js","../node_modules/three/src/math/Vector2.js","../node_modules/three/src/math/Matrix3.js","../node_modules/three/src/utils.js","../node_modules/three/src/math/ColorManagement.js","../node_modules/three/src/extras/ImageUtils.js","../node_modules/three/src/textures/Source.js","../node_modules/three/src/textures/Texture.js","../node_modules/three/src/math/Vector4.js","../node_modules/three/src/core/RenderTarget.js","../node_modules/three/src/renderers/WebGLRenderTarget.js","../node_modules/three/src/textures/DataArrayTexture.js","../node_modules/three/src/textures/Data3DTexture.js","../node_modules/three/src/math/Quaternion.js","../node_modules/three/src/math/Vector3.js","../node_modules/three/src/math/Box3.js","../node_modules/three/src/math/Sphere.js","../node_modules/three/src/math/Ray.js","../node_modules/three/src/math/Matrix4.js","../node_modules/three/src/math/Euler.js","../node_modules/three/src/core/Layers.js","../node_modules/three/src/core/Object3D.js","../node_modules/three/src/math/Triangle.js","../node_modules/three/src/math/Color.js","../node_modules/three/src/materials/Material.js","../node_modules/three/src/materials/MeshBasicMaterial.js","../node_modules/three/src/core/BufferAttribute.js","../node_modules/three/src/core/BufferGeometry.js","../node_modules/three/src/objects/Mesh.js","../node_modules/three/src/geometries/BoxGeometry.js","../node_modules/three/src/renderers/shaders/UniformsUtils.js","../node_modules/three/src/materials/ShaderMaterial.js","../node_modules/three/src/renderers/shaders/ShaderChunk/default_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/default_fragment.glsl.js","../node_modules/three/src/cameras/Camera.js","../node_modules/three/src/cameras/PerspectiveCamera.js","../node_modules/three/src/cameras/CubeCamera.js","../node_modules/three/src/textures/CubeTexture.js","../node_modules/three/src/renderers/WebGLCubeRenderTarget.js","../node_modules/three/src/math/Plane.js","../node_modules/three/src/math/Frustum.js","../node_modules/three/src/renderers/webgl/WebGLAnimation.js","../node_modules/three/src/renderers/webgl/WebGLAttributes.js","../node_modules/three/src/geometries/PlaneGeometry.js","../node_modules/three/src/renderers/shaders/ShaderLib/background.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk.js","../node_modules/three/src/renderers/shaders/ShaderChunk/alphahash_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/alphahash_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/alphamap_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/alphamap_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/alphatest_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/alphatest_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/aomap_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/aomap_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/begin_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/beginnormal_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/bsdfs.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/iridescence_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/bumpmap_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/clipping_planes_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/clipping_planes_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/clipping_planes_pars_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/clipping_planes_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/color_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/color_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/color_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/common.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/cube_uv_reflection_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/displacementmap_pars_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/displacementmap_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/emissivemap_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/colorspace_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/colorspace_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/envmap_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/envmap_common_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/envmap_physical_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/envmap_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/fog_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/fog_pars_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/fog_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/fog_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/gradientmap_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/lightmap_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/lightmap_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/lights_lambert_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/lights_lambert_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/lights_pars_begin.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/lights_toon_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/lights_toon_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/lights_phong_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/lights_physical_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/lights_fragment_begin.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/lights_fragment_maps.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/lights_fragment_end.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/logdepthbuf_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/logdepthbuf_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/logdepthbuf_pars_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/logdepthbuf_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/map_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/map_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/map_particle_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/metalnessmap_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/metalnessmap_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/morphcolor_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/morphnormal_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/morphtarget_pars_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/morphtarget_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/normal_fragment_begin.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/normal_fragment_maps.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/normal_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/normal_pars_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/normal_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/clearcoat_normal_fragment_begin.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/clearcoat_normal_fragment_maps.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/clearcoat_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/iridescence_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/opaque_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/packing.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/premultiplied_alpha_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/project_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/dithering_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/dithering_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/roughnessmap_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/roughnessmap_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/shadowmask_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/skinbase_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/skinning_pars_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/skinning_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/skinnormal_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/specularmap_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/specularmap_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/tonemapping_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/tonemapping_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/transmission_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/transmission_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/uv_pars_fragment.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/uv_pars_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/uv_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl.js","../node_modules/three/src/renderers/shaders/ShaderLib/backgroundCube.glsl.js","../node_modules/three/src/renderers/shaders/ShaderLib/cube.glsl.js","../node_modules/three/src/renderers/shaders/ShaderLib/depth.glsl.js","../node_modules/three/src/renderers/shaders/ShaderLib/distanceRGBA.glsl.js","../node_modules/three/src/renderers/shaders/ShaderLib/equirect.glsl.js","../node_modules/three/src/renderers/shaders/ShaderLib/linedashed.glsl.js","../node_modules/three/src/renderers/shaders/ShaderLib/meshbasic.glsl.js","../node_modules/three/src/renderers/shaders/ShaderLib/meshlambert.glsl.js","../node_modules/three/src/renderers/shaders/ShaderLib/meshmatcap.glsl.js","../node_modules/three/src/renderers/shaders/ShaderLib/meshnormal.glsl.js","../node_modules/three/src/renderers/shaders/ShaderLib/meshphong.glsl.js","../node_modules/three/src/renderers/shaders/ShaderLib/meshphysical.glsl.js","../node_modules/three/src/renderers/shaders/ShaderLib/meshtoon.glsl.js","../node_modules/three/src/renderers/shaders/ShaderLib/points.glsl.js","../node_modules/three/src/renderers/shaders/ShaderLib/shadow.glsl.js","../node_modules/three/src/renderers/shaders/ShaderLib/sprite.glsl.js","../node_modules/three/src/renderers/shaders/UniformsLib.js","../node_modules/three/src/renderers/shaders/ShaderLib.js","../node_modules/three/src/renderers/webgl/WebGLBackground.js","../node_modules/three/src/renderers/webgl/WebGLBindingStates.js","../node_modules/three/src/renderers/webgl/WebGLBufferRenderer.js","../node_modules/three/src/renderers/webgl/WebGLCapabilities.js","../node_modules/three/src/renderers/webgl/WebGLClipping.js","../node_modules/three/src/renderers/webgl/WebGLCubeMaps.js","../node_modules/three/src/cameras/OrthographicCamera.js","../node_modules/three/src/extras/PMREMGenerator.js","../node_modules/three/src/renderers/webgl/WebGLCubeUVMaps.js","../node_modules/three/src/renderers/webgl/WebGLExtensions.js","../node_modules/three/src/renderers/webgl/WebGLGeometries.js","../node_modules/three/src/renderers/webgl/WebGLIndexedBufferRenderer.js","../node_modules/three/src/renderers/webgl/WebGLInfo.js","../node_modules/three/src/renderers/webgl/WebGLMorphtargets.js","../node_modules/three/src/renderers/webgl/WebGLObjects.js","../node_modules/three/src/renderers/webgl/WebGLUniforms.js","../node_modules/three/src/renderers/webgl/WebGLShader.js","../node_modules/three/src/renderers/webgl/WebGLProgram.js","../node_modules/three/src/renderers/webgl/WebGLShaderCache.js","../node_modules/three/src/renderers/webgl/WebGLPrograms.js","../node_modules/three/src/renderers/webgl/WebGLProperties.js","../node_modules/three/src/renderers/webgl/WebGLRenderLists.js","../node_modules/three/src/renderers/webgl/WebGLLights.js","../node_modules/three/src/renderers/webgl/WebGLRenderStates.js","../node_modules/three/src/materials/MeshDepthMaterial.js","../node_modules/three/src/materials/MeshDistanceMaterial.js","../node_modules/three/src/renderers/webgl/WebGLShadowMap.js","../node_modules/three/src/renderers/shaders/ShaderLib/vsm.glsl.js","../node_modules/three/src/renderers/webgl/WebGLState.js","../node_modules/three/src/renderers/webgl/WebGLTextures.js","../node_modules/three/src/renderers/webgl/WebGLUtils.js","../node_modules/three/src/cameras/ArrayCamera.js","../node_modules/three/src/objects/Group.js","../node_modules/three/src/renderers/webxr/WebXRController.js","../node_modules/three/src/textures/DepthTexture.js","../node_modules/three/src/renderers/webxr/WebXRManager.js","../node_modules/three/src/renderers/webgl/WebGLMaterials.js","../node_modules/three/src/renderers/webgl/WebGLUniformsGroups.js","../node_modules/three/src/renderers/WebGLRenderer.js","../node_modules/three/src/scenes/Fog.js","../node_modules/three/src/scenes/Scene.js","../node_modules/three/src/textures/DataTexture.js","../node_modules/three/src/materials/LineBasicMaterial.js","../node_modules/three/src/objects/Line.js","../node_modules/three/src/objects/LineSegments.js","../node_modules/three/src/materials/PointsMaterial.js","../node_modules/three/src/objects/Points.js","../node_modules/three/src/textures/CanvasTexture.js","../node_modules/three/src/geometries/CylinderGeometry.js","../node_modules/three/src/geometries/ConeGeometry.js","../node_modules/three/src/geometries/PolyhedronGeometry.js","../node_modules/three/src/geometries/IcosahedronGeometry.js","../node_modules/three/src/geometries/OctahedronGeometry.js","../node_modules/three/src/geometries/TetrahedronGeometry.js","../node_modules/three/src/geometries/TorusGeometry.js","../node_modules/three/src/lights/Light.js","../node_modules/three/src/lights/LightShadow.js","../node_modules/three/src/lights/DirectionalLightShadow.js","../node_modules/three/src/lights/DirectionalLight.js","../node_modules/three/src/lights/AmbientLight.js","../node_modules/three/src/cameras/StereoCamera.js","../node_modules/three/src/core/Uniform.js","../src/utils.ts","../src/utils/registry.ts","../src/math/math-utils.ts","../node_modules/chroma-js/chroma.js","../src/color/colormaker.ts","../src/selection/selection-constants.ts","../node_modules/signals/dist/signals.js","../src/selection/selection-test.ts","../src/selection/selection.ts","../src/selection/selection-parser.ts","../src/color/selection-colormaker.ts","../src/color/colormaker-registry.ts","../src/worker/worker-utils.ts","../src/globals.ts","../src/worker/worker-registry.ts","../src/parser/parser-registry.ts","../src/streamer/streamer.ts","../src/streamer/file-streamer.ts","../src/streamer/network-streamer.ts","../src/loader/loader.ts","../src/loader/parser-loader.ts","../src/script.ts","../src/loader/script-loader.ts","../src/loader/loader-utils.ts","../node_modules/sprintf-js/src/sprintf.js","../src/writer/writer.ts","../src/utils/io-buffer.ts","../src/utils/counter.ts","../src/viewer/stats.ts","../src/shader/shader-utils.ts","../src/viewer/viewer-constants.ts","../src/viewer/tiled-renderer.ts","../src/math/math-constants.ts","../src/math/array-utils.ts","../src/viewer/viewer-utils.ts","../src/viewer/gl-utils.ts","../src/viewer/viewer.ts","../src/stage/mouse-observer.ts","../src/constants.ts","../src/controls/trackball-controls.ts","../src/controls/picking-proxy.ts","../src/controls/picking-controls.ts","../src/controls/viewer-controls.ts","../src/animation/animation.ts","../src/controls/animation-controls.ts","../src/utils/queue.ts","../src/representation/representation.ts","../src/worker/worker.ts","../src/worker/worker-pool.ts","../src/math/vector-utils.ts","../src/geometry/dash.ts","../src/geometry/primitive.ts","../src/geometry/spatial-hash.ts","../src/store/store.ts","../src/store/contact-store.ts","../src/utils/bitarray.ts","../src/utils/adjacency-list.ts","../src/chemistry/interactions/features.ts","../src/structure/structure-constants.ts","../src/chemistry/geometry.ts","../src/chemistry/valence-model.ts","../src/structure/data.ts","../src/chemistry/functional-groups.ts","../src/chemistry/interactions/charged.ts","../src/chemistry/interactions/hydrogen-bonds.ts","../src/chemistry/interactions/metal-binding.ts","../src/chemistry/interactions/halogen-bonds.ts","../src/chemistry/interactions/refine-contacts.ts","../src/chemistry/interactions/contact.ts","../src/chemistry/interactions/hydrophobic.ts","../src/utils/picker.ts","../src/surface/marching-cubes.ts","../src/math/matrix-utils.ts","../src/surface/surface-utils.ts","../src/surface/surface.ts","../src/surface/volume.ts","../src/buffer/buffer.ts","../src/buffer/mesh-buffer.ts","../src/buffer/surface-buffer.ts","../src/buffer/doublesided-buffer.ts","../src/buffer/contour-buffer.ts","../src/representation/surface-representation.ts","../src/controls/mouse-actions.ts","../src/controls/mouse-controls.ts","../src/controls/key-actions.ts","../src/controls/key-controls.ts","../src/stage/picking-behavior.ts","../src/stage/mouse-behavior.ts","../src/stage/animation-behavior.ts","../src/stage/key-behavior.ts","../src/component/annotation.ts","../src/controls/component-controls.ts","../src/utils/radius-factory.ts","../src/math/principal-axes.ts","../src/surface/filtered-volume.ts","../src/store/bond-hash.ts","../src/store/bond-store.ts","../src/store/atom-store.ts","../src/store/residue-store.ts","../src/store/chain-store.ts","../src/store/model-store.ts","../src/geometry/helixorient.ts","../src/geometry/helixbundle.ts","../src/utils/binary-heap.ts","../src/utils/kdtree.ts","../src/proxy/atom-proxy.ts","../src/geometry/kdtree.ts","../src/symmetry/symmetry-constants.ts","../src/symmetry/symmetry-utils.ts","../src/symmetry/assembly.ts","../src/structure/structure-builder.ts","../src/structure/structure-utils.ts","../src/store/atom-type.ts","../src/store/atom-map.ts","../src/store/residue-type.ts","../src/store/residue-map.ts","../src/proxy/bond-proxy.ts","../src/proxy/residue-proxy.ts","../src/proxy/polymer.ts","../src/proxy/chain-proxy.ts","../src/proxy/model-proxy.ts","../src/structure/structure.ts","../src/geometry/shape.ts","../src/representation/buffer-representation.ts","../src/buffer/geometry-buffer.ts","../src/buffer/spheregeometry-buffer.ts","../src/buffer/mapped-buffer.ts","../src/buffer/mappedquad-buffer.ts","../src/buffer/sphereimpostor-buffer.ts","../src/buffer/sphere-buffer.ts","../src/buffer/point-buffer.ts","../src/representation/dot-representation.ts","../src/buffer/image-buffer.ts","../src/surface/volume-slice.ts","../src/representation/slice-representation.ts","../src/representation/representation-utils.ts","../src/component/element.ts","../src/component/representation-element.ts","../src/component/component.ts","../src/component/collection.ts","../src/component/representation-collection.ts","../src/component/trajectory-element.ts","../src/trajectory/frames.ts","../src/align/superposition.ts","../src/trajectory/trajectory-player.ts","../src/trajectory/trajectory.ts","../src/trajectory/frames-trajectory.ts","../src/trajectory/structure-trajectory.ts","../src/trajectory/remote-trajectory.ts","../src/trajectory/callback-trajectory.ts","../src/structure/structure-view.ts","../src/align/alignment.ts","../src/align/align-utils.ts","../src/component/structure-component.ts","../src/trajectory/trajectory-utils.ts","../src/component/surface-component.ts","../src/component/volume-component.ts","../src/component/component-collection.ts","../src/stage/stage.ts","../src/component/shape-component.ts","../node_modules/tslib/tslib.es6.js","../src/color/atomindex-colormaker.ts","../src/color/bfactor-colormaker.ts","../src/color/chainid-colormaker.ts","../src/color/chainindex-colormaker.ts","../src/color/chainname-colormaker.ts","../src/color/densityfit-colormaker.ts","../src/color/electrostatic-colormaker.ts","../src/color/element-colormaker.ts","../src/color/entityindex-colormaker.ts","../src/color/entitytype-colormaker.ts","../src/color/geoquality-colormaker.ts","../src/color/hydrophobicity-colormaker.ts","../src/color/modelindex-colormaker.ts","../src/color/moleculetype-colormaker.ts","../src/color/occupancy-colormaker.ts","../src/color/partialcharge-colormaker.ts","../src/color/random-colormaker.ts","../src/color/randomcoilindex-colormaker.ts","../src/color/residueindex-colormaker.ts","../src/color/resname-colormaker.ts","../src/color/sstruc-colormaker.ts","../src/color/structuredata-colormaker.ts","../src/color/uniform-colormaker.ts","../src/color/value-colormaker.ts","../src/color/volume-colormaker.ts","../src/representation/structure-representation.ts","../src/representation/measurement-representation.ts","../src/utils/edt.ts","../src/buffer/text-buffer.ts","../src/buffer/wideline-buffer.ts","../src/representation/angle-representation.ts","../src/buffer/cylindergeometry-buffer.ts","../src/buffer/mappedalignedbox-buffer.ts","../src/buffer/cylinderimpostor-buffer.ts","../src/buffer/cylinder-buffer.ts","../src/representation/axes-representation.ts","../src/representation/ballandstick-representation.ts","../src/representation/backbone-representation.ts","../src/representation/base-representation.ts","../src/geometry/spline.ts","../src/buffer/tubemesh-buffer.ts","../src/representation/cartoon-representation.ts","../src/representation/contact-representation.ts","../src/representation/dihedral-representation.ts","../src/representation/dihedral-histogram-representation.ts","../src/representation/distance-representation.ts","../src/buffer/vector-buffer.ts","../src/representation/helixorient-representation.ts","../src/representation/licorice-representation.ts","../src/buffer/mappedbox-buffer.ts","../src/buffer/hyperballstickimpostor-buffer.ts","../src/buffer/hyperballstick-buffer.ts","../src/representation/hyperball-representation.ts","../src/utils/label-factory.ts","../src/representation/label-representation.ts","../src/representation/line-representation.ts","../src/geometry/grid.ts","../src/surface/edt-surface.ts","../src/surface/av-surface.ts","../src/surface/molecular-surface.ts","../src/representation/molecularsurface-representation.ts","../src/representation/point-representation.ts","../src/buffer/ribbon-buffer.ts","../src/representation/ribbon-representation.ts","../src/representation/rocket-representation.ts","../src/representation/rope-representation.ts","../src/representation/spacefill-representation.ts","../src/buffer/trace-buffer.ts","../src/representation/trace-representation.ts","../src/representation/tube-representation.ts","../src/representation/unitcell-representation.ts","../src/representation/validation-representation.ts","../src/buffer/cone-buffer.ts","../src/viewer/geometry-group.ts","../src/buffer/arrow-buffer.ts","../src/buffer/box-buffer.ts","../src/buffer/ellipsoid-buffer.ts","../src/buffer/octahedron-buffer.ts","../src/buffer/tetrahedron-buffer.ts","../src/buffer/torus-buffer.ts","../node_modules/molstar/lib/mol-data/db/column-helpers.js","../node_modules/molstar/lib/mol-math/linear-algebra/3d/common.js","../node_modules/molstar/lib/mol-math/interpolate.js","../node_modules/molstar/lib/mol-math/linear-algebra/3d/vec3.js","../node_modules/molstar/lib/mol-math/misc.js","../node_modules/molstar/lib/mol-math/linear-algebra/3d/mat4.js","../node_modules/molstar/lib/mol-math/linear-algebra/3d/mat3.js","../node_modules/molstar/lib/mol-math/linear-algebra/3d/vec2.js","../node_modules/molstar/lib/mol-math/linear-algebra/3d/vec4.js","../node_modules/molstar/lib/mol-util/type-helpers.js","../node_modules/molstar/lib/mol-math/linear-algebra/3d/quat.js","../node_modules/molstar/lib/mol-math/linear-algebra/tensor.js","../node_modules/molstar/lib/mol-data/db/column.js","../node_modules/molstar/lib/mol-util/bit-flags.js","../node_modules/molstar/lib/mol-util/string-builder.js","../node_modules/molstar/lib/mol-io/reader/common/text/number-parser.js","../node_modules/molstar/lib/mol-data/util/sort.js","../node_modules/molstar/lib/mol-util/now.js","../node_modules/molstar/lib/mol-util/uuid.js","../node_modules/molstar/lib/mol-util/mask.js","../node_modules/molstar/lib/mol-util/value-cell.js","../node_modules/molstar/lib/mol-util/id-factory.js","../node_modules/molstar/lib/mol-data/db/table.js","../node_modules/molstar/lib/mol-data/db/database.js","../node_modules/molstar/lib/mol-io/reader/cif/data-model.js","../node_modules/molstar/lib/mol-io/reader/common/text/column/token.js","../node_modules/molstar/lib/mol-task/util/scheduler.js","../node_modules/molstar/lib/mol-util/debug.js","../node_modules/molstar/lib/mol-task/util/user-timing.js","../node_modules/molstar/lib/mol-task/execution/observable.js","../node_modules/molstar/lib/mol-task/execution/synchronous.js","../node_modules/molstar/lib/mol-task/task.js","../node_modules/molstar/lib/mol-task/execution/runtime-context.js","../node_modules/molstar/lib/mol-task/execution/progress.js","../node_modules/molstar/lib/mol-io/reader/common/text/tokenizer.js","../node_modules/molstar/lib/mol-io/reader/result.js","../node_modules/molstar/lib/mol-io/common/binary-cif/encoding.js","../node_modules/molstar/lib/mol-task/util/chunked.js","../node_modules/molstar/lib/mol-io/reader/cif/text/parser.js","../node_modules/molstar/lib/mol-io/common/binary.js","../node_modules/molstar/lib/mol-io/common/binary-cif/decoder.js","../node_modules/molstar/lib/mol-data/util/chunked-array.js","../node_modules/molstar/lib/mol-io/common/binary-cif/classifier.js","../node_modules/molstar/lib/mol-io/common/binary-cif/array-encoder.js","../node_modules/molstar/lib/mol-util/number.js","../node_modules/molstar/lib/mol-io/common/utf8.js","../node_modules/molstar/lib/mol-io/common/msgpack/decode.js","../node_modules/molstar/lib/mol-io/reader/cif/binary/parser.js","../node_modules/molstar/lib/mol-io/reader/cif/binary/field.js","../node_modules/molstar/lib/mol-io/reader/cif/schema.js","../node_modules/molstar/lib/mol-util/index.js","../node_modules/molstar/lib/mol-io/reader/cif/schema/mmcif.js","../node_modules/molstar/lib/mol-io/reader/cif/schema/ccd.js","../node_modules/molstar/lib/mol-io/reader/cif/schema/bird.js","../node_modules/molstar/lib/mol-io/reader/cif/schema/dic.js","../node_modules/molstar/lib/mol-io/reader/cif/schema/density-server.js","../node_modules/molstar/lib/mol-io/reader/cif/schema/cif-core.js","../node_modules/molstar/lib/mol-io/reader/cif/schema/segmentation.js","../node_modules/molstar/lib/mol-io/reader/cif.js","../src/parser/parser.ts","../src/parser/structure-parser.ts","../src/structure/entity.ts","../src/symmetry/unitcell.ts","../src/parser/pdb-parser.ts","../src/store/chemcomp-map.ts","../src/parser/cif-parser.ts","../src/parser/gro-parser.ts","../lib/mmtf.es6.js","../src/parser/mmtf-parser.ts","../src/parser/mol2-parser.ts","../src/parser/pdbqt-parser.ts","../src/parser/pqr-parser.ts","../src/parser/sdf-parser.ts","../src/parser/prmtop-parser.ts","../src/parser/psf-parser.ts","../src/parser/top-parser.ts","../src/parser/trajectory-parser.ts","../src/utils/netcdf-reader.ts","../src/parser/dcd-parser.ts","../src/parser/nctraj-parser.ts","../src/parser/trr-parser.ts","../src/parser/xtc-parser.ts","../src/parser/volume-parser.ts","../src/parser/cube-parser.ts","../src/parser/dsn6-parser.ts","../src/parser/dx-parser.ts","../src/parser/dxbin-parser.ts","../src/parser/mrc-parser.ts","../src/parser/xplor-parser.ts","../src/parser/kin-parser.ts","../src/parser/surface-parser.ts","../src/parser/obj-parser.ts","../src/parser/ply-parser.ts","../src/parser/csv-parser.ts","../src/parser/json-parser.ts","../src/parser/msgpack-parser.ts","../src/parser/netcdf-parser.ts","../src/parser/text-parser.ts","../src/utils/parse-xml.ts","../src/parser/xml-parser.ts","../src/structure/validation.ts","../lib/pako_inflate.es6.js","../src/parser/validation-parser.ts","../src/utils/gzip-decompressor.ts","../src/datasource/datasource.ts","../src/datasource/rcsb-datasource.ts","../src/datasource/pdbe-datasource.ts","../src/datasource/pubchem-datasource.ts","../src/datasource/passthrough-datasource.ts","../src/datasource/alphafold-datasource.ts","../src/datasource/static-datasource.ts","../src/ui/parameters.ts","../src/datasource/mdsrv-datasource.ts","../src/writer/pdb-writer.ts","../src/writer/sdf-writer.ts","../src/writer/stl-writer.ts"],"sourcesContent":["export const REVISION = '158';\n\nexport const MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2 };\nexport const TOUCH = { ROTATE: 0, PAN: 1, DOLLY_PAN: 2, DOLLY_ROTATE: 3 };\nexport const CullFaceNone = 0;\nexport const CullFaceBack = 1;\nexport const CullFaceFront = 2;\nexport const CullFaceFrontBack = 3;\nexport const BasicShadowMap = 0;\nexport const PCFShadowMap = 1;\nexport const PCFSoftShadowMap = 2;\nexport const VSMShadowMap = 3;\nexport const FrontSide = 0;\nexport const BackSide = 1;\nexport const DoubleSide = 2;\nexport const TwoPassDoubleSide = 2; // r149\nexport const NoBlending = 0;\nexport const NormalBlending = 1;\nexport const AdditiveBlending = 2;\nexport const SubtractiveBlending = 3;\nexport const MultiplyBlending = 4;\nexport const CustomBlending = 5;\nexport const AddEquation = 100;\nexport const SubtractEquation = 101;\nexport const ReverseSubtractEquation = 102;\nexport const MinEquation = 103;\nexport const MaxEquation = 104;\nexport const ZeroFactor = 200;\nexport const OneFactor = 201;\nexport const SrcColorFactor = 202;\nexport const OneMinusSrcColorFactor = 203;\nexport const SrcAlphaFactor = 204;\nexport const OneMinusSrcAlphaFactor = 205;\nexport const DstAlphaFactor = 206;\nexport const OneMinusDstAlphaFactor = 207;\nexport const DstColorFactor = 208;\nexport const OneMinusDstColorFactor = 209;\nexport const SrcAlphaSaturateFactor = 210;\nexport const ConstantColorFactor = 211;\nexport const OneMinusConstantColorFactor = 212;\nexport const ConstantAlphaFactor = 213;\nexport const OneMinusConstantAlphaFactor = 214;\nexport const NeverDepth = 0;\nexport const AlwaysDepth = 1;\nexport const LessDepth = 2;\nexport const LessEqualDepth = 3;\nexport const EqualDepth = 4;\nexport const GreaterEqualDepth = 5;\nexport const GreaterDepth = 6;\nexport const NotEqualDepth = 7;\nexport const MultiplyOperation = 0;\nexport const MixOperation = 1;\nexport const AddOperation = 2;\nexport const NoToneMapping = 0;\nexport const LinearToneMapping = 1;\nexport const ReinhardToneMapping = 2;\nexport const CineonToneMapping = 3;\nexport const ACESFilmicToneMapping = 4;\nexport const CustomToneMapping = 5;\nexport const AttachedBindMode = 'attached';\nexport const DetachedBindMode = 'detached';\n\nexport const UVMapping = 300;\nexport const CubeReflectionMapping = 301;\nexport const CubeRefractionMapping = 302;\nexport const EquirectangularReflectionMapping = 303;\nexport const EquirectangularRefractionMapping = 304;\nexport const CubeUVReflectionMapping = 306;\nexport const RepeatWrapping = 1000;\nexport const ClampToEdgeWrapping = 1001;\nexport const MirroredRepeatWrapping = 1002;\nexport const NearestFilter = 1003;\nexport const NearestMipmapNearestFilter = 1004;\nexport const NearestMipMapNearestFilter = 1004;\nexport const NearestMipmapLinearFilter = 1005;\nexport const NearestMipMapLinearFilter = 1005;\nexport const LinearFilter = 1006;\nexport const LinearMipmapNearestFilter = 1007;\nexport const LinearMipMapNearestFilter = 1007;\nexport const LinearMipmapLinearFilter = 1008;\nexport const LinearMipMapLinearFilter = 1008;\nexport const UnsignedByteType = 1009;\nexport const ByteType = 1010;\nexport const ShortType = 1011;\nexport const UnsignedShortType = 1012;\nexport const IntType = 1013;\nexport const UnsignedIntType = 1014;\nexport const FloatType = 1015;\nexport const HalfFloatType = 1016;\nexport const UnsignedShort4444Type = 1017;\nexport const UnsignedShort5551Type = 1018;\nexport const UnsignedInt248Type = 1020;\nexport const AlphaFormat = 1021;\nexport const RGBAFormat = 1023;\nexport const LuminanceFormat = 1024;\nexport const LuminanceAlphaFormat = 1025;\nexport const DepthFormat = 1026;\nexport const DepthStencilFormat = 1027;\nexport const RedFormat = 1028;\nexport const RedIntegerFormat = 1029;\nexport const RGFormat = 1030;\nexport const RGIntegerFormat = 1031;\nexport const RGBAIntegerFormat = 1033;\n\nexport const RGB_S3TC_DXT1_Format = 33776;\nexport const RGBA_S3TC_DXT1_Format = 33777;\nexport const RGBA_S3TC_DXT3_Format = 33778;\nexport const RGBA_S3TC_DXT5_Format = 33779;\nexport const RGB_PVRTC_4BPPV1_Format = 35840;\nexport const RGB_PVRTC_2BPPV1_Format = 35841;\nexport const RGBA_PVRTC_4BPPV1_Format = 35842;\nexport const RGBA_PVRTC_2BPPV1_Format = 35843;\nexport const RGB_ETC1_Format = 36196;\nexport const RGB_ETC2_Format = 37492;\nexport const RGBA_ETC2_EAC_Format = 37496;\nexport const RGBA_ASTC_4x4_Format = 37808;\nexport const RGBA_ASTC_5x4_Format = 37809;\nexport const RGBA_ASTC_5x5_Format = 37810;\nexport const RGBA_ASTC_6x5_Format = 37811;\nexport const RGBA_ASTC_6x6_Format = 37812;\nexport const RGBA_ASTC_8x5_Format = 37813;\nexport const RGBA_ASTC_8x6_Format = 37814;\nexport const RGBA_ASTC_8x8_Format = 37815;\nexport const RGBA_ASTC_10x5_Format = 37816;\nexport const RGBA_ASTC_10x6_Format = 37817;\nexport const RGBA_ASTC_10x8_Format = 37818;\nexport const RGBA_ASTC_10x10_Format = 37819;\nexport const RGBA_ASTC_12x10_Format = 37820;\nexport const RGBA_ASTC_12x12_Format = 37821;\nexport const RGBA_BPTC_Format = 36492;\nexport const RGB_BPTC_SIGNED_Format = 36494;\nexport const RGB_BPTC_UNSIGNED_Format = 36495;\nexport const RED_RGTC1_Format = 36283;\nexport const SIGNED_RED_RGTC1_Format = 36284;\nexport const RED_GREEN_RGTC2_Format = 36285;\nexport const SIGNED_RED_GREEN_RGTC2_Format = 36286;\nexport const LoopOnce = 2200;\nexport const LoopRepeat = 2201;\nexport const LoopPingPong = 2202;\nexport const InterpolateDiscrete = 2300;\nexport const InterpolateLinear = 2301;\nexport const InterpolateSmooth = 2302;\nexport const ZeroCurvatureEnding = 2400;\nexport const ZeroSlopeEnding = 2401;\nexport const WrapAroundEnding = 2402;\nexport const NormalAnimationBlendMode = 2500;\nexport const AdditiveAnimationBlendMode = 2501;\nexport const TrianglesDrawMode = 0;\nexport const TriangleStripDrawMode = 1;\nexport const TriangleFanDrawMode = 2;\n/** @deprecated Use LinearSRGBColorSpace or NoColorSpace in three.js r152+. */\nexport const LinearEncoding = 3000;\n/** @deprecated Use SRGBColorSpace in three.js r152+. */\nexport const sRGBEncoding = 3001;\nexport const BasicDepthPacking = 3200;\nexport const RGBADepthPacking = 3201;\nexport const TangentSpaceNormalMap = 0;\nexport const ObjectSpaceNormalMap = 1;\n\n// Color space string identifiers, matching CSS Color Module Level 4 and WebGPU names where available.\nexport const NoColorSpace = '';\nexport const SRGBColorSpace = 'srgb';\nexport const LinearSRGBColorSpace = 'srgb-linear';\nexport const DisplayP3ColorSpace = 'display-p3';\nexport const LinearDisplayP3ColorSpace = 'display-p3-linear';\n\nexport const LinearTransfer = 'linear';\nexport const SRGBTransfer = 'srgb';\n\nexport const Rec709Primaries = 'rec709';\nexport const P3Primaries = 'p3';\n\nexport const ZeroStencilOp = 0;\nexport const KeepStencilOp = 7680;\nexport const ReplaceStencilOp = 7681;\nexport const IncrementStencilOp = 7682;\nexport const DecrementStencilOp = 7683;\nexport const IncrementWrapStencilOp = 34055;\nexport const DecrementWrapStencilOp = 34056;\nexport const InvertStencilOp = 5386;\n\nexport const NeverStencilFunc = 512;\nexport const LessStencilFunc = 513;\nexport const EqualStencilFunc = 514;\nexport const LessEqualStencilFunc = 515;\nexport const GreaterStencilFunc = 516;\nexport const NotEqualStencilFunc = 517;\nexport const GreaterEqualStencilFunc = 518;\nexport const AlwaysStencilFunc = 519;\n\nexport const NeverCompare = 512;\nexport const LessCompare = 513;\nexport const EqualCompare = 514;\nexport const LessEqualCompare = 515;\nexport const GreaterCompare = 516;\nexport const NotEqualCompare = 517;\nexport const GreaterEqualCompare = 518;\nexport const AlwaysCompare = 519;\n\nexport const StaticDrawUsage = 35044;\nexport const DynamicDrawUsage = 35048;\nexport const StreamDrawUsage = 35040;\nexport const StaticReadUsage = 35045;\nexport const DynamicReadUsage = 35049;\nexport const StreamReadUsage = 35041;\nexport const StaticCopyUsage = 35046;\nexport const DynamicCopyUsage = 35050;\nexport const StreamCopyUsage = 35042;\n\nexport const GLSL1 = '100';\nexport const GLSL3 = '300 es';\n\nexport const _SRGBAFormat = 1035; // fallback for WebGL 1\n\nexport const WebGLCoordinateSystem = 2000;\nexport const WebGPUCoordinateSystem = 2001;\n","/**\n * https://github.com/mrdoob/eventdispatcher.js/\n */\n\nclass EventDispatcher {\n\n\taddEventListener( type, listener ) {\n\n\t\tif ( this._listeners === undefined ) this._listeners = {};\n\n\t\tconst listeners = this._listeners;\n\n\t\tif ( listeners[ type ] === undefined ) {\n\n\t\t\tlisteners[ type ] = [];\n\n\t\t}\n\n\t\tif ( listeners[ type ].indexOf( listener ) === - 1 ) {\n\n\t\t\tlisteners[ type ].push( listener );\n\n\t\t}\n\n\t}\n\n\thasEventListener( type, listener ) {\n\n\t\tif ( this._listeners === undefined ) return false;\n\n\t\tconst listeners = this._listeners;\n\n\t\treturn listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1;\n\n\t}\n\n\tremoveEventListener( type, listener ) {\n\n\t\tif ( this._listeners === undefined ) return;\n\n\t\tconst listeners = this._listeners;\n\t\tconst listenerArray = listeners[ type ];\n\n\t\tif ( listenerArray !== undefined ) {\n\n\t\t\tconst index = listenerArray.indexOf( listener );\n\n\t\t\tif ( index !== - 1 ) {\n\n\t\t\t\tlistenerArray.splice( index, 1 );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tdispatchEvent( event ) {\n\n\t\tif ( this._listeners === undefined ) return;\n\n\t\tconst listeners = this._listeners;\n\t\tconst listenerArray = listeners[ event.type ];\n\n\t\tif ( listenerArray !== undefined ) {\n\n\t\t\tevent.target = this;\n\n\t\t\t// Make a copy, in case listeners are removed while iterating.\n\t\t\tconst array = listenerArray.slice( 0 );\n\n\t\t\tfor ( let i = 0, l = array.length; i < l; i ++ ) {\n\n\t\t\t\tarray[ i ].call( this, event );\n\n\t\t\t}\n\n\t\t\tevent.target = null;\n\n\t\t}\n\n\t}\n\n}\n\n\nexport { EventDispatcher };\n","const _lut = [ '00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '0a', '0b', '0c', '0d', '0e', '0f', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '1a', '1b', '1c', '1d', '1e', '1f', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '2a', '2b', '2c', '2d', '2e', '2f', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '3a', '3b', '3c', '3d', '3e', '3f', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '4a', '4b', '4c', '4d', '4e', '4f', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '5a', '5b', '5c', '5d', '5e', '5f', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '6a', '6b', '6c', '6d', '6e', '6f', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '7a', '7b', '7c', '7d', '7e', '7f', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '8a', '8b', '8c', '8d', '8e', '8f', '90', '91', '92', '93', '94', '95', '96', '97', '98', '99', '9a', '9b', '9c', '9d', '9e', '9f', 'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8', 'a9', 'aa', 'ab', 'ac', 'ad', 'ae', 'af', 'b0', 'b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'b7', 'b8', 'b9', 'ba', 'bb', 'bc', 'bd', 'be', 'bf', 'c0', 'c1', 'c2', 'c3', 'c4', 'c5', 'c6', 'c7', 'c8', 'c9', 'ca', 'cb', 'cc', 'cd', 'ce', 'cf', 'd0', 'd1', 'd2', 'd3', 'd4', 'd5', 'd6', 'd7', 'd8', 'd9', 'da', 'db', 'dc', 'dd', 'de', 'df', 'e0', 'e1', 'e2', 'e3', 'e4', 'e5', 'e6', 'e7', 'e8', 'e9', 'ea', 'eb', 'ec', 'ed', 'ee', 'ef', 'f0', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'fa', 'fb', 'fc', 'fd', 'fe', 'ff' ];\n\nlet _seed = 1234567;\n\n\nconst DEG2RAD = Math.PI / 180;\nconst RAD2DEG = 180 / Math.PI;\n\n// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136\nfunction generateUUID() {\n\n\tconst d0 = Math.random() * 0xffffffff | 0;\n\tconst d1 = Math.random() * 0xffffffff | 0;\n\tconst d2 = Math.random() * 0xffffffff | 0;\n\tconst d3 = Math.random() * 0xffffffff | 0;\n\tconst uuid = _lut[ d0 & 0xff ] + _lut[ d0 >> 8 & 0xff ] + _lut[ d0 >> 16 & 0xff ] + _lut[ d0 >> 24 & 0xff ] + '-' +\n\t\t\t_lut[ d1 & 0xff ] + _lut[ d1 >> 8 & 0xff ] + '-' + _lut[ d1 >> 16 & 0x0f | 0x40 ] + _lut[ d1 >> 24 & 0xff ] + '-' +\n\t\t\t_lut[ d2 & 0x3f | 0x80 ] + _lut[ d2 >> 8 & 0xff ] + '-' + _lut[ d2 >> 16 & 0xff ] + _lut[ d2 >> 24 & 0xff ] +\n\t\t\t_lut[ d3 & 0xff ] + _lut[ d3 >> 8 & 0xff ] + _lut[ d3 >> 16 & 0xff ] + _lut[ d3 >> 24 & 0xff ];\n\n\t// .toLowerCase() here flattens concatenated strings to save heap memory space.\n\treturn uuid.toLowerCase();\n\n}\n\nfunction clamp( value, min, max ) {\n\n\treturn Math.max( min, Math.min( max, value ) );\n\n}\n\n// compute euclidean modulo of m % n\n// https://en.wikipedia.org/wiki/Modulo_operation\nfunction euclideanModulo( n, m ) {\n\n\treturn ( ( n % m ) + m ) % m;\n\n}\n\n// Linear mapping from range to range \nfunction mapLinear( x, a1, a2, b1, b2 ) {\n\n\treturn b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );\n\n}\n\n// https://www.gamedev.net/tutorials/programming/general-and-gameplay-programming/inverse-lerp-a-super-useful-yet-often-overlooked-function-r5230/\nfunction inverseLerp( x, y, value ) {\n\n\tif ( x !== y ) {\n\n\t\treturn ( value - x ) / ( y - x );\n\n\t} else {\n\n\t\treturn 0;\n\n\t}\n\n}\n\n// https://en.wikipedia.org/wiki/Linear_interpolation\nfunction lerp( x, y, t ) {\n\n\treturn ( 1 - t ) * x + t * y;\n\n}\n\n// http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/\nfunction damp( x, y, lambda, dt ) {\n\n\treturn lerp( x, y, 1 - Math.exp( - lambda * dt ) );\n\n}\n\n// https://www.desmos.com/calculator/vcsjnyz7x4\nfunction pingpong( x, length = 1 ) {\n\n\treturn length - Math.abs( euclideanModulo( x, length * 2 ) - length );\n\n}\n\n// http://en.wikipedia.org/wiki/Smoothstep\nfunction smoothstep( x, min, max ) {\n\n\tif ( x <= min ) return 0;\n\tif ( x >= max ) return 1;\n\n\tx = ( x - min ) / ( max - min );\n\n\treturn x * x * ( 3 - 2 * x );\n\n}\n\nfunction smootherstep( x, min, max ) {\n\n\tif ( x <= min ) return 0;\n\tif ( x >= max ) return 1;\n\n\tx = ( x - min ) / ( max - min );\n\n\treturn x * x * x * ( x * ( x * 6 - 15 ) + 10 );\n\n}\n\n// Random integer from interval\nfunction randInt( low, high ) {\n\n\treturn low + Math.floor( Math.random() * ( high - low + 1 ) );\n\n}\n\n// Random float from interval\nfunction randFloat( low, high ) {\n\n\treturn low + Math.random() * ( high - low );\n\n}\n\n// Random float from <-range/2, range/2> interval\nfunction randFloatSpread( range ) {\n\n\treturn range * ( 0.5 - Math.random() );\n\n}\n\n// Deterministic pseudo-random float in the interval [ 0, 1 ]\nfunction seededRandom( s ) {\n\n\tif ( s !== undefined ) _seed = s;\n\n\t// Mulberry32 generator\n\n\tlet t = _seed += 0x6D2B79F5;\n\n\tt = Math.imul( t ^ t >>> 15, t | 1 );\n\n\tt ^= t + Math.imul( t ^ t >>> 7, t | 61 );\n\n\treturn ( ( t ^ t >>> 14 ) >>> 0 ) / 4294967296;\n\n}\n\nfunction degToRad( degrees ) {\n\n\treturn degrees * DEG2RAD;\n\n}\n\nfunction radToDeg( radians ) {\n\n\treturn radians * RAD2DEG;\n\n}\n\nfunction isPowerOfTwo( value ) {\n\n\treturn ( value & ( value - 1 ) ) === 0 && value !== 0;\n\n}\n\nfunction ceilPowerOfTwo( value ) {\n\n\treturn Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) );\n\n}\n\nfunction floorPowerOfTwo( value ) {\n\n\treturn Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) );\n\n}\n\nfunction setQuaternionFromProperEuler( q, a, b, c, order ) {\n\n\t// Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles\n\n\t// rotations are applied to the axes in the order specified by 'order'\n\t// rotation by angle 'a' is applied first, then by angle 'b', then by angle 'c'\n\t// angles are in radians\n\n\tconst cos = Math.cos;\n\tconst sin = Math.sin;\n\n\tconst c2 = cos( b / 2 );\n\tconst s2 = sin( b / 2 );\n\n\tconst c13 = cos( ( a + c ) / 2 );\n\tconst s13 = sin( ( a + c ) / 2 );\n\n\tconst c1_3 = cos( ( a - c ) / 2 );\n\tconst s1_3 = sin( ( a - c ) / 2 );\n\n\tconst c3_1 = cos( ( c - a ) / 2 );\n\tconst s3_1 = sin( ( c - a ) / 2 );\n\n\tswitch ( order ) {\n\n\t\tcase 'XYX':\n\t\t\tq.set( c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13 );\n\t\t\tbreak;\n\n\t\tcase 'YZY':\n\t\t\tq.set( s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13 );\n\t\t\tbreak;\n\n\t\tcase 'ZXZ':\n\t\t\tq.set( s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13 );\n\t\t\tbreak;\n\n\t\tcase 'XZX':\n\t\t\tq.set( c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13 );\n\t\t\tbreak;\n\n\t\tcase 'YXY':\n\t\t\tq.set( s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13 );\n\t\t\tbreak;\n\n\t\tcase 'ZYZ':\n\t\t\tq.set( s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13 );\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tconsole.warn( 'THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: ' + order );\n\n\t}\n\n}\n\nfunction denormalize( value, array ) {\n\n\tswitch ( array.constructor ) {\n\n\t\tcase Float32Array:\n\n\t\t\treturn value;\n\n\t\tcase Uint32Array:\n\n\t\t\treturn value / 4294967295.0;\n\n\t\tcase Uint16Array:\n\n\t\t\treturn value / 65535.0;\n\n\t\tcase Uint8Array:\n\n\t\t\treturn value / 255.0;\n\n\t\tcase Int32Array:\n\n\t\t\treturn Math.max( value / 2147483647.0, - 1.0 );\n\n\t\tcase Int16Array:\n\n\t\t\treturn Math.max( value / 32767.0, - 1.0 );\n\n\t\tcase Int8Array:\n\n\t\t\treturn Math.max( value / 127.0, - 1.0 );\n\n\t\tdefault:\n\n\t\t\tthrow new Error( 'Invalid component type.' );\n\n\t}\n\n}\n\nfunction normalize( value, array ) {\n\n\tswitch ( array.constructor ) {\n\n\t\tcase Float32Array:\n\n\t\t\treturn value;\n\n\t\tcase Uint32Array:\n\n\t\t\treturn Math.round( value * 4294967295.0 );\n\n\t\tcase Uint16Array:\n\n\t\t\treturn Math.round( value * 65535.0 );\n\n\t\tcase Uint8Array:\n\n\t\t\treturn Math.round( value * 255.0 );\n\n\t\tcase Int32Array:\n\n\t\t\treturn Math.round( value * 2147483647.0 );\n\n\t\tcase Int16Array:\n\n\t\t\treturn Math.round( value * 32767.0 );\n\n\t\tcase Int8Array:\n\n\t\t\treturn Math.round( value * 127.0 );\n\n\t\tdefault:\n\n\t\t\tthrow new Error( 'Invalid component type.' );\n\n\t}\n\n}\n\nconst MathUtils = {\n\tDEG2RAD: DEG2RAD,\n\tRAD2DEG: RAD2DEG,\n\tgenerateUUID: generateUUID,\n\tclamp: clamp,\n\teuclideanModulo: euclideanModulo,\n\tmapLinear: mapLinear,\n\tinverseLerp: inverseLerp,\n\tlerp: lerp,\n\tdamp: damp,\n\tpingpong: pingpong,\n\tsmoothstep: smoothstep,\n\tsmootherstep: smootherstep,\n\trandInt: randInt,\n\trandFloat: randFloat,\n\trandFloatSpread: randFloatSpread,\n\tseededRandom: seededRandom,\n\tdegToRad: degToRad,\n\tradToDeg: radToDeg,\n\tisPowerOfTwo: isPowerOfTwo,\n\tceilPowerOfTwo: ceilPowerOfTwo,\n\tfloorPowerOfTwo: floorPowerOfTwo,\n\tsetQuaternionFromProperEuler: setQuaternionFromProperEuler,\n\tnormalize: normalize,\n\tdenormalize: denormalize\n};\n\nexport {\n\tDEG2RAD,\n\tRAD2DEG,\n\tgenerateUUID,\n\tclamp,\n\teuclideanModulo,\n\tmapLinear,\n\tinverseLerp,\n\tlerp,\n\tdamp,\n\tpingpong,\n\tsmoothstep,\n\tsmootherstep,\n\trandInt,\n\trandFloat,\n\trandFloatSpread,\n\tseededRandom,\n\tdegToRad,\n\tradToDeg,\n\tisPowerOfTwo,\n\tceilPowerOfTwo,\n\tfloorPowerOfTwo,\n\tsetQuaternionFromProperEuler,\n\tnormalize,\n\tdenormalize,\n\tMathUtils\n};\n","import * as MathUtils from './MathUtils.js';\n\nclass Vector2 {\n\n\tconstructor( x = 0, y = 0 ) {\n\n\t\tVector2.prototype.isVector2 = true;\n\n\t\tthis.x = x;\n\t\tthis.y = y;\n\n\t}\n\n\tget width() {\n\n\t\treturn this.x;\n\n\t}\n\n\tset width( value ) {\n\n\t\tthis.x = value;\n\n\t}\n\n\tget height() {\n\n\t\treturn this.y;\n\n\t}\n\n\tset height( value ) {\n\n\t\tthis.y = value;\n\n\t}\n\n\tset( x, y ) {\n\n\t\tthis.x = x;\n\t\tthis.y = y;\n\n\t\treturn this;\n\n\t}\n\n\tsetScalar( scalar ) {\n\n\t\tthis.x = scalar;\n\t\tthis.y = scalar;\n\n\t\treturn this;\n\n\t}\n\n\tsetX( x ) {\n\n\t\tthis.x = x;\n\n\t\treturn this;\n\n\t}\n\n\tsetY( y ) {\n\n\t\tthis.y = y;\n\n\t\treturn this;\n\n\t}\n\n\tsetComponent( index, value ) {\n\n\t\tswitch ( index ) {\n\n\t\t\tcase 0: this.x = value; break;\n\t\t\tcase 1: this.y = value; break;\n\t\t\tdefault: throw new Error( 'index is out of range: ' + index );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tgetComponent( index ) {\n\n\t\tswitch ( index ) {\n\n\t\t\tcase 0: return this.x;\n\t\t\tcase 1: return this.y;\n\t\t\tdefault: throw new Error( 'index is out of range: ' + index );\n\n\t\t}\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor( this.x, this.y );\n\n\t}\n\n\tcopy( v ) {\n\n\t\tthis.x = v.x;\n\t\tthis.y = v.y;\n\n\t\treturn this;\n\n\t}\n\n\tadd( v ) {\n\n\t\tthis.x += v.x;\n\t\tthis.y += v.y;\n\n\t\treturn this;\n\n\t}\n\n\taddScalar( s ) {\n\n\t\tthis.x += s;\n\t\tthis.y += s;\n\n\t\treturn this;\n\n\t}\n\n\taddVectors( a, b ) {\n\n\t\tthis.x = a.x + b.x;\n\t\tthis.y = a.y + b.y;\n\n\t\treturn this;\n\n\t}\n\n\taddScaledVector( v, s ) {\n\n\t\tthis.x += v.x * s;\n\t\tthis.y += v.y * s;\n\n\t\treturn this;\n\n\t}\n\n\tsub( v ) {\n\n\t\tthis.x -= v.x;\n\t\tthis.y -= v.y;\n\n\t\treturn this;\n\n\t}\n\n\tsubScalar( s ) {\n\n\t\tthis.x -= s;\n\t\tthis.y -= s;\n\n\t\treturn this;\n\n\t}\n\n\tsubVectors( a, b ) {\n\n\t\tthis.x = a.x - b.x;\n\t\tthis.y = a.y - b.y;\n\n\t\treturn this;\n\n\t}\n\n\tmultiply( v ) {\n\n\t\tthis.x *= v.x;\n\t\tthis.y *= v.y;\n\n\t\treturn this;\n\n\t}\n\n\tmultiplyScalar( scalar ) {\n\n\t\tthis.x *= scalar;\n\t\tthis.y *= scalar;\n\n\t\treturn this;\n\n\t}\n\n\tdivide( v ) {\n\n\t\tthis.x /= v.x;\n\t\tthis.y /= v.y;\n\n\t\treturn this;\n\n\t}\n\n\tdivideScalar( scalar ) {\n\n\t\treturn this.multiplyScalar( 1 / scalar );\n\n\t}\n\n\tapplyMatrix3( m ) {\n\n\t\tconst x = this.x, y = this.y;\n\t\tconst e = m.elements;\n\n\t\tthis.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ];\n\t\tthis.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ];\n\n\t\treturn this;\n\n\t}\n\n\tmin( v ) {\n\n\t\tthis.x = Math.min( this.x, v.x );\n\t\tthis.y = Math.min( this.y, v.y );\n\n\t\treturn this;\n\n\t}\n\n\tmax( v ) {\n\n\t\tthis.x = Math.max( this.x, v.x );\n\t\tthis.y = Math.max( this.y, v.y );\n\n\t\treturn this;\n\n\t}\n\n\tclamp( min, max ) {\n\n\t\t// assumes min < max, componentwise\n\n\t\tthis.x = Math.max( min.x, Math.min( max.x, this.x ) );\n\t\tthis.y = Math.max( min.y, Math.min( max.y, this.y ) );\n\n\t\treturn this;\n\n\t}\n\n\tclampScalar( minVal, maxVal ) {\n\n\t\tthis.x = Math.max( minVal, Math.min( maxVal, this.x ) );\n\t\tthis.y = Math.max( minVal, Math.min( maxVal, this.y ) );\n\n\t\treturn this;\n\n\t}\n\n\tclampLength( min, max ) {\n\n\t\tconst length = this.length();\n\n\t\treturn this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );\n\n\t}\n\n\tfloor() {\n\n\t\tthis.x = Math.floor( this.x );\n\t\tthis.y = Math.floor( this.y );\n\n\t\treturn this;\n\n\t}\n\n\tceil() {\n\n\t\tthis.x = Math.ceil( this.x );\n\t\tthis.y = Math.ceil( this.y );\n\n\t\treturn this;\n\n\t}\n\n\tround() {\n\n\t\tthis.x = Math.round( this.x );\n\t\tthis.y = Math.round( this.y );\n\n\t\treturn this;\n\n\t}\n\n\troundToZero() {\n\n\t\tthis.x = Math.trunc( this.x );\n\t\tthis.y = Math.trunc( this.y );\n\n\t\treturn this;\n\n\t}\n\n\tnegate() {\n\n\t\tthis.x = - this.x;\n\t\tthis.y = - this.y;\n\n\t\treturn this;\n\n\t}\n\n\tdot( v ) {\n\n\t\treturn this.x * v.x + this.y * v.y;\n\n\t}\n\n\tcross( v ) {\n\n\t\treturn this.x * v.y - this.y * v.x;\n\n\t}\n\n\tlengthSq() {\n\n\t\treturn this.x * this.x + this.y * this.y;\n\n\t}\n\n\tlength() {\n\n\t\treturn Math.sqrt( this.x * this.x + this.y * this.y );\n\n\t}\n\n\tmanhattanLength() {\n\n\t\treturn Math.abs( this.x ) + Math.abs( this.y );\n\n\t}\n\n\tnormalize() {\n\n\t\treturn this.divideScalar( this.length() || 1 );\n\n\t}\n\n\tangle() {\n\n\t\t// computes the angle in radians with respect to the positive x-axis\n\n\t\tconst angle = Math.atan2( - this.y, - this.x ) + Math.PI;\n\n\t\treturn angle;\n\n\t}\n\n\tangleTo( v ) {\n\n\t\tconst denominator = Math.sqrt( this.lengthSq() * v.lengthSq() );\n\n\t\tif ( denominator === 0 ) return Math.PI / 2;\n\n\t\tconst theta = this.dot( v ) / denominator;\n\n\t\t// clamp, to handle numerical problems\n\n\t\treturn Math.acos( MathUtils.clamp( theta, - 1, 1 ) );\n\n\t}\n\n\tdistanceTo( v ) {\n\n\t\treturn Math.sqrt( this.distanceToSquared( v ) );\n\n\t}\n\n\tdistanceToSquared( v ) {\n\n\t\tconst dx = this.x - v.x, dy = this.y - v.y;\n\t\treturn dx * dx + dy * dy;\n\n\t}\n\n\tmanhattanDistanceTo( v ) {\n\n\t\treturn Math.abs( this.x - v.x ) + Math.abs( this.y - v.y );\n\n\t}\n\n\tsetLength( length ) {\n\n\t\treturn this.normalize().multiplyScalar( length );\n\n\t}\n\n\tlerp( v, alpha ) {\n\n\t\tthis.x += ( v.x - this.x ) * alpha;\n\t\tthis.y += ( v.y - this.y ) * alpha;\n\n\t\treturn this;\n\n\t}\n\n\tlerpVectors( v1, v2, alpha ) {\n\n\t\tthis.x = v1.x + ( v2.x - v1.x ) * alpha;\n\t\tthis.y = v1.y + ( v2.y - v1.y ) * alpha;\n\n\t\treturn this;\n\n\t}\n\n\tequals( v ) {\n\n\t\treturn ( ( v.x === this.x ) && ( v.y === this.y ) );\n\n\t}\n\n\tfromArray( array, offset = 0 ) {\n\n\t\tthis.x = array[ offset ];\n\t\tthis.y = array[ offset + 1 ];\n\n\t\treturn this;\n\n\t}\n\n\ttoArray( array = [], offset = 0 ) {\n\n\t\tarray[ offset ] = this.x;\n\t\tarray[ offset + 1 ] = this.y;\n\n\t\treturn array;\n\n\t}\n\n\tfromBufferAttribute( attribute, index ) {\n\n\t\tthis.x = attribute.getX( index );\n\t\tthis.y = attribute.getY( index );\n\n\t\treturn this;\n\n\t}\n\n\trotateAround( center, angle ) {\n\n\t\tconst c = Math.cos( angle ), s = Math.sin( angle );\n\n\t\tconst x = this.x - center.x;\n\t\tconst y = this.y - center.y;\n\n\t\tthis.x = x * c - y * s + center.x;\n\t\tthis.y = x * s + y * c + center.y;\n\n\t\treturn this;\n\n\t}\n\n\trandom() {\n\n\t\tthis.x = Math.random();\n\t\tthis.y = Math.random();\n\n\t\treturn this;\n\n\t}\n\n\t*[ Symbol.iterator ]() {\n\n\t\tyield this.x;\n\t\tyield this.y;\n\n\t}\n\n}\n\nexport { Vector2 };\n","class Matrix3 {\n\n\tconstructor( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {\n\n\t\tMatrix3.prototype.isMatrix3 = true;\n\n\t\tthis.elements = [\n\n\t\t\t1, 0, 0,\n\t\t\t0, 1, 0,\n\t\t\t0, 0, 1\n\n\t\t];\n\n\t\tif ( n11 !== undefined ) {\n\n\t\t\tthis.set( n11, n12, n13, n21, n22, n23, n31, n32, n33 );\n\n\t\t}\n\n\t}\n\n\tset( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {\n\n\t\tconst te = this.elements;\n\n\t\tte[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31;\n\t\tte[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32;\n\t\tte[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33;\n\n\t\treturn this;\n\n\t}\n\n\tidentity() {\n\n\t\tthis.set(\n\n\t\t\t1, 0, 0,\n\t\t\t0, 1, 0,\n\t\t\t0, 0, 1\n\n\t\t);\n\n\t\treturn this;\n\n\t}\n\n\tcopy( m ) {\n\n\t\tconst te = this.elements;\n\t\tconst me = m.elements;\n\n\t\tte[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ];\n\t\tte[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ];\n\t\tte[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ];\n\n\t\treturn this;\n\n\t}\n\n\textractBasis( xAxis, yAxis, zAxis ) {\n\n\t\txAxis.setFromMatrix3Column( this, 0 );\n\t\tyAxis.setFromMatrix3Column( this, 1 );\n\t\tzAxis.setFromMatrix3Column( this, 2 );\n\n\t\treturn this;\n\n\t}\n\n\tsetFromMatrix4( m ) {\n\n\t\tconst me = m.elements;\n\n\t\tthis.set(\n\n\t\t\tme[ 0 ], me[ 4 ], me[ 8 ],\n\t\t\tme[ 1 ], me[ 5 ], me[ 9 ],\n\t\t\tme[ 2 ], me[ 6 ], me[ 10 ]\n\n\t\t);\n\n\t\treturn this;\n\n\t}\n\n\tmultiply( m ) {\n\n\t\treturn this.multiplyMatrices( this, m );\n\n\t}\n\n\tpremultiply( m ) {\n\n\t\treturn this.multiplyMatrices( m, this );\n\n\t}\n\n\tmultiplyMatrices( a, b ) {\n\n\t\tconst ae = a.elements;\n\t\tconst be = b.elements;\n\t\tconst te = this.elements;\n\n\t\tconst a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ];\n\t\tconst a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ];\n\t\tconst a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ];\n\n\t\tconst b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ];\n\t\tconst b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ];\n\t\tconst b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ];\n\n\t\tte[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31;\n\t\tte[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32;\n\t\tte[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33;\n\n\t\tte[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31;\n\t\tte[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32;\n\t\tte[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33;\n\n\t\tte[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31;\n\t\tte[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32;\n\t\tte[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33;\n\n\t\treturn this;\n\n\t}\n\n\tmultiplyScalar( s ) {\n\n\t\tconst te = this.elements;\n\n\t\tte[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s;\n\t\tte[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s;\n\t\tte[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s;\n\n\t\treturn this;\n\n\t}\n\n\tdeterminant() {\n\n\t\tconst te = this.elements;\n\n\t\tconst a = te[ 0 ], b = te[ 1 ], c = te[ 2 ],\n\t\t\td = te[ 3 ], e = te[ 4 ], f = te[ 5 ],\n\t\t\tg = te[ 6 ], h = te[ 7 ], i = te[ 8 ];\n\n\t\treturn a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g;\n\n\t}\n\n\tinvert() {\n\n\t\tconst te = this.elements,\n\n\t\t\tn11 = te[ 0 ], n21 = te[ 1 ], n31 = te[ 2 ],\n\t\t\tn12 = te[ 3 ], n22 = te[ 4 ], n32 = te[ 5 ],\n\t\t\tn13 = te[ 6 ], n23 = te[ 7 ], n33 = te[ 8 ],\n\n\t\t\tt11 = n33 * n22 - n32 * n23,\n\t\t\tt12 = n32 * n13 - n33 * n12,\n\t\t\tt13 = n23 * n12 - n22 * n13,\n\n\t\t\tdet = n11 * t11 + n21 * t12 + n31 * t13;\n\n\t\tif ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0 );\n\n\t\tconst detInv = 1 / det;\n\n\t\tte[ 0 ] = t11 * detInv;\n\t\tte[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv;\n\t\tte[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv;\n\n\t\tte[ 3 ] = t12 * detInv;\n\t\tte[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv;\n\t\tte[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv;\n\n\t\tte[ 6 ] = t13 * detInv;\n\t\tte[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv;\n\t\tte[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv;\n\n\t\treturn this;\n\n\t}\n\n\ttranspose() {\n\n\t\tlet tmp;\n\t\tconst m = this.elements;\n\n\t\ttmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp;\n\t\ttmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp;\n\t\ttmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp;\n\n\t\treturn this;\n\n\t}\n\n\tgetNormalMatrix( matrix4 ) {\n\n\t\treturn this.setFromMatrix4( matrix4 ).invert().transpose();\n\n\t}\n\n\ttransposeIntoArray( r ) {\n\n\t\tconst m = this.elements;\n\n\t\tr[ 0 ] = m[ 0 ];\n\t\tr[ 1 ] = m[ 3 ];\n\t\tr[ 2 ] = m[ 6 ];\n\t\tr[ 3 ] = m[ 1 ];\n\t\tr[ 4 ] = m[ 4 ];\n\t\tr[ 5 ] = m[ 7 ];\n\t\tr[ 6 ] = m[ 2 ];\n\t\tr[ 7 ] = m[ 5 ];\n\t\tr[ 8 ] = m[ 8 ];\n\n\t\treturn this;\n\n\t}\n\n\tsetUvTransform( tx, ty, sx, sy, rotation, cx, cy ) {\n\n\t\tconst c = Math.cos( rotation );\n\t\tconst s = Math.sin( rotation );\n\n\t\tthis.set(\n\t\t\tsx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx,\n\t\t\t- sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty,\n\t\t\t0, 0, 1\n\t\t);\n\n\t\treturn this;\n\n\t}\n\n\t//\n\n\tscale( sx, sy ) {\n\n\t\tthis.premultiply( _m3.makeScale( sx, sy ) );\n\n\t\treturn this;\n\n\t}\n\n\trotate( theta ) {\n\n\t\tthis.premultiply( _m3.makeRotation( - theta ) );\n\n\t\treturn this;\n\n\t}\n\n\ttranslate( tx, ty ) {\n\n\t\tthis.premultiply( _m3.makeTranslation( tx, ty ) );\n\n\t\treturn this;\n\n\t}\n\n\t// for 2D Transforms\n\n\tmakeTranslation( x, y ) {\n\n\t\tif ( x.isVector2 ) {\n\n\t\t\tthis.set(\n\n\t\t\t\t1, 0, x.x,\n\t\t\t\t0, 1, x.y,\n\t\t\t\t0, 0, 1\n\n\t\t\t);\n\n\t\t} else {\n\n\t\t\tthis.set(\n\n\t\t\t\t1, 0, x,\n\t\t\t\t0, 1, y,\n\t\t\t\t0, 0, 1\n\n\t\t\t);\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tmakeRotation( theta ) {\n\n\t\t// counterclockwise\n\n\t\tconst c = Math.cos( theta );\n\t\tconst s = Math.sin( theta );\n\n\t\tthis.set(\n\n\t\t\tc, - s, 0,\n\t\t\ts, c, 0,\n\t\t\t0, 0, 1\n\n\t\t);\n\n\t\treturn this;\n\n\t}\n\n\tmakeScale( x, y ) {\n\n\t\tthis.set(\n\n\t\t\tx, 0, 0,\n\t\t\t0, y, 0,\n\t\t\t0, 0, 1\n\n\t\t);\n\n\t\treturn this;\n\n\t}\n\n\t//\n\n\tequals( matrix ) {\n\n\t\tconst te = this.elements;\n\t\tconst me = matrix.elements;\n\n\t\tfor ( let i = 0; i < 9; i ++ ) {\n\n\t\t\tif ( te[ i ] !== me[ i ] ) return false;\n\n\t\t}\n\n\t\treturn true;\n\n\t}\n\n\tfromArray( array, offset = 0 ) {\n\n\t\tfor ( let i = 0; i < 9; i ++ ) {\n\n\t\t\tthis.elements[ i ] = array[ i + offset ];\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\ttoArray( array = [], offset = 0 ) {\n\n\t\tconst te = this.elements;\n\n\t\tarray[ offset ] = te[ 0 ];\n\t\tarray[ offset + 1 ] = te[ 1 ];\n\t\tarray[ offset + 2 ] = te[ 2 ];\n\n\t\tarray[ offset + 3 ] = te[ 3 ];\n\t\tarray[ offset + 4 ] = te[ 4 ];\n\t\tarray[ offset + 5 ] = te[ 5 ];\n\n\t\tarray[ offset + 6 ] = te[ 6 ];\n\t\tarray[ offset + 7 ] = te[ 7 ];\n\t\tarray[ offset + 8 ] = te[ 8 ];\n\n\t\treturn array;\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().fromArray( this.elements );\n\n\t}\n\n}\n\nconst _m3 = /*@__PURE__*/ new Matrix3();\n\nexport { Matrix3 };\n","function arrayMin( array ) {\n\n\tif ( array.length === 0 ) return Infinity;\n\n\tlet min = array[ 0 ];\n\n\tfor ( let i = 1, l = array.length; i < l; ++ i ) {\n\n\t\tif ( array[ i ] < min ) min = array[ i ];\n\n\t}\n\n\treturn min;\n\n}\n\nfunction arrayMax( array ) {\n\n\tif ( array.length === 0 ) return - Infinity;\n\n\tlet max = array[ 0 ];\n\n\tfor ( let i = 1, l = array.length; i < l; ++ i ) {\n\n\t\tif ( array[ i ] > max ) max = array[ i ];\n\n\t}\n\n\treturn max;\n\n}\n\nfunction arrayNeedsUint32( array ) {\n\n\t// assumes larger values usually on last\n\n\tfor ( let i = array.length - 1; i >= 0; -- i ) {\n\n\t\tif ( array[ i ] >= 65535 ) return true; // account for PRIMITIVE_RESTART_FIXED_INDEX, #24565\n\n\t}\n\n\treturn false;\n\n}\n\nconst TYPED_ARRAYS = {\n\tInt8Array: Int8Array,\n\tUint8Array: Uint8Array,\n\tUint8ClampedArray: Uint8ClampedArray,\n\tInt16Array: Int16Array,\n\tUint16Array: Uint16Array,\n\tInt32Array: Int32Array,\n\tUint32Array: Uint32Array,\n\tFloat32Array: Float32Array,\n\tFloat64Array: Float64Array\n};\n\nfunction getTypedArray( type, buffer ) {\n\n\treturn new TYPED_ARRAYS[ type ]( buffer );\n\n}\n\nfunction createElementNS( name ) {\n\n\treturn document.createElementNS( 'http://www.w3.org/1999/xhtml', name );\n\n}\n\nfunction createCanvasElement() {\n\n\tconst canvas = createElementNS( 'canvas' );\n\tcanvas.style.display = 'block';\n\treturn canvas;\n\n}\n\nconst _cache = {};\n\nfunction warnOnce( message ) {\n\n\tif ( message in _cache ) return;\n\n\t_cache[ message ] = true;\n\n\tconsole.warn( message );\n\n}\n\nexport { arrayMin, arrayMax, arrayNeedsUint32, getTypedArray, createElementNS, createCanvasElement, warnOnce };\n","import { SRGBColorSpace, LinearSRGBColorSpace, DisplayP3ColorSpace, LinearDisplayP3ColorSpace, Rec709Primaries, P3Primaries, SRGBTransfer, LinearTransfer, NoColorSpace, } from '../constants.js';\nimport { Matrix3 } from './Matrix3.js';\n\n/**\n * Matrices converting P3 <-> Rec. 709 primaries, without gamut mapping\n * or clipping. Based on W3C specifications for sRGB and Display P3,\n * and ICC specifications for the D50 connection space. Values in/out\n * are _linear_ sRGB and _linear_ Display P3.\n *\n * Note that both sRGB and Display P3 use the sRGB transfer functions.\n *\n * Reference:\n * - http://www.russellcottrell.com/photo/matrixCalculator.htm\n */\n\nconst LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 = /*@__PURE__*/ new Matrix3().set(\n\t0.8224621, 0.177538, 0.0,\n\t0.0331941, 0.9668058, 0.0,\n\t0.0170827, 0.0723974, 0.9105199,\n);\n\nconst LINEAR_DISPLAY_P3_TO_LINEAR_SRGB = /*@__PURE__*/ new Matrix3().set(\n\t1.2249401, - 0.2249404, 0.0,\n\t- 0.0420569, 1.0420571, 0.0,\n\t- 0.0196376, - 0.0786361, 1.0982735\n);\n\n/**\n * Defines supported color spaces by transfer function and primaries,\n * and provides conversions to/from the Linear-sRGB reference space.\n */\nconst COLOR_SPACES = {\n\t[ LinearSRGBColorSpace ]: {\n\t\ttransfer: LinearTransfer,\n\t\tprimaries: Rec709Primaries,\n\t\ttoReference: ( color ) => color,\n\t\tfromReference: ( color ) => color,\n\t},\n\t[ SRGBColorSpace ]: {\n\t\ttransfer: SRGBTransfer,\n\t\tprimaries: Rec709Primaries,\n\t\ttoReference: ( color ) => color.convertSRGBToLinear(),\n\t\tfromReference: ( color ) => color.convertLinearToSRGB(),\n\t},\n\t[ LinearDisplayP3ColorSpace ]: {\n\t\ttransfer: LinearTransfer,\n\t\tprimaries: P3Primaries,\n\t\ttoReference: ( color ) => color.applyMatrix3( LINEAR_DISPLAY_P3_TO_LINEAR_SRGB ),\n\t\tfromReference: ( color ) => color.applyMatrix3( LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 ),\n\t},\n\t[ DisplayP3ColorSpace ]: {\n\t\ttransfer: SRGBTransfer,\n\t\tprimaries: P3Primaries,\n\t\ttoReference: ( color ) => color.convertSRGBToLinear().applyMatrix3( LINEAR_DISPLAY_P3_TO_LINEAR_SRGB ),\n\t\tfromReference: ( color ) => color.applyMatrix3( LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 ).convertLinearToSRGB(),\n\t},\n};\n\nconst SUPPORTED_WORKING_COLOR_SPACES = new Set( [ LinearSRGBColorSpace, LinearDisplayP3ColorSpace ] );\n\nexport const ColorManagement = {\n\n\tenabled: true,\n\n\t_workingColorSpace: LinearSRGBColorSpace,\n\n\tget legacyMode() {\n\n\t\tconsole.warn( 'THREE.ColorManagement: .legacyMode=false renamed to .enabled=true in r150.' );\n\n\t\treturn ! this.enabled;\n\n\t},\n\n\tset legacyMode( legacyMode ) {\n\n\t\tconsole.warn( 'THREE.ColorManagement: .legacyMode=false renamed to .enabled=true in r150.' );\n\n\t\tthis.enabled = ! legacyMode;\n\n\t},\n\n\tget workingColorSpace() {\n\n\t\treturn this._workingColorSpace;\n\n\t},\n\n\tset workingColorSpace( colorSpace ) {\n\n\t\tif ( ! SUPPORTED_WORKING_COLOR_SPACES.has( colorSpace ) ) {\n\n\t\t\tthrow new Error( `Unsupported working color space, \"${ colorSpace }\".` );\n\n\t\t}\n\n\t\tthis._workingColorSpace = colorSpace;\n\n\t},\n\n\tconvert: function ( color, sourceColorSpace, targetColorSpace ) {\n\n\t\tif ( this.enabled === false || sourceColorSpace === targetColorSpace || ! sourceColorSpace || ! targetColorSpace ) {\n\n\t\t\treturn color;\n\n\t\t}\n\n\t\tconst sourceToReference = COLOR_SPACES[ sourceColorSpace ].toReference;\n\t\tconst targetFromReference = COLOR_SPACES[ targetColorSpace ].fromReference;\n\n\t\treturn targetFromReference( sourceToReference( color ) );\n\n\t},\n\n\tfromWorkingColorSpace: function ( color, targetColorSpace ) {\n\n\t\treturn this.convert( color, this._workingColorSpace, targetColorSpace );\n\n\t},\n\n\ttoWorkingColorSpace: function ( color, sourceColorSpace ) {\n\n\t\treturn this.convert( color, sourceColorSpace, this._workingColorSpace );\n\n\t},\n\n\tgetPrimaries: function ( colorSpace ) {\n\n\t\treturn COLOR_SPACES[ colorSpace ].primaries;\n\n\t},\n\n\tgetTransfer: function ( colorSpace ) {\n\n\t\tif ( colorSpace === NoColorSpace ) return LinearTransfer;\n\n\t\treturn COLOR_SPACES[ colorSpace ].transfer;\n\n\t},\n\n};\n\n\nexport function SRGBToLinear( c ) {\n\n\treturn ( c < 0.04045 ) ? c * 0.0773993808 : Math.pow( c * 0.9478672986 + 0.0521327014, 2.4 );\n\n}\n\nexport function LinearToSRGB( c ) {\n\n\treturn ( c < 0.0031308 ) ? c * 12.92 : 1.055 * ( Math.pow( c, 0.41666 ) ) - 0.055;\n\n}\n","import { createElementNS } from '../utils.js';\nimport { SRGBToLinear } from '../math/ColorManagement.js';\n\nlet _canvas;\n\nclass ImageUtils {\n\n\tstatic getDataURL( image ) {\n\n\t\tif ( /^data:/i.test( image.src ) ) {\n\n\t\t\treturn image.src;\n\n\t\t}\n\n\t\tif ( typeof HTMLCanvasElement === 'undefined' ) {\n\n\t\t\treturn image.src;\n\n\t\t}\n\n\t\tlet canvas;\n\n\t\tif ( image instanceof HTMLCanvasElement ) {\n\n\t\t\tcanvas = image;\n\n\t\t} else {\n\n\t\t\tif ( _canvas === undefined ) _canvas = createElementNS( 'canvas' );\n\n\t\t\t_canvas.width = image.width;\n\t\t\t_canvas.height = image.height;\n\n\t\t\tconst context = _canvas.getContext( '2d' );\n\n\t\t\tif ( image instanceof ImageData ) {\n\n\t\t\t\tcontext.putImageData( image, 0, 0 );\n\n\t\t\t} else {\n\n\t\t\t\tcontext.drawImage( image, 0, 0, image.width, image.height );\n\n\t\t\t}\n\n\t\t\tcanvas = _canvas;\n\n\t\t}\n\n\t\tif ( canvas.width > 2048 || canvas.height > 2048 ) {\n\n\t\t\tconsole.warn( 'THREE.ImageUtils.getDataURL: Image converted to jpg for performance reasons', image );\n\n\t\t\treturn canvas.toDataURL( 'image/jpeg', 0.6 );\n\n\t\t} else {\n\n\t\t\treturn canvas.toDataURL( 'image/png' );\n\n\t\t}\n\n\t}\n\n\tstatic sRGBToLinear( image ) {\n\n\t\tif ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||\n\t\t\t( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||\n\t\t\t( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {\n\n\t\t\tconst canvas = createElementNS( 'canvas' );\n\n\t\t\tcanvas.width = image.width;\n\t\t\tcanvas.height = image.height;\n\n\t\t\tconst context = canvas.getContext( '2d' );\n\t\t\tcontext.drawImage( image, 0, 0, image.width, image.height );\n\n\t\t\tconst imageData = context.getImageData( 0, 0, image.width, image.height );\n\t\t\tconst data = imageData.data;\n\n\t\t\tfor ( let i = 0; i < data.length; i ++ ) {\n\n\t\t\t\tdata[ i ] = SRGBToLinear( data[ i ] / 255 ) * 255;\n\n\t\t\t}\n\n\t\t\tcontext.putImageData( imageData, 0, 0 );\n\n\t\t\treturn canvas;\n\n\t\t} else if ( image.data ) {\n\n\t\t\tconst data = image.data.slice( 0 );\n\n\t\t\tfor ( let i = 0; i < data.length; i ++ ) {\n\n\t\t\t\tif ( data instanceof Uint8Array || data instanceof Uint8ClampedArray ) {\n\n\t\t\t\t\tdata[ i ] = Math.floor( SRGBToLinear( data[ i ] / 255 ) * 255 );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// assuming float\n\n\t\t\t\t\tdata[ i ] = SRGBToLinear( data[ i ] );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tdata: data,\n\t\t\t\twidth: image.width,\n\t\t\t\theight: image.height\n\t\t\t};\n\n\t\t} else {\n\n\t\t\tconsole.warn( 'THREE.ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied.' );\n\t\t\treturn image;\n\n\t\t}\n\n\t}\n\n}\n\nexport { ImageUtils };\n","import { ImageUtils } from '../extras/ImageUtils.js';\nimport * as MathUtils from '../math/MathUtils.js';\n\nlet _sourceId = 0;\n\nclass Source {\n\n\tconstructor( data = null ) {\n\n\t\tthis.isSource = true;\n\n\t\tObject.defineProperty( this, 'id', { value: _sourceId ++ } );\n\n\t\tthis.uuid = MathUtils.generateUUID();\n\n\t\tthis.data = data;\n\n\t\tthis.version = 0;\n\n\t}\n\n\tset needsUpdate( value ) {\n\n\t\tif ( value === true ) this.version ++;\n\n\t}\n\n\ttoJSON( meta ) {\n\n\t\tconst isRootObject = ( meta === undefined || typeof meta === 'string' );\n\n\t\tif ( ! isRootObject && meta.images[ this.uuid ] !== undefined ) {\n\n\t\t\treturn meta.images[ this.uuid ];\n\n\t\t}\n\n\t\tconst output = {\n\t\t\tuuid: this.uuid,\n\t\t\turl: ''\n\t\t};\n\n\t\tconst data = this.data;\n\n\t\tif ( data !== null ) {\n\n\t\t\tlet url;\n\n\t\t\tif ( Array.isArray( data ) ) {\n\n\t\t\t\t// cube texture\n\n\t\t\t\turl = [];\n\n\t\t\t\tfor ( let i = 0, l = data.length; i < l; i ++ ) {\n\n\t\t\t\t\tif ( data[ i ].isDataTexture ) {\n\n\t\t\t\t\t\turl.push( serializeImage( data[ i ].image ) );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\turl.push( serializeImage( data[ i ] ) );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\t// texture\n\n\t\t\t\turl = serializeImage( data );\n\n\t\t\t}\n\n\t\t\toutput.url = url;\n\n\t\t}\n\n\t\tif ( ! isRootObject ) {\n\n\t\t\tmeta.images[ this.uuid ] = output;\n\n\t\t}\n\n\t\treturn output;\n\n\t}\n\n}\n\nfunction serializeImage( image ) {\n\n\tif ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||\n\t\t( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||\n\t\t( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {\n\n\t\t// default images\n\n\t\treturn ImageUtils.getDataURL( image );\n\n\t} else {\n\n\t\tif ( image.data ) {\n\n\t\t\t// images of DataTexture\n\n\t\t\treturn {\n\t\t\t\tdata: Array.from( image.data ),\n\t\t\t\twidth: image.width,\n\t\t\t\theight: image.height,\n\t\t\t\ttype: image.data.constructor.name\n\t\t\t};\n\n\t\t} else {\n\n\t\t\tconsole.warn( 'THREE.Texture: Unable to serialize Texture.' );\n\t\t\treturn {};\n\n\t\t}\n\n\t}\n\n}\n\nexport { Source };\n","import { EventDispatcher } from '../core/EventDispatcher.js';\nimport {\n\tMirroredRepeatWrapping,\n\tClampToEdgeWrapping,\n\tRepeatWrapping,\n\tUnsignedByteType,\n\tRGBAFormat,\n\tLinearMipmapLinearFilter,\n\tLinearFilter,\n\tUVMapping,\n\tsRGBEncoding,\n\tSRGBColorSpace,\n\tNoColorSpace,\n\tLinearEncoding\n} from '../constants.js';\nimport * as MathUtils from '../math/MathUtils.js';\nimport { Vector2 } from '../math/Vector2.js';\nimport { Matrix3 } from '../math/Matrix3.js';\nimport { Source } from './Source.js';\nimport { warnOnce } from '../utils.js';\n\nlet _textureId = 0;\n\nclass Texture extends EventDispatcher {\n\n\tconstructor( image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = Texture.DEFAULT_ANISOTROPY, colorSpace = NoColorSpace ) {\n\n\t\tsuper();\n\n\t\tthis.isTexture = true;\n\n\t\tObject.defineProperty( this, 'id', { value: _textureId ++ } );\n\n\t\tthis.uuid = MathUtils.generateUUID();\n\n\t\tthis.name = '';\n\n\t\tthis.source = new Source( image );\n\t\tthis.mipmaps = [];\n\n\t\tthis.mapping = mapping;\n\t\tthis.channel = 0;\n\n\t\tthis.wrapS = wrapS;\n\t\tthis.wrapT = wrapT;\n\n\t\tthis.magFilter = magFilter;\n\t\tthis.minFilter = minFilter;\n\n\t\tthis.anisotropy = anisotropy;\n\n\t\tthis.format = format;\n\t\tthis.internalFormat = null;\n\t\tthis.type = type;\n\n\t\tthis.offset = new Vector2( 0, 0 );\n\t\tthis.repeat = new Vector2( 1, 1 );\n\t\tthis.center = new Vector2( 0, 0 );\n\t\tthis.rotation = 0;\n\n\t\tthis.matrixAutoUpdate = true;\n\t\tthis.matrix = new Matrix3();\n\n\t\tthis.generateMipmaps = true;\n\t\tthis.premultiplyAlpha = false;\n\t\tthis.flipY = true;\n\t\tthis.unpackAlignment = 4;\t// valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml)\n\n\t\tif ( typeof colorSpace === 'string' ) {\n\n\t\t\tthis.colorSpace = colorSpace;\n\n\t\t} else { // @deprecated, r152\n\n\t\t\twarnOnce( 'THREE.Texture: Property .encoding has been replaced by .colorSpace.' );\n\t\t\tthis.colorSpace = colorSpace === sRGBEncoding ? SRGBColorSpace : NoColorSpace;\n\n\t\t}\n\n\n\t\tthis.userData = {};\n\n\t\tthis.version = 0;\n\t\tthis.onUpdate = null;\n\n\t\tthis.isRenderTargetTexture = false; // indicates whether a texture belongs to a render target or not\n\t\tthis.needsPMREMUpdate = false; // indicates whether this texture should be processed by PMREMGenerator or not (only relevant for render target textures)\n\n\t}\n\n\tget image() {\n\n\t\treturn this.source.data;\n\n\t}\n\n\tset image( value = null ) {\n\n\t\tthis.source.data = value;\n\n\t}\n\n\tupdateMatrix() {\n\n\t\tthis.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y );\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tthis.name = source.name;\n\n\t\tthis.source = source.source;\n\t\tthis.mipmaps = source.mipmaps.slice( 0 );\n\n\t\tthis.mapping = source.mapping;\n\t\tthis.channel = source.channel;\n\n\t\tthis.wrapS = source.wrapS;\n\t\tthis.wrapT = source.wrapT;\n\n\t\tthis.magFilter = source.magFilter;\n\t\tthis.minFilter = source.minFilter;\n\n\t\tthis.anisotropy = source.anisotropy;\n\n\t\tthis.format = source.format;\n\t\tthis.internalFormat = source.internalFormat;\n\t\tthis.type = source.type;\n\n\t\tthis.offset.copy( source.offset );\n\t\tthis.repeat.copy( source.repeat );\n\t\tthis.center.copy( source.center );\n\t\tthis.rotation = source.rotation;\n\n\t\tthis.matrixAutoUpdate = source.matrixAutoUpdate;\n\t\tthis.matrix.copy( source.matrix );\n\n\t\tthis.generateMipmaps = source.generateMipmaps;\n\t\tthis.premultiplyAlpha = source.premultiplyAlpha;\n\t\tthis.flipY = source.flipY;\n\t\tthis.unpackAlignment = source.unpackAlignment;\n\t\tthis.colorSpace = source.colorSpace;\n\n\t\tthis.userData = JSON.parse( JSON.stringify( source.userData ) );\n\n\t\tthis.needsUpdate = true;\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON( meta ) {\n\n\t\tconst isRootObject = ( meta === undefined || typeof meta === 'string' );\n\n\t\tif ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) {\n\n\t\t\treturn meta.textures[ this.uuid ];\n\n\t\t}\n\n\t\tconst output = {\n\n\t\t\tmetadata: {\n\t\t\t\tversion: 4.6,\n\t\t\t\ttype: 'Texture',\n\t\t\t\tgenerator: 'Texture.toJSON'\n\t\t\t},\n\n\t\t\tuuid: this.uuid,\n\t\t\tname: this.name,\n\n\t\t\timage: this.source.toJSON( meta ).uuid,\n\n\t\t\tmapping: this.mapping,\n\t\t\tchannel: this.channel,\n\n\t\t\trepeat: [ this.repeat.x, this.repeat.y ],\n\t\t\toffset: [ this.offset.x, this.offset.y ],\n\t\t\tcenter: [ this.center.x, this.center.y ],\n\t\t\trotation: this.rotation,\n\n\t\t\twrap: [ this.wrapS, this.wrapT ],\n\n\t\t\tformat: this.format,\n\t\t\tinternalFormat: this.internalFormat,\n\t\t\ttype: this.type,\n\t\t\tcolorSpace: this.colorSpace,\n\n\t\t\tminFilter: this.minFilter,\n\t\t\tmagFilter: this.magFilter,\n\t\t\tanisotropy: this.anisotropy,\n\n\t\t\tflipY: this.flipY,\n\n\t\t\tgenerateMipmaps: this.generateMipmaps,\n\t\t\tpremultiplyAlpha: this.premultiplyAlpha,\n\t\t\tunpackAlignment: this.unpackAlignment\n\n\t\t};\n\n\t\tif ( Object.keys( this.userData ).length > 0 ) output.userData = this.userData;\n\n\t\tif ( ! isRootObject ) {\n\n\t\t\tmeta.textures[ this.uuid ] = output;\n\n\t\t}\n\n\t\treturn output;\n\n\t}\n\n\tdispose() {\n\n\t\tthis.dispatchEvent( { type: 'dispose' } );\n\n\t}\n\n\ttransformUv( uv ) {\n\n\t\tif ( this.mapping !== UVMapping ) return uv;\n\n\t\tuv.applyMatrix3( this.matrix );\n\n\t\tif ( uv.x < 0 || uv.x > 1 ) {\n\n\t\t\tswitch ( this.wrapS ) {\n\n\t\t\t\tcase RepeatWrapping:\n\n\t\t\t\t\tuv.x = uv.x - Math.floor( uv.x );\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase ClampToEdgeWrapping:\n\n\t\t\t\t\tuv.x = uv.x < 0 ? 0 : 1;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase MirroredRepeatWrapping:\n\n\t\t\t\t\tif ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) {\n\n\t\t\t\t\t\tuv.x = Math.ceil( uv.x ) - uv.x;\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tuv.x = uv.x - Math.floor( uv.x );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( uv.y < 0 || uv.y > 1 ) {\n\n\t\t\tswitch ( this.wrapT ) {\n\n\t\t\t\tcase RepeatWrapping:\n\n\t\t\t\t\tuv.y = uv.y - Math.floor( uv.y );\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase ClampToEdgeWrapping:\n\n\t\t\t\t\tuv.y = uv.y < 0 ? 0 : 1;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase MirroredRepeatWrapping:\n\n\t\t\t\t\tif ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) {\n\n\t\t\t\t\t\tuv.y = Math.ceil( uv.y ) - uv.y;\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tuv.y = uv.y - Math.floor( uv.y );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( this.flipY ) {\n\n\t\t\tuv.y = 1 - uv.y;\n\n\t\t}\n\n\t\treturn uv;\n\n\t}\n\n\tset needsUpdate( value ) {\n\n\t\tif ( value === true ) {\n\n\t\t\tthis.version ++;\n\t\t\tthis.source.needsUpdate = true;\n\n\t\t}\n\n\t}\n\n\tget encoding() { // @deprecated, r152\n\n\t\twarnOnce( 'THREE.Texture: Property .encoding has been replaced by .colorSpace.' );\n\t\treturn this.colorSpace === SRGBColorSpace ? sRGBEncoding : LinearEncoding;\n\n\t}\n\n\tset encoding( encoding ) { // @deprecated, r152\n\n\t\twarnOnce( 'THREE.Texture: Property .encoding has been replaced by .colorSpace.' );\n\t\tthis.colorSpace = encoding === sRGBEncoding ? SRGBColorSpace : NoColorSpace;\n\n\t}\n\n}\n\nTexture.DEFAULT_IMAGE = null;\nTexture.DEFAULT_MAPPING = UVMapping;\nTexture.DEFAULT_ANISOTROPY = 1;\n\nexport { Texture };\n","class Vector4 {\n\n\tconstructor( x = 0, y = 0, z = 0, w = 1 ) {\n\n\t\tVector4.prototype.isVector4 = true;\n\n\t\tthis.x = x;\n\t\tthis.y = y;\n\t\tthis.z = z;\n\t\tthis.w = w;\n\n\t}\n\n\tget width() {\n\n\t\treturn this.z;\n\n\t}\n\n\tset width( value ) {\n\n\t\tthis.z = value;\n\n\t}\n\n\tget height() {\n\n\t\treturn this.w;\n\n\t}\n\n\tset height( value ) {\n\n\t\tthis.w = value;\n\n\t}\n\n\tset( x, y, z, w ) {\n\n\t\tthis.x = x;\n\t\tthis.y = y;\n\t\tthis.z = z;\n\t\tthis.w = w;\n\n\t\treturn this;\n\n\t}\n\n\tsetScalar( scalar ) {\n\n\t\tthis.x = scalar;\n\t\tthis.y = scalar;\n\t\tthis.z = scalar;\n\t\tthis.w = scalar;\n\n\t\treturn this;\n\n\t}\n\n\tsetX( x ) {\n\n\t\tthis.x = x;\n\n\t\treturn this;\n\n\t}\n\n\tsetY( y ) {\n\n\t\tthis.y = y;\n\n\t\treturn this;\n\n\t}\n\n\tsetZ( z ) {\n\n\t\tthis.z = z;\n\n\t\treturn this;\n\n\t}\n\n\tsetW( w ) {\n\n\t\tthis.w = w;\n\n\t\treturn this;\n\n\t}\n\n\tsetComponent( index, value ) {\n\n\t\tswitch ( index ) {\n\n\t\t\tcase 0: this.x = value; break;\n\t\t\tcase 1: this.y = value; break;\n\t\t\tcase 2: this.z = value; break;\n\t\t\tcase 3: this.w = value; break;\n\t\t\tdefault: throw new Error( 'index is out of range: ' + index );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tgetComponent( index ) {\n\n\t\tswitch ( index ) {\n\n\t\t\tcase 0: return this.x;\n\t\t\tcase 1: return this.y;\n\t\t\tcase 2: return this.z;\n\t\t\tcase 3: return this.w;\n\t\t\tdefault: throw new Error( 'index is out of range: ' + index );\n\n\t\t}\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor( this.x, this.y, this.z, this.w );\n\n\t}\n\n\tcopy( v ) {\n\n\t\tthis.x = v.x;\n\t\tthis.y = v.y;\n\t\tthis.z = v.z;\n\t\tthis.w = ( v.w !== undefined ) ? v.w : 1;\n\n\t\treturn this;\n\n\t}\n\n\tadd( v ) {\n\n\t\tthis.x += v.x;\n\t\tthis.y += v.y;\n\t\tthis.z += v.z;\n\t\tthis.w += v.w;\n\n\t\treturn this;\n\n\t}\n\n\taddScalar( s ) {\n\n\t\tthis.x += s;\n\t\tthis.y += s;\n\t\tthis.z += s;\n\t\tthis.w += s;\n\n\t\treturn this;\n\n\t}\n\n\taddVectors( a, b ) {\n\n\t\tthis.x = a.x + b.x;\n\t\tthis.y = a.y + b.y;\n\t\tthis.z = a.z + b.z;\n\t\tthis.w = a.w + b.w;\n\n\t\treturn this;\n\n\t}\n\n\taddScaledVector( v, s ) {\n\n\t\tthis.x += v.x * s;\n\t\tthis.y += v.y * s;\n\t\tthis.z += v.z * s;\n\t\tthis.w += v.w * s;\n\n\t\treturn this;\n\n\t}\n\n\tsub( v ) {\n\n\t\tthis.x -= v.x;\n\t\tthis.y -= v.y;\n\t\tthis.z -= v.z;\n\t\tthis.w -= v.w;\n\n\t\treturn this;\n\n\t}\n\n\tsubScalar( s ) {\n\n\t\tthis.x -= s;\n\t\tthis.y -= s;\n\t\tthis.z -= s;\n\t\tthis.w -= s;\n\n\t\treturn this;\n\n\t}\n\n\tsubVectors( a, b ) {\n\n\t\tthis.x = a.x - b.x;\n\t\tthis.y = a.y - b.y;\n\t\tthis.z = a.z - b.z;\n\t\tthis.w = a.w - b.w;\n\n\t\treturn this;\n\n\t}\n\n\tmultiply( v ) {\n\n\t\tthis.x *= v.x;\n\t\tthis.y *= v.y;\n\t\tthis.z *= v.z;\n\t\tthis.w *= v.w;\n\n\t\treturn this;\n\n\t}\n\n\tmultiplyScalar( scalar ) {\n\n\t\tthis.x *= scalar;\n\t\tthis.y *= scalar;\n\t\tthis.z *= scalar;\n\t\tthis.w *= scalar;\n\n\t\treturn this;\n\n\t}\n\n\tapplyMatrix4( m ) {\n\n\t\tconst x = this.x, y = this.y, z = this.z, w = this.w;\n\t\tconst e = m.elements;\n\n\t\tthis.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w;\n\t\tthis.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w;\n\t\tthis.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w;\n\t\tthis.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w;\n\n\t\treturn this;\n\n\t}\n\n\tdivideScalar( scalar ) {\n\n\t\treturn this.multiplyScalar( 1 / scalar );\n\n\t}\n\n\tsetAxisAngleFromQuaternion( q ) {\n\n\t\t// http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm\n\n\t\t// q is assumed to be normalized\n\n\t\tthis.w = 2 * Math.acos( q.w );\n\n\t\tconst s = Math.sqrt( 1 - q.w * q.w );\n\n\t\tif ( s < 0.0001 ) {\n\n\t\t\tthis.x = 1;\n\t\t\tthis.y = 0;\n\t\t\tthis.z = 0;\n\n\t\t} else {\n\n\t\t\tthis.x = q.x / s;\n\t\t\tthis.y = q.y / s;\n\t\t\tthis.z = q.z / s;\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tsetAxisAngleFromRotationMatrix( m ) {\n\n\t\t// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm\n\n\t\t// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)\n\n\t\tlet angle, x, y, z; // variables for result\n\t\tconst epsilon = 0.01,\t\t// margin to allow for rounding errors\n\t\t\tepsilon2 = 0.1,\t\t// margin to distinguish between 0 and 180 degrees\n\n\t\t\tte = m.elements,\n\n\t\t\tm11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],\n\t\t\tm21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],\n\t\t\tm31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];\n\n\t\tif ( ( Math.abs( m12 - m21 ) < epsilon ) &&\n\t\t ( Math.abs( m13 - m31 ) < epsilon ) &&\n\t\t ( Math.abs( m23 - m32 ) < epsilon ) ) {\n\n\t\t\t// singularity found\n\t\t\t// first check for identity matrix which must have +1 for all terms\n\t\t\t// in leading diagonal and zero in other terms\n\n\t\t\tif ( ( Math.abs( m12 + m21 ) < epsilon2 ) &&\n\t\t\t ( Math.abs( m13 + m31 ) < epsilon2 ) &&\n\t\t\t ( Math.abs( m23 + m32 ) < epsilon2 ) &&\n\t\t\t ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) {\n\n\t\t\t\t// this singularity is identity matrix so angle = 0\n\n\t\t\t\tthis.set( 1, 0, 0, 0 );\n\n\t\t\t\treturn this; // zero angle, arbitrary axis\n\n\t\t\t}\n\n\t\t\t// otherwise this singularity is angle = 180\n\n\t\t\tangle = Math.PI;\n\n\t\t\tconst xx = ( m11 + 1 ) / 2;\n\t\t\tconst yy = ( m22 + 1 ) / 2;\n\t\t\tconst zz = ( m33 + 1 ) / 2;\n\t\t\tconst xy = ( m12 + m21 ) / 4;\n\t\t\tconst xz = ( m13 + m31 ) / 4;\n\t\t\tconst yz = ( m23 + m32 ) / 4;\n\n\t\t\tif ( ( xx > yy ) && ( xx > zz ) ) {\n\n\t\t\t\t// m11 is the largest diagonal term\n\n\t\t\t\tif ( xx < epsilon ) {\n\n\t\t\t\t\tx = 0;\n\t\t\t\t\ty = 0.707106781;\n\t\t\t\t\tz = 0.707106781;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tx = Math.sqrt( xx );\n\t\t\t\t\ty = xy / x;\n\t\t\t\t\tz = xz / x;\n\n\t\t\t\t}\n\n\t\t\t} else if ( yy > zz ) {\n\n\t\t\t\t// m22 is the largest diagonal term\n\n\t\t\t\tif ( yy < epsilon ) {\n\n\t\t\t\t\tx = 0.707106781;\n\t\t\t\t\ty = 0;\n\t\t\t\t\tz = 0.707106781;\n\n\t\t\t\t} else {\n\n\t\t\t\t\ty = Math.sqrt( yy );\n\t\t\t\t\tx = xy / y;\n\t\t\t\t\tz = yz / y;\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\t// m33 is the largest diagonal term so base result on this\n\n\t\t\t\tif ( zz < epsilon ) {\n\n\t\t\t\t\tx = 0.707106781;\n\t\t\t\t\ty = 0.707106781;\n\t\t\t\t\tz = 0;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tz = Math.sqrt( zz );\n\t\t\t\t\tx = xz / z;\n\t\t\t\t\ty = yz / z;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tthis.set( x, y, z, angle );\n\n\t\t\treturn this; // return 180 deg rotation\n\n\t\t}\n\n\t\t// as we have reached here there are no singularities so we can handle normally\n\n\t\tlet s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) +\n\t\t\t( m13 - m31 ) * ( m13 - m31 ) +\n\t\t\t( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize\n\n\t\tif ( Math.abs( s ) < 0.001 ) s = 1;\n\n\t\t// prevent divide by zero, should not happen if matrix is orthogonal and should be\n\t\t// caught by singularity test above, but I've left it in just in case\n\n\t\tthis.x = ( m32 - m23 ) / s;\n\t\tthis.y = ( m13 - m31 ) / s;\n\t\tthis.z = ( m21 - m12 ) / s;\n\t\tthis.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 );\n\n\t\treturn this;\n\n\t}\n\n\tmin( v ) {\n\n\t\tthis.x = Math.min( this.x, v.x );\n\t\tthis.y = Math.min( this.y, v.y );\n\t\tthis.z = Math.min( this.z, v.z );\n\t\tthis.w = Math.min( this.w, v.w );\n\n\t\treturn this;\n\n\t}\n\n\tmax( v ) {\n\n\t\tthis.x = Math.max( this.x, v.x );\n\t\tthis.y = Math.max( this.y, v.y );\n\t\tthis.z = Math.max( this.z, v.z );\n\t\tthis.w = Math.max( this.w, v.w );\n\n\t\treturn this;\n\n\t}\n\n\tclamp( min, max ) {\n\n\t\t// assumes min < max, componentwise\n\n\t\tthis.x = Math.max( min.x, Math.min( max.x, this.x ) );\n\t\tthis.y = Math.max( min.y, Math.min( max.y, this.y ) );\n\t\tthis.z = Math.max( min.z, Math.min( max.z, this.z ) );\n\t\tthis.w = Math.max( min.w, Math.min( max.w, this.w ) );\n\n\t\treturn this;\n\n\t}\n\n\tclampScalar( minVal, maxVal ) {\n\n\t\tthis.x = Math.max( minVal, Math.min( maxVal, this.x ) );\n\t\tthis.y = Math.max( minVal, Math.min( maxVal, this.y ) );\n\t\tthis.z = Math.max( minVal, Math.min( maxVal, this.z ) );\n\t\tthis.w = Math.max( minVal, Math.min( maxVal, this.w ) );\n\n\t\treturn this;\n\n\t}\n\n\tclampLength( min, max ) {\n\n\t\tconst length = this.length();\n\n\t\treturn this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );\n\n\t}\n\n\tfloor() {\n\n\t\tthis.x = Math.floor( this.x );\n\t\tthis.y = Math.floor( this.y );\n\t\tthis.z = Math.floor( this.z );\n\t\tthis.w = Math.floor( this.w );\n\n\t\treturn this;\n\n\t}\n\n\tceil() {\n\n\t\tthis.x = Math.ceil( this.x );\n\t\tthis.y = Math.ceil( this.y );\n\t\tthis.z = Math.ceil( this.z );\n\t\tthis.w = Math.ceil( this.w );\n\n\t\treturn this;\n\n\t}\n\n\tround() {\n\n\t\tthis.x = Math.round( this.x );\n\t\tthis.y = Math.round( this.y );\n\t\tthis.z = Math.round( this.z );\n\t\tthis.w = Math.round( this.w );\n\n\t\treturn this;\n\n\t}\n\n\troundToZero() {\n\n\t\tthis.x = Math.trunc( this.x );\n\t\tthis.y = Math.trunc( this.y );\n\t\tthis.z = Math.trunc( this.z );\n\t\tthis.w = Math.trunc( this.w );\n\n\t\treturn this;\n\n\t}\n\n\tnegate() {\n\n\t\tthis.x = - this.x;\n\t\tthis.y = - this.y;\n\t\tthis.z = - this.z;\n\t\tthis.w = - this.w;\n\n\t\treturn this;\n\n\t}\n\n\tdot( v ) {\n\n\t\treturn this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w;\n\n\t}\n\n\tlengthSq() {\n\n\t\treturn this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;\n\n\t}\n\n\tlength() {\n\n\t\treturn Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w );\n\n\t}\n\n\tmanhattanLength() {\n\n\t\treturn Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w );\n\n\t}\n\n\tnormalize() {\n\n\t\treturn this.divideScalar( this.length() || 1 );\n\n\t}\n\n\tsetLength( length ) {\n\n\t\treturn this.normalize().multiplyScalar( length );\n\n\t}\n\n\tlerp( v, alpha ) {\n\n\t\tthis.x += ( v.x - this.x ) * alpha;\n\t\tthis.y += ( v.y - this.y ) * alpha;\n\t\tthis.z += ( v.z - this.z ) * alpha;\n\t\tthis.w += ( v.w - this.w ) * alpha;\n\n\t\treturn this;\n\n\t}\n\n\tlerpVectors( v1, v2, alpha ) {\n\n\t\tthis.x = v1.x + ( v2.x - v1.x ) * alpha;\n\t\tthis.y = v1.y + ( v2.y - v1.y ) * alpha;\n\t\tthis.z = v1.z + ( v2.z - v1.z ) * alpha;\n\t\tthis.w = v1.w + ( v2.w - v1.w ) * alpha;\n\n\t\treturn this;\n\n\t}\n\n\tequals( v ) {\n\n\t\treturn ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) );\n\n\t}\n\n\tfromArray( array, offset = 0 ) {\n\n\t\tthis.x = array[ offset ];\n\t\tthis.y = array[ offset + 1 ];\n\t\tthis.z = array[ offset + 2 ];\n\t\tthis.w = array[ offset + 3 ];\n\n\t\treturn this;\n\n\t}\n\n\ttoArray( array = [], offset = 0 ) {\n\n\t\tarray[ offset ] = this.x;\n\t\tarray[ offset + 1 ] = this.y;\n\t\tarray[ offset + 2 ] = this.z;\n\t\tarray[ offset + 3 ] = this.w;\n\n\t\treturn array;\n\n\t}\n\n\tfromBufferAttribute( attribute, index ) {\n\n\t\tthis.x = attribute.getX( index );\n\t\tthis.y = attribute.getY( index );\n\t\tthis.z = attribute.getZ( index );\n\t\tthis.w = attribute.getW( index );\n\n\t\treturn this;\n\n\t}\n\n\trandom() {\n\n\t\tthis.x = Math.random();\n\t\tthis.y = Math.random();\n\t\tthis.z = Math.random();\n\t\tthis.w = Math.random();\n\n\t\treturn this;\n\n\t}\n\n\t*[ Symbol.iterator ]() {\n\n\t\tyield this.x;\n\t\tyield this.y;\n\t\tyield this.z;\n\t\tyield this.w;\n\n\t}\n\n}\n\nexport { Vector4 };\n","import { EventDispatcher } from './EventDispatcher.js';\nimport { Texture } from '../textures/Texture.js';\nimport { LinearFilter, NoColorSpace, SRGBColorSpace, sRGBEncoding } from '../constants.js';\nimport { Vector4 } from '../math/Vector4.js';\nimport { Source } from '../textures/Source.js';\nimport { warnOnce } from '../utils.js';\n\n/*\n In options, we can specify:\n * Texture parameters for an auto-generated target texture\n * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers\n*/\nclass RenderTarget extends EventDispatcher {\n\n\tconstructor( width = 1, height = 1, options = {} ) {\n\n\t\tsuper();\n\n\t\tthis.isRenderTarget = true;\n\n\t\tthis.width = width;\n\t\tthis.height = height;\n\t\tthis.depth = 1;\n\n\t\tthis.scissor = new Vector4( 0, 0, width, height );\n\t\tthis.scissorTest = false;\n\n\t\tthis.viewport = new Vector4( 0, 0, width, height );\n\n\t\tconst image = { width: width, height: height, depth: 1 };\n\n\t\tif ( options.encoding !== undefined ) {\n\n\t\t\t// @deprecated, r152\n\t\t\twarnOnce( 'THREE.WebGLRenderTarget: option.encoding has been replaced by option.colorSpace.' );\n\t\t\toptions.colorSpace = options.encoding === sRGBEncoding ? SRGBColorSpace : NoColorSpace;\n\n\t\t}\n\n\t\toptions = Object.assign( {\n\t\t\tgenerateMipmaps: false,\n\t\t\tinternalFormat: null,\n\t\t\tminFilter: LinearFilter,\n\t\t\tdepthBuffer: true,\n\t\t\tstencilBuffer: false,\n\t\t\tdepthTexture: null,\n\t\t\tsamples: 0\n\t\t}, options );\n\n\t\tthis.texture = new Texture( image, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.colorSpace );\n\t\tthis.texture.isRenderTargetTexture = true;\n\n\t\tthis.texture.flipY = false;\n\t\tthis.texture.generateMipmaps = options.generateMipmaps;\n\t\tthis.texture.internalFormat = options.internalFormat;\n\n\t\tthis.depthBuffer = options.depthBuffer;\n\t\tthis.stencilBuffer = options.stencilBuffer;\n\n\t\tthis.depthTexture = options.depthTexture;\n\n\t\tthis.samples = options.samples;\n\n\t}\n\n\tsetSize( width, height, depth = 1 ) {\n\n\t\tif ( this.width !== width || this.height !== height || this.depth !== depth ) {\n\n\t\t\tthis.width = width;\n\t\t\tthis.height = height;\n\t\t\tthis.depth = depth;\n\n\t\t\tthis.texture.image.width = width;\n\t\t\tthis.texture.image.height = height;\n\t\t\tthis.texture.image.depth = depth;\n\n\t\t\tthis.dispose();\n\n\t\t}\n\n\t\tthis.viewport.set( 0, 0, width, height );\n\t\tthis.scissor.set( 0, 0, width, height );\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tthis.width = source.width;\n\t\tthis.height = source.height;\n\t\tthis.depth = source.depth;\n\n\t\tthis.scissor.copy( source.scissor );\n\t\tthis.scissorTest = source.scissorTest;\n\n\t\tthis.viewport.copy( source.viewport );\n\n\t\tthis.texture = source.texture.clone();\n\t\tthis.texture.isRenderTargetTexture = true;\n\n\t\t// ensure image object is not shared, see #20328\n\n\t\tconst image = Object.assign( {}, source.texture.image );\n\t\tthis.texture.source = new Source( image );\n\n\t\tthis.depthBuffer = source.depthBuffer;\n\t\tthis.stencilBuffer = source.stencilBuffer;\n\n\t\tif ( source.depthTexture !== null ) this.depthTexture = source.depthTexture.clone();\n\n\t\tthis.samples = source.samples;\n\n\t\treturn this;\n\n\t}\n\n\tdispose() {\n\n\t\tthis.dispatchEvent( { type: 'dispose' } );\n\n\t}\n\n}\n\nexport { RenderTarget };\n","import { RenderTarget } from '../core/RenderTarget.js';\n\nclass WebGLRenderTarget extends RenderTarget {\n\n\tconstructor( width = 1, height = 1, options = {} ) {\n\n\t\tsuper( width, height, options );\n\n\t\tthis.isWebGLRenderTarget = true;\n\n\t}\n\n}\n\nexport { WebGLRenderTarget };\n","import { Texture } from './Texture.js';\nimport { ClampToEdgeWrapping, NearestFilter } from '../constants.js';\n\nclass DataArrayTexture extends Texture {\n\n\tconstructor( data = null, width = 1, height = 1, depth = 1 ) {\n\n\t\tsuper( null );\n\n\t\tthis.isDataArrayTexture = true;\n\n\t\tthis.image = { data, width, height, depth };\n\n\t\tthis.magFilter = NearestFilter;\n\t\tthis.minFilter = NearestFilter;\n\n\t\tthis.wrapR = ClampToEdgeWrapping;\n\n\t\tthis.generateMipmaps = false;\n\t\tthis.flipY = false;\n\t\tthis.unpackAlignment = 1;\n\n\t}\n\n}\n\nexport { DataArrayTexture };\n","import { Texture } from './Texture.js';\nimport { ClampToEdgeWrapping, NearestFilter } from '../constants.js';\n\nclass Data3DTexture extends Texture {\n\n\tconstructor( data = null, width = 1, height = 1, depth = 1 ) {\n\n\t\t// We're going to add .setXXX() methods for setting properties later.\n\t\t// Users can still set in DataTexture3D directly.\n\t\t//\n\t\t//\tconst texture = new THREE.DataTexture3D( data, width, height, depth );\n\t\t// \ttexture.anisotropy = 16;\n\t\t//\n\t\t// See #14839\n\n\t\tsuper( null );\n\n\t\tthis.isData3DTexture = true;\n\n\t\tthis.image = { data, width, height, depth };\n\n\t\tthis.magFilter = NearestFilter;\n\t\tthis.minFilter = NearestFilter;\n\n\t\tthis.wrapR = ClampToEdgeWrapping;\n\n\t\tthis.generateMipmaps = false;\n\t\tthis.flipY = false;\n\t\tthis.unpackAlignment = 1;\n\n\t}\n\n}\n\nexport { Data3DTexture };\n","import * as MathUtils from './MathUtils.js';\n\nclass Quaternion {\n\n\tconstructor( x = 0, y = 0, z = 0, w = 1 ) {\n\n\t\tthis.isQuaternion = true;\n\n\t\tthis._x = x;\n\t\tthis._y = y;\n\t\tthis._z = z;\n\t\tthis._w = w;\n\n\t}\n\n\tstatic slerpFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) {\n\n\t\t// fuzz-free, array-based Quaternion SLERP operation\n\n\t\tlet x0 = src0[ srcOffset0 + 0 ],\n\t\t\ty0 = src0[ srcOffset0 + 1 ],\n\t\t\tz0 = src0[ srcOffset0 + 2 ],\n\t\t\tw0 = src0[ srcOffset0 + 3 ];\n\n\t\tconst x1 = src1[ srcOffset1 + 0 ],\n\t\t\ty1 = src1[ srcOffset1 + 1 ],\n\t\t\tz1 = src1[ srcOffset1 + 2 ],\n\t\t\tw1 = src1[ srcOffset1 + 3 ];\n\n\t\tif ( t === 0 ) {\n\n\t\t\tdst[ dstOffset + 0 ] = x0;\n\t\t\tdst[ dstOffset + 1 ] = y0;\n\t\t\tdst[ dstOffset + 2 ] = z0;\n\t\t\tdst[ dstOffset + 3 ] = w0;\n\t\t\treturn;\n\n\t\t}\n\n\t\tif ( t === 1 ) {\n\n\t\t\tdst[ dstOffset + 0 ] = x1;\n\t\t\tdst[ dstOffset + 1 ] = y1;\n\t\t\tdst[ dstOffset + 2 ] = z1;\n\t\t\tdst[ dstOffset + 3 ] = w1;\n\t\t\treturn;\n\n\t\t}\n\n\t\tif ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) {\n\n\t\t\tlet s = 1 - t;\n\t\t\tconst cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1,\n\t\t\t\tdir = ( cos >= 0 ? 1 : - 1 ),\n\t\t\t\tsqrSin = 1 - cos * cos;\n\n\t\t\t// Skip the Slerp for tiny steps to avoid numeric problems:\n\t\t\tif ( sqrSin > Number.EPSILON ) {\n\n\t\t\t\tconst sin = Math.sqrt( sqrSin ),\n\t\t\t\t\tlen = Math.atan2( sin, cos * dir );\n\n\t\t\t\ts = Math.sin( s * len ) / sin;\n\t\t\t\tt = Math.sin( t * len ) / sin;\n\n\t\t\t}\n\n\t\t\tconst tDir = t * dir;\n\n\t\t\tx0 = x0 * s + x1 * tDir;\n\t\t\ty0 = y0 * s + y1 * tDir;\n\t\t\tz0 = z0 * s + z1 * tDir;\n\t\t\tw0 = w0 * s + w1 * tDir;\n\n\t\t\t// Normalize in case we just did a lerp:\n\t\t\tif ( s === 1 - t ) {\n\n\t\t\t\tconst f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 );\n\n\t\t\t\tx0 *= f;\n\t\t\t\ty0 *= f;\n\t\t\t\tz0 *= f;\n\t\t\t\tw0 *= f;\n\n\t\t\t}\n\n\t\t}\n\n\t\tdst[ dstOffset ] = x0;\n\t\tdst[ dstOffset + 1 ] = y0;\n\t\tdst[ dstOffset + 2 ] = z0;\n\t\tdst[ dstOffset + 3 ] = w0;\n\n\t}\n\n\tstatic multiplyQuaternionsFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1 ) {\n\n\t\tconst x0 = src0[ srcOffset0 ];\n\t\tconst y0 = src0[ srcOffset0 + 1 ];\n\t\tconst z0 = src0[ srcOffset0 + 2 ];\n\t\tconst w0 = src0[ srcOffset0 + 3 ];\n\n\t\tconst x1 = src1[ srcOffset1 ];\n\t\tconst y1 = src1[ srcOffset1 + 1 ];\n\t\tconst z1 = src1[ srcOffset1 + 2 ];\n\t\tconst w1 = src1[ srcOffset1 + 3 ];\n\n\t\tdst[ dstOffset ] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1;\n\t\tdst[ dstOffset + 1 ] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1;\n\t\tdst[ dstOffset + 2 ] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1;\n\t\tdst[ dstOffset + 3 ] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1;\n\n\t\treturn dst;\n\n\t}\n\n\tget x() {\n\n\t\treturn this._x;\n\n\t}\n\n\tset x( value ) {\n\n\t\tthis._x = value;\n\t\tthis._onChangeCallback();\n\n\t}\n\n\tget y() {\n\n\t\treturn this._y;\n\n\t}\n\n\tset y( value ) {\n\n\t\tthis._y = value;\n\t\tthis._onChangeCallback();\n\n\t}\n\n\tget z() {\n\n\t\treturn this._z;\n\n\t}\n\n\tset z( value ) {\n\n\t\tthis._z = value;\n\t\tthis._onChangeCallback();\n\n\t}\n\n\tget w() {\n\n\t\treturn this._w;\n\n\t}\n\n\tset w( value ) {\n\n\t\tthis._w = value;\n\t\tthis._onChangeCallback();\n\n\t}\n\n\tset( x, y, z, w ) {\n\n\t\tthis._x = x;\n\t\tthis._y = y;\n\t\tthis._z = z;\n\t\tthis._w = w;\n\n\t\tthis._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor( this._x, this._y, this._z, this._w );\n\n\t}\n\n\tcopy( quaternion ) {\n\n\t\tthis._x = quaternion.x;\n\t\tthis._y = quaternion.y;\n\t\tthis._z = quaternion.z;\n\t\tthis._w = quaternion.w;\n\n\t\tthis._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\tsetFromEuler( euler, update ) {\n\n\t\tconst x = euler._x, y = euler._y, z = euler._z, order = euler._order;\n\n\t\t// http://www.mathworks.com/matlabcentral/fileexchange/\n\t\t// \t20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/\n\t\t//\tcontent/SpinCalc.m\n\n\t\tconst cos = Math.cos;\n\t\tconst sin = Math.sin;\n\n\t\tconst c1 = cos( x / 2 );\n\t\tconst c2 = cos( y / 2 );\n\t\tconst c3 = cos( z / 2 );\n\n\t\tconst s1 = sin( x / 2 );\n\t\tconst s2 = sin( y / 2 );\n\t\tconst s3 = sin( z / 2 );\n\n\t\tswitch ( order ) {\n\n\t\t\tcase 'XYZ':\n\t\t\t\tthis._x = s1 * c2 * c3 + c1 * s2 * s3;\n\t\t\t\tthis._y = c1 * s2 * c3 - s1 * c2 * s3;\n\t\t\t\tthis._z = c1 * c2 * s3 + s1 * s2 * c3;\n\t\t\t\tthis._w = c1 * c2 * c3 - s1 * s2 * s3;\n\t\t\t\tbreak;\n\n\t\t\tcase 'YXZ':\n\t\t\t\tthis._x = s1 * c2 * c3 + c1 * s2 * s3;\n\t\t\t\tthis._y = c1 * s2 * c3 - s1 * c2 * s3;\n\t\t\t\tthis._z = c1 * c2 * s3 - s1 * s2 * c3;\n\t\t\t\tthis._w = c1 * c2 * c3 + s1 * s2 * s3;\n\t\t\t\tbreak;\n\n\t\t\tcase 'ZXY':\n\t\t\t\tthis._x = s1 * c2 * c3 - c1 * s2 * s3;\n\t\t\t\tthis._y = c1 * s2 * c3 + s1 * c2 * s3;\n\t\t\t\tthis._z = c1 * c2 * s3 + s1 * s2 * c3;\n\t\t\t\tthis._w = c1 * c2 * c3 - s1 * s2 * s3;\n\t\t\t\tbreak;\n\n\t\t\tcase 'ZYX':\n\t\t\t\tthis._x = s1 * c2 * c3 - c1 * s2 * s3;\n\t\t\t\tthis._y = c1 * s2 * c3 + s1 * c2 * s3;\n\t\t\t\tthis._z = c1 * c2 * s3 - s1 * s2 * c3;\n\t\t\t\tthis._w = c1 * c2 * c3 + s1 * s2 * s3;\n\t\t\t\tbreak;\n\n\t\t\tcase 'YZX':\n\t\t\t\tthis._x = s1 * c2 * c3 + c1 * s2 * s3;\n\t\t\t\tthis._y = c1 * s2 * c3 + s1 * c2 * s3;\n\t\t\t\tthis._z = c1 * c2 * s3 - s1 * s2 * c3;\n\t\t\t\tthis._w = c1 * c2 * c3 - s1 * s2 * s3;\n\t\t\t\tbreak;\n\n\t\t\tcase 'XZY':\n\t\t\t\tthis._x = s1 * c2 * c3 - c1 * s2 * s3;\n\t\t\t\tthis._y = c1 * s2 * c3 - s1 * c2 * s3;\n\t\t\t\tthis._z = c1 * c2 * s3 + s1 * s2 * c3;\n\t\t\t\tthis._w = c1 * c2 * c3 + s1 * s2 * s3;\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tconsole.warn( 'THREE.Quaternion: .setFromEuler() encountered an unknown order: ' + order );\n\n\t\t}\n\n\t\tif ( update !== false ) this._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\tsetFromAxisAngle( axis, angle ) {\n\n\t\t// http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm\n\n\t\t// assumes axis is normalized\n\n\t\tconst halfAngle = angle / 2, s = Math.sin( halfAngle );\n\n\t\tthis._x = axis.x * s;\n\t\tthis._y = axis.y * s;\n\t\tthis._z = axis.z * s;\n\t\tthis._w = Math.cos( halfAngle );\n\n\t\tthis._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\tsetFromRotationMatrix( m ) {\n\n\t\t// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm\n\n\t\t// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)\n\n\t\tconst te = m.elements,\n\n\t\t\tm11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],\n\t\t\tm21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],\n\t\t\tm31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ],\n\n\t\t\ttrace = m11 + m22 + m33;\n\n\t\tif ( trace > 0 ) {\n\n\t\t\tconst s = 0.5 / Math.sqrt( trace + 1.0 );\n\n\t\t\tthis._w = 0.25 / s;\n\t\t\tthis._x = ( m32 - m23 ) * s;\n\t\t\tthis._y = ( m13 - m31 ) * s;\n\t\t\tthis._z = ( m21 - m12 ) * s;\n\n\t\t} else if ( m11 > m22 && m11 > m33 ) {\n\n\t\t\tconst s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 );\n\n\t\t\tthis._w = ( m32 - m23 ) / s;\n\t\t\tthis._x = 0.25 * s;\n\t\t\tthis._y = ( m12 + m21 ) / s;\n\t\t\tthis._z = ( m13 + m31 ) / s;\n\n\t\t} else if ( m22 > m33 ) {\n\n\t\t\tconst s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 );\n\n\t\t\tthis._w = ( m13 - m31 ) / s;\n\t\t\tthis._x = ( m12 + m21 ) / s;\n\t\t\tthis._y = 0.25 * s;\n\t\t\tthis._z = ( m23 + m32 ) / s;\n\n\t\t} else {\n\n\t\t\tconst s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 );\n\n\t\t\tthis._w = ( m21 - m12 ) / s;\n\t\t\tthis._x = ( m13 + m31 ) / s;\n\t\t\tthis._y = ( m23 + m32 ) / s;\n\t\t\tthis._z = 0.25 * s;\n\n\t\t}\n\n\t\tthis._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\tsetFromUnitVectors( vFrom, vTo ) {\n\n\t\t// assumes direction vectors vFrom and vTo are normalized\n\n\t\tlet r = vFrom.dot( vTo ) + 1;\n\n\t\tif ( r < Number.EPSILON ) {\n\n\t\t\t// vFrom and vTo point in opposite directions\n\n\t\t\tr = 0;\n\n\t\t\tif ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) {\n\n\t\t\t\tthis._x = - vFrom.y;\n\t\t\t\tthis._y = vFrom.x;\n\t\t\t\tthis._z = 0;\n\t\t\t\tthis._w = r;\n\n\t\t\t} else {\n\n\t\t\t\tthis._x = 0;\n\t\t\t\tthis._y = - vFrom.z;\n\t\t\t\tthis._z = vFrom.y;\n\t\t\t\tthis._w = r;\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\t// crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3\n\n\t\t\tthis._x = vFrom.y * vTo.z - vFrom.z * vTo.y;\n\t\t\tthis._y = vFrom.z * vTo.x - vFrom.x * vTo.z;\n\t\t\tthis._z = vFrom.x * vTo.y - vFrom.y * vTo.x;\n\t\t\tthis._w = r;\n\n\t\t}\n\n\t\treturn this.normalize();\n\n\t}\n\n\tangleTo( q ) {\n\n\t\treturn 2 * Math.acos( Math.abs( MathUtils.clamp( this.dot( q ), - 1, 1 ) ) );\n\n\t}\n\n\trotateTowards( q, step ) {\n\n\t\tconst angle = this.angleTo( q );\n\n\t\tif ( angle === 0 ) return this;\n\n\t\tconst t = Math.min( 1, step / angle );\n\n\t\tthis.slerp( q, t );\n\n\t\treturn this;\n\n\t}\n\n\tidentity() {\n\n\t\treturn this.set( 0, 0, 0, 1 );\n\n\t}\n\n\tinvert() {\n\n\t\t// quaternion is assumed to have unit length\n\n\t\treturn this.conjugate();\n\n\t}\n\n\tconjugate() {\n\n\t\tthis._x *= - 1;\n\t\tthis._y *= - 1;\n\t\tthis._z *= - 1;\n\n\t\tthis._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\tdot( v ) {\n\n\t\treturn this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w;\n\n\t}\n\n\tlengthSq() {\n\n\t\treturn this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w;\n\n\t}\n\n\tlength() {\n\n\t\treturn Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w );\n\n\t}\n\n\tnormalize() {\n\n\t\tlet l = this.length();\n\n\t\tif ( l === 0 ) {\n\n\t\t\tthis._x = 0;\n\t\t\tthis._y = 0;\n\t\t\tthis._z = 0;\n\t\t\tthis._w = 1;\n\n\t\t} else {\n\n\t\t\tl = 1 / l;\n\n\t\t\tthis._x = this._x * l;\n\t\t\tthis._y = this._y * l;\n\t\t\tthis._z = this._z * l;\n\t\t\tthis._w = this._w * l;\n\n\t\t}\n\n\t\tthis._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\tmultiply( q ) {\n\n\t\treturn this.multiplyQuaternions( this, q );\n\n\t}\n\n\tpremultiply( q ) {\n\n\t\treturn this.multiplyQuaternions( q, this );\n\n\t}\n\n\tmultiplyQuaternions( a, b ) {\n\n\t\t// from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm\n\n\t\tconst qax = a._x, qay = a._y, qaz = a._z, qaw = a._w;\n\t\tconst qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w;\n\n\t\tthis._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;\n\t\tthis._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;\n\t\tthis._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;\n\t\tthis._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;\n\n\t\tthis._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\tslerp( qb, t ) {\n\n\t\tif ( t === 0 ) return this;\n\t\tif ( t === 1 ) return this.copy( qb );\n\n\t\tconst x = this._x, y = this._y, z = this._z, w = this._w;\n\n\t\t// http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/\n\n\t\tlet cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z;\n\n\t\tif ( cosHalfTheta < 0 ) {\n\n\t\t\tthis._w = - qb._w;\n\t\t\tthis._x = - qb._x;\n\t\t\tthis._y = - qb._y;\n\t\t\tthis._z = - qb._z;\n\n\t\t\tcosHalfTheta = - cosHalfTheta;\n\n\t\t} else {\n\n\t\t\tthis.copy( qb );\n\n\t\t}\n\n\t\tif ( cosHalfTheta >= 1.0 ) {\n\n\t\t\tthis._w = w;\n\t\t\tthis._x = x;\n\t\t\tthis._y = y;\n\t\t\tthis._z = z;\n\n\t\t\treturn this;\n\n\t\t}\n\n\t\tconst sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta;\n\n\t\tif ( sqrSinHalfTheta <= Number.EPSILON ) {\n\n\t\t\tconst s = 1 - t;\n\t\t\tthis._w = s * w + t * this._w;\n\t\t\tthis._x = s * x + t * this._x;\n\t\t\tthis._y = s * y + t * this._y;\n\t\t\tthis._z = s * z + t * this._z;\n\n\t\t\tthis.normalize();\n\t\t\tthis._onChangeCallback();\n\n\t\t\treturn this;\n\n\t\t}\n\n\t\tconst sinHalfTheta = Math.sqrt( sqrSinHalfTheta );\n\t\tconst halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta );\n\t\tconst ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,\n\t\t\tratioB = Math.sin( t * halfTheta ) / sinHalfTheta;\n\n\t\tthis._w = ( w * ratioA + this._w * ratioB );\n\t\tthis._x = ( x * ratioA + this._x * ratioB );\n\t\tthis._y = ( y * ratioA + this._y * ratioB );\n\t\tthis._z = ( z * ratioA + this._z * ratioB );\n\n\t\tthis._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\tslerpQuaternions( qa, qb, t ) {\n\n\t\treturn this.copy( qa ).slerp( qb, t );\n\n\t}\n\n\trandom() {\n\n\t\t// Derived from http://planning.cs.uiuc.edu/node198.html\n\t\t// Note, this source uses w, x, y, z ordering,\n\t\t// so we swap the order below.\n\n\t\tconst u1 = Math.random();\n\t\tconst sqrt1u1 = Math.sqrt( 1 - u1 );\n\t\tconst sqrtu1 = Math.sqrt( u1 );\n\n\t\tconst u2 = 2 * Math.PI * Math.random();\n\n\t\tconst u3 = 2 * Math.PI * Math.random();\n\n\t\treturn this.set(\n\t\t\tsqrt1u1 * Math.cos( u2 ),\n\t\t\tsqrtu1 * Math.sin( u3 ),\n\t\t\tsqrtu1 * Math.cos( u3 ),\n\t\t\tsqrt1u1 * Math.sin( u2 ),\n\t\t);\n\n\t}\n\n\tequals( quaternion ) {\n\n\t\treturn ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w );\n\n\t}\n\n\tfromArray( array, offset = 0 ) {\n\n\t\tthis._x = array[ offset ];\n\t\tthis._y = array[ offset + 1 ];\n\t\tthis._z = array[ offset + 2 ];\n\t\tthis._w = array[ offset + 3 ];\n\n\t\tthis._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\ttoArray( array = [], offset = 0 ) {\n\n\t\tarray[ offset ] = this._x;\n\t\tarray[ offset + 1 ] = this._y;\n\t\tarray[ offset + 2 ] = this._z;\n\t\tarray[ offset + 3 ] = this._w;\n\n\t\treturn array;\n\n\t}\n\n\tfromBufferAttribute( attribute, index ) {\n\n\t\tthis._x = attribute.getX( index );\n\t\tthis._y = attribute.getY( index );\n\t\tthis._z = attribute.getZ( index );\n\t\tthis._w = attribute.getW( index );\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON() {\n\n\t\treturn this.toArray();\n\n\t}\n\n\t_onChange( callback ) {\n\n\t\tthis._onChangeCallback = callback;\n\n\t\treturn this;\n\n\t}\n\n\t_onChangeCallback() {}\n\n\t*[ Symbol.iterator ]() {\n\n\t\tyield this._x;\n\t\tyield this._y;\n\t\tyield this._z;\n\t\tyield this._w;\n\n\t}\n\n}\n\nexport { Quaternion };\n","import * as MathUtils from './MathUtils.js';\nimport { Quaternion } from './Quaternion.js';\n\nclass Vector3 {\n\n\tconstructor( x = 0, y = 0, z = 0 ) {\n\n\t\tVector3.prototype.isVector3 = true;\n\n\t\tthis.x = x;\n\t\tthis.y = y;\n\t\tthis.z = z;\n\n\t}\n\n\tset( x, y, z ) {\n\n\t\tif ( z === undefined ) z = this.z; // sprite.scale.set(x,y)\n\n\t\tthis.x = x;\n\t\tthis.y = y;\n\t\tthis.z = z;\n\n\t\treturn this;\n\n\t}\n\n\tsetScalar( scalar ) {\n\n\t\tthis.x = scalar;\n\t\tthis.y = scalar;\n\t\tthis.z = scalar;\n\n\t\treturn this;\n\n\t}\n\n\tsetX( x ) {\n\n\t\tthis.x = x;\n\n\t\treturn this;\n\n\t}\n\n\tsetY( y ) {\n\n\t\tthis.y = y;\n\n\t\treturn this;\n\n\t}\n\n\tsetZ( z ) {\n\n\t\tthis.z = z;\n\n\t\treturn this;\n\n\t}\n\n\tsetComponent( index, value ) {\n\n\t\tswitch ( index ) {\n\n\t\t\tcase 0: this.x = value; break;\n\t\t\tcase 1: this.y = value; break;\n\t\t\tcase 2: this.z = value; break;\n\t\t\tdefault: throw new Error( 'index is out of range: ' + index );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tgetComponent( index ) {\n\n\t\tswitch ( index ) {\n\n\t\t\tcase 0: return this.x;\n\t\t\tcase 1: return this.y;\n\t\t\tcase 2: return this.z;\n\t\t\tdefault: throw new Error( 'index is out of range: ' + index );\n\n\t\t}\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor( this.x, this.y, this.z );\n\n\t}\n\n\tcopy( v ) {\n\n\t\tthis.x = v.x;\n\t\tthis.y = v.y;\n\t\tthis.z = v.z;\n\n\t\treturn this;\n\n\t}\n\n\tadd( v ) {\n\n\t\tthis.x += v.x;\n\t\tthis.y += v.y;\n\t\tthis.z += v.z;\n\n\t\treturn this;\n\n\t}\n\n\taddScalar( s ) {\n\n\t\tthis.x += s;\n\t\tthis.y += s;\n\t\tthis.z += s;\n\n\t\treturn this;\n\n\t}\n\n\taddVectors( a, b ) {\n\n\t\tthis.x = a.x + b.x;\n\t\tthis.y = a.y + b.y;\n\t\tthis.z = a.z + b.z;\n\n\t\treturn this;\n\n\t}\n\n\taddScaledVector( v, s ) {\n\n\t\tthis.x += v.x * s;\n\t\tthis.y += v.y * s;\n\t\tthis.z += v.z * s;\n\n\t\treturn this;\n\n\t}\n\n\tsub( v ) {\n\n\t\tthis.x -= v.x;\n\t\tthis.y -= v.y;\n\t\tthis.z -= v.z;\n\n\t\treturn this;\n\n\t}\n\n\tsubScalar( s ) {\n\n\t\tthis.x -= s;\n\t\tthis.y -= s;\n\t\tthis.z -= s;\n\n\t\treturn this;\n\n\t}\n\n\tsubVectors( a, b ) {\n\n\t\tthis.x = a.x - b.x;\n\t\tthis.y = a.y - b.y;\n\t\tthis.z = a.z - b.z;\n\n\t\treturn this;\n\n\t}\n\n\tmultiply( v ) {\n\n\t\tthis.x *= v.x;\n\t\tthis.y *= v.y;\n\t\tthis.z *= v.z;\n\n\t\treturn this;\n\n\t}\n\n\tmultiplyScalar( scalar ) {\n\n\t\tthis.x *= scalar;\n\t\tthis.y *= scalar;\n\t\tthis.z *= scalar;\n\n\t\treturn this;\n\n\t}\n\n\tmultiplyVectors( a, b ) {\n\n\t\tthis.x = a.x * b.x;\n\t\tthis.y = a.y * b.y;\n\t\tthis.z = a.z * b.z;\n\n\t\treturn this;\n\n\t}\n\n\tapplyEuler( euler ) {\n\n\t\treturn this.applyQuaternion( _quaternion.setFromEuler( euler ) );\n\n\t}\n\n\tapplyAxisAngle( axis, angle ) {\n\n\t\treturn this.applyQuaternion( _quaternion.setFromAxisAngle( axis, angle ) );\n\n\t}\n\n\tapplyMatrix3( m ) {\n\n\t\tconst x = this.x, y = this.y, z = this.z;\n\t\tconst e = m.elements;\n\n\t\tthis.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z;\n\t\tthis.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z;\n\t\tthis.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z;\n\n\t\treturn this;\n\n\t}\n\n\tapplyNormalMatrix( m ) {\n\n\t\treturn this.applyMatrix3( m ).normalize();\n\n\t}\n\n\tapplyMatrix4( m ) {\n\n\t\tconst x = this.x, y = this.y, z = this.z;\n\t\tconst e = m.elements;\n\n\t\tconst w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] );\n\n\t\tthis.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w;\n\t\tthis.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w;\n\t\tthis.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w;\n\n\t\treturn this;\n\n\t}\n\n\tapplyQuaternion( q ) {\n\n\t\t// quaternion q is assumed to have unit length\n\n\t\tconst vx = this.x, vy = this.y, vz = this.z;\n\t\tconst qx = q.x, qy = q.y, qz = q.z, qw = q.w;\n\n\t\t// t = 2 * cross( q.xyz, v );\n\t\tconst tx = 2 * ( qy * vz - qz * vy );\n\t\tconst ty = 2 * ( qz * vx - qx * vz );\n\t\tconst tz = 2 * ( qx * vy - qy * vx );\n\n\t\t// v + q.w * t + cross( q.xyz, t );\n\t\tthis.x = vx + qw * tx + qy * tz - qz * ty;\n\t\tthis.y = vy + qw * ty + qz * tx - qx * tz;\n\t\tthis.z = vz + qw * tz + qx * ty - qy * tx;\n\n\t\treturn this;\n\n\t}\n\n\tproject( camera ) {\n\n\t\treturn this.applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix );\n\n\t}\n\n\tunproject( camera ) {\n\n\t\treturn this.applyMatrix4( camera.projectionMatrixInverse ).applyMatrix4( camera.matrixWorld );\n\n\t}\n\n\ttransformDirection( m ) {\n\n\t\t// input: THREE.Matrix4 affine matrix\n\t\t// vector interpreted as a direction\n\n\t\tconst x = this.x, y = this.y, z = this.z;\n\t\tconst e = m.elements;\n\n\t\tthis.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z;\n\t\tthis.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z;\n\t\tthis.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z;\n\n\t\treturn this.normalize();\n\n\t}\n\n\tdivide( v ) {\n\n\t\tthis.x /= v.x;\n\t\tthis.y /= v.y;\n\t\tthis.z /= v.z;\n\n\t\treturn this;\n\n\t}\n\n\tdivideScalar( scalar ) {\n\n\t\treturn this.multiplyScalar( 1 / scalar );\n\n\t}\n\n\tmin( v ) {\n\n\t\tthis.x = Math.min( this.x, v.x );\n\t\tthis.y = Math.min( this.y, v.y );\n\t\tthis.z = Math.min( this.z, v.z );\n\n\t\treturn this;\n\n\t}\n\n\tmax( v ) {\n\n\t\tthis.x = Math.max( this.x, v.x );\n\t\tthis.y = Math.max( this.y, v.y );\n\t\tthis.z = Math.max( this.z, v.z );\n\n\t\treturn this;\n\n\t}\n\n\tclamp( min, max ) {\n\n\t\t// assumes min < max, componentwise\n\n\t\tthis.x = Math.max( min.x, Math.min( max.x, this.x ) );\n\t\tthis.y = Math.max( min.y, Math.min( max.y, this.y ) );\n\t\tthis.z = Math.max( min.z, Math.min( max.z, this.z ) );\n\n\t\treturn this;\n\n\t}\n\n\tclampScalar( minVal, maxVal ) {\n\n\t\tthis.x = Math.max( minVal, Math.min( maxVal, this.x ) );\n\t\tthis.y = Math.max( minVal, Math.min( maxVal, this.y ) );\n\t\tthis.z = Math.max( minVal, Math.min( maxVal, this.z ) );\n\n\t\treturn this;\n\n\t}\n\n\tclampLength( min, max ) {\n\n\t\tconst length = this.length();\n\n\t\treturn this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );\n\n\t}\n\n\tfloor() {\n\n\t\tthis.x = Math.floor( this.x );\n\t\tthis.y = Math.floor( this.y );\n\t\tthis.z = Math.floor( this.z );\n\n\t\treturn this;\n\n\t}\n\n\tceil() {\n\n\t\tthis.x = Math.ceil( this.x );\n\t\tthis.y = Math.ceil( this.y );\n\t\tthis.z = Math.ceil( this.z );\n\n\t\treturn this;\n\n\t}\n\n\tround() {\n\n\t\tthis.x = Math.round( this.x );\n\t\tthis.y = Math.round( this.y );\n\t\tthis.z = Math.round( this.z );\n\n\t\treturn this;\n\n\t}\n\n\troundToZero() {\n\n\t\tthis.x = Math.trunc( this.x );\n\t\tthis.y = Math.trunc( this.y );\n\t\tthis.z = Math.trunc( this.z );\n\n\t\treturn this;\n\n\t}\n\n\tnegate() {\n\n\t\tthis.x = - this.x;\n\t\tthis.y = - this.y;\n\t\tthis.z = - this.z;\n\n\t\treturn this;\n\n\t}\n\n\tdot( v ) {\n\n\t\treturn this.x * v.x + this.y * v.y + this.z * v.z;\n\n\t}\n\n\t// TODO lengthSquared?\n\n\tlengthSq() {\n\n\t\treturn this.x * this.x + this.y * this.y + this.z * this.z;\n\n\t}\n\n\tlength() {\n\n\t\treturn Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z );\n\n\t}\n\n\tmanhattanLength() {\n\n\t\treturn Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z );\n\n\t}\n\n\tnormalize() {\n\n\t\treturn this.divideScalar( this.length() || 1 );\n\n\t}\n\n\tsetLength( length ) {\n\n\t\treturn this.normalize().multiplyScalar( length );\n\n\t}\n\n\tlerp( v, alpha ) {\n\n\t\tthis.x += ( v.x - this.x ) * alpha;\n\t\tthis.y += ( v.y - this.y ) * alpha;\n\t\tthis.z += ( v.z - this.z ) * alpha;\n\n\t\treturn this;\n\n\t}\n\n\tlerpVectors( v1, v2, alpha ) {\n\n\t\tthis.x = v1.x + ( v2.x - v1.x ) * alpha;\n\t\tthis.y = v1.y + ( v2.y - v1.y ) * alpha;\n\t\tthis.z = v1.z + ( v2.z - v1.z ) * alpha;\n\n\t\treturn this;\n\n\t}\n\n\tcross( v ) {\n\n\t\treturn this.crossVectors( this, v );\n\n\t}\n\n\tcrossVectors( a, b ) {\n\n\t\tconst ax = a.x, ay = a.y, az = a.z;\n\t\tconst bx = b.x, by = b.y, bz = b.z;\n\n\t\tthis.x = ay * bz - az * by;\n\t\tthis.y = az * bx - ax * bz;\n\t\tthis.z = ax * by - ay * bx;\n\n\t\treturn this;\n\n\t}\n\n\tprojectOnVector( v ) {\n\n\t\tconst denominator = v.lengthSq();\n\n\t\tif ( denominator === 0 ) return this.set( 0, 0, 0 );\n\n\t\tconst scalar = v.dot( this ) / denominator;\n\n\t\treturn this.copy( v ).multiplyScalar( scalar );\n\n\t}\n\n\tprojectOnPlane( planeNormal ) {\n\n\t\t_vector.copy( this ).projectOnVector( planeNormal );\n\n\t\treturn this.sub( _vector );\n\n\t}\n\n\treflect( normal ) {\n\n\t\t// reflect incident vector off plane orthogonal to normal\n\t\t// normal is assumed to have unit length\n\n\t\treturn this.sub( _vector.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) );\n\n\t}\n\n\tangleTo( v ) {\n\n\t\tconst denominator = Math.sqrt( this.lengthSq() * v.lengthSq() );\n\n\t\tif ( denominator === 0 ) return Math.PI / 2;\n\n\t\tconst theta = this.dot( v ) / denominator;\n\n\t\t// clamp, to handle numerical problems\n\n\t\treturn Math.acos( MathUtils.clamp( theta, - 1, 1 ) );\n\n\t}\n\n\tdistanceTo( v ) {\n\n\t\treturn Math.sqrt( this.distanceToSquared( v ) );\n\n\t}\n\n\tdistanceToSquared( v ) {\n\n\t\tconst dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z;\n\n\t\treturn dx * dx + dy * dy + dz * dz;\n\n\t}\n\n\tmanhattanDistanceTo( v ) {\n\n\t\treturn Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z );\n\n\t}\n\n\tsetFromSpherical( s ) {\n\n\t\treturn this.setFromSphericalCoords( s.radius, s.phi, s.theta );\n\n\t}\n\n\tsetFromSphericalCoords( radius, phi, theta ) {\n\n\t\tconst sinPhiRadius = Math.sin( phi ) * radius;\n\n\t\tthis.x = sinPhiRadius * Math.sin( theta );\n\t\tthis.y = Math.cos( phi ) * radius;\n\t\tthis.z = sinPhiRadius * Math.cos( theta );\n\n\t\treturn this;\n\n\t}\n\n\tsetFromCylindrical( c ) {\n\n\t\treturn this.setFromCylindricalCoords( c.radius, c.theta, c.y );\n\n\t}\n\n\tsetFromCylindricalCoords( radius, theta, y ) {\n\n\t\tthis.x = radius * Math.sin( theta );\n\t\tthis.y = y;\n\t\tthis.z = radius * Math.cos( theta );\n\n\t\treturn this;\n\n\t}\n\n\tsetFromMatrixPosition( m ) {\n\n\t\tconst e = m.elements;\n\n\t\tthis.x = e[ 12 ];\n\t\tthis.y = e[ 13 ];\n\t\tthis.z = e[ 14 ];\n\n\t\treturn this;\n\n\t}\n\n\tsetFromMatrixScale( m ) {\n\n\t\tconst sx = this.setFromMatrixColumn( m, 0 ).length();\n\t\tconst sy = this.setFromMatrixColumn( m, 1 ).length();\n\t\tconst sz = this.setFromMatrixColumn( m, 2 ).length();\n\n\t\tthis.x = sx;\n\t\tthis.y = sy;\n\t\tthis.z = sz;\n\n\t\treturn this;\n\n\t}\n\n\tsetFromMatrixColumn( m, index ) {\n\n\t\treturn this.fromArray( m.elements, index * 4 );\n\n\t}\n\n\tsetFromMatrix3Column( m, index ) {\n\n\t\treturn this.fromArray( m.elements, index * 3 );\n\n\t}\n\n\tsetFromEuler( e ) {\n\n\t\tthis.x = e._x;\n\t\tthis.y = e._y;\n\t\tthis.z = e._z;\n\n\t\treturn this;\n\n\t}\n\n\tsetFromColor( c ) {\n\n\t\tthis.x = c.r;\n\t\tthis.y = c.g;\n\t\tthis.z = c.b;\n\n\t\treturn this;\n\n\t}\n\n\tequals( v ) {\n\n\t\treturn ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );\n\n\t}\n\n\tfromArray( array, offset = 0 ) {\n\n\t\tthis.x = array[ offset ];\n\t\tthis.y = array[ offset + 1 ];\n\t\tthis.z = array[ offset + 2 ];\n\n\t\treturn this;\n\n\t}\n\n\ttoArray( array = [], offset = 0 ) {\n\n\t\tarray[ offset ] = this.x;\n\t\tarray[ offset + 1 ] = this.y;\n\t\tarray[ offset + 2 ] = this.z;\n\n\t\treturn array;\n\n\t}\n\n\tfromBufferAttribute( attribute, index ) {\n\n\t\tthis.x = attribute.getX( index );\n\t\tthis.y = attribute.getY( index );\n\t\tthis.z = attribute.getZ( index );\n\n\t\treturn this;\n\n\t}\n\n\trandom() {\n\n\t\tthis.x = Math.random();\n\t\tthis.y = Math.random();\n\t\tthis.z = Math.random();\n\n\t\treturn this;\n\n\t}\n\n\trandomDirection() {\n\n\t\t// Derived from https://mathworld.wolfram.com/SpherePointPicking.html\n\n\t\tconst u = ( Math.random() - 0.5 ) * 2;\n\t\tconst t = Math.random() * Math.PI * 2;\n\t\tconst f = Math.sqrt( 1 - u ** 2 );\n\n\t\tthis.x = f * Math.cos( t );\n\t\tthis.y = f * Math.sin( t );\n\t\tthis.z = u;\n\n\t\treturn this;\n\n\t}\n\n\t*[ Symbol.iterator ]() {\n\n\t\tyield this.x;\n\t\tyield this.y;\n\t\tyield this.z;\n\n\t}\n\n}\n\nconst _vector = /*@__PURE__*/ new Vector3();\nconst _quaternion = /*@__PURE__*/ new Quaternion();\n\nexport { Vector3 };\n","import { Vector3 } from './Vector3.js';\n\nclass Box3 {\n\n\tconstructor( min = new Vector3( + Infinity, + Infinity, + Infinity ), max = new Vector3( - Infinity, - Infinity, - Infinity ) ) {\n\n\t\tthis.isBox3 = true;\n\n\t\tthis.min = min;\n\t\tthis.max = max;\n\n\t}\n\n\tset( min, max ) {\n\n\t\tthis.min.copy( min );\n\t\tthis.max.copy( max );\n\n\t\treturn this;\n\n\t}\n\n\tsetFromArray( array ) {\n\n\t\tthis.makeEmpty();\n\n\t\tfor ( let i = 0, il = array.length; i < il; i += 3 ) {\n\n\t\t\tthis.expandByPoint( _vector.fromArray( array, i ) );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tsetFromBufferAttribute( attribute ) {\n\n\t\tthis.makeEmpty();\n\n\t\tfor ( let i = 0, il = attribute.count; i < il; i ++ ) {\n\n\t\t\tthis.expandByPoint( _vector.fromBufferAttribute( attribute, i ) );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tsetFromPoints( points ) {\n\n\t\tthis.makeEmpty();\n\n\t\tfor ( let i = 0, il = points.length; i < il; i ++ ) {\n\n\t\t\tthis.expandByPoint( points[ i ] );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tsetFromCenterAndSize( center, size ) {\n\n\t\tconst halfSize = _vector.copy( size ).multiplyScalar( 0.5 );\n\n\t\tthis.min.copy( center ).sub( halfSize );\n\t\tthis.max.copy( center ).add( halfSize );\n\n\t\treturn this;\n\n\t}\n\n\tsetFromObject( object, precise = false ) {\n\n\t\tthis.makeEmpty();\n\n\t\treturn this.expandByObject( object, precise );\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n\tcopy( box ) {\n\n\t\tthis.min.copy( box.min );\n\t\tthis.max.copy( box.max );\n\n\t\treturn this;\n\n\t}\n\n\tmakeEmpty() {\n\n\t\tthis.min.x = this.min.y = this.min.z = + Infinity;\n\t\tthis.max.x = this.max.y = this.max.z = - Infinity;\n\n\t\treturn this;\n\n\t}\n\n\tisEmpty() {\n\n\t\t// this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes\n\n\t\treturn ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z );\n\n\t}\n\n\tgetCenter( target ) {\n\n\t\treturn this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 );\n\n\t}\n\n\tgetSize( target ) {\n\n\t\treturn this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min );\n\n\t}\n\n\texpandByPoint( point ) {\n\n\t\tthis.min.min( point );\n\t\tthis.max.max( point );\n\n\t\treturn this;\n\n\t}\n\n\texpandByVector( vector ) {\n\n\t\tthis.min.sub( vector );\n\t\tthis.max.add( vector );\n\n\t\treturn this;\n\n\t}\n\n\texpandByScalar( scalar ) {\n\n\t\tthis.min.addScalar( - scalar );\n\t\tthis.max.addScalar( scalar );\n\n\t\treturn this;\n\n\t}\n\n\texpandByObject( object, precise = false ) {\n\n\t\t// Computes the world-axis-aligned bounding box of an object (including its children),\n\t\t// accounting for both the object's, and children's, world transforms\n\n\t\tobject.updateWorldMatrix( false, false );\n\n\t\tconst geometry = object.geometry;\n\n\t\tif ( geometry !== undefined ) {\n\n\t\t\tconst positionAttribute = geometry.getAttribute( 'position' );\n\n\t\t\t// precise AABB computation based on vertex data requires at least a position attribute.\n\t\t\t// instancing isn't supported so far and uses the normal (conservative) code path.\n\n\t\t\tif ( precise === true && positionAttribute !== undefined && object.isInstancedMesh !== true ) {\n\n\t\t\t\tfor ( let i = 0, l = positionAttribute.count; i < l; i ++ ) {\n\n\t\t\t\t\tif ( object.isMesh === true ) {\n\n\t\t\t\t\t\tobject.getVertexPosition( i, _vector );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t_vector.fromBufferAttribute( positionAttribute, i );\n\n\t\t\t\t\t}\n\n\t\t\t\t\t_vector.applyMatrix4( object.matrixWorld );\n\t\t\t\t\tthis.expandByPoint( _vector );\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tif ( object.boundingBox !== undefined ) {\n\n\t\t\t\t\t// object-level bounding box\n\n\t\t\t\t\tif ( object.boundingBox === null ) {\n\n\t\t\t\t\t\tobject.computeBoundingBox();\n\n\t\t\t\t\t}\n\n\t\t\t\t\t_box.copy( object.boundingBox );\n\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// geometry-level bounding box\n\n\t\t\t\t\tif ( geometry.boundingBox === null ) {\n\n\t\t\t\t\t\tgeometry.computeBoundingBox();\n\n\t\t\t\t\t}\n\n\t\t\t\t\t_box.copy( geometry.boundingBox );\n\n\t\t\t\t}\n\n\t\t\t\t_box.applyMatrix4( object.matrixWorld );\n\n\t\t\t\tthis.union( _box );\n\n\t\t\t}\n\n\t\t}\n\n\t\tconst children = object.children;\n\n\t\tfor ( let i = 0, l = children.length; i < l; i ++ ) {\n\n\t\t\tthis.expandByObject( children[ i ], precise );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tcontainsPoint( point ) {\n\n\t\treturn point.x < this.min.x || point.x > this.max.x ||\n\t\t\tpoint.y < this.min.y || point.y > this.max.y ||\n\t\t\tpoint.z < this.min.z || point.z > this.max.z ? false : true;\n\n\t}\n\n\tcontainsBox( box ) {\n\n\t\treturn this.min.x <= box.min.x && box.max.x <= this.max.x &&\n\t\t\tthis.min.y <= box.min.y && box.max.y <= this.max.y &&\n\t\t\tthis.min.z <= box.min.z && box.max.z <= this.max.z;\n\n\t}\n\n\tgetParameter( point, target ) {\n\n\t\t// This can potentially have a divide by zero if the box\n\t\t// has a size dimension of 0.\n\n\t\treturn target.set(\n\t\t\t( point.x - this.min.x ) / ( this.max.x - this.min.x ),\n\t\t\t( point.y - this.min.y ) / ( this.max.y - this.min.y ),\n\t\t\t( point.z - this.min.z ) / ( this.max.z - this.min.z )\n\t\t);\n\n\t}\n\n\tintersectsBox( box ) {\n\n\t\t// using 6 splitting planes to rule out intersections.\n\t\treturn box.max.x < this.min.x || box.min.x > this.max.x ||\n\t\t\tbox.max.y < this.min.y || box.min.y > this.max.y ||\n\t\t\tbox.max.z < this.min.z || box.min.z > this.max.z ? false : true;\n\n\t}\n\n\tintersectsSphere( sphere ) {\n\n\t\t// Find the point on the AABB closest to the sphere center.\n\t\tthis.clampPoint( sphere.center, _vector );\n\n\t\t// If that point is inside the sphere, the AABB and sphere intersect.\n\t\treturn _vector.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius );\n\n\t}\n\n\tintersectsPlane( plane ) {\n\n\t\t// We compute the minimum and maximum dot product values. If those values\n\t\t// are on the same side (back or front) of the plane, then there is no intersection.\n\n\t\tlet min, max;\n\n\t\tif ( plane.normal.x > 0 ) {\n\n\t\t\tmin = plane.normal.x * this.min.x;\n\t\t\tmax = plane.normal.x * this.max.x;\n\n\t\t} else {\n\n\t\t\tmin = plane.normal.x * this.max.x;\n\t\t\tmax = plane.normal.x * this.min.x;\n\n\t\t}\n\n\t\tif ( plane.normal.y > 0 ) {\n\n\t\t\tmin += plane.normal.y * this.min.y;\n\t\t\tmax += plane.normal.y * this.max.y;\n\n\t\t} else {\n\n\t\t\tmin += plane.normal.y * this.max.y;\n\t\t\tmax += plane.normal.y * this.min.y;\n\n\t\t}\n\n\t\tif ( plane.normal.z > 0 ) {\n\n\t\t\tmin += plane.normal.z * this.min.z;\n\t\t\tmax += plane.normal.z * this.max.z;\n\n\t\t} else {\n\n\t\t\tmin += plane.normal.z * this.max.z;\n\t\t\tmax += plane.normal.z * this.min.z;\n\n\t\t}\n\n\t\treturn ( min <= - plane.constant && max >= - plane.constant );\n\n\t}\n\n\tintersectsTriangle( triangle ) {\n\n\t\tif ( this.isEmpty() ) {\n\n\t\t\treturn false;\n\n\t\t}\n\n\t\t// compute box center and extents\n\t\tthis.getCenter( _center );\n\t\t_extents.subVectors( this.max, _center );\n\n\t\t// translate triangle to aabb origin\n\t\t_v0.subVectors( triangle.a, _center );\n\t\t_v1.subVectors( triangle.b, _center );\n\t\t_v2.subVectors( triangle.c, _center );\n\n\t\t// compute edge vectors for triangle\n\t\t_f0.subVectors( _v1, _v0 );\n\t\t_f1.subVectors( _v2, _v1 );\n\t\t_f2.subVectors( _v0, _v2 );\n\n\t\t// test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb\n\t\t// make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation\n\t\t// axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned)\n\t\tlet axes = [\n\t\t\t0, - _f0.z, _f0.y, 0, - _f1.z, _f1.y, 0, - _f2.z, _f2.y,\n\t\t\t_f0.z, 0, - _f0.x, _f1.z, 0, - _f1.x, _f2.z, 0, - _f2.x,\n\t\t\t- _f0.y, _f0.x, 0, - _f1.y, _f1.x, 0, - _f2.y, _f2.x, 0\n\t\t];\n\t\tif ( ! satForAxes( axes, _v0, _v1, _v2, _extents ) ) {\n\n\t\t\treturn false;\n\n\t\t}\n\n\t\t// test 3 face normals from the aabb\n\t\taxes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ];\n\t\tif ( ! satForAxes( axes, _v0, _v1, _v2, _extents ) ) {\n\n\t\t\treturn false;\n\n\t\t}\n\n\t\t// finally testing the face normal of the triangle\n\t\t// use already existing triangle edge vectors here\n\t\t_triangleNormal.crossVectors( _f0, _f1 );\n\t\taxes = [ _triangleNormal.x, _triangleNormal.y, _triangleNormal.z ];\n\n\t\treturn satForAxes( axes, _v0, _v1, _v2, _extents );\n\n\t}\n\n\tclampPoint( point, target ) {\n\n\t\treturn target.copy( point ).clamp( this.min, this.max );\n\n\t}\n\n\tdistanceToPoint( point ) {\n\n\t\treturn this.clampPoint( point, _vector ).distanceTo( point );\n\n\t}\n\n\tgetBoundingSphere( target ) {\n\n\t\tif ( this.isEmpty() ) {\n\n\t\t\ttarget.makeEmpty();\n\n\t\t} else {\n\n\t\t\tthis.getCenter( target.center );\n\n\t\t\ttarget.radius = this.getSize( _vector ).length() * 0.5;\n\n\t\t}\n\n\t\treturn target;\n\n\t}\n\n\tintersect( box ) {\n\n\t\tthis.min.max( box.min );\n\t\tthis.max.min( box.max );\n\n\t\t// ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values.\n\t\tif ( this.isEmpty() ) this.makeEmpty();\n\n\t\treturn this;\n\n\t}\n\n\tunion( box ) {\n\n\t\tthis.min.min( box.min );\n\t\tthis.max.max( box.max );\n\n\t\treturn this;\n\n\t}\n\n\tapplyMatrix4( matrix ) {\n\n\t\t// transform of empty box is an empty box.\n\t\tif ( this.isEmpty() ) return this;\n\n\t\t// NOTE: I am using a binary pattern to specify all 2^3 combinations below\n\t\t_points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000\n\t\t_points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001\n\t\t_points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010\n\t\t_points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011\n\t\t_points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100\n\t\t_points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101\n\t\t_points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110\n\t\t_points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111\n\n\t\tthis.setFromPoints( _points );\n\n\t\treturn this;\n\n\t}\n\n\ttranslate( offset ) {\n\n\t\tthis.min.add( offset );\n\t\tthis.max.add( offset );\n\n\t\treturn this;\n\n\t}\n\n\tequals( box ) {\n\n\t\treturn box.min.equals( this.min ) && box.max.equals( this.max );\n\n\t}\n\n}\n\nconst _points = [\n\t/*@__PURE__*/ new Vector3(),\n\t/*@__PURE__*/ new Vector3(),\n\t/*@__PURE__*/ new Vector3(),\n\t/*@__PURE__*/ new Vector3(),\n\t/*@__PURE__*/ new Vector3(),\n\t/*@__PURE__*/ new Vector3(),\n\t/*@__PURE__*/ new Vector3(),\n\t/*@__PURE__*/ new Vector3()\n];\n\nconst _vector = /*@__PURE__*/ new Vector3();\n\nconst _box = /*@__PURE__*/ new Box3();\n\n// triangle centered vertices\n\nconst _v0 = /*@__PURE__*/ new Vector3();\nconst _v1 = /*@__PURE__*/ new Vector3();\nconst _v2 = /*@__PURE__*/ new Vector3();\n\n// triangle edge vectors\n\nconst _f0 = /*@__PURE__*/ new Vector3();\nconst _f1 = /*@__PURE__*/ new Vector3();\nconst _f2 = /*@__PURE__*/ new Vector3();\n\nconst _center = /*@__PURE__*/ new Vector3();\nconst _extents = /*@__PURE__*/ new Vector3();\nconst _triangleNormal = /*@__PURE__*/ new Vector3();\nconst _testAxis = /*@__PURE__*/ new Vector3();\n\nfunction satForAxes( axes, v0, v1, v2, extents ) {\n\n\tfor ( let i = 0, j = axes.length - 3; i <= j; i += 3 ) {\n\n\t\t_testAxis.fromArray( axes, i );\n\t\t// project the aabb onto the separating axis\n\t\tconst r = extents.x * Math.abs( _testAxis.x ) + extents.y * Math.abs( _testAxis.y ) + extents.z * Math.abs( _testAxis.z );\n\t\t// project all 3 vertices of the triangle onto the separating axis\n\t\tconst p0 = v0.dot( _testAxis );\n\t\tconst p1 = v1.dot( _testAxis );\n\t\tconst p2 = v2.dot( _testAxis );\n\t\t// actual test, basically see if either of the most extreme of the triangle points intersects r\n\t\tif ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) {\n\n\t\t\t// points of the projected triangle are outside the projected half-length of the aabb\n\t\t\t// the axis is separating and we can exit\n\t\t\treturn false;\n\n\t\t}\n\n\t}\n\n\treturn true;\n\n}\n\nexport { Box3 };\n","import { Box3 } from './Box3.js';\nimport { Vector3 } from './Vector3.js';\n\nconst _box = /*@__PURE__*/ new Box3();\nconst _v1 = /*@__PURE__*/ new Vector3();\nconst _v2 = /*@__PURE__*/ new Vector3();\n\nclass Sphere {\n\n\tconstructor( center = new Vector3(), radius = - 1 ) {\n\n\t\tthis.center = center;\n\t\tthis.radius = radius;\n\n\t}\n\n\tset( center, radius ) {\n\n\t\tthis.center.copy( center );\n\t\tthis.radius = radius;\n\n\t\treturn this;\n\n\t}\n\n\tsetFromPoints( points, optionalCenter ) {\n\n\t\tconst center = this.center;\n\n\t\tif ( optionalCenter !== undefined ) {\n\n\t\t\tcenter.copy( optionalCenter );\n\n\t\t} else {\n\n\t\t\t_box.setFromPoints( points ).getCenter( center );\n\n\t\t}\n\n\t\tlet maxRadiusSq = 0;\n\n\t\tfor ( let i = 0, il = points.length; i < il; i ++ ) {\n\n\t\t\tmaxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) );\n\n\t\t}\n\n\t\tthis.radius = Math.sqrt( maxRadiusSq );\n\n\t\treturn this;\n\n\t}\n\n\tcopy( sphere ) {\n\n\t\tthis.center.copy( sphere.center );\n\t\tthis.radius = sphere.radius;\n\n\t\treturn this;\n\n\t}\n\n\tisEmpty() {\n\n\t\treturn ( this.radius < 0 );\n\n\t}\n\n\tmakeEmpty() {\n\n\t\tthis.center.set( 0, 0, 0 );\n\t\tthis.radius = - 1;\n\n\t\treturn this;\n\n\t}\n\n\tcontainsPoint( point ) {\n\n\t\treturn ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) );\n\n\t}\n\n\tdistanceToPoint( point ) {\n\n\t\treturn ( point.distanceTo( this.center ) - this.radius );\n\n\t}\n\n\tintersectsSphere( sphere ) {\n\n\t\tconst radiusSum = this.radius + sphere.radius;\n\n\t\treturn sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum );\n\n\t}\n\n\tintersectsBox( box ) {\n\n\t\treturn box.intersectsSphere( this );\n\n\t}\n\n\tintersectsPlane( plane ) {\n\n\t\treturn Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius;\n\n\t}\n\n\tclampPoint( point, target ) {\n\n\t\tconst deltaLengthSq = this.center.distanceToSquared( point );\n\n\t\ttarget.copy( point );\n\n\t\tif ( deltaLengthSq > ( this.radius * this.radius ) ) {\n\n\t\t\ttarget.sub( this.center ).normalize();\n\t\t\ttarget.multiplyScalar( this.radius ).add( this.center );\n\n\t\t}\n\n\t\treturn target;\n\n\t}\n\n\tgetBoundingBox( target ) {\n\n\t\tif ( this.isEmpty() ) {\n\n\t\t\t// Empty sphere produces empty bounding box\n\t\t\ttarget.makeEmpty();\n\t\t\treturn target;\n\n\t\t}\n\n\t\ttarget.set( this.center, this.center );\n\t\ttarget.expandByScalar( this.radius );\n\n\t\treturn target;\n\n\t}\n\n\tapplyMatrix4( matrix ) {\n\n\t\tthis.center.applyMatrix4( matrix );\n\t\tthis.radius = this.radius * matrix.getMaxScaleOnAxis();\n\n\t\treturn this;\n\n\t}\n\n\ttranslate( offset ) {\n\n\t\tthis.center.add( offset );\n\n\t\treturn this;\n\n\t}\n\n\texpandByPoint( point ) {\n\n\t\tif ( this.isEmpty() ) {\n\n\t\t\tthis.center.copy( point );\n\n\t\t\tthis.radius = 0;\n\n\t\t\treturn this;\n\n\t\t}\n\n\t\t_v1.subVectors( point, this.center );\n\n\t\tconst lengthSq = _v1.lengthSq();\n\n\t\tif ( lengthSq > ( this.radius * this.radius ) ) {\n\n\t\t\t// calculate the minimal sphere\n\n\t\t\tconst length = Math.sqrt( lengthSq );\n\n\t\t\tconst delta = ( length - this.radius ) * 0.5;\n\n\t\t\tthis.center.addScaledVector( _v1, delta / length );\n\n\t\t\tthis.radius += delta;\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tunion( sphere ) {\n\n\t\tif ( sphere.isEmpty() ) {\n\n\t\t\treturn this;\n\n\t\t}\n\n\t\tif ( this.isEmpty() ) {\n\n\t\t\tthis.copy( sphere );\n\n\t\t\treturn this;\n\n\t\t}\n\n\t\tif ( this.center.equals( sphere.center ) === true ) {\n\n\t\t\t this.radius = Math.max( this.radius, sphere.radius );\n\n\t\t} else {\n\n\t\t\t_v2.subVectors( sphere.center, this.center ).setLength( sphere.radius );\n\n\t\t\tthis.expandByPoint( _v1.copy( sphere.center ).add( _v2 ) );\n\n\t\t\tthis.expandByPoint( _v1.copy( sphere.center ).sub( _v2 ) );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tequals( sphere ) {\n\n\t\treturn sphere.center.equals( this.center ) && ( sphere.radius === this.radius );\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n}\n\nexport { Sphere };\n","import { Vector3 } from './Vector3.js';\n\nconst _vector = /*@__PURE__*/ new Vector3();\nconst _segCenter = /*@__PURE__*/ new Vector3();\nconst _segDir = /*@__PURE__*/ new Vector3();\nconst _diff = /*@__PURE__*/ new Vector3();\n\nconst _edge1 = /*@__PURE__*/ new Vector3();\nconst _edge2 = /*@__PURE__*/ new Vector3();\nconst _normal = /*@__PURE__*/ new Vector3();\n\nclass Ray {\n\n\tconstructor( origin = new Vector3(), direction = new Vector3( 0, 0, - 1 ) ) {\n\n\t\tthis.origin = origin;\n\t\tthis.direction = direction;\n\n\t}\n\n\tset( origin, direction ) {\n\n\t\tthis.origin.copy( origin );\n\t\tthis.direction.copy( direction );\n\n\t\treturn this;\n\n\t}\n\n\tcopy( ray ) {\n\n\t\tthis.origin.copy( ray.origin );\n\t\tthis.direction.copy( ray.direction );\n\n\t\treturn this;\n\n\t}\n\n\tat( t, target ) {\n\n\t\treturn target.copy( this.origin ).addScaledVector( this.direction, t );\n\n\t}\n\n\tlookAt( v ) {\n\n\t\tthis.direction.copy( v ).sub( this.origin ).normalize();\n\n\t\treturn this;\n\n\t}\n\n\trecast( t ) {\n\n\t\tthis.origin.copy( this.at( t, _vector ) );\n\n\t\treturn this;\n\n\t}\n\n\tclosestPointToPoint( point, target ) {\n\n\t\ttarget.subVectors( point, this.origin );\n\n\t\tconst directionDistance = target.dot( this.direction );\n\n\t\tif ( directionDistance < 0 ) {\n\n\t\t\treturn target.copy( this.origin );\n\n\t\t}\n\n\t\treturn target.copy( this.origin ).addScaledVector( this.direction, directionDistance );\n\n\t}\n\n\tdistanceToPoint( point ) {\n\n\t\treturn Math.sqrt( this.distanceSqToPoint( point ) );\n\n\t}\n\n\tdistanceSqToPoint( point ) {\n\n\t\tconst directionDistance = _vector.subVectors( point, this.origin ).dot( this.direction );\n\n\t\t// point behind the ray\n\n\t\tif ( directionDistance < 0 ) {\n\n\t\t\treturn this.origin.distanceToSquared( point );\n\n\t\t}\n\n\t\t_vector.copy( this.origin ).addScaledVector( this.direction, directionDistance );\n\n\t\treturn _vector.distanceToSquared( point );\n\n\t}\n\n\tdistanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) {\n\n\t\t// from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteDistRaySegment.h\n\t\t// It returns the min distance between the ray and the segment\n\t\t// defined by v0 and v1\n\t\t// It can also set two optional targets :\n\t\t// - The closest point on the ray\n\t\t// - The closest point on the segment\n\n\t\t_segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 );\n\t\t_segDir.copy( v1 ).sub( v0 ).normalize();\n\t\t_diff.copy( this.origin ).sub( _segCenter );\n\n\t\tconst segExtent = v0.distanceTo( v1 ) * 0.5;\n\t\tconst a01 = - this.direction.dot( _segDir );\n\t\tconst b0 = _diff.dot( this.direction );\n\t\tconst b1 = - _diff.dot( _segDir );\n\t\tconst c = _diff.lengthSq();\n\t\tconst det = Math.abs( 1 - a01 * a01 );\n\t\tlet s0, s1, sqrDist, extDet;\n\n\t\tif ( det > 0 ) {\n\n\t\t\t// The ray and segment are not parallel.\n\n\t\t\ts0 = a01 * b1 - b0;\n\t\t\ts1 = a01 * b0 - b1;\n\t\t\textDet = segExtent * det;\n\n\t\t\tif ( s0 >= 0 ) {\n\n\t\t\t\tif ( s1 >= - extDet ) {\n\n\t\t\t\t\tif ( s1 <= extDet ) {\n\n\t\t\t\t\t\t// region 0\n\t\t\t\t\t\t// Minimum at interior points of ray and segment.\n\n\t\t\t\t\t\tconst invDet = 1 / det;\n\t\t\t\t\t\ts0 *= invDet;\n\t\t\t\t\t\ts1 *= invDet;\n\t\t\t\t\t\tsqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c;\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t// region 1\n\n\t\t\t\t\t\ts1 = segExtent;\n\t\t\t\t\t\ts0 = Math.max( 0, - ( a01 * s1 + b0 ) );\n\t\t\t\t\t\tsqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// region 5\n\n\t\t\t\t\ts1 = - segExtent;\n\t\t\t\t\ts0 = Math.max( 0, - ( a01 * s1 + b0 ) );\n\t\t\t\t\tsqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tif ( s1 <= - extDet ) {\n\n\t\t\t\t\t// region 4\n\n\t\t\t\t\ts0 = Math.max( 0, - ( - a01 * segExtent + b0 ) );\n\t\t\t\t\ts1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );\n\t\t\t\t\tsqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;\n\n\t\t\t\t} else if ( s1 <= extDet ) {\n\n\t\t\t\t\t// region 3\n\n\t\t\t\t\ts0 = 0;\n\t\t\t\t\ts1 = Math.min( Math.max( - segExtent, - b1 ), segExtent );\n\t\t\t\t\tsqrDist = s1 * ( s1 + 2 * b1 ) + c;\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// region 2\n\n\t\t\t\t\ts0 = Math.max( 0, - ( a01 * segExtent + b0 ) );\n\t\t\t\t\ts1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );\n\t\t\t\t\tsqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\t// Ray and segment are parallel.\n\n\t\t\ts1 = ( a01 > 0 ) ? - segExtent : segExtent;\n\t\t\ts0 = Math.max( 0, - ( a01 * s1 + b0 ) );\n\t\t\tsqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;\n\n\t\t}\n\n\t\tif ( optionalPointOnRay ) {\n\n\t\t\toptionalPointOnRay.copy( this.origin ).addScaledVector( this.direction, s0 );\n\n\t\t}\n\n\t\tif ( optionalPointOnSegment ) {\n\n\t\t\toptionalPointOnSegment.copy( _segCenter ).addScaledVector( _segDir, s1 );\n\n\t\t}\n\n\t\treturn sqrDist;\n\n\t}\n\n\tintersectSphere( sphere, target ) {\n\n\t\t_vector.subVectors( sphere.center, this.origin );\n\t\tconst tca = _vector.dot( this.direction );\n\t\tconst d2 = _vector.dot( _vector ) - tca * tca;\n\t\tconst radius2 = sphere.radius * sphere.radius;\n\n\t\tif ( d2 > radius2 ) return null;\n\n\t\tconst thc = Math.sqrt( radius2 - d2 );\n\n\t\t// t0 = first intersect point - entrance on front of sphere\n\t\tconst t0 = tca - thc;\n\n\t\t// t1 = second intersect point - exit point on back of sphere\n\t\tconst t1 = tca + thc;\n\n\t\t// test to see if t1 is behind the ray - if so, return null\n\t\tif ( t1 < 0 ) return null;\n\n\t\t// test to see if t0 is behind the ray:\n\t\t// if it is, the ray is inside the sphere, so return the second exit point scaled by t1,\n\t\t// in order to always return an intersect point that is in front of the ray.\n\t\tif ( t0 < 0 ) return this.at( t1, target );\n\n\t\t// else t0 is in front of the ray, so return the first collision point scaled by t0\n\t\treturn this.at( t0, target );\n\n\t}\n\n\tintersectsSphere( sphere ) {\n\n\t\treturn this.distanceSqToPoint( sphere.center ) <= ( sphere.radius * sphere.radius );\n\n\t}\n\n\tdistanceToPlane( plane ) {\n\n\t\tconst denominator = plane.normal.dot( this.direction );\n\n\t\tif ( denominator === 0 ) {\n\n\t\t\t// line is coplanar, return origin\n\t\t\tif ( plane.distanceToPoint( this.origin ) === 0 ) {\n\n\t\t\t\treturn 0;\n\n\t\t\t}\n\n\t\t\t// Null is preferable to undefined since undefined means.... it is undefined\n\n\t\t\treturn null;\n\n\t\t}\n\n\t\tconst t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator;\n\n\t\t// Return if the ray never intersects the plane\n\n\t\treturn t >= 0 ? t : null;\n\n\t}\n\n\tintersectPlane( plane, target ) {\n\n\t\tconst t = this.distanceToPlane( plane );\n\n\t\tif ( t === null ) {\n\n\t\t\treturn null;\n\n\t\t}\n\n\t\treturn this.at( t, target );\n\n\t}\n\n\tintersectsPlane( plane ) {\n\n\t\t// check if the ray lies on the plane first\n\n\t\tconst distToPoint = plane.distanceToPoint( this.origin );\n\n\t\tif ( distToPoint === 0 ) {\n\n\t\t\treturn true;\n\n\t\t}\n\n\t\tconst denominator = plane.normal.dot( this.direction );\n\n\t\tif ( denominator * distToPoint < 0 ) {\n\n\t\t\treturn true;\n\n\t\t}\n\n\t\t// ray origin is behind the plane (and is pointing behind it)\n\n\t\treturn false;\n\n\t}\n\n\tintersectBox( box, target ) {\n\n\t\tlet tmin, tmax, tymin, tymax, tzmin, tzmax;\n\n\t\tconst invdirx = 1 / this.direction.x,\n\t\t\tinvdiry = 1 / this.direction.y,\n\t\t\tinvdirz = 1 / this.direction.z;\n\n\t\tconst origin = this.origin;\n\n\t\tif ( invdirx >= 0 ) {\n\n\t\t\ttmin = ( box.min.x - origin.x ) * invdirx;\n\t\t\ttmax = ( box.max.x - origin.x ) * invdirx;\n\n\t\t} else {\n\n\t\t\ttmin = ( box.max.x - origin.x ) * invdirx;\n\t\t\ttmax = ( box.min.x - origin.x ) * invdirx;\n\n\t\t}\n\n\t\tif ( invdiry >= 0 ) {\n\n\t\t\ttymin = ( box.min.y - origin.y ) * invdiry;\n\t\t\ttymax = ( box.max.y - origin.y ) * invdiry;\n\n\t\t} else {\n\n\t\t\ttymin = ( box.max.y - origin.y ) * invdiry;\n\t\t\ttymax = ( box.min.y - origin.y ) * invdiry;\n\n\t\t}\n\n\t\tif ( ( tmin > tymax ) || ( tymin > tmax ) ) return null;\n\n\t\tif ( tymin > tmin || isNaN( tmin ) ) tmin = tymin;\n\n\t\tif ( tymax < tmax || isNaN( tmax ) ) tmax = tymax;\n\n\t\tif ( invdirz >= 0 ) {\n\n\t\t\ttzmin = ( box.min.z - origin.z ) * invdirz;\n\t\t\ttzmax = ( box.max.z - origin.z ) * invdirz;\n\n\t\t} else {\n\n\t\t\ttzmin = ( box.max.z - origin.z ) * invdirz;\n\t\t\ttzmax = ( box.min.z - origin.z ) * invdirz;\n\n\t\t}\n\n\t\tif ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null;\n\n\t\tif ( tzmin > tmin || tmin !== tmin ) tmin = tzmin;\n\n\t\tif ( tzmax < tmax || tmax !== tmax ) tmax = tzmax;\n\n\t\t//return point closest to the ray (positive side)\n\n\t\tif ( tmax < 0 ) return null;\n\n\t\treturn this.at( tmin >= 0 ? tmin : tmax, target );\n\n\t}\n\n\tintersectsBox( box ) {\n\n\t\treturn this.intersectBox( box, _vector ) !== null;\n\n\t}\n\n\tintersectTriangle( a, b, c, backfaceCulling, target ) {\n\n\t\t// Compute the offset origin, edges, and normal.\n\n\t\t// from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h\n\n\t\t_edge1.subVectors( b, a );\n\t\t_edge2.subVectors( c, a );\n\t\t_normal.crossVectors( _edge1, _edge2 );\n\n\t\t// Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction,\n\t\t// E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by\n\t\t// |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2))\n\t\t// |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q))\n\t\t// |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N)\n\t\tlet DdN = this.direction.dot( _normal );\n\t\tlet sign;\n\n\t\tif ( DdN > 0 ) {\n\n\t\t\tif ( backfaceCulling ) return null;\n\t\t\tsign = 1;\n\n\t\t} else if ( DdN < 0 ) {\n\n\t\t\tsign = - 1;\n\t\t\tDdN = - DdN;\n\n\t\t} else {\n\n\t\t\treturn null;\n\n\t\t}\n\n\t\t_diff.subVectors( this.origin, a );\n\t\tconst DdQxE2 = sign * this.direction.dot( _edge2.crossVectors( _diff, _edge2 ) );\n\n\t\t// b1 < 0, no intersection\n\t\tif ( DdQxE2 < 0 ) {\n\n\t\t\treturn null;\n\n\t\t}\n\n\t\tconst DdE1xQ = sign * this.direction.dot( _edge1.cross( _diff ) );\n\n\t\t// b2 < 0, no intersection\n\t\tif ( DdE1xQ < 0 ) {\n\n\t\t\treturn null;\n\n\t\t}\n\n\t\t// b1+b2 > 1, no intersection\n\t\tif ( DdQxE2 + DdE1xQ > DdN ) {\n\n\t\t\treturn null;\n\n\t\t}\n\n\t\t// Line intersects triangle, check if ray does.\n\t\tconst QdN = - sign * _diff.dot( _normal );\n\n\t\t// t < 0, no intersection\n\t\tif ( QdN < 0 ) {\n\n\t\t\treturn null;\n\n\t\t}\n\n\t\t// Ray intersects triangle.\n\t\treturn this.at( QdN / DdN, target );\n\n\t}\n\n\tapplyMatrix4( matrix4 ) {\n\n\t\tthis.origin.applyMatrix4( matrix4 );\n\t\tthis.direction.transformDirection( matrix4 );\n\n\t\treturn this;\n\n\t}\n\n\tequals( ray ) {\n\n\t\treturn ray.origin.equals( this.origin ) && ray.direction.equals( this.direction );\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n}\n\nexport { Ray };\n","import { WebGLCoordinateSystem, WebGPUCoordinateSystem } from '../constants.js';\nimport { Vector3 } from './Vector3.js';\n\nclass Matrix4 {\n\n\tconstructor( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {\n\n\t\tMatrix4.prototype.isMatrix4 = true;\n\n\t\tthis.elements = [\n\n\t\t\t1, 0, 0, 0,\n\t\t\t0, 1, 0, 0,\n\t\t\t0, 0, 1, 0,\n\t\t\t0, 0, 0, 1\n\n\t\t];\n\n\t\tif ( n11 !== undefined ) {\n\n\t\t\tthis.set( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 );\n\n\t\t}\n\n\t}\n\n\tset( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {\n\n\t\tconst te = this.elements;\n\n\t\tte[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14;\n\t\tte[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24;\n\t\tte[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34;\n\t\tte[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44;\n\n\t\treturn this;\n\n\t}\n\n\tidentity() {\n\n\t\tthis.set(\n\n\t\t\t1, 0, 0, 0,\n\t\t\t0, 1, 0, 0,\n\t\t\t0, 0, 1, 0,\n\t\t\t0, 0, 0, 1\n\n\t\t);\n\n\t\treturn this;\n\n\t}\n\n\tclone() {\n\n\t\treturn new Matrix4().fromArray( this.elements );\n\n\t}\n\n\tcopy( m ) {\n\n\t\tconst te = this.elements;\n\t\tconst me = m.elements;\n\n\t\tte[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ];\n\t\tte[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ];\n\t\tte[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ];\n\t\tte[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ];\n\n\t\treturn this;\n\n\t}\n\n\tcopyPosition( m ) {\n\n\t\tconst te = this.elements, me = m.elements;\n\n\t\tte[ 12 ] = me[ 12 ];\n\t\tte[ 13 ] = me[ 13 ];\n\t\tte[ 14 ] = me[ 14 ];\n\n\t\treturn this;\n\n\t}\n\n\tsetFromMatrix3( m ) {\n\n\t\tconst me = m.elements;\n\n\t\tthis.set(\n\n\t\t\tme[ 0 ], me[ 3 ], me[ 6 ], 0,\n\t\t\tme[ 1 ], me[ 4 ], me[ 7 ], 0,\n\t\t\tme[ 2 ], me[ 5 ], me[ 8 ], 0,\n\t\t\t0, 0, 0, 1\n\n\t\t);\n\n\t\treturn this;\n\n\t}\n\n\textractBasis( xAxis, yAxis, zAxis ) {\n\n\t\txAxis.setFromMatrixColumn( this, 0 );\n\t\tyAxis.setFromMatrixColumn( this, 1 );\n\t\tzAxis.setFromMatrixColumn( this, 2 );\n\n\t\treturn this;\n\n\t}\n\n\tmakeBasis( xAxis, yAxis, zAxis ) {\n\n\t\tthis.set(\n\t\t\txAxis.x, yAxis.x, zAxis.x, 0,\n\t\t\txAxis.y, yAxis.y, zAxis.y, 0,\n\t\t\txAxis.z, yAxis.z, zAxis.z, 0,\n\t\t\t0, 0, 0, 1\n\t\t);\n\n\t\treturn this;\n\n\t}\n\n\textractRotation( m ) {\n\n\t\t// this method does not support reflection matrices\n\n\t\tconst te = this.elements;\n\t\tconst me = m.elements;\n\n\t\tconst scaleX = 1 / _v1.setFromMatrixColumn( m, 0 ).length();\n\t\tconst scaleY = 1 / _v1.setFromMatrixColumn( m, 1 ).length();\n\t\tconst scaleZ = 1 / _v1.setFromMatrixColumn( m, 2 ).length();\n\n\t\tte[ 0 ] = me[ 0 ] * scaleX;\n\t\tte[ 1 ] = me[ 1 ] * scaleX;\n\t\tte[ 2 ] = me[ 2 ] * scaleX;\n\t\tte[ 3 ] = 0;\n\n\t\tte[ 4 ] = me[ 4 ] * scaleY;\n\t\tte[ 5 ] = me[ 5 ] * scaleY;\n\t\tte[ 6 ] = me[ 6 ] * scaleY;\n\t\tte[ 7 ] = 0;\n\n\t\tte[ 8 ] = me[ 8 ] * scaleZ;\n\t\tte[ 9 ] = me[ 9 ] * scaleZ;\n\t\tte[ 10 ] = me[ 10 ] * scaleZ;\n\t\tte[ 11 ] = 0;\n\n\t\tte[ 12 ] = 0;\n\t\tte[ 13 ] = 0;\n\t\tte[ 14 ] = 0;\n\t\tte[ 15 ] = 1;\n\n\t\treturn this;\n\n\t}\n\n\tmakeRotationFromEuler( euler ) {\n\n\t\tconst te = this.elements;\n\n\t\tconst x = euler.x, y = euler.y, z = euler.z;\n\t\tconst a = Math.cos( x ), b = Math.sin( x );\n\t\tconst c = Math.cos( y ), d = Math.sin( y );\n\t\tconst e = Math.cos( z ), f = Math.sin( z );\n\n\t\tif ( euler.order === 'XYZ' ) {\n\n\t\t\tconst ae = a * e, af = a * f, be = b * e, bf = b * f;\n\n\t\t\tte[ 0 ] = c * e;\n\t\t\tte[ 4 ] = - c * f;\n\t\t\tte[ 8 ] = d;\n\n\t\t\tte[ 1 ] = af + be * d;\n\t\t\tte[ 5 ] = ae - bf * d;\n\t\t\tte[ 9 ] = - b * c;\n\n\t\t\tte[ 2 ] = bf - ae * d;\n\t\t\tte[ 6 ] = be + af * d;\n\t\t\tte[ 10 ] = a * c;\n\n\t\t} else if ( euler.order === 'YXZ' ) {\n\n\t\t\tconst ce = c * e, cf = c * f, de = d * e, df = d * f;\n\n\t\t\tte[ 0 ] = ce + df * b;\n\t\t\tte[ 4 ] = de * b - cf;\n\t\t\tte[ 8 ] = a * d;\n\n\t\t\tte[ 1 ] = a * f;\n\t\t\tte[ 5 ] = a * e;\n\t\t\tte[ 9 ] = - b;\n\n\t\t\tte[ 2 ] = cf * b - de;\n\t\t\tte[ 6 ] = df + ce * b;\n\t\t\tte[ 10 ] = a * c;\n\n\t\t} else if ( euler.order === 'ZXY' ) {\n\n\t\t\tconst ce = c * e, cf = c * f, de = d * e, df = d * f;\n\n\t\t\tte[ 0 ] = ce - df * b;\n\t\t\tte[ 4 ] = - a * f;\n\t\t\tte[ 8 ] = de + cf * b;\n\n\t\t\tte[ 1 ] = cf + de * b;\n\t\t\tte[ 5 ] = a * e;\n\t\t\tte[ 9 ] = df - ce * b;\n\n\t\t\tte[ 2 ] = - a * d;\n\t\t\tte[ 6 ] = b;\n\t\t\tte[ 10 ] = a * c;\n\n\t\t} else if ( euler.order === 'ZYX' ) {\n\n\t\t\tconst ae = a * e, af = a * f, be = b * e, bf = b * f;\n\n\t\t\tte[ 0 ] = c * e;\n\t\t\tte[ 4 ] = be * d - af;\n\t\t\tte[ 8 ] = ae * d + bf;\n\n\t\t\tte[ 1 ] = c * f;\n\t\t\tte[ 5 ] = bf * d + ae;\n\t\t\tte[ 9 ] = af * d - be;\n\n\t\t\tte[ 2 ] = - d;\n\t\t\tte[ 6 ] = b * c;\n\t\t\tte[ 10 ] = a * c;\n\n\t\t} else if ( euler.order === 'YZX' ) {\n\n\t\t\tconst ac = a * c, ad = a * d, bc = b * c, bd = b * d;\n\n\t\t\tte[ 0 ] = c * e;\n\t\t\tte[ 4 ] = bd - ac * f;\n\t\t\tte[ 8 ] = bc * f + ad;\n\n\t\t\tte[ 1 ] = f;\n\t\t\tte[ 5 ] = a * e;\n\t\t\tte[ 9 ] = - b * e;\n\n\t\t\tte[ 2 ] = - d * e;\n\t\t\tte[ 6 ] = ad * f + bc;\n\t\t\tte[ 10 ] = ac - bd * f;\n\n\t\t} else if ( euler.order === 'XZY' ) {\n\n\t\t\tconst ac = a * c, ad = a * d, bc = b * c, bd = b * d;\n\n\t\t\tte[ 0 ] = c * e;\n\t\t\tte[ 4 ] = - f;\n\t\t\tte[ 8 ] = d * e;\n\n\t\t\tte[ 1 ] = ac * f + bd;\n\t\t\tte[ 5 ] = a * e;\n\t\t\tte[ 9 ] = ad * f - bc;\n\n\t\t\tte[ 2 ] = bc * f - ad;\n\t\t\tte[ 6 ] = b * e;\n\t\t\tte[ 10 ] = bd * f + ac;\n\n\t\t}\n\n\t\t// bottom row\n\t\tte[ 3 ] = 0;\n\t\tte[ 7 ] = 0;\n\t\tte[ 11 ] = 0;\n\n\t\t// last column\n\t\tte[ 12 ] = 0;\n\t\tte[ 13 ] = 0;\n\t\tte[ 14 ] = 0;\n\t\tte[ 15 ] = 1;\n\n\t\treturn this;\n\n\t}\n\n\tmakeRotationFromQuaternion( q ) {\n\n\t\treturn this.compose( _zero, q, _one );\n\n\t}\n\n\tlookAt( eye, target, up ) {\n\n\t\tconst te = this.elements;\n\n\t\t_z.subVectors( eye, target );\n\n\t\tif ( _z.lengthSq() === 0 ) {\n\n\t\t\t// eye and target are in the same position\n\n\t\t\t_z.z = 1;\n\n\t\t}\n\n\t\t_z.normalize();\n\t\t_x.crossVectors( up, _z );\n\n\t\tif ( _x.lengthSq() === 0 ) {\n\n\t\t\t// up and z are parallel\n\n\t\t\tif ( Math.abs( up.z ) === 1 ) {\n\n\t\t\t\t_z.x += 0.0001;\n\n\t\t\t} else {\n\n\t\t\t\t_z.z += 0.0001;\n\n\t\t\t}\n\n\t\t\t_z.normalize();\n\t\t\t_x.crossVectors( up, _z );\n\n\t\t}\n\n\t\t_x.normalize();\n\t\t_y.crossVectors( _z, _x );\n\n\t\tte[ 0 ] = _x.x; te[ 4 ] = _y.x; te[ 8 ] = _z.x;\n\t\tte[ 1 ] = _x.y; te[ 5 ] = _y.y; te[ 9 ] = _z.y;\n\t\tte[ 2 ] = _x.z; te[ 6 ] = _y.z; te[ 10 ] = _z.z;\n\n\t\treturn this;\n\n\t}\n\n\tmultiply( m ) {\n\n\t\treturn this.multiplyMatrices( this, m );\n\n\t}\n\n\tpremultiply( m ) {\n\n\t\treturn this.multiplyMatrices( m, this );\n\n\t}\n\n\tmultiplyMatrices( a, b ) {\n\n\t\tconst ae = a.elements;\n\t\tconst be = b.elements;\n\t\tconst te = this.elements;\n\n\t\tconst a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ];\n\t\tconst a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ];\n\t\tconst a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ];\n\t\tconst a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ];\n\n\t\tconst b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ];\n\t\tconst b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ];\n\t\tconst b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ];\n\t\tconst b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ];\n\n\t\tte[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41;\n\t\tte[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42;\n\t\tte[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43;\n\t\tte[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44;\n\n\t\tte[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41;\n\t\tte[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42;\n\t\tte[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43;\n\t\tte[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44;\n\n\t\tte[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41;\n\t\tte[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42;\n\t\tte[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43;\n\t\tte[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44;\n\n\t\tte[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41;\n\t\tte[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42;\n\t\tte[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43;\n\t\tte[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44;\n\n\t\treturn this;\n\n\t}\n\n\tmultiplyScalar( s ) {\n\n\t\tconst te = this.elements;\n\n\t\tte[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s;\n\t\tte[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s;\n\t\tte[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s;\n\t\tte[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s;\n\n\t\treturn this;\n\n\t}\n\n\tdeterminant() {\n\n\t\tconst te = this.elements;\n\n\t\tconst n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ];\n\t\tconst n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ];\n\t\tconst n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ];\n\t\tconst n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ];\n\n\t\t//TODO: make this more efficient\n\t\t//( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm )\n\n\t\treturn (\n\t\t\tn41 * (\n\t\t\t\t+ n14 * n23 * n32\n\t\t\t\t - n13 * n24 * n32\n\t\t\t\t - n14 * n22 * n33\n\t\t\t\t + n12 * n24 * n33\n\t\t\t\t + n13 * n22 * n34\n\t\t\t\t - n12 * n23 * n34\n\t\t\t) +\n\t\t\tn42 * (\n\t\t\t\t+ n11 * n23 * n34\n\t\t\t\t - n11 * n24 * n33\n\t\t\t\t + n14 * n21 * n33\n\t\t\t\t - n13 * n21 * n34\n\t\t\t\t + n13 * n24 * n31\n\t\t\t\t - n14 * n23 * n31\n\t\t\t) +\n\t\t\tn43 * (\n\t\t\t\t+ n11 * n24 * n32\n\t\t\t\t - n11 * n22 * n34\n\t\t\t\t - n14 * n21 * n32\n\t\t\t\t + n12 * n21 * n34\n\t\t\t\t + n14 * n22 * n31\n\t\t\t\t - n12 * n24 * n31\n\t\t\t) +\n\t\t\tn44 * (\n\t\t\t\t- n13 * n22 * n31\n\t\t\t\t - n11 * n23 * n32\n\t\t\t\t + n11 * n22 * n33\n\t\t\t\t + n13 * n21 * n32\n\t\t\t\t - n12 * n21 * n33\n\t\t\t\t + n12 * n23 * n31\n\t\t\t)\n\n\t\t);\n\n\t}\n\n\ttranspose() {\n\n\t\tconst te = this.elements;\n\t\tlet tmp;\n\n\t\ttmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp;\n\t\ttmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp;\n\t\ttmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp;\n\n\t\ttmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp;\n\t\ttmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp;\n\t\ttmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp;\n\n\t\treturn this;\n\n\t}\n\n\tsetPosition( x, y, z ) {\n\n\t\tconst te = this.elements;\n\n\t\tif ( x.isVector3 ) {\n\n\t\t\tte[ 12 ] = x.x;\n\t\t\tte[ 13 ] = x.y;\n\t\t\tte[ 14 ] = x.z;\n\n\t\t} else {\n\n\t\t\tte[ 12 ] = x;\n\t\t\tte[ 13 ] = y;\n\t\t\tte[ 14 ] = z;\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tinvert() {\n\n\t\t// based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm\n\t\tconst te = this.elements,\n\n\t\t\tn11 = te[ 0 ], n21 = te[ 1 ], n31 = te[ 2 ], n41 = te[ 3 ],\n\t\t\tn12 = te[ 4 ], n22 = te[ 5 ], n32 = te[ 6 ], n42 = te[ 7 ],\n\t\t\tn13 = te[ 8 ], n23 = te[ 9 ], n33 = te[ 10 ], n43 = te[ 11 ],\n\t\t\tn14 = te[ 12 ], n24 = te[ 13 ], n34 = te[ 14 ], n44 = te[ 15 ],\n\n\t\t\tt11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44,\n\t\t\tt12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44,\n\t\t\tt13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44,\n\t\t\tt14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34;\n\n\t\tconst det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14;\n\n\t\tif ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 );\n\n\t\tconst detInv = 1 / det;\n\n\t\tte[ 0 ] = t11 * detInv;\n\t\tte[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv;\n\t\tte[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv;\n\t\tte[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv;\n\n\t\tte[ 4 ] = t12 * detInv;\n\t\tte[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv;\n\t\tte[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv;\n\t\tte[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv;\n\n\t\tte[ 8 ] = t13 * detInv;\n\t\tte[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv;\n\t\tte[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv;\n\t\tte[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv;\n\n\t\tte[ 12 ] = t14 * detInv;\n\t\tte[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv;\n\t\tte[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv;\n\t\tte[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv;\n\n\t\treturn this;\n\n\t}\n\n\tscale( v ) {\n\n\t\tconst te = this.elements;\n\t\tconst x = v.x, y = v.y, z = v.z;\n\n\t\tte[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z;\n\t\tte[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z;\n\t\tte[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z;\n\t\tte[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z;\n\n\t\treturn this;\n\n\t}\n\n\tgetMaxScaleOnAxis() {\n\n\t\tconst te = this.elements;\n\n\t\tconst scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ];\n\t\tconst scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ];\n\t\tconst scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ];\n\n\t\treturn Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) );\n\n\t}\n\n\tmakeTranslation( x, y, z ) {\n\n\t\tif ( x.isVector3 ) {\n\n\t\t\tthis.set(\n\n\t\t\t\t1, 0, 0, x.x,\n\t\t\t\t0, 1, 0, x.y,\n\t\t\t\t0, 0, 1, x.z,\n\t\t\t\t0, 0, 0, 1\n\n\t\t\t);\n\n\t\t} else {\n\n\t\t\tthis.set(\n\n\t\t\t\t1, 0, 0, x,\n\t\t\t\t0, 1, 0, y,\n\t\t\t\t0, 0, 1, z,\n\t\t\t\t0, 0, 0, 1\n\n\t\t\t);\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tmakeRotationX( theta ) {\n\n\t\tconst c = Math.cos( theta ), s = Math.sin( theta );\n\n\t\tthis.set(\n\n\t\t\t1, 0, 0, 0,\n\t\t\t0, c, - s, 0,\n\t\t\t0, s, c, 0,\n\t\t\t0, 0, 0, 1\n\n\t\t);\n\n\t\treturn this;\n\n\t}\n\n\tmakeRotationY( theta ) {\n\n\t\tconst c = Math.cos( theta ), s = Math.sin( theta );\n\n\t\tthis.set(\n\n\t\t\t c, 0, s, 0,\n\t\t\t 0, 1, 0, 0,\n\t\t\t- s, 0, c, 0,\n\t\t\t 0, 0, 0, 1\n\n\t\t);\n\n\t\treturn this;\n\n\t}\n\n\tmakeRotationZ( theta ) {\n\n\t\tconst c = Math.cos( theta ), s = Math.sin( theta );\n\n\t\tthis.set(\n\n\t\t\tc, - s, 0, 0,\n\t\t\ts, c, 0, 0,\n\t\t\t0, 0, 1, 0,\n\t\t\t0, 0, 0, 1\n\n\t\t);\n\n\t\treturn this;\n\n\t}\n\n\tmakeRotationAxis( axis, angle ) {\n\n\t\t// Based on http://www.gamedev.net/reference/articles/article1199.asp\n\n\t\tconst c = Math.cos( angle );\n\t\tconst s = Math.sin( angle );\n\t\tconst t = 1 - c;\n\t\tconst x = axis.x, y = axis.y, z = axis.z;\n\t\tconst tx = t * x, ty = t * y;\n\n\t\tthis.set(\n\n\t\t\ttx * x + c, tx * y - s * z, tx * z + s * y, 0,\n\t\t\ttx * y + s * z, ty * y + c, ty * z - s * x, 0,\n\t\t\ttx * z - s * y, ty * z + s * x, t * z * z + c, 0,\n\t\t\t0, 0, 0, 1\n\n\t\t);\n\n\t\treturn this;\n\n\t}\n\n\tmakeScale( x, y, z ) {\n\n\t\tthis.set(\n\n\t\t\tx, 0, 0, 0,\n\t\t\t0, y, 0, 0,\n\t\t\t0, 0, z, 0,\n\t\t\t0, 0, 0, 1\n\n\t\t);\n\n\t\treturn this;\n\n\t}\n\n\tmakeShear( xy, xz, yx, yz, zx, zy ) {\n\n\t\tthis.set(\n\n\t\t\t1, yx, zx, 0,\n\t\t\txy, 1, zy, 0,\n\t\t\txz, yz, 1, 0,\n\t\t\t0, 0, 0, 1\n\n\t\t);\n\n\t\treturn this;\n\n\t}\n\n\tcompose( position, quaternion, scale ) {\n\n\t\tconst te = this.elements;\n\n\t\tconst x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w;\n\t\tconst x2 = x + x,\ty2 = y + y, z2 = z + z;\n\t\tconst xx = x * x2, xy = x * y2, xz = x * z2;\n\t\tconst yy = y * y2, yz = y * z2, zz = z * z2;\n\t\tconst wx = w * x2, wy = w * y2, wz = w * z2;\n\n\t\tconst sx = scale.x, sy = scale.y, sz = scale.z;\n\n\t\tte[ 0 ] = ( 1 - ( yy + zz ) ) * sx;\n\t\tte[ 1 ] = ( xy + wz ) * sx;\n\t\tte[ 2 ] = ( xz - wy ) * sx;\n\t\tte[ 3 ] = 0;\n\n\t\tte[ 4 ] = ( xy - wz ) * sy;\n\t\tte[ 5 ] = ( 1 - ( xx + zz ) ) * sy;\n\t\tte[ 6 ] = ( yz + wx ) * sy;\n\t\tte[ 7 ] = 0;\n\n\t\tte[ 8 ] = ( xz + wy ) * sz;\n\t\tte[ 9 ] = ( yz - wx ) * sz;\n\t\tte[ 10 ] = ( 1 - ( xx + yy ) ) * sz;\n\t\tte[ 11 ] = 0;\n\n\t\tte[ 12 ] = position.x;\n\t\tte[ 13 ] = position.y;\n\t\tte[ 14 ] = position.z;\n\t\tte[ 15 ] = 1;\n\n\t\treturn this;\n\n\t}\n\n\tdecompose( position, quaternion, scale ) {\n\n\t\tconst te = this.elements;\n\n\t\tlet sx = _v1.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length();\n\t\tconst sy = _v1.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length();\n\t\tconst sz = _v1.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length();\n\n\t\t// if determine is negative, we need to invert one scale\n\t\tconst det = this.determinant();\n\t\tif ( det < 0 ) sx = - sx;\n\n\t\tposition.x = te[ 12 ];\n\t\tposition.y = te[ 13 ];\n\t\tposition.z = te[ 14 ];\n\n\t\t// scale the rotation part\n\t\t_m1.copy( this );\n\n\t\tconst invSX = 1 / sx;\n\t\tconst invSY = 1 / sy;\n\t\tconst invSZ = 1 / sz;\n\n\t\t_m1.elements[ 0 ] *= invSX;\n\t\t_m1.elements[ 1 ] *= invSX;\n\t\t_m1.elements[ 2 ] *= invSX;\n\n\t\t_m1.elements[ 4 ] *= invSY;\n\t\t_m1.elements[ 5 ] *= invSY;\n\t\t_m1.elements[ 6 ] *= invSY;\n\n\t\t_m1.elements[ 8 ] *= invSZ;\n\t\t_m1.elements[ 9 ] *= invSZ;\n\t\t_m1.elements[ 10 ] *= invSZ;\n\n\t\tquaternion.setFromRotationMatrix( _m1 );\n\n\t\tscale.x = sx;\n\t\tscale.y = sy;\n\t\tscale.z = sz;\n\n\t\treturn this;\n\n\t}\n\n\tmakePerspective( left, right, top, bottom, near, far, coordinateSystem = WebGLCoordinateSystem ) {\n\n\t\tconst te = this.elements;\n\t\tconst x = 2 * near / ( right - left );\n\t\tconst y = 2 * near / ( top - bottom );\n\n\t\tconst a = ( right + left ) / ( right - left );\n\t\tconst b = ( top + bottom ) / ( top - bottom );\n\n\t\tlet c, d;\n\n\t\tif ( coordinateSystem === WebGLCoordinateSystem ) {\n\n\t\t\tc = - ( far + near ) / ( far - near );\n\t\t\td = ( - 2 * far * near ) / ( far - near );\n\n\t\t} else if ( coordinateSystem === WebGPUCoordinateSystem ) {\n\n\t\t\tc = - far / ( far - near );\n\t\t\td = ( - far * near ) / ( far - near );\n\n\t\t} else {\n\n\t\t\tthrow new Error( 'THREE.Matrix4.makePerspective(): Invalid coordinate system: ' + coordinateSystem );\n\n\t\t}\n\n\t\tte[ 0 ] = x;\tte[ 4 ] = 0;\tte[ 8 ] = a; \tte[ 12 ] = 0;\n\t\tte[ 1 ] = 0;\tte[ 5 ] = y;\tte[ 9 ] = b; \tte[ 13 ] = 0;\n\t\tte[ 2 ] = 0;\tte[ 6 ] = 0;\tte[ 10 ] = c; \tte[ 14 ] = d;\n\t\tte[ 3 ] = 0;\tte[ 7 ] = 0;\tte[ 11 ] = - 1;\tte[ 15 ] = 0;\n\n\t\treturn this;\n\n\t}\n\n\tmakeOrthographic( left, right, top, bottom, near, far, coordinateSystem = WebGLCoordinateSystem ) {\n\n\t\tconst te = this.elements;\n\t\tconst w = 1.0 / ( right - left );\n\t\tconst h = 1.0 / ( top - bottom );\n\t\tconst p = 1.0 / ( far - near );\n\n\t\tconst x = ( right + left ) * w;\n\t\tconst y = ( top + bottom ) * h;\n\n\t\tlet z, zInv;\n\n\t\tif ( coordinateSystem === WebGLCoordinateSystem ) {\n\n\t\t\tz = ( far + near ) * p;\n\t\t\tzInv = - 2 * p;\n\n\t\t} else if ( coordinateSystem === WebGPUCoordinateSystem ) {\n\n\t\t\tz = near * p;\n\t\t\tzInv = - 1 * p;\n\n\t\t} else {\n\n\t\t\tthrow new Error( 'THREE.Matrix4.makeOrthographic(): Invalid coordinate system: ' + coordinateSystem );\n\n\t\t}\n\n\t\tte[ 0 ] = 2 * w;\tte[ 4 ] = 0;\t\tte[ 8 ] = 0; \t\tte[ 12 ] = - x;\n\t\tte[ 1 ] = 0; \t\tte[ 5 ] = 2 * h;\tte[ 9 ] = 0; \t\tte[ 13 ] = - y;\n\t\tte[ 2 ] = 0; \t\tte[ 6 ] = 0;\t\tte[ 10 ] = zInv;\tte[ 14 ] = - z;\n\t\tte[ 3 ] = 0; \t\tte[ 7 ] = 0;\t\tte[ 11 ] = 0;\t\tte[ 15 ] = 1;\n\n\t\treturn this;\n\n\t}\n\n\tequals( matrix ) {\n\n\t\tconst te = this.elements;\n\t\tconst me = matrix.elements;\n\n\t\tfor ( let i = 0; i < 16; i ++ ) {\n\n\t\t\tif ( te[ i ] !== me[ i ] ) return false;\n\n\t\t}\n\n\t\treturn true;\n\n\t}\n\n\tfromArray( array, offset = 0 ) {\n\n\t\tfor ( let i = 0; i < 16; i ++ ) {\n\n\t\t\tthis.elements[ i ] = array[ i + offset ];\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\ttoArray( array = [], offset = 0 ) {\n\n\t\tconst te = this.elements;\n\n\t\tarray[ offset ] = te[ 0 ];\n\t\tarray[ offset + 1 ] = te[ 1 ];\n\t\tarray[ offset + 2 ] = te[ 2 ];\n\t\tarray[ offset + 3 ] = te[ 3 ];\n\n\t\tarray[ offset + 4 ] = te[ 4 ];\n\t\tarray[ offset + 5 ] = te[ 5 ];\n\t\tarray[ offset + 6 ] = te[ 6 ];\n\t\tarray[ offset + 7 ] = te[ 7 ];\n\n\t\tarray[ offset + 8 ] = te[ 8 ];\n\t\tarray[ offset + 9 ] = te[ 9 ];\n\t\tarray[ offset + 10 ] = te[ 10 ];\n\t\tarray[ offset + 11 ] = te[ 11 ];\n\n\t\tarray[ offset + 12 ] = te[ 12 ];\n\t\tarray[ offset + 13 ] = te[ 13 ];\n\t\tarray[ offset + 14 ] = te[ 14 ];\n\t\tarray[ offset + 15 ] = te[ 15 ];\n\n\t\treturn array;\n\n\t}\n\n}\n\nconst _v1 = /*@__PURE__*/ new Vector3();\nconst _m1 = /*@__PURE__*/ new Matrix4();\nconst _zero = /*@__PURE__*/ new Vector3( 0, 0, 0 );\nconst _one = /*@__PURE__*/ new Vector3( 1, 1, 1 );\nconst _x = /*@__PURE__*/ new Vector3();\nconst _y = /*@__PURE__*/ new Vector3();\nconst _z = /*@__PURE__*/ new Vector3();\n\nexport { Matrix4 };\n","import { Quaternion } from './Quaternion.js';\nimport { Matrix4 } from './Matrix4.js';\nimport { clamp } from './MathUtils.js';\n\nconst _matrix = /*@__PURE__*/ new Matrix4();\nconst _quaternion = /*@__PURE__*/ new Quaternion();\n\nclass Euler {\n\n\tconstructor( x = 0, y = 0, z = 0, order = Euler.DEFAULT_ORDER ) {\n\n\t\tthis.isEuler = true;\n\n\t\tthis._x = x;\n\t\tthis._y = y;\n\t\tthis._z = z;\n\t\tthis._order = order;\n\n\t}\n\n\tget x() {\n\n\t\treturn this._x;\n\n\t}\n\n\tset x( value ) {\n\n\t\tthis._x = value;\n\t\tthis._onChangeCallback();\n\n\t}\n\n\tget y() {\n\n\t\treturn this._y;\n\n\t}\n\n\tset y( value ) {\n\n\t\tthis._y = value;\n\t\tthis._onChangeCallback();\n\n\t}\n\n\tget z() {\n\n\t\treturn this._z;\n\n\t}\n\n\tset z( value ) {\n\n\t\tthis._z = value;\n\t\tthis._onChangeCallback();\n\n\t}\n\n\tget order() {\n\n\t\treturn this._order;\n\n\t}\n\n\tset order( value ) {\n\n\t\tthis._order = value;\n\t\tthis._onChangeCallback();\n\n\t}\n\n\tset( x, y, z, order = this._order ) {\n\n\t\tthis._x = x;\n\t\tthis._y = y;\n\t\tthis._z = z;\n\t\tthis._order = order;\n\n\t\tthis._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor( this._x, this._y, this._z, this._order );\n\n\t}\n\n\tcopy( euler ) {\n\n\t\tthis._x = euler._x;\n\t\tthis._y = euler._y;\n\t\tthis._z = euler._z;\n\t\tthis._order = euler._order;\n\n\t\tthis._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\tsetFromRotationMatrix( m, order = this._order, update = true ) {\n\n\t\t// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)\n\n\t\tconst te = m.elements;\n\t\tconst m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ];\n\t\tconst m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ];\n\t\tconst m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];\n\n\t\tswitch ( order ) {\n\n\t\t\tcase 'XYZ':\n\n\t\t\t\tthis._y = Math.asin( clamp( m13, - 1, 1 ) );\n\n\t\t\t\tif ( Math.abs( m13 ) < 0.9999999 ) {\n\n\t\t\t\t\tthis._x = Math.atan2( - m23, m33 );\n\t\t\t\t\tthis._z = Math.atan2( - m12, m11 );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tthis._x = Math.atan2( m32, m22 );\n\t\t\t\t\tthis._z = 0;\n\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'YXZ':\n\n\t\t\t\tthis._x = Math.asin( - clamp( m23, - 1, 1 ) );\n\n\t\t\t\tif ( Math.abs( m23 ) < 0.9999999 ) {\n\n\t\t\t\t\tthis._y = Math.atan2( m13, m33 );\n\t\t\t\t\tthis._z = Math.atan2( m21, m22 );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tthis._y = Math.atan2( - m31, m11 );\n\t\t\t\t\tthis._z = 0;\n\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'ZXY':\n\n\t\t\t\tthis._x = Math.asin( clamp( m32, - 1, 1 ) );\n\n\t\t\t\tif ( Math.abs( m32 ) < 0.9999999 ) {\n\n\t\t\t\t\tthis._y = Math.atan2( - m31, m33 );\n\t\t\t\t\tthis._z = Math.atan2( - m12, m22 );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tthis._y = 0;\n\t\t\t\t\tthis._z = Math.atan2( m21, m11 );\n\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'ZYX':\n\n\t\t\t\tthis._y = Math.asin( - clamp( m31, - 1, 1 ) );\n\n\t\t\t\tif ( Math.abs( m31 ) < 0.9999999 ) {\n\n\t\t\t\t\tthis._x = Math.atan2( m32, m33 );\n\t\t\t\t\tthis._z = Math.atan2( m21, m11 );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tthis._x = 0;\n\t\t\t\t\tthis._z = Math.atan2( - m12, m22 );\n\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'YZX':\n\n\t\t\t\tthis._z = Math.asin( clamp( m21, - 1, 1 ) );\n\n\t\t\t\tif ( Math.abs( m21 ) < 0.9999999 ) {\n\n\t\t\t\t\tthis._x = Math.atan2( - m23, m22 );\n\t\t\t\t\tthis._y = Math.atan2( - m31, m11 );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tthis._x = 0;\n\t\t\t\t\tthis._y = Math.atan2( m13, m33 );\n\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\n\t\t\tcase 'XZY':\n\n\t\t\t\tthis._z = Math.asin( - clamp( m12, - 1, 1 ) );\n\n\t\t\t\tif ( Math.abs( m12 ) < 0.9999999 ) {\n\n\t\t\t\t\tthis._x = Math.atan2( m32, m22 );\n\t\t\t\t\tthis._y = Math.atan2( m13, m11 );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tthis._x = Math.atan2( - m23, m33 );\n\t\t\t\t\tthis._y = 0;\n\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\n\t\t\t\tconsole.warn( 'THREE.Euler: .setFromRotationMatrix() encountered an unknown order: ' + order );\n\n\t\t}\n\n\t\tthis._order = order;\n\n\t\tif ( update === true ) this._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\tsetFromQuaternion( q, order, update ) {\n\n\t\t_matrix.makeRotationFromQuaternion( q );\n\n\t\treturn this.setFromRotationMatrix( _matrix, order, update );\n\n\t}\n\n\tsetFromVector3( v, order = this._order ) {\n\n\t\treturn this.set( v.x, v.y, v.z, order );\n\n\t}\n\n\treorder( newOrder ) {\n\n\t\t// WARNING: this discards revolution information -bhouston\n\n\t\t_quaternion.setFromEuler( this );\n\n\t\treturn this.setFromQuaternion( _quaternion, newOrder );\n\n\t}\n\n\tequals( euler ) {\n\n\t\treturn ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order );\n\n\t}\n\n\tfromArray( array ) {\n\n\t\tthis._x = array[ 0 ];\n\t\tthis._y = array[ 1 ];\n\t\tthis._z = array[ 2 ];\n\t\tif ( array[ 3 ] !== undefined ) this._order = array[ 3 ];\n\n\t\tthis._onChangeCallback();\n\n\t\treturn this;\n\n\t}\n\n\ttoArray( array = [], offset = 0 ) {\n\n\t\tarray[ offset ] = this._x;\n\t\tarray[ offset + 1 ] = this._y;\n\t\tarray[ offset + 2 ] = this._z;\n\t\tarray[ offset + 3 ] = this._order;\n\n\t\treturn array;\n\n\t}\n\n\t_onChange( callback ) {\n\n\t\tthis._onChangeCallback = callback;\n\n\t\treturn this;\n\n\t}\n\n\t_onChangeCallback() {}\n\n\t*[ Symbol.iterator ]() {\n\n\t\tyield this._x;\n\t\tyield this._y;\n\t\tyield this._z;\n\t\tyield this._order;\n\n\t}\n\n}\n\nEuler.DEFAULT_ORDER = 'XYZ';\n\nexport { Euler };\n","class Layers {\n\n\tconstructor() {\n\n\t\tthis.mask = 1 | 0;\n\n\t}\n\n\tset( channel ) {\n\n\t\tthis.mask = ( 1 << channel | 0 ) >>> 0;\n\n\t}\n\n\tenable( channel ) {\n\n\t\tthis.mask |= 1 << channel | 0;\n\n\t}\n\n\tenableAll() {\n\n\t\tthis.mask = 0xffffffff | 0;\n\n\t}\n\n\ttoggle( channel ) {\n\n\t\tthis.mask ^= 1 << channel | 0;\n\n\t}\n\n\tdisable( channel ) {\n\n\t\tthis.mask &= ~ ( 1 << channel | 0 );\n\n\t}\n\n\tdisableAll() {\n\n\t\tthis.mask = 0;\n\n\t}\n\n\ttest( layers ) {\n\n\t\treturn ( this.mask & layers.mask ) !== 0;\n\n\t}\n\n\tisEnabled( channel ) {\n\n\t\treturn ( this.mask & ( 1 << channel | 0 ) ) !== 0;\n\n\t}\n\n}\n\n\nexport { Layers };\n","import { Quaternion } from '../math/Quaternion.js';\nimport { Vector3 } from '../math/Vector3.js';\nimport { Matrix4 } from '../math/Matrix4.js';\nimport { EventDispatcher } from './EventDispatcher.js';\nimport { Euler } from '../math/Euler.js';\nimport { Layers } from './Layers.js';\nimport { Matrix3 } from '../math/Matrix3.js';\nimport * as MathUtils from '../math/MathUtils.js';\n\nlet _object3DId = 0;\n\nconst _v1 = /*@__PURE__*/ new Vector3();\nconst _q1 = /*@__PURE__*/ new Quaternion();\nconst _m1 = /*@__PURE__*/ new Matrix4();\nconst _target = /*@__PURE__*/ new Vector3();\n\nconst _position = /*@__PURE__*/ new Vector3();\nconst _scale = /*@__PURE__*/ new Vector3();\nconst _quaternion = /*@__PURE__*/ new Quaternion();\n\nconst _xAxis = /*@__PURE__*/ new Vector3( 1, 0, 0 );\nconst _yAxis = /*@__PURE__*/ new Vector3( 0, 1, 0 );\nconst _zAxis = /*@__PURE__*/ new Vector3( 0, 0, 1 );\n\nconst _addedEvent = { type: 'added' };\nconst _removedEvent = { type: 'removed' };\n\nclass Object3D extends EventDispatcher {\n\n\tconstructor() {\n\n\t\tsuper();\n\n\t\tthis.isObject3D = true;\n\n\t\tObject.defineProperty( this, 'id', { value: _object3DId ++ } );\n\n\t\tthis.uuid = MathUtils.generateUUID();\n\n\t\tthis.name = '';\n\t\tthis.type = 'Object3D';\n\n\t\tthis.parent = null;\n\t\tthis.children = [];\n\n\t\tthis.up = Object3D.DEFAULT_UP.clone();\n\n\t\tconst position = new Vector3();\n\t\tconst rotation = new Euler();\n\t\tconst quaternion = new Quaternion();\n\t\tconst scale = new Vector3( 1, 1, 1 );\n\n\t\tfunction onRotationChange() {\n\n\t\t\tquaternion.setFromEuler( rotation, false );\n\n\t\t}\n\n\t\tfunction onQuaternionChange() {\n\n\t\t\trotation.setFromQuaternion( quaternion, undefined, false );\n\n\t\t}\n\n\t\trotation._onChange( onRotationChange );\n\t\tquaternion._onChange( onQuaternionChange );\n\n\t\tObject.defineProperties( this, {\n\t\t\tposition: {\n\t\t\t\tconfigurable: true,\n\t\t\t\tenumerable: true,\n\t\t\t\tvalue: position\n\t\t\t},\n\t\t\trotation: {\n\t\t\t\tconfigurable: true,\n\t\t\t\tenumerable: true,\n\t\t\t\tvalue: rotation\n\t\t\t},\n\t\t\tquaternion: {\n\t\t\t\tconfigurable: true,\n\t\t\t\tenumerable: true,\n\t\t\t\tvalue: quaternion\n\t\t\t},\n\t\t\tscale: {\n\t\t\t\tconfigurable: true,\n\t\t\t\tenumerable: true,\n\t\t\t\tvalue: scale\n\t\t\t},\n\t\t\tmodelViewMatrix: {\n\t\t\t\tvalue: new Matrix4()\n\t\t\t},\n\t\t\tnormalMatrix: {\n\t\t\t\tvalue: new Matrix3()\n\t\t\t}\n\t\t} );\n\n\t\tthis.matrix = new Matrix4();\n\t\tthis.matrixWorld = new Matrix4();\n\n\t\tthis.matrixAutoUpdate = Object3D.DEFAULT_MATRIX_AUTO_UPDATE;\n\t\tthis.matrixWorldNeedsUpdate = false;\n\n\t\tthis.matrixWorldAutoUpdate = Object3D.DEFAULT_MATRIX_WORLD_AUTO_UPDATE; // checked by the renderer\n\n\t\tthis.layers = new Layers();\n\t\tthis.visible = true;\n\n\t\tthis.castShadow = false;\n\t\tthis.receiveShadow = false;\n\n\t\tthis.frustumCulled = true;\n\t\tthis.renderOrder = 0;\n\n\t\tthis.animations = [];\n\n\t\tthis.userData = {};\n\n\t}\n\n\tonBeforeRender( /* renderer, scene, camera, geometry, material, group */ ) {}\n\n\tonAfterRender( /* renderer, scene, camera, geometry, material, group */ ) {}\n\n\tapplyMatrix4( matrix ) {\n\n\t\tif ( this.matrixAutoUpdate ) this.updateMatrix();\n\n\t\tthis.matrix.premultiply( matrix );\n\n\t\tthis.matrix.decompose( this.position, this.quaternion, this.scale );\n\n\t}\n\n\tapplyQuaternion( q ) {\n\n\t\tthis.quaternion.premultiply( q );\n\n\t\treturn this;\n\n\t}\n\n\tsetRotationFromAxisAngle( axis, angle ) {\n\n\t\t// assumes axis is normalized\n\n\t\tthis.quaternion.setFromAxisAngle( axis, angle );\n\n\t}\n\n\tsetRotationFromEuler( euler ) {\n\n\t\tthis.quaternion.setFromEuler( euler, true );\n\n\t}\n\n\tsetRotationFromMatrix( m ) {\n\n\t\t// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)\n\n\t\tthis.quaternion.setFromRotationMatrix( m );\n\n\t}\n\n\tsetRotationFromQuaternion( q ) {\n\n\t\t// assumes q is normalized\n\n\t\tthis.quaternion.copy( q );\n\n\t}\n\n\trotateOnAxis( axis, angle ) {\n\n\t\t// rotate object on axis in object space\n\t\t// axis is assumed to be normalized\n\n\t\t_q1.setFromAxisAngle( axis, angle );\n\n\t\tthis.quaternion.multiply( _q1 );\n\n\t\treturn this;\n\n\t}\n\n\trotateOnWorldAxis( axis, angle ) {\n\n\t\t// rotate object on axis in world space\n\t\t// axis is assumed to be normalized\n\t\t// method assumes no rotated parent\n\n\t\t_q1.setFromAxisAngle( axis, angle );\n\n\t\tthis.quaternion.premultiply( _q1 );\n\n\t\treturn this;\n\n\t}\n\n\trotateX( angle ) {\n\n\t\treturn this.rotateOnAxis( _xAxis, angle );\n\n\t}\n\n\trotateY( angle ) {\n\n\t\treturn this.rotateOnAxis( _yAxis, angle );\n\n\t}\n\n\trotateZ( angle ) {\n\n\t\treturn this.rotateOnAxis( _zAxis, angle );\n\n\t}\n\n\ttranslateOnAxis( axis, distance ) {\n\n\t\t// translate object by distance along axis in object space\n\t\t// axis is assumed to be normalized\n\n\t\t_v1.copy( axis ).applyQuaternion( this.quaternion );\n\n\t\tthis.position.add( _v1.multiplyScalar( distance ) );\n\n\t\treturn this;\n\n\t}\n\n\ttranslateX( distance ) {\n\n\t\treturn this.translateOnAxis( _xAxis, distance );\n\n\t}\n\n\ttranslateY( distance ) {\n\n\t\treturn this.translateOnAxis( _yAxis, distance );\n\n\t}\n\n\ttranslateZ( distance ) {\n\n\t\treturn this.translateOnAxis( _zAxis, distance );\n\n\t}\n\n\tlocalToWorld( vector ) {\n\n\t\tthis.updateWorldMatrix( true, false );\n\n\t\treturn vector.applyMatrix4( this.matrixWorld );\n\n\t}\n\n\tworldToLocal( vector ) {\n\n\t\tthis.updateWorldMatrix( true, false );\n\n\t\treturn vector.applyMatrix4( _m1.copy( this.matrixWorld ).invert() );\n\n\t}\n\n\tlookAt( x, y, z ) {\n\n\t\t// This method does not support objects having non-uniformly-scaled parent(s)\n\n\t\tif ( x.isVector3 ) {\n\n\t\t\t_target.copy( x );\n\n\t\t} else {\n\n\t\t\t_target.set( x, y, z );\n\n\t\t}\n\n\t\tconst parent = this.parent;\n\n\t\tthis.updateWorldMatrix( true, false );\n\n\t\t_position.setFromMatrixPosition( this.matrixWorld );\n\n\t\tif ( this.isCamera || this.isLight ) {\n\n\t\t\t_m1.lookAt( _position, _target, this.up );\n\n\t\t} else {\n\n\t\t\t_m1.lookAt( _target, _position, this.up );\n\n\t\t}\n\n\t\tthis.quaternion.setFromRotationMatrix( _m1 );\n\n\t\tif ( parent ) {\n\n\t\t\t_m1.extractRotation( parent.matrixWorld );\n\t\t\t_q1.setFromRotationMatrix( _m1 );\n\t\t\tthis.quaternion.premultiply( _q1.invert() );\n\n\t\t}\n\n\t}\n\n\tadd( object ) {\n\n\t\tif ( arguments.length > 1 ) {\n\n\t\t\tfor ( let i = 0; i < arguments.length; i ++ ) {\n\n\t\t\t\tthis.add( arguments[ i ] );\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t}\n\n\t\tif ( object === this ) {\n\n\t\t\tconsole.error( 'THREE.Object3D.add: object can\\'t be added as a child of itself.', object );\n\t\t\treturn this;\n\n\t\t}\n\n\t\tif ( object && object.isObject3D ) {\n\n\t\t\tif ( object.parent !== null ) {\n\n\t\t\t\tobject.parent.remove( object );\n\n\t\t\t}\n\n\t\t\tobject.parent = this;\n\t\t\tthis.children.push( object );\n\n\t\t\tobject.dispatchEvent( _addedEvent );\n\n\t\t} else {\n\n\t\t\tconsole.error( 'THREE.Object3D.add: object not an instance of THREE.Object3D.', object );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tremove( object ) {\n\n\t\tif ( arguments.length > 1 ) {\n\n\t\t\tfor ( let i = 0; i < arguments.length; i ++ ) {\n\n\t\t\t\tthis.remove( arguments[ i ] );\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t}\n\n\t\tconst index = this.children.indexOf( object );\n\n\t\tif ( index !== - 1 ) {\n\n\t\t\tobject.parent = null;\n\t\t\tthis.children.splice( index, 1 );\n\n\t\t\tobject.dispatchEvent( _removedEvent );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tremoveFromParent() {\n\n\t\tconst parent = this.parent;\n\n\t\tif ( parent !== null ) {\n\n\t\t\tparent.remove( this );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tclear() {\n\n\t\treturn this.remove( ... this.children );\n\n\t}\n\n\tattach( object ) {\n\n\t\t// adds object as a child of this, while maintaining the object's world transform\n\n\t\t// Note: This method does not support scene graphs having non-uniformly-scaled nodes(s)\n\n\t\tthis.updateWorldMatrix( true, false );\n\n\t\t_m1.copy( this.matrixWorld ).invert();\n\n\t\tif ( object.parent !== null ) {\n\n\t\t\tobject.parent.updateWorldMatrix( true, false );\n\n\t\t\t_m1.multiply( object.parent.matrixWorld );\n\n\t\t}\n\n\t\tobject.applyMatrix4( _m1 );\n\n\t\tthis.add( object );\n\n\t\tobject.updateWorldMatrix( false, true );\n\n\t\treturn this;\n\n\t}\n\n\tgetObjectById( id ) {\n\n\t\treturn this.getObjectByProperty( 'id', id );\n\n\t}\n\n\tgetObjectByName( name ) {\n\n\t\treturn this.getObjectByProperty( 'name', name );\n\n\t}\n\n\tgetObjectByProperty( name, value ) {\n\n\t\tif ( this[ name ] === value ) return this;\n\n\t\tfor ( let i = 0, l = this.children.length; i < l; i ++ ) {\n\n\t\t\tconst child = this.children[ i ];\n\t\t\tconst object = child.getObjectByProperty( name, value );\n\n\t\t\tif ( object !== undefined ) {\n\n\t\t\t\treturn object;\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn undefined;\n\n\t}\n\n\tgetObjectsByProperty( name, value ) {\n\n\t\tlet result = [];\n\n\t\tif ( this[ name ] === value ) result.push( this );\n\n\t\tfor ( let i = 0, l = this.children.length; i < l; i ++ ) {\n\n\t\t\tconst childResult = this.children[ i ].getObjectsByProperty( name, value );\n\n\t\t\tif ( childResult.length > 0 ) {\n\n\t\t\t\tresult = result.concat( childResult );\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn result;\n\n\t}\n\n\tgetWorldPosition( target ) {\n\n\t\tthis.updateWorldMatrix( true, false );\n\n\t\treturn target.setFromMatrixPosition( this.matrixWorld );\n\n\t}\n\n\tgetWorldQuaternion( target ) {\n\n\t\tthis.updateWorldMatrix( true, false );\n\n\t\tthis.matrixWorld.decompose( _position, target, _scale );\n\n\t\treturn target;\n\n\t}\n\n\tgetWorldScale( target ) {\n\n\t\tthis.updateWorldMatrix( true, false );\n\n\t\tthis.matrixWorld.decompose( _position, _quaternion, target );\n\n\t\treturn target;\n\n\t}\n\n\tgetWorldDirection( target ) {\n\n\t\tthis.updateWorldMatrix( true, false );\n\n\t\tconst e = this.matrixWorld.elements;\n\n\t\treturn target.set( e[ 8 ], e[ 9 ], e[ 10 ] ).normalize();\n\n\t}\n\n\traycast( /* raycaster, intersects */ ) {}\n\n\ttraverse( callback ) {\n\n\t\tcallback( this );\n\n\t\tconst children = this.children;\n\n\t\tfor ( let i = 0, l = children.length; i < l; i ++ ) {\n\n\t\t\tchildren[ i ].traverse( callback );\n\n\t\t}\n\n\t}\n\n\ttraverseVisible( callback ) {\n\n\t\tif ( this.visible === false ) return;\n\n\t\tcallback( this );\n\n\t\tconst children = this.children;\n\n\t\tfor ( let i = 0, l = children.length; i < l; i ++ ) {\n\n\t\t\tchildren[ i ].traverseVisible( callback );\n\n\t\t}\n\n\t}\n\n\ttraverseAncestors( callback ) {\n\n\t\tconst parent = this.parent;\n\n\t\tif ( parent !== null ) {\n\n\t\t\tcallback( parent );\n\n\t\t\tparent.traverseAncestors( callback );\n\n\t\t}\n\n\t}\n\n\tupdateMatrix() {\n\n\t\tthis.matrix.compose( this.position, this.quaternion, this.scale );\n\n\t\tthis.matrixWorldNeedsUpdate = true;\n\n\t}\n\n\tupdateMatrixWorld( force ) {\n\n\t\tif ( this.matrixAutoUpdate ) this.updateMatrix();\n\n\t\tif ( this.matrixWorldNeedsUpdate || force ) {\n\n\t\t\tif ( this.parent === null ) {\n\n\t\t\t\tthis.matrixWorld.copy( this.matrix );\n\n\t\t\t} else {\n\n\t\t\t\tthis.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );\n\n\t\t\t}\n\n\t\t\tthis.matrixWorldNeedsUpdate = false;\n\n\t\t\tforce = true;\n\n\t\t}\n\n\t\t// update children\n\n\t\tconst children = this.children;\n\n\t\tfor ( let i = 0, l = children.length; i < l; i ++ ) {\n\n\t\t\tconst child = children[ i ];\n\n\t\t\tif ( child.matrixWorldAutoUpdate === true || force === true ) {\n\n\t\t\t\tchild.updateMatrixWorld( force );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tupdateWorldMatrix( updateParents, updateChildren ) {\n\n\t\tconst parent = this.parent;\n\n\t\tif ( updateParents === true && parent !== null && parent.matrixWorldAutoUpdate === true ) {\n\n\t\t\tparent.updateWorldMatrix( true, false );\n\n\t\t}\n\n\t\tif ( this.matrixAutoUpdate ) this.updateMatrix();\n\n\t\tif ( this.parent === null ) {\n\n\t\t\tthis.matrixWorld.copy( this.matrix );\n\n\t\t} else {\n\n\t\t\tthis.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );\n\n\t\t}\n\n\t\t// update children\n\n\t\tif ( updateChildren === true ) {\n\n\t\t\tconst children = this.children;\n\n\t\t\tfor ( let i = 0, l = children.length; i < l; i ++ ) {\n\n\t\t\t\tconst child = children[ i ];\n\n\t\t\t\tif ( child.matrixWorldAutoUpdate === true ) {\n\n\t\t\t\t\tchild.updateWorldMatrix( false, true );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\ttoJSON( meta ) {\n\n\t\t// meta is a string when called from JSON.stringify\n\t\tconst isRootObject = ( meta === undefined || typeof meta === 'string' );\n\n\t\tconst output = {};\n\n\t\t// meta is a hash used to collect geometries, materials.\n\t\t// not providing it implies that this is the root object\n\t\t// being serialized.\n\t\tif ( isRootObject ) {\n\n\t\t\t// initialize meta obj\n\t\t\tmeta = {\n\t\t\t\tgeometries: {},\n\t\t\t\tmaterials: {},\n\t\t\t\ttextures: {},\n\t\t\t\timages: {},\n\t\t\t\tshapes: {},\n\t\t\t\tskeletons: {},\n\t\t\t\tanimations: {},\n\t\t\t\tnodes: {}\n\t\t\t};\n\n\t\t\toutput.metadata = {\n\t\t\t\tversion: 4.6,\n\t\t\t\ttype: 'Object',\n\t\t\t\tgenerator: 'Object3D.toJSON'\n\t\t\t};\n\n\t\t}\n\n\t\t// standard Object3D serialization\n\n\t\tconst object = {};\n\n\t\tobject.uuid = this.uuid;\n\t\tobject.type = this.type;\n\n\t\tif ( this.name !== '' ) object.name = this.name;\n\t\tif ( this.castShadow === true ) object.castShadow = true;\n\t\tif ( this.receiveShadow === true ) object.receiveShadow = true;\n\t\tif ( this.visible === false ) object.visible = false;\n\t\tif ( this.frustumCulled === false ) object.frustumCulled = false;\n\t\tif ( this.renderOrder !== 0 ) object.renderOrder = this.renderOrder;\n\t\tif ( Object.keys( this.userData ).length > 0 ) object.userData = this.userData;\n\n\t\tobject.layers = this.layers.mask;\n\t\tobject.matrix = this.matrix.toArray();\n\t\tobject.up = this.up.toArray();\n\n\t\tif ( this.matrixAutoUpdate === false ) object.matrixAutoUpdate = false;\n\n\t\t// object specific properties\n\n\t\tif ( this.isInstancedMesh ) {\n\n\t\t\tobject.type = 'InstancedMesh';\n\t\t\tobject.count = this.count;\n\t\t\tobject.instanceMatrix = this.instanceMatrix.toJSON();\n\t\t\tif ( this.instanceColor !== null ) object.instanceColor = this.instanceColor.toJSON();\n\n\t\t}\n\n\t\t//\n\n\t\tfunction serialize( library, element ) {\n\n\t\t\tif ( library[ element.uuid ] === undefined ) {\n\n\t\t\t\tlibrary[ element.uuid ] = element.toJSON( meta );\n\n\t\t\t}\n\n\t\t\treturn element.uuid;\n\n\t\t}\n\n\t\tif ( this.isScene ) {\n\n\t\t\tif ( this.background ) {\n\n\t\t\t\tif ( this.background.isColor ) {\n\n\t\t\t\t\tobject.background = this.background.toJSON();\n\n\t\t\t\t} else if ( this.background.isTexture ) {\n\n\t\t\t\t\tobject.background = this.background.toJSON( meta ).uuid;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( this.environment && this.environment.isTexture && this.environment.isRenderTargetTexture !== true ) {\n\n\t\t\t\tobject.environment = this.environment.toJSON( meta ).uuid;\n\n\t\t\t}\n\n\t\t} else if ( this.isMesh || this.isLine || this.isPoints ) {\n\n\t\t\tobject.geometry = serialize( meta.geometries, this.geometry );\n\n\t\t\tconst parameters = this.geometry.parameters;\n\n\t\t\tif ( parameters !== undefined && parameters.shapes !== undefined ) {\n\n\t\t\t\tconst shapes = parameters.shapes;\n\n\t\t\t\tif ( Array.isArray( shapes ) ) {\n\n\t\t\t\t\tfor ( let i = 0, l = shapes.length; i < l; i ++ ) {\n\n\t\t\t\t\t\tconst shape = shapes[ i ];\n\n\t\t\t\t\t\tserialize( meta.shapes, shape );\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\tserialize( meta.shapes, shapes );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( this.isSkinnedMesh ) {\n\n\t\t\tobject.bindMode = this.bindMode;\n\t\t\tobject.bindMatrix = this.bindMatrix.toArray();\n\n\t\t\tif ( this.skeleton !== undefined ) {\n\n\t\t\t\tserialize( meta.skeletons, this.skeleton );\n\n\t\t\t\tobject.skeleton = this.skeleton.uuid;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( this.material !== undefined ) {\n\n\t\t\tif ( Array.isArray( this.material ) ) {\n\n\t\t\t\tconst uuids = [];\n\n\t\t\t\tfor ( let i = 0, l = this.material.length; i < l; i ++ ) {\n\n\t\t\t\t\tuuids.push( serialize( meta.materials, this.material[ i ] ) );\n\n\t\t\t\t}\n\n\t\t\t\tobject.material = uuids;\n\n\t\t\t} else {\n\n\t\t\t\tobject.material = serialize( meta.materials, this.material );\n\n\t\t\t}\n\n\t\t}\n\n\t\t//\n\n\t\tif ( this.children.length > 0 ) {\n\n\t\t\tobject.children = [];\n\n\t\t\tfor ( let i = 0; i < this.children.length; i ++ ) {\n\n\t\t\t\tobject.children.push( this.children[ i ].toJSON( meta ).object );\n\n\t\t\t}\n\n\t\t}\n\n\t\t//\n\n\t\tif ( this.animations.length > 0 ) {\n\n\t\t\tobject.animations = [];\n\n\t\t\tfor ( let i = 0; i < this.animations.length; i ++ ) {\n\n\t\t\t\tconst animation = this.animations[ i ];\n\n\t\t\t\tobject.animations.push( serialize( meta.animations, animation ) );\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( isRootObject ) {\n\n\t\t\tconst geometries = extractFromCache( meta.geometries );\n\t\t\tconst materials = extractFromCache( meta.materials );\n\t\t\tconst textures = extractFromCache( meta.textures );\n\t\t\tconst images = extractFromCache( meta.images );\n\t\t\tconst shapes = extractFromCache( meta.shapes );\n\t\t\tconst skeletons = extractFromCache( meta.skeletons );\n\t\t\tconst animations = extractFromCache( meta.animations );\n\t\t\tconst nodes = extractFromCache( meta.nodes );\n\n\t\t\tif ( geometries.length > 0 ) output.geometries = geometries;\n\t\t\tif ( materials.length > 0 ) output.materials = materials;\n\t\t\tif ( textures.length > 0 ) output.textures = textures;\n\t\t\tif ( images.length > 0 ) output.images = images;\n\t\t\tif ( shapes.length > 0 ) output.shapes = shapes;\n\t\t\tif ( skeletons.length > 0 ) output.skeletons = skeletons;\n\t\t\tif ( animations.length > 0 ) output.animations = animations;\n\t\t\tif ( nodes.length > 0 ) output.nodes = nodes;\n\n\t\t}\n\n\t\toutput.object = object;\n\n\t\treturn output;\n\n\t\t// extract data from the cache hash\n\t\t// remove metadata on each item\n\t\t// and return as array\n\t\tfunction extractFromCache( cache ) {\n\n\t\t\tconst values = [];\n\t\t\tfor ( const key in cache ) {\n\n\t\t\t\tconst data = cache[ key ];\n\t\t\t\tdelete data.metadata;\n\t\t\t\tvalues.push( data );\n\n\t\t\t}\n\n\t\t\treturn values;\n\n\t\t}\n\n\t}\n\n\tclone( recursive ) {\n\n\t\treturn new this.constructor().copy( this, recursive );\n\n\t}\n\n\tcopy( source, recursive = true ) {\n\n\t\tthis.name = source.name;\n\n\t\tthis.up.copy( source.up );\n\n\t\tthis.position.copy( source.position );\n\t\tthis.rotation.order = source.rotation.order;\n\t\tthis.quaternion.copy( source.quaternion );\n\t\tthis.scale.copy( source.scale );\n\n\t\tthis.matrix.copy( source.matrix );\n\t\tthis.matrixWorld.copy( source.matrixWorld );\n\n\t\tthis.matrixAutoUpdate = source.matrixAutoUpdate;\n\t\tthis.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate;\n\n\t\tthis.matrixWorldAutoUpdate = source.matrixWorldAutoUpdate;\n\n\t\tthis.layers.mask = source.layers.mask;\n\t\tthis.visible = source.visible;\n\n\t\tthis.castShadow = source.castShadow;\n\t\tthis.receiveShadow = source.receiveShadow;\n\n\t\tthis.frustumCulled = source.frustumCulled;\n\t\tthis.renderOrder = source.renderOrder;\n\n\t\tthis.animations = source.animations.slice();\n\n\t\tthis.userData = JSON.parse( JSON.stringify( source.userData ) );\n\n\t\tif ( recursive === true ) {\n\n\t\t\tfor ( let i = 0; i < source.children.length; i ++ ) {\n\n\t\t\t\tconst child = source.children[ i ];\n\t\t\t\tthis.add( child.clone() );\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n}\n\nObject3D.DEFAULT_UP = /*@__PURE__*/ new Vector3( 0, 1, 0 );\nObject3D.DEFAULT_MATRIX_AUTO_UPDATE = true;\nObject3D.DEFAULT_MATRIX_WORLD_AUTO_UPDATE = true;\n\nexport { Object3D };\n","import { Vector3 } from './Vector3.js';\n\nconst _v0 = /*@__PURE__*/ new Vector3();\nconst _v1 = /*@__PURE__*/ new Vector3();\nconst _v2 = /*@__PURE__*/ new Vector3();\nconst _v3 = /*@__PURE__*/ new Vector3();\n\nconst _vab = /*@__PURE__*/ new Vector3();\nconst _vac = /*@__PURE__*/ new Vector3();\nconst _vbc = /*@__PURE__*/ new Vector3();\nconst _vap = /*@__PURE__*/ new Vector3();\nconst _vbp = /*@__PURE__*/ new Vector3();\nconst _vcp = /*@__PURE__*/ new Vector3();\n\nlet warnedGetUV = false;\n\nclass Triangle {\n\n\tconstructor( a = new Vector3(), b = new Vector3(), c = new Vector3() ) {\n\n\t\tthis.a = a;\n\t\tthis.b = b;\n\t\tthis.c = c;\n\n\t}\n\n\tstatic getNormal( a, b, c, target ) {\n\n\t\ttarget.subVectors( c, b );\n\t\t_v0.subVectors( a, b );\n\t\ttarget.cross( _v0 );\n\n\t\tconst targetLengthSq = target.lengthSq();\n\t\tif ( targetLengthSq > 0 ) {\n\n\t\t\treturn target.multiplyScalar( 1 / Math.sqrt( targetLengthSq ) );\n\n\t\t}\n\n\t\treturn target.set( 0, 0, 0 );\n\n\t}\n\n\t// static/instance method to calculate barycentric coordinates\n\t// based on: http://www.blackpawn.com/texts/pointinpoly/default.html\n\tstatic getBarycoord( point, a, b, c, target ) {\n\n\t\t_v0.subVectors( c, a );\n\t\t_v1.subVectors( b, a );\n\t\t_v2.subVectors( point, a );\n\n\t\tconst dot00 = _v0.dot( _v0 );\n\t\tconst dot01 = _v0.dot( _v1 );\n\t\tconst dot02 = _v0.dot( _v2 );\n\t\tconst dot11 = _v1.dot( _v1 );\n\t\tconst dot12 = _v1.dot( _v2 );\n\n\t\tconst denom = ( dot00 * dot11 - dot01 * dot01 );\n\n\t\t// collinear or singular triangle\n\t\tif ( denom === 0 ) {\n\n\t\t\t// arbitrary location outside of triangle?\n\t\t\t// not sure if this is the best idea, maybe should be returning undefined\n\t\t\treturn target.set( - 2, - 1, - 1 );\n\n\t\t}\n\n\t\tconst invDenom = 1 / denom;\n\t\tconst u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom;\n\t\tconst v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom;\n\n\t\t// barycentric coordinates must always sum to 1\n\t\treturn target.set( 1 - u - v, v, u );\n\n\t}\n\n\tstatic containsPoint( point, a, b, c ) {\n\n\t\tthis.getBarycoord( point, a, b, c, _v3 );\n\n\t\treturn ( _v3.x >= 0 ) && ( _v3.y >= 0 ) && ( ( _v3.x + _v3.y ) <= 1 );\n\n\t}\n\n\tstatic getUV( point, p1, p2, p3, uv1, uv2, uv3, target ) { // @deprecated, r151\n\n\t\tif ( warnedGetUV === false ) {\n\n\t\t\tconsole.warn( 'THREE.Triangle.getUV() has been renamed to THREE.Triangle.getInterpolation().' );\n\n\t\t\twarnedGetUV = true;\n\n\t\t}\n\n\t\treturn this.getInterpolation( point, p1, p2, p3, uv1, uv2, uv3, target );\n\n\t}\n\n\tstatic getInterpolation( point, p1, p2, p3, v1, v2, v3, target ) {\n\n\t\tthis.getBarycoord( point, p1, p2, p3, _v3 );\n\n\t\ttarget.setScalar( 0 );\n\t\ttarget.addScaledVector( v1, _v3.x );\n\t\ttarget.addScaledVector( v2, _v3.y );\n\t\ttarget.addScaledVector( v3, _v3.z );\n\n\t\treturn target;\n\n\t}\n\n\tstatic isFrontFacing( a, b, c, direction ) {\n\n\t\t_v0.subVectors( c, b );\n\t\t_v1.subVectors( a, b );\n\n\t\t// strictly front facing\n\t\treturn ( _v0.cross( _v1 ).dot( direction ) < 0 ) ? true : false;\n\n\t}\n\n\tset( a, b, c ) {\n\n\t\tthis.a.copy( a );\n\t\tthis.b.copy( b );\n\t\tthis.c.copy( c );\n\n\t\treturn this;\n\n\t}\n\n\tsetFromPointsAndIndices( points, i0, i1, i2 ) {\n\n\t\tthis.a.copy( points[ i0 ] );\n\t\tthis.b.copy( points[ i1 ] );\n\t\tthis.c.copy( points[ i2 ] );\n\n\t\treturn this;\n\n\t}\n\n\tsetFromAttributeAndIndices( attribute, i0, i1, i2 ) {\n\n\t\tthis.a.fromBufferAttribute( attribute, i0 );\n\t\tthis.b.fromBufferAttribute( attribute, i1 );\n\t\tthis.c.fromBufferAttribute( attribute, i2 );\n\n\t\treturn this;\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n\tcopy( triangle ) {\n\n\t\tthis.a.copy( triangle.a );\n\t\tthis.b.copy( triangle.b );\n\t\tthis.c.copy( triangle.c );\n\n\t\treturn this;\n\n\t}\n\n\tgetArea() {\n\n\t\t_v0.subVectors( this.c, this.b );\n\t\t_v1.subVectors( this.a, this.b );\n\n\t\treturn _v0.cross( _v1 ).length() * 0.5;\n\n\t}\n\n\tgetMidpoint( target ) {\n\n\t\treturn target.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 );\n\n\t}\n\n\tgetNormal( target ) {\n\n\t\treturn Triangle.getNormal( this.a, this.b, this.c, target );\n\n\t}\n\n\tgetPlane( target ) {\n\n\t\treturn target.setFromCoplanarPoints( this.a, this.b, this.c );\n\n\t}\n\n\tgetBarycoord( point, target ) {\n\n\t\treturn Triangle.getBarycoord( point, this.a, this.b, this.c, target );\n\n\t}\n\n\tgetUV( point, uv1, uv2, uv3, target ) { // @deprecated, r151\n\n\t\tif ( warnedGetUV === false ) {\n\n\t\t\tconsole.warn( 'THREE.Triangle.getUV() has been renamed to THREE.Triangle.getInterpolation().' );\n\n\t\t\twarnedGetUV = true;\n\n\t\t}\n\n\t\treturn Triangle.getInterpolation( point, this.a, this.b, this.c, uv1, uv2, uv3, target );\n\n\t}\n\n\tgetInterpolation( point, v1, v2, v3, target ) {\n\n\t\treturn Triangle.getInterpolation( point, this.a, this.b, this.c, v1, v2, v3, target );\n\n\t}\n\n\tcontainsPoint( point ) {\n\n\t\treturn Triangle.containsPoint( point, this.a, this.b, this.c );\n\n\t}\n\n\tisFrontFacing( direction ) {\n\n\t\treturn Triangle.isFrontFacing( this.a, this.b, this.c, direction );\n\n\t}\n\n\tintersectsBox( box ) {\n\n\t\treturn box.intersectsTriangle( this );\n\n\t}\n\n\tclosestPointToPoint( p, target ) {\n\n\t\tconst a = this.a, b = this.b, c = this.c;\n\t\tlet v, w;\n\n\t\t// algorithm thanks to Real-Time Collision Detection by Christer Ericson,\n\t\t// published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc.,\n\t\t// under the accompanying license; see chapter 5.1.5 for detailed explanation.\n\t\t// basically, we're distinguishing which of the voronoi regions of the triangle\n\t\t// the point lies in with the minimum amount of redundant computation.\n\n\t\t_vab.subVectors( b, a );\n\t\t_vac.subVectors( c, a );\n\t\t_vap.subVectors( p, a );\n\t\tconst d1 = _vab.dot( _vap );\n\t\tconst d2 = _vac.dot( _vap );\n\t\tif ( d1 <= 0 && d2 <= 0 ) {\n\n\t\t\t// vertex region of A; barycentric coords (1, 0, 0)\n\t\t\treturn target.copy( a );\n\n\t\t}\n\n\t\t_vbp.subVectors( p, b );\n\t\tconst d3 = _vab.dot( _vbp );\n\t\tconst d4 = _vac.dot( _vbp );\n\t\tif ( d3 >= 0 && d4 <= d3 ) {\n\n\t\t\t// vertex region of B; barycentric coords (0, 1, 0)\n\t\t\treturn target.copy( b );\n\n\t\t}\n\n\t\tconst vc = d1 * d4 - d3 * d2;\n\t\tif ( vc <= 0 && d1 >= 0 && d3 <= 0 ) {\n\n\t\t\tv = d1 / ( d1 - d3 );\n\t\t\t// edge region of AB; barycentric coords (1-v, v, 0)\n\t\t\treturn target.copy( a ).addScaledVector( _vab, v );\n\n\t\t}\n\n\t\t_vcp.subVectors( p, c );\n\t\tconst d5 = _vab.dot( _vcp );\n\t\tconst d6 = _vac.dot( _vcp );\n\t\tif ( d6 >= 0 && d5 <= d6 ) {\n\n\t\t\t// vertex region of C; barycentric coords (0, 0, 1)\n\t\t\treturn target.copy( c );\n\n\t\t}\n\n\t\tconst vb = d5 * d2 - d1 * d6;\n\t\tif ( vb <= 0 && d2 >= 0 && d6 <= 0 ) {\n\n\t\t\tw = d2 / ( d2 - d6 );\n\t\t\t// edge region of AC; barycentric coords (1-w, 0, w)\n\t\t\treturn target.copy( a ).addScaledVector( _vac, w );\n\n\t\t}\n\n\t\tconst va = d3 * d6 - d5 * d4;\n\t\tif ( va <= 0 && ( d4 - d3 ) >= 0 && ( d5 - d6 ) >= 0 ) {\n\n\t\t\t_vbc.subVectors( c, b );\n\t\t\tw = ( d4 - d3 ) / ( ( d4 - d3 ) + ( d5 - d6 ) );\n\t\t\t// edge region of BC; barycentric coords (0, 1-w, w)\n\t\t\treturn target.copy( b ).addScaledVector( _vbc, w ); // edge region of BC\n\n\t\t}\n\n\t\t// face region\n\t\tconst denom = 1 / ( va + vb + vc );\n\t\t// u = va * denom\n\t\tv = vb * denom;\n\t\tw = vc * denom;\n\n\t\treturn target.copy( a ).addScaledVector( _vab, v ).addScaledVector( _vac, w );\n\n\t}\n\n\tequals( triangle ) {\n\n\t\treturn triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c );\n\n\t}\n\n}\n\nexport { Triangle };\n","import { clamp, euclideanModulo, lerp } from './MathUtils.js';\nimport { ColorManagement, SRGBToLinear, LinearToSRGB } from './ColorManagement.js';\nimport { SRGBColorSpace } from '../constants.js';\n\nconst _colorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF,\n\t'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2,\n\t'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50,\n\t'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B,\n\t'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B,\n\t'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F,\n\t'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3,\n\t'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222,\n\t'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700,\n\t'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4,\n\t'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00,\n\t'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3,\n\t'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA,\n\t'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32,\n\t'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3,\n\t'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC,\n\t'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD,\n\t'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6,\n\t'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9,\n\t'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'rebeccapurple': 0x663399, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F,\n\t'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE,\n\t'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA,\n\t'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0,\n\t'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 };\n\nconst _hslA = { h: 0, s: 0, l: 0 };\nconst _hslB = { h: 0, s: 0, l: 0 };\n\nfunction hue2rgb( p, q, t ) {\n\n\tif ( t < 0 ) t += 1;\n\tif ( t > 1 ) t -= 1;\n\tif ( t < 1 / 6 ) return p + ( q - p ) * 6 * t;\n\tif ( t < 1 / 2 ) return q;\n\tif ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t );\n\treturn p;\n\n}\n\nclass Color {\n\n\tconstructor( r, g, b ) {\n\n\t\tthis.isColor = true;\n\n\t\tthis.r = 1;\n\t\tthis.g = 1;\n\t\tthis.b = 1;\n\n\t\treturn this.set( r, g, b );\n\n\t}\n\n\tset( r, g, b ) {\n\n\t\tif ( g === undefined && b === undefined ) {\n\n\t\t\t// r is THREE.Color, hex or string\n\n\t\t\tconst value = r;\n\n\t\t\tif ( value && value.isColor ) {\n\n\t\t\t\tthis.copy( value );\n\n\t\t\t} else if ( typeof value === 'number' ) {\n\n\t\t\t\tthis.setHex( value );\n\n\t\t\t} else if ( typeof value === 'string' ) {\n\n\t\t\t\tthis.setStyle( value );\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\tthis.setRGB( r, g, b );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tsetScalar( scalar ) {\n\n\t\tthis.r = scalar;\n\t\tthis.g = scalar;\n\t\tthis.b = scalar;\n\n\t\treturn this;\n\n\t}\n\n\tsetHex( hex, colorSpace = SRGBColorSpace ) {\n\n\t\thex = Math.floor( hex );\n\n\t\tthis.r = ( hex >> 16 & 255 ) / 255;\n\t\tthis.g = ( hex >> 8 & 255 ) / 255;\n\t\tthis.b = ( hex & 255 ) / 255;\n\n\t\tColorManagement.toWorkingColorSpace( this, colorSpace );\n\n\t\treturn this;\n\n\t}\n\n\tsetRGB( r, g, b, colorSpace = ColorManagement.workingColorSpace ) {\n\n\t\tthis.r = r;\n\t\tthis.g = g;\n\t\tthis.b = b;\n\n\t\tColorManagement.toWorkingColorSpace( this, colorSpace );\n\n\t\treturn this;\n\n\t}\n\n\tsetHSL( h, s, l, colorSpace = ColorManagement.workingColorSpace ) {\n\n\t\t// h,s,l ranges are in 0.0 - 1.0\n\t\th = euclideanModulo( h, 1 );\n\t\ts = clamp( s, 0, 1 );\n\t\tl = clamp( l, 0, 1 );\n\n\t\tif ( s === 0 ) {\n\n\t\t\tthis.r = this.g = this.b = l;\n\n\t\t} else {\n\n\t\t\tconst p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s );\n\t\t\tconst q = ( 2 * l ) - p;\n\n\t\t\tthis.r = hue2rgb( q, p, h + 1 / 3 );\n\t\t\tthis.g = hue2rgb( q, p, h );\n\t\t\tthis.b = hue2rgb( q, p, h - 1 / 3 );\n\n\t\t}\n\n\t\tColorManagement.toWorkingColorSpace( this, colorSpace );\n\n\t\treturn this;\n\n\t}\n\n\tsetStyle( style, colorSpace = SRGBColorSpace ) {\n\n\t\tfunction handleAlpha( string ) {\n\n\t\t\tif ( string === undefined ) return;\n\n\t\t\tif ( parseFloat( string ) < 1 ) {\n\n\t\t\t\tconsole.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' );\n\n\t\t\t}\n\n\t\t}\n\n\n\t\tlet m;\n\n\t\tif ( m = /^(\\w+)\\(([^\\)]*)\\)/.exec( style ) ) {\n\n\t\t\t// rgb / hsl\n\n\t\t\tlet color;\n\t\t\tconst name = m[ 1 ];\n\t\t\tconst components = m[ 2 ];\n\n\t\t\tswitch ( name ) {\n\n\t\t\t\tcase 'rgb':\n\t\t\t\tcase 'rgba':\n\n\t\t\t\t\tif ( color = /^\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*(?:,\\s*(\\d*\\.?\\d+)\\s*)?$/.exec( components ) ) {\n\n\t\t\t\t\t\t// rgb(255,0,0) rgba(255,0,0,0.5)\n\n\t\t\t\t\t\thandleAlpha( color[ 4 ] );\n\n\t\t\t\t\t\treturn this.setRGB(\n\t\t\t\t\t\t\tMath.min( 255, parseInt( color[ 1 ], 10 ) ) / 255,\n\t\t\t\t\t\t\tMath.min( 255, parseInt( color[ 2 ], 10 ) ) / 255,\n\t\t\t\t\t\t\tMath.min( 255, parseInt( color[ 3 ], 10 ) ) / 255,\n\t\t\t\t\t\t\tcolorSpace\n\t\t\t\t\t\t);\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( color = /^\\s*(\\d+)\\%\\s*,\\s*(\\d+)\\%\\s*,\\s*(\\d+)\\%\\s*(?:,\\s*(\\d*\\.?\\d+)\\s*)?$/.exec( components ) ) {\n\n\t\t\t\t\t\t// rgb(100%,0%,0%) rgba(100%,0%,0%,0.5)\n\n\t\t\t\t\t\thandleAlpha( color[ 4 ] );\n\n\t\t\t\t\t\treturn this.setRGB(\n\t\t\t\t\t\t\tMath.min( 100, parseInt( color[ 1 ], 10 ) ) / 100,\n\t\t\t\t\t\t\tMath.min( 100, parseInt( color[ 2 ], 10 ) ) / 100,\n\t\t\t\t\t\t\tMath.min( 100, parseInt( color[ 3 ], 10 ) ) / 100,\n\t\t\t\t\t\t\tcolorSpace\n\t\t\t\t\t\t);\n\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'hsl':\n\t\t\t\tcase 'hsla':\n\n\t\t\t\t\tif ( color = /^\\s*(\\d*\\.?\\d+)\\s*,\\s*(\\d*\\.?\\d+)\\%\\s*,\\s*(\\d*\\.?\\d+)\\%\\s*(?:,\\s*(\\d*\\.?\\d+)\\s*)?$/.exec( components ) ) {\n\n\t\t\t\t\t\t// hsl(120,50%,50%) hsla(120,50%,50%,0.5)\n\n\t\t\t\t\t\thandleAlpha( color[ 4 ] );\n\n\t\t\t\t\t\treturn this.setHSL(\n\t\t\t\t\t\t\tparseFloat( color[ 1 ] ) / 360,\n\t\t\t\t\t\t\tparseFloat( color[ 2 ] ) / 100,\n\t\t\t\t\t\t\tparseFloat( color[ 3 ] ) / 100,\n\t\t\t\t\t\t\tcolorSpace\n\t\t\t\t\t\t);\n\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\n\t\t\t\t\tconsole.warn( 'THREE.Color: Unknown color model ' + style );\n\n\t\t\t}\n\n\t\t} else if ( m = /^\\#([A-Fa-f\\d]+)$/.exec( style ) ) {\n\n\t\t\t// hex color\n\n\t\t\tconst hex = m[ 1 ];\n\t\t\tconst size = hex.length;\n\n\t\t\tif ( size === 3 ) {\n\n\t\t\t\t// #ff0\n\t\t\t\treturn this.setRGB(\n\t\t\t\t\tparseInt( hex.charAt( 0 ), 16 ) / 15,\n\t\t\t\t\tparseInt( hex.charAt( 1 ), 16 ) / 15,\n\t\t\t\t\tparseInt( hex.charAt( 2 ), 16 ) / 15,\n\t\t\t\t\tcolorSpace\n\t\t\t\t);\n\n\t\t\t} else if ( size === 6 ) {\n\n\t\t\t\t// #ff0000\n\t\t\t\treturn this.setHex( parseInt( hex, 16 ), colorSpace );\n\n\t\t\t} else {\n\n\t\t\t\tconsole.warn( 'THREE.Color: Invalid hex color ' + style );\n\n\t\t\t}\n\n\t\t} else if ( style && style.length > 0 ) {\n\n\t\t\treturn this.setColorName( style, colorSpace );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tsetColorName( style, colorSpace = SRGBColorSpace ) {\n\n\t\t// color keywords\n\t\tconst hex = _colorKeywords[ style.toLowerCase() ];\n\n\t\tif ( hex !== undefined ) {\n\n\t\t\t// red\n\t\t\tthis.setHex( hex, colorSpace );\n\n\t\t} else {\n\n\t\t\t// unknown color\n\t\t\tconsole.warn( 'THREE.Color: Unknown color ' + style );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor( this.r, this.g, this.b );\n\n\t}\n\n\tcopy( color ) {\n\n\t\tthis.r = color.r;\n\t\tthis.g = color.g;\n\t\tthis.b = color.b;\n\n\t\treturn this;\n\n\t}\n\n\tcopySRGBToLinear( color ) {\n\n\t\tthis.r = SRGBToLinear( color.r );\n\t\tthis.g = SRGBToLinear( color.g );\n\t\tthis.b = SRGBToLinear( color.b );\n\n\t\treturn this;\n\n\t}\n\n\tcopyLinearToSRGB( color ) {\n\n\t\tthis.r = LinearToSRGB( color.r );\n\t\tthis.g = LinearToSRGB( color.g );\n\t\tthis.b = LinearToSRGB( color.b );\n\n\t\treturn this;\n\n\t}\n\n\tconvertSRGBToLinear() {\n\n\t\tthis.copySRGBToLinear( this );\n\n\t\treturn this;\n\n\t}\n\n\tconvertLinearToSRGB() {\n\n\t\tthis.copyLinearToSRGB( this );\n\n\t\treturn this;\n\n\t}\n\n\tgetHex( colorSpace = SRGBColorSpace ) {\n\n\t\tColorManagement.fromWorkingColorSpace( _color.copy( this ), colorSpace );\n\n\t\treturn Math.round( clamp( _color.r * 255, 0, 255 ) ) * 65536 + Math.round( clamp( _color.g * 255, 0, 255 ) ) * 256 + Math.round( clamp( _color.b * 255, 0, 255 ) );\n\n\t}\n\n\tgetHexString( colorSpace = SRGBColorSpace ) {\n\n\t\treturn ( '000000' + this.getHex( colorSpace ).toString( 16 ) ).slice( - 6 );\n\n\t}\n\n\tgetHSL( target, colorSpace = ColorManagement.workingColorSpace ) {\n\n\t\t// h,s,l ranges are in 0.0 - 1.0\n\n\t\tColorManagement.fromWorkingColorSpace( _color.copy( this ), colorSpace );\n\n\t\tconst r = _color.r, g = _color.g, b = _color.b;\n\n\t\tconst max = Math.max( r, g, b );\n\t\tconst min = Math.min( r, g, b );\n\n\t\tlet hue, saturation;\n\t\tconst lightness = ( min + max ) / 2.0;\n\n\t\tif ( min === max ) {\n\n\t\t\thue = 0;\n\t\t\tsaturation = 0;\n\n\t\t} else {\n\n\t\t\tconst delta = max - min;\n\n\t\t\tsaturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min );\n\n\t\t\tswitch ( max ) {\n\n\t\t\t\tcase r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break;\n\t\t\t\tcase g: hue = ( b - r ) / delta + 2; break;\n\t\t\t\tcase b: hue = ( r - g ) / delta + 4; break;\n\n\t\t\t}\n\n\t\t\thue /= 6;\n\n\t\t}\n\n\t\ttarget.h = hue;\n\t\ttarget.s = saturation;\n\t\ttarget.l = lightness;\n\n\t\treturn target;\n\n\t}\n\n\tgetRGB( target, colorSpace = ColorManagement.workingColorSpace ) {\n\n\t\tColorManagement.fromWorkingColorSpace( _color.copy( this ), colorSpace );\n\n\t\ttarget.r = _color.r;\n\t\ttarget.g = _color.g;\n\t\ttarget.b = _color.b;\n\n\t\treturn target;\n\n\t}\n\n\tgetStyle( colorSpace = SRGBColorSpace ) {\n\n\t\tColorManagement.fromWorkingColorSpace( _color.copy( this ), colorSpace );\n\n\t\tconst r = _color.r, g = _color.g, b = _color.b;\n\n\t\tif ( colorSpace !== SRGBColorSpace ) {\n\n\t\t\t// Requires CSS Color Module Level 4 (https://www.w3.org/TR/css-color-4/).\n\t\t\treturn `color(${ colorSpace } ${ r.toFixed( 3 ) } ${ g.toFixed( 3 ) } ${ b.toFixed( 3 ) })`;\n\n\t\t}\n\n\t\treturn `rgb(${ Math.round( r * 255 ) },${ Math.round( g * 255 ) },${ Math.round( b * 255 ) })`;\n\n\t}\n\n\toffsetHSL( h, s, l ) {\n\n\t\tthis.getHSL( _hslA );\n\n\t\treturn this.setHSL( _hslA.h + h, _hslA.s + s, _hslA.l + l );\n\n\t}\n\n\tadd( color ) {\n\n\t\tthis.r += color.r;\n\t\tthis.g += color.g;\n\t\tthis.b += color.b;\n\n\t\treturn this;\n\n\t}\n\n\taddColors( color1, color2 ) {\n\n\t\tthis.r = color1.r + color2.r;\n\t\tthis.g = color1.g + color2.g;\n\t\tthis.b = color1.b + color2.b;\n\n\t\treturn this;\n\n\t}\n\n\taddScalar( s ) {\n\n\t\tthis.r += s;\n\t\tthis.g += s;\n\t\tthis.b += s;\n\n\t\treturn this;\n\n\t}\n\n\tsub( color ) {\n\n\t\tthis.r = Math.max( 0, this.r - color.r );\n\t\tthis.g = Math.max( 0, this.g - color.g );\n\t\tthis.b = Math.max( 0, this.b - color.b );\n\n\t\treturn this;\n\n\t}\n\n\tmultiply( color ) {\n\n\t\tthis.r *= color.r;\n\t\tthis.g *= color.g;\n\t\tthis.b *= color.b;\n\n\t\treturn this;\n\n\t}\n\n\tmultiplyScalar( s ) {\n\n\t\tthis.r *= s;\n\t\tthis.g *= s;\n\t\tthis.b *= s;\n\n\t\treturn this;\n\n\t}\n\n\tlerp( color, alpha ) {\n\n\t\tthis.r += ( color.r - this.r ) * alpha;\n\t\tthis.g += ( color.g - this.g ) * alpha;\n\t\tthis.b += ( color.b - this.b ) * alpha;\n\n\t\treturn this;\n\n\t}\n\n\tlerpColors( color1, color2, alpha ) {\n\n\t\tthis.r = color1.r + ( color2.r - color1.r ) * alpha;\n\t\tthis.g = color1.g + ( color2.g - color1.g ) * alpha;\n\t\tthis.b = color1.b + ( color2.b - color1.b ) * alpha;\n\n\t\treturn this;\n\n\t}\n\n\tlerpHSL( color, alpha ) {\n\n\t\tthis.getHSL( _hslA );\n\t\tcolor.getHSL( _hslB );\n\n\t\tconst h = lerp( _hslA.h, _hslB.h, alpha );\n\t\tconst s = lerp( _hslA.s, _hslB.s, alpha );\n\t\tconst l = lerp( _hslA.l, _hslB.l, alpha );\n\n\t\tthis.setHSL( h, s, l );\n\n\t\treturn this;\n\n\t}\n\n\tsetFromVector3( v ) {\n\n\t\tthis.r = v.x;\n\t\tthis.g = v.y;\n\t\tthis.b = v.z;\n\n\t\treturn this;\n\n\t}\n\n\tapplyMatrix3( m ) {\n\n\t\tconst r = this.r, g = this.g, b = this.b;\n\t\tconst e = m.elements;\n\n\t\tthis.r = e[ 0 ] * r + e[ 3 ] * g + e[ 6 ] * b;\n\t\tthis.g = e[ 1 ] * r + e[ 4 ] * g + e[ 7 ] * b;\n\t\tthis.b = e[ 2 ] * r + e[ 5 ] * g + e[ 8 ] * b;\n\n\t\treturn this;\n\n\t}\n\n\tequals( c ) {\n\n\t\treturn ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b );\n\n\t}\n\n\tfromArray( array, offset = 0 ) {\n\n\t\tthis.r = array[ offset ];\n\t\tthis.g = array[ offset + 1 ];\n\t\tthis.b = array[ offset + 2 ];\n\n\t\treturn this;\n\n\t}\n\n\ttoArray( array = [], offset = 0 ) {\n\n\t\tarray[ offset ] = this.r;\n\t\tarray[ offset + 1 ] = this.g;\n\t\tarray[ offset + 2 ] = this.b;\n\n\t\treturn array;\n\n\t}\n\n\tfromBufferAttribute( attribute, index ) {\n\n\t\tthis.r = attribute.getX( index );\n\t\tthis.g = attribute.getY( index );\n\t\tthis.b = attribute.getZ( index );\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON() {\n\n\t\treturn this.getHex();\n\n\t}\n\n\t*[ Symbol.iterator ]() {\n\n\t\tyield this.r;\n\t\tyield this.g;\n\t\tyield this.b;\n\n\t}\n\n}\n\nconst _color = /*@__PURE__*/ new Color();\n\nColor.NAMES = _colorKeywords;\n\nexport { Color };\n","import { Color } from '../math/Color.js';\nimport { EventDispatcher } from '../core/EventDispatcher.js';\nimport { FrontSide, NormalBlending, LessEqualDepth, AddEquation, OneMinusSrcAlphaFactor, SrcAlphaFactor, AlwaysStencilFunc, KeepStencilOp } from '../constants.js';\nimport * as MathUtils from '../math/MathUtils.js';\n\nlet _materialId = 0;\n\nclass Material extends EventDispatcher {\n\n\tconstructor() {\n\n\t\tsuper();\n\n\t\tthis.isMaterial = true;\n\n\t\tObject.defineProperty( this, 'id', { value: _materialId ++ } );\n\n\t\tthis.uuid = MathUtils.generateUUID();\n\n\t\tthis.name = '';\n\t\tthis.type = 'Material';\n\n\t\tthis.blending = NormalBlending;\n\t\tthis.side = FrontSide;\n\t\tthis.vertexColors = false;\n\n\t\tthis.opacity = 1;\n\t\tthis.transparent = false;\n\t\tthis.alphaHash = false;\n\n\t\tthis.blendSrc = SrcAlphaFactor;\n\t\tthis.blendDst = OneMinusSrcAlphaFactor;\n\t\tthis.blendEquation = AddEquation;\n\t\tthis.blendSrcAlpha = null;\n\t\tthis.blendDstAlpha = null;\n\t\tthis.blendEquationAlpha = null;\n\t\tthis.blendColor = new Color( 0, 0, 0 );\n\t\tthis.blendAlpha = 0;\n\n\t\tthis.depthFunc = LessEqualDepth;\n\t\tthis.depthTest = true;\n\t\tthis.depthWrite = true;\n\n\t\tthis.stencilWriteMask = 0xff;\n\t\tthis.stencilFunc = AlwaysStencilFunc;\n\t\tthis.stencilRef = 0;\n\t\tthis.stencilFuncMask = 0xff;\n\t\tthis.stencilFail = KeepStencilOp;\n\t\tthis.stencilZFail = KeepStencilOp;\n\t\tthis.stencilZPass = KeepStencilOp;\n\t\tthis.stencilWrite = false;\n\n\t\tthis.clippingPlanes = null;\n\t\tthis.clipIntersection = false;\n\t\tthis.clipShadows = false;\n\n\t\tthis.shadowSide = null;\n\n\t\tthis.colorWrite = true;\n\n\t\tthis.precision = null; // override the renderer's default precision for this material\n\n\t\tthis.polygonOffset = false;\n\t\tthis.polygonOffsetFactor = 0;\n\t\tthis.polygonOffsetUnits = 0;\n\n\t\tthis.dithering = false;\n\n\t\tthis.alphaToCoverage = false;\n\t\tthis.premultipliedAlpha = false;\n\t\tthis.forceSinglePass = false;\n\n\t\tthis.visible = true;\n\n\t\tthis.toneMapped = true;\n\n\t\tthis.userData = {};\n\n\t\tthis.version = 0;\n\n\t\tthis._alphaTest = 0;\n\n\t}\n\n\tget alphaTest() {\n\n\t\treturn this._alphaTest;\n\n\t}\n\n\tset alphaTest( value ) {\n\n\t\tif ( this._alphaTest > 0 !== value > 0 ) {\n\n\t\t\tthis.version ++;\n\n\t\t}\n\n\t\tthis._alphaTest = value;\n\n\t}\n\n\tonBuild( /* shaderobject, renderer */ ) {}\n\n\tonBeforeRender( /* renderer, scene, camera, geometry, object, group */ ) {}\n\n\tonBeforeCompile( /* shaderobject, renderer */ ) {}\n\n\tcustomProgramCacheKey() {\n\n\t\treturn this.onBeforeCompile.toString();\n\n\t}\n\n\tsetValues( values ) {\n\n\t\tif ( values === undefined ) return;\n\n\t\tfor ( const key in values ) {\n\n\t\t\tconst newValue = values[ key ];\n\n\t\t\tif ( newValue === undefined ) {\n\n\t\t\t\tconsole.warn( `THREE.Material: parameter '${ key }' has value of undefined.` );\n\t\t\t\tcontinue;\n\n\t\t\t}\n\n\t\t\tconst currentValue = this[ key ];\n\n\t\t\tif ( currentValue === undefined ) {\n\n\t\t\t\tconsole.warn( `THREE.Material: '${ key }' is not a property of THREE.${ this.type }.` );\n\t\t\t\tcontinue;\n\n\t\t\t}\n\n\t\t\tif ( currentValue && currentValue.isColor ) {\n\n\t\t\t\tcurrentValue.set( newValue );\n\n\t\t\t} else if ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) {\n\n\t\t\t\tcurrentValue.copy( newValue );\n\n\t\t\t} else {\n\n\t\t\t\tthis[ key ] = newValue;\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\ttoJSON( meta ) {\n\n\t\tconst isRootObject = ( meta === undefined || typeof meta === 'string' );\n\n\t\tif ( isRootObject ) {\n\n\t\t\tmeta = {\n\t\t\t\ttextures: {},\n\t\t\t\timages: {}\n\t\t\t};\n\n\t\t}\n\n\t\tconst data = {\n\t\t\tmetadata: {\n\t\t\t\tversion: 4.6,\n\t\t\t\ttype: 'Material',\n\t\t\t\tgenerator: 'Material.toJSON'\n\t\t\t}\n\t\t};\n\n\t\t// standard Material serialization\n\t\tdata.uuid = this.uuid;\n\t\tdata.type = this.type;\n\n\t\tif ( this.name !== '' ) data.name = this.name;\n\n\t\tif ( this.color && this.color.isColor ) data.color = this.color.getHex();\n\n\t\tif ( this.roughness !== undefined ) data.roughness = this.roughness;\n\t\tif ( this.metalness !== undefined ) data.metalness = this.metalness;\n\n\t\tif ( this.sheen !== undefined ) data.sheen = this.sheen;\n\t\tif ( this.sheenColor && this.sheenColor.isColor ) data.sheenColor = this.sheenColor.getHex();\n\t\tif ( this.sheenRoughness !== undefined ) data.sheenRoughness = this.sheenRoughness;\n\t\tif ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex();\n\t\tif ( this.emissiveIntensity && this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity;\n\n\t\tif ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex();\n\t\tif ( this.specularIntensity !== undefined ) data.specularIntensity = this.specularIntensity;\n\t\tif ( this.specularColor && this.specularColor.isColor ) data.specularColor = this.specularColor.getHex();\n\t\tif ( this.shininess !== undefined ) data.shininess = this.shininess;\n\t\tif ( this.clearcoat !== undefined ) data.clearcoat = this.clearcoat;\n\t\tif ( this.clearcoatRoughness !== undefined ) data.clearcoatRoughness = this.clearcoatRoughness;\n\n\t\tif ( this.clearcoatMap && this.clearcoatMap.isTexture ) {\n\n\t\t\tdata.clearcoatMap = this.clearcoatMap.toJSON( meta ).uuid;\n\n\t\t}\n\n\t\tif ( this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture ) {\n\n\t\t\tdata.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON( meta ).uuid;\n\n\t\t}\n\n\t\tif ( this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture ) {\n\n\t\t\tdata.clearcoatNormalMap = this.clearcoatNormalMap.toJSON( meta ).uuid;\n\t\t\tdata.clearcoatNormalScale = this.clearcoatNormalScale.toArray();\n\n\t\t}\n\n\t\tif ( this.iridescence !== undefined ) data.iridescence = this.iridescence;\n\t\tif ( this.iridescenceIOR !== undefined ) data.iridescenceIOR = this.iridescenceIOR;\n\t\tif ( this.iridescenceThicknessRange !== undefined ) data.iridescenceThicknessRange = this.iridescenceThicknessRange;\n\n\t\tif ( this.iridescenceMap && this.iridescenceMap.isTexture ) {\n\n\t\t\tdata.iridescenceMap = this.iridescenceMap.toJSON( meta ).uuid;\n\n\t\t}\n\n\t\tif ( this.iridescenceThicknessMap && this.iridescenceThicknessMap.isTexture ) {\n\n\t\t\tdata.iridescenceThicknessMap = this.iridescenceThicknessMap.toJSON( meta ).uuid;\n\n\t\t}\n\n\t\tif ( this.anisotropy !== undefined ) data.anisotropy = this.anisotropy;\n\t\tif ( this.anisotropyRotation !== undefined ) data.anisotropyRotation = this.anisotropyRotation;\n\n\t\tif ( this.anisotropyMap && this.anisotropyMap.isTexture ) {\n\n\t\t\tdata.anisotropyMap = this.anisotropyMap.toJSON( meta ).uuid;\n\n\t\t}\n\n\t\tif ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid;\n\t\tif ( this.matcap && this.matcap.isTexture ) data.matcap = this.matcap.toJSON( meta ).uuid;\n\t\tif ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid;\n\n\t\tif ( this.lightMap && this.lightMap.isTexture ) {\n\n\t\t\tdata.lightMap = this.lightMap.toJSON( meta ).uuid;\n\t\t\tdata.lightMapIntensity = this.lightMapIntensity;\n\n\t\t}\n\n\t\tif ( this.aoMap && this.aoMap.isTexture ) {\n\n\t\t\tdata.aoMap = this.aoMap.toJSON( meta ).uuid;\n\t\t\tdata.aoMapIntensity = this.aoMapIntensity;\n\n\t\t}\n\n\t\tif ( this.bumpMap && this.bumpMap.isTexture ) {\n\n\t\t\tdata.bumpMap = this.bumpMap.toJSON( meta ).uuid;\n\t\t\tdata.bumpScale = this.bumpScale;\n\n\t\t}\n\n\t\tif ( this.normalMap && this.normalMap.isTexture ) {\n\n\t\t\tdata.normalMap = this.normalMap.toJSON( meta ).uuid;\n\t\t\tdata.normalMapType = this.normalMapType;\n\t\t\tdata.normalScale = this.normalScale.toArray();\n\n\t\t}\n\n\t\tif ( this.displacementMap && this.displacementMap.isTexture ) {\n\n\t\t\tdata.displacementMap = this.displacementMap.toJSON( meta ).uuid;\n\t\t\tdata.displacementScale = this.displacementScale;\n\t\t\tdata.displacementBias = this.displacementBias;\n\n\t\t}\n\n\t\tif ( this.roughnessMap && this.roughnessMap.isTexture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid;\n\t\tif ( this.metalnessMap && this.metalnessMap.isTexture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid;\n\n\t\tif ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid;\n\t\tif ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid;\n\t\tif ( this.specularIntensityMap && this.specularIntensityMap.isTexture ) data.specularIntensityMap = this.specularIntensityMap.toJSON( meta ).uuid;\n\t\tif ( this.specularColorMap && this.specularColorMap.isTexture ) data.specularColorMap = this.specularColorMap.toJSON( meta ).uuid;\n\n\t\tif ( this.envMap && this.envMap.isTexture ) {\n\n\t\t\tdata.envMap = this.envMap.toJSON( meta ).uuid;\n\n\t\t\tif ( this.combine !== undefined ) data.combine = this.combine;\n\n\t\t}\n\n\t\tif ( this.envMapIntensity !== undefined ) data.envMapIntensity = this.envMapIntensity;\n\t\tif ( this.reflectivity !== undefined ) data.reflectivity = this.reflectivity;\n\t\tif ( this.refractionRatio !== undefined ) data.refractionRatio = this.refractionRatio;\n\n\t\tif ( this.gradientMap && this.gradientMap.isTexture ) {\n\n\t\t\tdata.gradientMap = this.gradientMap.toJSON( meta ).uuid;\n\n\t\t}\n\n\t\tif ( this.transmission !== undefined ) data.transmission = this.transmission;\n\t\tif ( this.transmissionMap && this.transmissionMap.isTexture ) data.transmissionMap = this.transmissionMap.toJSON( meta ).uuid;\n\t\tif ( this.thickness !== undefined ) data.thickness = this.thickness;\n\t\tif ( this.thicknessMap && this.thicknessMap.isTexture ) data.thicknessMap = this.thicknessMap.toJSON( meta ).uuid;\n\t\tif ( this.attenuationDistance !== undefined && this.attenuationDistance !== Infinity ) data.attenuationDistance = this.attenuationDistance;\n\t\tif ( this.attenuationColor !== undefined ) data.attenuationColor = this.attenuationColor.getHex();\n\n\t\tif ( this.size !== undefined ) data.size = this.size;\n\t\tif ( this.shadowSide !== null ) data.shadowSide = this.shadowSide;\n\t\tif ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation;\n\n\t\tif ( this.blending !== NormalBlending ) data.blending = this.blending;\n\t\tif ( this.side !== FrontSide ) data.side = this.side;\n\t\tif ( this.vertexColors === true ) data.vertexColors = true;\n\n\t\tif ( this.opacity < 1 ) data.opacity = this.opacity;\n\t\tif ( this.transparent === true ) data.transparent = true;\n\n\t\tif ( this.blendSrc !== SrcAlphaFactor ) data.blendSrc = this.blendSrc;\n\t\tif ( this.blendDst !== OneMinusSrcAlphaFactor ) data.blendDst = this.blendDst;\n\t\tif ( this.blendEquation !== AddEquation ) data.blendEquation = this.blendEquation;\n\t\tif ( this.blendSrcAlpha !== null ) data.blendSrcAlpha = this.blendSrcAlpha;\n\t\tif ( this.blendDstAlpha !== null ) data.blendDstAlpha = this.blendDstAlpha;\n\t\tif ( this.blendEquationAlpha !== null ) data.blendEquationAlpha = this.blendEquationAlpha;\n\t\tif ( this.blendColor && this.blendColor.isColor ) data.blendColor = this.blendColor.getHex();\n\t\tif ( this.blendAlpha !== 0 ) data.blendAlpha = this.blendAlpha;\n\n\t\tif ( this.depthFunc !== LessEqualDepth ) data.depthFunc = this.depthFunc;\n\t\tif ( this.depthTest === false ) data.depthTest = this.depthTest;\n\t\tif ( this.depthWrite === false ) data.depthWrite = this.depthWrite;\n\t\tif ( this.colorWrite === false ) data.colorWrite = this.colorWrite;\n\n\t\tif ( this.stencilWriteMask !== 0xff ) data.stencilWriteMask = this.stencilWriteMask;\n\t\tif ( this.stencilFunc !== AlwaysStencilFunc ) data.stencilFunc = this.stencilFunc;\n\t\tif ( this.stencilRef !== 0 ) data.stencilRef = this.stencilRef;\n\t\tif ( this.stencilFuncMask !== 0xff ) data.stencilFuncMask = this.stencilFuncMask;\n\t\tif ( this.stencilFail !== KeepStencilOp ) data.stencilFail = this.stencilFail;\n\t\tif ( this.stencilZFail !== KeepStencilOp ) data.stencilZFail = this.stencilZFail;\n\t\tif ( this.stencilZPass !== KeepStencilOp ) data.stencilZPass = this.stencilZPass;\n\t\tif ( this.stencilWrite === true ) data.stencilWrite = this.stencilWrite;\n\n\t\t// rotation (SpriteMaterial)\n\t\tif ( this.rotation !== undefined && this.rotation !== 0 ) data.rotation = this.rotation;\n\n\t\tif ( this.polygonOffset === true ) data.polygonOffset = true;\n\t\tif ( this.polygonOffsetFactor !== 0 ) data.polygonOffsetFactor = this.polygonOffsetFactor;\n\t\tif ( this.polygonOffsetUnits !== 0 ) data.polygonOffsetUnits = this.polygonOffsetUnits;\n\n\t\tif ( this.linewidth !== undefined && this.linewidth !== 1 ) data.linewidth = this.linewidth;\n\t\tif ( this.dashSize !== undefined ) data.dashSize = this.dashSize;\n\t\tif ( this.gapSize !== undefined ) data.gapSize = this.gapSize;\n\t\tif ( this.scale !== undefined ) data.scale = this.scale;\n\n\t\tif ( this.dithering === true ) data.dithering = true;\n\n\t\tif ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest;\n\t\tif ( this.alphaHash === true ) data.alphaHash = true;\n\t\tif ( this.alphaToCoverage === true ) data.alphaToCoverage = true;\n\t\tif ( this.premultipliedAlpha === true ) data.premultipliedAlpha = true;\n\t\tif ( this.forceSinglePass === true ) data.forceSinglePass = true;\n\n\t\tif ( this.wireframe === true ) data.wireframe = true;\n\t\tif ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth;\n\t\tif ( this.wireframeLinecap !== 'round' ) data.wireframeLinecap = this.wireframeLinecap;\n\t\tif ( this.wireframeLinejoin !== 'round' ) data.wireframeLinejoin = this.wireframeLinejoin;\n\n\t\tif ( this.flatShading === true ) data.flatShading = true;\n\n\t\tif ( this.visible === false ) data.visible = false;\n\n\t\tif ( this.toneMapped === false ) data.toneMapped = false;\n\n\t\tif ( this.fog === false ) data.fog = false;\n\n\t\tif ( Object.keys( this.userData ).length > 0 ) data.userData = this.userData;\n\n\t\t// TODO: Copied from Object3D.toJSON\n\n\t\tfunction extractFromCache( cache ) {\n\n\t\t\tconst values = [];\n\n\t\t\tfor ( const key in cache ) {\n\n\t\t\t\tconst data = cache[ key ];\n\t\t\t\tdelete data.metadata;\n\t\t\t\tvalues.push( data );\n\n\t\t\t}\n\n\t\t\treturn values;\n\n\t\t}\n\n\t\tif ( isRootObject ) {\n\n\t\t\tconst textures = extractFromCache( meta.textures );\n\t\t\tconst images = extractFromCache( meta.images );\n\n\t\t\tif ( textures.length > 0 ) data.textures = textures;\n\t\t\tif ( images.length > 0 ) data.images = images;\n\n\t\t}\n\n\t\treturn data;\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tthis.name = source.name;\n\n\t\tthis.blending = source.blending;\n\t\tthis.side = source.side;\n\t\tthis.vertexColors = source.vertexColors;\n\n\t\tthis.opacity = source.opacity;\n\t\tthis.transparent = source.transparent;\n\n\t\tthis.blendSrc = source.blendSrc;\n\t\tthis.blendDst = source.blendDst;\n\t\tthis.blendEquation = source.blendEquation;\n\t\tthis.blendSrcAlpha = source.blendSrcAlpha;\n\t\tthis.blendDstAlpha = source.blendDstAlpha;\n\t\tthis.blendEquationAlpha = source.blendEquationAlpha;\n\t\tthis.blendColor.copy( source.blendColor );\n\t\tthis.blendAlpha = source.blendAlpha;\n\n\t\tthis.depthFunc = source.depthFunc;\n\t\tthis.depthTest = source.depthTest;\n\t\tthis.depthWrite = source.depthWrite;\n\n\t\tthis.stencilWriteMask = source.stencilWriteMask;\n\t\tthis.stencilFunc = source.stencilFunc;\n\t\tthis.stencilRef = source.stencilRef;\n\t\tthis.stencilFuncMask = source.stencilFuncMask;\n\t\tthis.stencilFail = source.stencilFail;\n\t\tthis.stencilZFail = source.stencilZFail;\n\t\tthis.stencilZPass = source.stencilZPass;\n\t\tthis.stencilWrite = source.stencilWrite;\n\n\t\tconst srcPlanes = source.clippingPlanes;\n\t\tlet dstPlanes = null;\n\n\t\tif ( srcPlanes !== null ) {\n\n\t\t\tconst n = srcPlanes.length;\n\t\t\tdstPlanes = new Array( n );\n\n\t\t\tfor ( let i = 0; i !== n; ++ i ) {\n\n\t\t\t\tdstPlanes[ i ] = srcPlanes[ i ].clone();\n\n\t\t\t}\n\n\t\t}\n\n\t\tthis.clippingPlanes = dstPlanes;\n\t\tthis.clipIntersection = source.clipIntersection;\n\t\tthis.clipShadows = source.clipShadows;\n\n\t\tthis.shadowSide = source.shadowSide;\n\n\t\tthis.colorWrite = source.colorWrite;\n\n\t\tthis.precision = source.precision;\n\n\t\tthis.polygonOffset = source.polygonOffset;\n\t\tthis.polygonOffsetFactor = source.polygonOffsetFactor;\n\t\tthis.polygonOffsetUnits = source.polygonOffsetUnits;\n\n\t\tthis.dithering = source.dithering;\n\n\t\tthis.alphaTest = source.alphaTest;\n\t\tthis.alphaHash = source.alphaHash;\n\t\tthis.alphaToCoverage = source.alphaToCoverage;\n\t\tthis.premultipliedAlpha = source.premultipliedAlpha;\n\t\tthis.forceSinglePass = source.forceSinglePass;\n\n\t\tthis.visible = source.visible;\n\n\t\tthis.toneMapped = source.toneMapped;\n\n\t\tthis.userData = JSON.parse( JSON.stringify( source.userData ) );\n\n\t\treturn this;\n\n\t}\n\n\tdispose() {\n\n\t\tthis.dispatchEvent( { type: 'dispose' } );\n\n\t}\n\n\tset needsUpdate( value ) {\n\n\t\tif ( value === true ) this.version ++;\n\n\t}\n\n}\n\nexport { Material };\n","import { Material } from './Material.js';\nimport { MultiplyOperation } from '../constants.js';\nimport { Color } from '../math/Color.js';\n\nclass MeshBasicMaterial extends Material {\n\n\tconstructor( parameters ) {\n\n\t\tsuper();\n\n\t\tthis.isMeshBasicMaterial = true;\n\n\t\tthis.type = 'MeshBasicMaterial';\n\n\t\tthis.color = new Color( 0xffffff ); // emissive\n\n\t\tthis.map = null;\n\n\t\tthis.lightMap = null;\n\t\tthis.lightMapIntensity = 1.0;\n\n\t\tthis.aoMap = null;\n\t\tthis.aoMapIntensity = 1.0;\n\n\t\tthis.specularMap = null;\n\n\t\tthis.alphaMap = null;\n\n\t\tthis.envMap = null;\n\t\tthis.combine = MultiplyOperation;\n\t\tthis.reflectivity = 1;\n\t\tthis.refractionRatio = 0.98;\n\n\t\tthis.wireframe = false;\n\t\tthis.wireframeLinewidth = 1;\n\t\tthis.wireframeLinecap = 'round';\n\t\tthis.wireframeLinejoin = 'round';\n\n\t\tthis.fog = true;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.color.copy( source.color );\n\n\t\tthis.map = source.map;\n\n\t\tthis.lightMap = source.lightMap;\n\t\tthis.lightMapIntensity = source.lightMapIntensity;\n\n\t\tthis.aoMap = source.aoMap;\n\t\tthis.aoMapIntensity = source.aoMapIntensity;\n\n\t\tthis.specularMap = source.specularMap;\n\n\t\tthis.alphaMap = source.alphaMap;\n\n\t\tthis.envMap = source.envMap;\n\t\tthis.combine = source.combine;\n\t\tthis.reflectivity = source.reflectivity;\n\t\tthis.refractionRatio = source.refractionRatio;\n\n\t\tthis.wireframe = source.wireframe;\n\t\tthis.wireframeLinewidth = source.wireframeLinewidth;\n\t\tthis.wireframeLinecap = source.wireframeLinecap;\n\t\tthis.wireframeLinejoin = source.wireframeLinejoin;\n\n\t\tthis.fog = source.fog;\n\n\t\treturn this;\n\n\t}\n\n}\n\nexport { MeshBasicMaterial };\n","import { Vector3 } from '../math/Vector3.js';\nimport { Vector2 } from '../math/Vector2.js';\nimport { denormalize, normalize } from '../math/MathUtils.js';\nimport { StaticDrawUsage, FloatType } from '../constants.js';\nimport { fromHalfFloat, toHalfFloat } from '../extras/DataUtils.js';\n\nconst _vector = /*@__PURE__*/ new Vector3();\nconst _vector2 = /*@__PURE__*/ new Vector2();\n\nclass BufferAttribute {\n\n\tconstructor( array, itemSize, normalized = false ) {\n\n\t\tif ( Array.isArray( array ) ) {\n\n\t\t\tthrow new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' );\n\n\t\t}\n\n\t\tthis.isBufferAttribute = true;\n\n\t\tthis.name = '';\n\n\t\tthis.array = array;\n\t\tthis.itemSize = itemSize;\n\t\tthis.count = array !== undefined ? array.length / itemSize : 0;\n\t\tthis.normalized = normalized;\n\n\t\tthis.usage = StaticDrawUsage;\n\t\tthis.updateRange = { offset: 0, count: - 1 };\n\t\tthis.gpuType = FloatType;\n\n\t\tthis.version = 0;\n\n\t}\n\n\tonUploadCallback() {}\n\n\tset needsUpdate( value ) {\n\n\t\tif ( value === true ) this.version ++;\n\n\t}\n\n\tsetUsage( value ) {\n\n\t\tthis.usage = value;\n\n\t\treturn this;\n\n\t}\n\n\tcopy( source ) {\n\n\t\tthis.name = source.name;\n\t\tthis.array = new source.array.constructor( source.array );\n\t\tthis.itemSize = source.itemSize;\n\t\tthis.count = source.count;\n\t\tthis.normalized = source.normalized;\n\n\t\tthis.usage = source.usage;\n\t\tthis.gpuType = source.gpuType;\n\n\t\treturn this;\n\n\t}\n\n\tcopyAt( index1, attribute, index2 ) {\n\n\t\tindex1 *= this.itemSize;\n\t\tindex2 *= attribute.itemSize;\n\n\t\tfor ( let i = 0, l = this.itemSize; i < l; i ++ ) {\n\n\t\t\tthis.array[ index1 + i ] = attribute.array[ index2 + i ];\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tcopyArray( array ) {\n\n\t\tthis.array.set( array );\n\n\t\treturn this;\n\n\t}\n\n\tapplyMatrix3( m ) {\n\n\t\tif ( this.itemSize === 2 ) {\n\n\t\t\tfor ( let i = 0, l = this.count; i < l; i ++ ) {\n\n\t\t\t\t_vector2.fromBufferAttribute( this, i );\n\t\t\t\t_vector2.applyMatrix3( m );\n\n\t\t\t\tthis.setXY( i, _vector2.x, _vector2.y );\n\n\t\t\t}\n\n\t\t} else if ( this.itemSize === 3 ) {\n\n\t\t\tfor ( let i = 0, l = this.count; i < l; i ++ ) {\n\n\t\t\t\t_vector.fromBufferAttribute( this, i );\n\t\t\t\t_vector.applyMatrix3( m );\n\n\t\t\t\tthis.setXYZ( i, _vector.x, _vector.y, _vector.z );\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tapplyMatrix4( m ) {\n\n\t\tfor ( let i = 0, l = this.count; i < l; i ++ ) {\n\n\t\t\t_vector.fromBufferAttribute( this, i );\n\n\t\t\t_vector.applyMatrix4( m );\n\n\t\t\tthis.setXYZ( i, _vector.x, _vector.y, _vector.z );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tapplyNormalMatrix( m ) {\n\n\t\tfor ( let i = 0, l = this.count; i < l; i ++ ) {\n\n\t\t\t_vector.fromBufferAttribute( this, i );\n\n\t\t\t_vector.applyNormalMatrix( m );\n\n\t\t\tthis.setXYZ( i, _vector.x, _vector.y, _vector.z );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\ttransformDirection( m ) {\n\n\t\tfor ( let i = 0, l = this.count; i < l; i ++ ) {\n\n\t\t\t_vector.fromBufferAttribute( this, i );\n\n\t\t\t_vector.transformDirection( m );\n\n\t\t\tthis.setXYZ( i, _vector.x, _vector.y, _vector.z );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tset( value, offset = 0 ) {\n\n\t\t// Matching BufferAttribute constructor, do not normalize the array.\n\t\tthis.array.set( value, offset );\n\n\t\treturn this;\n\n\t}\n\n\tgetComponent( index, component ) {\n\n\t\tlet value = this.array[ index * this.itemSize + component ];\n\n\t\tif ( this.normalized ) value = denormalize( value, this.array );\n\n\t\treturn value;\n\n\t}\n\n\tsetComponent( index, component, value ) {\n\n\t\tif ( this.normalized ) value = normalize( value, this.array );\n\n\t\tthis.array[ index * this.itemSize + component ] = value;\n\n\t\treturn this;\n\n\t}\n\n\tgetX( index ) {\n\n\t\tlet x = this.array[ index * this.itemSize ];\n\n\t\tif ( this.normalized ) x = denormalize( x, this.array );\n\n\t\treturn x;\n\n\t}\n\n\tsetX( index, x ) {\n\n\t\tif ( this.normalized ) x = normalize( x, this.array );\n\n\t\tthis.array[ index * this.itemSize ] = x;\n\n\t\treturn this;\n\n\t}\n\n\tgetY( index ) {\n\n\t\tlet y = this.array[ index * this.itemSize + 1 ];\n\n\t\tif ( this.normalized ) y = denormalize( y, this.array );\n\n\t\treturn y;\n\n\t}\n\n\tsetY( index, y ) {\n\n\t\tif ( this.normalized ) y = normalize( y, this.array );\n\n\t\tthis.array[ index * this.itemSize + 1 ] = y;\n\n\t\treturn this;\n\n\t}\n\n\tgetZ( index ) {\n\n\t\tlet z = this.array[ index * this.itemSize + 2 ];\n\n\t\tif ( this.normalized ) z = denormalize( z, this.array );\n\n\t\treturn z;\n\n\t}\n\n\tsetZ( index, z ) {\n\n\t\tif ( this.normalized ) z = normalize( z, this.array );\n\n\t\tthis.array[ index * this.itemSize + 2 ] = z;\n\n\t\treturn this;\n\n\t}\n\n\tgetW( index ) {\n\n\t\tlet w = this.array[ index * this.itemSize + 3 ];\n\n\t\tif ( this.normalized ) w = denormalize( w, this.array );\n\n\t\treturn w;\n\n\t}\n\n\tsetW( index, w ) {\n\n\t\tif ( this.normalized ) w = normalize( w, this.array );\n\n\t\tthis.array[ index * this.itemSize + 3 ] = w;\n\n\t\treturn this;\n\n\t}\n\n\tsetXY( index, x, y ) {\n\n\t\tindex *= this.itemSize;\n\n\t\tif ( this.normalized ) {\n\n\t\t\tx = normalize( x, this.array );\n\t\t\ty = normalize( y, this.array );\n\n\t\t}\n\n\t\tthis.array[ index + 0 ] = x;\n\t\tthis.array[ index + 1 ] = y;\n\n\t\treturn this;\n\n\t}\n\n\tsetXYZ( index, x, y, z ) {\n\n\t\tindex *= this.itemSize;\n\n\t\tif ( this.normalized ) {\n\n\t\t\tx = normalize( x, this.array );\n\t\t\ty = normalize( y, this.array );\n\t\t\tz = normalize( z, this.array );\n\n\t\t}\n\n\t\tthis.array[ index + 0 ] = x;\n\t\tthis.array[ index + 1 ] = y;\n\t\tthis.array[ index + 2 ] = z;\n\n\t\treturn this;\n\n\t}\n\n\tsetXYZW( index, x, y, z, w ) {\n\n\t\tindex *= this.itemSize;\n\n\t\tif ( this.normalized ) {\n\n\t\t\tx = normalize( x, this.array );\n\t\t\ty = normalize( y, this.array );\n\t\t\tz = normalize( z, this.array );\n\t\t\tw = normalize( w, this.array );\n\n\t\t}\n\n\t\tthis.array[ index + 0 ] = x;\n\t\tthis.array[ index + 1 ] = y;\n\t\tthis.array[ index + 2 ] = z;\n\t\tthis.array[ index + 3 ] = w;\n\n\t\treturn this;\n\n\t}\n\n\tonUpload( callback ) {\n\n\t\tthis.onUploadCallback = callback;\n\n\t\treturn this;\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor( this.array, this.itemSize ).copy( this );\n\n\t}\n\n\ttoJSON() {\n\n\t\tconst data = {\n\t\t\titemSize: this.itemSize,\n\t\t\ttype: this.array.constructor.name,\n\t\t\tarray: Array.from( this.array ),\n\t\t\tnormalized: this.normalized\n\t\t};\n\n\t\tif ( this.name !== '' ) data.name = this.name;\n\t\tif ( this.usage !== StaticDrawUsage ) data.usage = this.usage;\n\t\tif ( this.updateRange.offset !== 0 || this.updateRange.count !== - 1 ) data.updateRange = this.updateRange;\n\n\t\treturn data;\n\n\t}\n\n}\n\n//\n\nclass Int8BufferAttribute extends BufferAttribute {\n\n\tconstructor( array, itemSize, normalized ) {\n\n\t\tsuper( new Int8Array( array ), itemSize, normalized );\n\n\t}\n\n}\n\nclass Uint8BufferAttribute extends BufferAttribute {\n\n\tconstructor( array, itemSize, normalized ) {\n\n\t\tsuper( new Uint8Array( array ), itemSize, normalized );\n\n\t}\n\n}\n\nclass Uint8ClampedBufferAttribute extends BufferAttribute {\n\n\tconstructor( array, itemSize, normalized ) {\n\n\t\tsuper( new Uint8ClampedArray( array ), itemSize, normalized );\n\n\t}\n\n}\n\nclass Int16BufferAttribute extends BufferAttribute {\n\n\tconstructor( array, itemSize, normalized ) {\n\n\t\tsuper( new Int16Array( array ), itemSize, normalized );\n\n\t}\n\n}\n\nclass Uint16BufferAttribute extends BufferAttribute {\n\n\tconstructor( array, itemSize, normalized ) {\n\n\t\tsuper( new Uint16Array( array ), itemSize, normalized );\n\n\t}\n\n}\n\nclass Int32BufferAttribute extends BufferAttribute {\n\n\tconstructor( array, itemSize, normalized ) {\n\n\t\tsuper( new Int32Array( array ), itemSize, normalized );\n\n\t}\n\n}\n\nclass Uint32BufferAttribute extends BufferAttribute {\n\n\tconstructor( array, itemSize, normalized ) {\n\n\t\tsuper( new Uint32Array( array ), itemSize, normalized );\n\n\t}\n\n}\n\nclass Float16BufferAttribute extends BufferAttribute {\n\n\tconstructor( array, itemSize, normalized ) {\n\n\t\tsuper( new Uint16Array( array ), itemSize, normalized );\n\n\t\tthis.isFloat16BufferAttribute = true;\n\n\t}\n\n\tgetX( index ) {\n\n\t\tlet x = fromHalfFloat( this.array[ index * this.itemSize ] );\n\n\t\tif ( this.normalized ) x = denormalize( x, this.array );\n\n\t\treturn x;\n\n\t}\n\n\tsetX( index, x ) {\n\n\t\tif ( this.normalized ) x = normalize( x, this.array );\n\n\t\tthis.array[ index * this.itemSize ] = toHalfFloat( x );\n\n\t\treturn this;\n\n\t}\n\n\tgetY( index ) {\n\n\t\tlet y = fromHalfFloat( this.array[ index * this.itemSize + 1 ] );\n\n\t\tif ( this.normalized ) y = denormalize( y, this.array );\n\n\t\treturn y;\n\n\t}\n\n\tsetY( index, y ) {\n\n\t\tif ( this.normalized ) y = normalize( y, this.array );\n\n\t\tthis.array[ index * this.itemSize + 1 ] = toHalfFloat( y );\n\n\t\treturn this;\n\n\t}\n\n\tgetZ( index ) {\n\n\t\tlet z = fromHalfFloat( this.array[ index * this.itemSize + 2 ] );\n\n\t\tif ( this.normalized ) z = denormalize( z, this.array );\n\n\t\treturn z;\n\n\t}\n\n\tsetZ( index, z ) {\n\n\t\tif ( this.normalized ) z = normalize( z, this.array );\n\n\t\tthis.array[ index * this.itemSize + 2 ] = toHalfFloat( z );\n\n\t\treturn this;\n\n\t}\n\n\tgetW( index ) {\n\n\t\tlet w = fromHalfFloat( this.array[ index * this.itemSize + 3 ] );\n\n\t\tif ( this.normalized ) w = denormalize( w, this.array );\n\n\t\treturn w;\n\n\t}\n\n\tsetW( index, w ) {\n\n\t\tif ( this.normalized ) w = normalize( w, this.array );\n\n\t\tthis.array[ index * this.itemSize + 3 ] = toHalfFloat( w );\n\n\t\treturn this;\n\n\t}\n\n\tsetXY( index, x, y ) {\n\n\t\tindex *= this.itemSize;\n\n\t\tif ( this.normalized ) {\n\n\t\t\tx = normalize( x, this.array );\n\t\t\ty = normalize( y, this.array );\n\n\t\t}\n\n\t\tthis.array[ index + 0 ] = toHalfFloat( x );\n\t\tthis.array[ index + 1 ] = toHalfFloat( y );\n\n\t\treturn this;\n\n\t}\n\n\tsetXYZ( index, x, y, z ) {\n\n\t\tindex *= this.itemSize;\n\n\t\tif ( this.normalized ) {\n\n\t\t\tx = normalize( x, this.array );\n\t\t\ty = normalize( y, this.array );\n\t\t\tz = normalize( z, this.array );\n\n\t\t}\n\n\t\tthis.array[ index + 0 ] = toHalfFloat( x );\n\t\tthis.array[ index + 1 ] = toHalfFloat( y );\n\t\tthis.array[ index + 2 ] = toHalfFloat( z );\n\n\t\treturn this;\n\n\t}\n\n\tsetXYZW( index, x, y, z, w ) {\n\n\t\tindex *= this.itemSize;\n\n\t\tif ( this.normalized ) {\n\n\t\t\tx = normalize( x, this.array );\n\t\t\ty = normalize( y, this.array );\n\t\t\tz = normalize( z, this.array );\n\t\t\tw = normalize( w, this.array );\n\n\t\t}\n\n\t\tthis.array[ index + 0 ] = toHalfFloat( x );\n\t\tthis.array[ index + 1 ] = toHalfFloat( y );\n\t\tthis.array[ index + 2 ] = toHalfFloat( z );\n\t\tthis.array[ index + 3 ] = toHalfFloat( w );\n\n\t\treturn this;\n\n\t}\n\n}\n\n\nclass Float32BufferAttribute extends BufferAttribute {\n\n\tconstructor( array, itemSize, normalized ) {\n\n\t\tsuper( new Float32Array( array ), itemSize, normalized );\n\n\t}\n\n}\n\nclass Float64BufferAttribute extends BufferAttribute {\n\n\tconstructor( array, itemSize, normalized ) {\n\n\t\tsuper( new Float64Array( array ), itemSize, normalized );\n\n\t}\n\n}\n\n//\n\nexport {\n\tFloat64BufferAttribute,\n\tFloat32BufferAttribute,\n\tFloat16BufferAttribute,\n\tUint32BufferAttribute,\n\tInt32BufferAttribute,\n\tUint16BufferAttribute,\n\tInt16BufferAttribute,\n\tUint8ClampedBufferAttribute,\n\tUint8BufferAttribute,\n\tInt8BufferAttribute,\n\tBufferAttribute\n};\n","import { Vector3 } from '../math/Vector3.js';\nimport { Vector2 } from '../math/Vector2.js';\nimport { Box3 } from '../math/Box3.js';\nimport { EventDispatcher } from './EventDispatcher.js';\nimport { BufferAttribute, Float32BufferAttribute, Uint16BufferAttribute, Uint32BufferAttribute } from './BufferAttribute.js';\nimport { Sphere } from '../math/Sphere.js';\nimport { Object3D } from './Object3D.js';\nimport { Matrix4 } from '../math/Matrix4.js';\nimport { Matrix3 } from '../math/Matrix3.js';\nimport * as MathUtils from '../math/MathUtils.js';\nimport { arrayNeedsUint32 } from '../utils.js';\n\nlet _id = 0;\n\nconst _m1 = /*@__PURE__*/ new Matrix4();\nconst _obj = /*@__PURE__*/ new Object3D();\nconst _offset = /*@__PURE__*/ new Vector3();\nconst _box = /*@__PURE__*/ new Box3();\nconst _boxMorphTargets = /*@__PURE__*/ new Box3();\nconst _vector = /*@__PURE__*/ new Vector3();\n\nclass BufferGeometry extends EventDispatcher {\n\n\tconstructor() {\n\n\t\tsuper();\n\n\t\tthis.isBufferGeometry = true;\n\n\t\tObject.defineProperty( this, 'id', { value: _id ++ } );\n\n\t\tthis.uuid = MathUtils.generateUUID();\n\n\t\tthis.name = '';\n\t\tthis.type = 'BufferGeometry';\n\n\t\tthis.index = null;\n\t\tthis.attributes = {};\n\n\t\tthis.morphAttributes = {};\n\t\tthis.morphTargetsRelative = false;\n\n\t\tthis.groups = [];\n\n\t\tthis.boundingBox = null;\n\t\tthis.boundingSphere = null;\n\n\t\tthis.drawRange = { start: 0, count: Infinity };\n\n\t\tthis.userData = {};\n\n\t}\n\n\tgetIndex() {\n\n\t\treturn this.index;\n\n\t}\n\n\tsetIndex( index ) {\n\n\t\tif ( Array.isArray( index ) ) {\n\n\t\t\tthis.index = new ( arrayNeedsUint32( index ) ? Uint32BufferAttribute : Uint16BufferAttribute )( index, 1 );\n\n\t\t} else {\n\n\t\t\tthis.index = index;\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tgetAttribute( name ) {\n\n\t\treturn this.attributes[ name ];\n\n\t}\n\n\tsetAttribute( name, attribute ) {\n\n\t\tthis.attributes[ name ] = attribute;\n\n\t\treturn this;\n\n\t}\n\n\tdeleteAttribute( name ) {\n\n\t\tdelete this.attributes[ name ];\n\n\t\treturn this;\n\n\t}\n\n\thasAttribute( name ) {\n\n\t\treturn this.attributes[ name ] !== undefined;\n\n\t}\n\n\taddGroup( start, count, materialIndex = 0 ) {\n\n\t\tthis.groups.push( {\n\n\t\t\tstart: start,\n\t\t\tcount: count,\n\t\t\tmaterialIndex: materialIndex\n\n\t\t} );\n\n\t}\n\n\tclearGroups() {\n\n\t\tthis.groups = [];\n\n\t}\n\n\tsetDrawRange( start, count ) {\n\n\t\tthis.drawRange.start = start;\n\t\tthis.drawRange.count = count;\n\n\t}\n\n\tapplyMatrix4( matrix ) {\n\n\t\tconst position = this.attributes.position;\n\n\t\tif ( position !== undefined ) {\n\n\t\t\tposition.applyMatrix4( matrix );\n\n\t\t\tposition.needsUpdate = true;\n\n\t\t}\n\n\t\tconst normal = this.attributes.normal;\n\n\t\tif ( normal !== undefined ) {\n\n\t\t\tconst normalMatrix = new Matrix3().getNormalMatrix( matrix );\n\n\t\t\tnormal.applyNormalMatrix( normalMatrix );\n\n\t\t\tnormal.needsUpdate = true;\n\n\t\t}\n\n\t\tconst tangent = this.attributes.tangent;\n\n\t\tif ( tangent !== undefined ) {\n\n\t\t\ttangent.transformDirection( matrix );\n\n\t\t\ttangent.needsUpdate = true;\n\n\t\t}\n\n\t\tif ( this.boundingBox !== null ) {\n\n\t\t\tthis.computeBoundingBox();\n\n\t\t}\n\n\t\tif ( this.boundingSphere !== null ) {\n\n\t\t\tthis.computeBoundingSphere();\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tapplyQuaternion( q ) {\n\n\t\t_m1.makeRotationFromQuaternion( q );\n\n\t\tthis.applyMatrix4( _m1 );\n\n\t\treturn this;\n\n\t}\n\n\trotateX( angle ) {\n\n\t\t// rotate geometry around world x-axis\n\n\t\t_m1.makeRotationX( angle );\n\n\t\tthis.applyMatrix4( _m1 );\n\n\t\treturn this;\n\n\t}\n\n\trotateY( angle ) {\n\n\t\t// rotate geometry around world y-axis\n\n\t\t_m1.makeRotationY( angle );\n\n\t\tthis.applyMatrix4( _m1 );\n\n\t\treturn this;\n\n\t}\n\n\trotateZ( angle ) {\n\n\t\t// rotate geometry around world z-axis\n\n\t\t_m1.makeRotationZ( angle );\n\n\t\tthis.applyMatrix4( _m1 );\n\n\t\treturn this;\n\n\t}\n\n\ttranslate( x, y, z ) {\n\n\t\t// translate geometry\n\n\t\t_m1.makeTranslation( x, y, z );\n\n\t\tthis.applyMatrix4( _m1 );\n\n\t\treturn this;\n\n\t}\n\n\tscale( x, y, z ) {\n\n\t\t// scale geometry\n\n\t\t_m1.makeScale( x, y, z );\n\n\t\tthis.applyMatrix4( _m1 );\n\n\t\treturn this;\n\n\t}\n\n\tlookAt( vector ) {\n\n\t\t_obj.lookAt( vector );\n\n\t\t_obj.updateMatrix();\n\n\t\tthis.applyMatrix4( _obj.matrix );\n\n\t\treturn this;\n\n\t}\n\n\tcenter() {\n\n\t\tthis.computeBoundingBox();\n\n\t\tthis.boundingBox.getCenter( _offset ).negate();\n\n\t\tthis.translate( _offset.x, _offset.y, _offset.z );\n\n\t\treturn this;\n\n\t}\n\n\tsetFromPoints( points ) {\n\n\t\tconst position = [];\n\n\t\tfor ( let i = 0, l = points.length; i < l; i ++ ) {\n\n\t\t\tconst point = points[ i ];\n\t\t\tposition.push( point.x, point.y, point.z || 0 );\n\n\t\t}\n\n\t\tthis.setAttribute( 'position', new Float32BufferAttribute( position, 3 ) );\n\n\t\treturn this;\n\n\t}\n\n\tcomputeBoundingBox() {\n\n\t\tif ( this.boundingBox === null ) {\n\n\t\t\tthis.boundingBox = new Box3();\n\n\t\t}\n\n\t\tconst position = this.attributes.position;\n\t\tconst morphAttributesPosition = this.morphAttributes.position;\n\n\t\tif ( position && position.isGLBufferAttribute ) {\n\n\t\t\tconsole.error( 'THREE.BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box. Alternatively set \"mesh.frustumCulled\" to \"false\".', this );\n\n\t\t\tthis.boundingBox.set(\n\t\t\t\tnew Vector3( - Infinity, - Infinity, - Infinity ),\n\t\t\t\tnew Vector3( + Infinity, + Infinity, + Infinity )\n\t\t\t);\n\n\t\t\treturn;\n\n\t\t}\n\n\t\tif ( position !== undefined ) {\n\n\t\t\tthis.boundingBox.setFromBufferAttribute( position );\n\n\t\t\t// process morph attributes if present\n\n\t\t\tif ( morphAttributesPosition ) {\n\n\t\t\t\tfor ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) {\n\n\t\t\t\t\tconst morphAttribute = morphAttributesPosition[ i ];\n\t\t\t\t\t_box.setFromBufferAttribute( morphAttribute );\n\n\t\t\t\t\tif ( this.morphTargetsRelative ) {\n\n\t\t\t\t\t\t_vector.addVectors( this.boundingBox.min, _box.min );\n\t\t\t\t\t\tthis.boundingBox.expandByPoint( _vector );\n\n\t\t\t\t\t\t_vector.addVectors( this.boundingBox.max, _box.max );\n\t\t\t\t\t\tthis.boundingBox.expandByPoint( _vector );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tthis.boundingBox.expandByPoint( _box.min );\n\t\t\t\t\t\tthis.boundingBox.expandByPoint( _box.max );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\tthis.boundingBox.makeEmpty();\n\n\t\t}\n\n\t\tif ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) {\n\n\t\t\tconsole.error( 'THREE.BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The \"position\" attribute is likely to have NaN values.', this );\n\n\t\t}\n\n\t}\n\n\tcomputeBoundingSphere() {\n\n\t\tif ( this.boundingSphere === null ) {\n\n\t\t\tthis.boundingSphere = new Sphere();\n\n\t\t}\n\n\t\tconst position = this.attributes.position;\n\t\tconst morphAttributesPosition = this.morphAttributes.position;\n\n\t\tif ( position && position.isGLBufferAttribute ) {\n\n\t\t\tconsole.error( 'THREE.BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere. Alternatively set \"mesh.frustumCulled\" to \"false\".', this );\n\n\t\t\tthis.boundingSphere.set( new Vector3(), Infinity );\n\n\t\t\treturn;\n\n\t\t}\n\n\t\tif ( position ) {\n\n\t\t\t// first, find the center of the bounding sphere\n\n\t\t\tconst center = this.boundingSphere.center;\n\n\t\t\t_box.setFromBufferAttribute( position );\n\n\t\t\t// process morph attributes if present\n\n\t\t\tif ( morphAttributesPosition ) {\n\n\t\t\t\tfor ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) {\n\n\t\t\t\t\tconst morphAttribute = morphAttributesPosition[ i ];\n\t\t\t\t\t_boxMorphTargets.setFromBufferAttribute( morphAttribute );\n\n\t\t\t\t\tif ( this.morphTargetsRelative ) {\n\n\t\t\t\t\t\t_vector.addVectors( _box.min, _boxMorphTargets.min );\n\t\t\t\t\t\t_box.expandByPoint( _vector );\n\n\t\t\t\t\t\t_vector.addVectors( _box.max, _boxMorphTargets.max );\n\t\t\t\t\t\t_box.expandByPoint( _vector );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t_box.expandByPoint( _boxMorphTargets.min );\n\t\t\t\t\t\t_box.expandByPoint( _boxMorphTargets.max );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t_box.getCenter( center );\n\n\t\t\t// second, try to find a boundingSphere with a radius smaller than the\n\t\t\t// boundingSphere of the boundingBox: sqrt(3) smaller in the best case\n\n\t\t\tlet maxRadiusSq = 0;\n\n\t\t\tfor ( let i = 0, il = position.count; i < il; i ++ ) {\n\n\t\t\t\t_vector.fromBufferAttribute( position, i );\n\n\t\t\t\tmaxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector ) );\n\n\t\t\t}\n\n\t\t\t// process morph attributes if present\n\n\t\t\tif ( morphAttributesPosition ) {\n\n\t\t\t\tfor ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) {\n\n\t\t\t\t\tconst morphAttribute = morphAttributesPosition[ i ];\n\t\t\t\t\tconst morphTargetsRelative = this.morphTargetsRelative;\n\n\t\t\t\t\tfor ( let j = 0, jl = morphAttribute.count; j < jl; j ++ ) {\n\n\t\t\t\t\t\t_vector.fromBufferAttribute( morphAttribute, j );\n\n\t\t\t\t\t\tif ( morphTargetsRelative ) {\n\n\t\t\t\t\t\t\t_offset.fromBufferAttribute( position, j );\n\t\t\t\t\t\t\t_vector.add( _offset );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tmaxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector ) );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tthis.boundingSphere.radius = Math.sqrt( maxRadiusSq );\n\n\t\t\tif ( isNaN( this.boundingSphere.radius ) ) {\n\n\t\t\t\tconsole.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The \"position\" attribute is likely to have NaN values.', this );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tcomputeTangents() {\n\n\t\tconst index = this.index;\n\t\tconst attributes = this.attributes;\n\n\t\t// based on http://www.terathon.com/code/tangent.html\n\t\t// (per vertex tangents)\n\n\t\tif ( index === null ||\n\t\t\t attributes.position === undefined ||\n\t\t\t attributes.normal === undefined ||\n\t\t\t attributes.uv === undefined ) {\n\n\t\t\tconsole.error( 'THREE.BufferGeometry: .computeTangents() failed. Missing required attributes (index, position, normal or uv)' );\n\t\t\treturn;\n\n\t\t}\n\n\t\tconst indices = index.array;\n\t\tconst positions = attributes.position.array;\n\t\tconst normals = attributes.normal.array;\n\t\tconst uvs = attributes.uv.array;\n\n\t\tconst nVertices = positions.length / 3;\n\n\t\tif ( this.hasAttribute( 'tangent' ) === false ) {\n\n\t\t\tthis.setAttribute( 'tangent', new BufferAttribute( new Float32Array( 4 * nVertices ), 4 ) );\n\n\t\t}\n\n\t\tconst tangents = this.getAttribute( 'tangent' ).array;\n\n\t\tconst tan1 = [], tan2 = [];\n\n\t\tfor ( let i = 0; i < nVertices; i ++ ) {\n\n\t\t\ttan1[ i ] = new Vector3();\n\t\t\ttan2[ i ] = new Vector3();\n\n\t\t}\n\n\t\tconst vA = new Vector3(),\n\t\t\tvB = new Vector3(),\n\t\t\tvC = new Vector3(),\n\n\t\t\tuvA = new Vector2(),\n\t\t\tuvB = new Vector2(),\n\t\t\tuvC = new Vector2(),\n\n\t\t\tsdir = new Vector3(),\n\t\t\ttdir = new Vector3();\n\n\t\tfunction handleTriangle( a, b, c ) {\n\n\t\t\tvA.fromArray( positions, a * 3 );\n\t\t\tvB.fromArray( positions, b * 3 );\n\t\t\tvC.fromArray( positions, c * 3 );\n\n\t\t\tuvA.fromArray( uvs, a * 2 );\n\t\t\tuvB.fromArray( uvs, b * 2 );\n\t\t\tuvC.fromArray( uvs, c * 2 );\n\n\t\t\tvB.sub( vA );\n\t\t\tvC.sub( vA );\n\n\t\t\tuvB.sub( uvA );\n\t\t\tuvC.sub( uvA );\n\n\t\t\tconst r = 1.0 / ( uvB.x * uvC.y - uvC.x * uvB.y );\n\n\t\t\t// silently ignore degenerate uv triangles having coincident or colinear vertices\n\n\t\t\tif ( ! isFinite( r ) ) return;\n\n\t\t\tsdir.copy( vB ).multiplyScalar( uvC.y ).addScaledVector( vC, - uvB.y ).multiplyScalar( r );\n\t\t\ttdir.copy( vC ).multiplyScalar( uvB.x ).addScaledVector( vB, - uvC.x ).multiplyScalar( r );\n\n\t\t\ttan1[ a ].add( sdir );\n\t\t\ttan1[ b ].add( sdir );\n\t\t\ttan1[ c ].add( sdir );\n\n\t\t\ttan2[ a ].add( tdir );\n\t\t\ttan2[ b ].add( tdir );\n\t\t\ttan2[ c ].add( tdir );\n\n\t\t}\n\n\t\tlet groups = this.groups;\n\n\t\tif ( groups.length === 0 ) {\n\n\t\t\tgroups = [ {\n\t\t\t\tstart: 0,\n\t\t\t\tcount: indices.length\n\t\t\t} ];\n\n\t\t}\n\n\t\tfor ( let i = 0, il = groups.length; i < il; ++ i ) {\n\n\t\t\tconst group = groups[ i ];\n\n\t\t\tconst start = group.start;\n\t\t\tconst count = group.count;\n\n\t\t\tfor ( let j = start, jl = start + count; j < jl; j += 3 ) {\n\n\t\t\t\thandleTriangle(\n\t\t\t\t\tindices[ j + 0 ],\n\t\t\t\t\tindices[ j + 1 ],\n\t\t\t\t\tindices[ j + 2 ]\n\t\t\t\t);\n\n\t\t\t}\n\n\t\t}\n\n\t\tconst tmp = new Vector3(), tmp2 = new Vector3();\n\t\tconst n = new Vector3(), n2 = new Vector3();\n\n\t\tfunction handleVertex( v ) {\n\n\t\t\tn.fromArray( normals, v * 3 );\n\t\t\tn2.copy( n );\n\n\t\t\tconst t = tan1[ v ];\n\n\t\t\t// Gram-Schmidt orthogonalize\n\n\t\t\ttmp.copy( t );\n\t\t\ttmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize();\n\n\t\t\t// Calculate handedness\n\n\t\t\ttmp2.crossVectors( n2, t );\n\t\t\tconst test = tmp2.dot( tan2[ v ] );\n\t\t\tconst w = ( test < 0.0 ) ? - 1.0 : 1.0;\n\n\t\t\ttangents[ v * 4 ] = tmp.x;\n\t\t\ttangents[ v * 4 + 1 ] = tmp.y;\n\t\t\ttangents[ v * 4 + 2 ] = tmp.z;\n\t\t\ttangents[ v * 4 + 3 ] = w;\n\n\t\t}\n\n\t\tfor ( let i = 0, il = groups.length; i < il; ++ i ) {\n\n\t\t\tconst group = groups[ i ];\n\n\t\t\tconst start = group.start;\n\t\t\tconst count = group.count;\n\n\t\t\tfor ( let j = start, jl = start + count; j < jl; j += 3 ) {\n\n\t\t\t\thandleVertex( indices[ j + 0 ] );\n\t\t\t\thandleVertex( indices[ j + 1 ] );\n\t\t\t\thandleVertex( indices[ j + 2 ] );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tcomputeVertexNormals() {\n\n\t\tconst index = this.index;\n\t\tconst positionAttribute = this.getAttribute( 'position' );\n\n\t\tif ( positionAttribute !== undefined ) {\n\n\t\t\tlet normalAttribute = this.getAttribute( 'normal' );\n\n\t\t\tif ( normalAttribute === undefined ) {\n\n\t\t\t\tnormalAttribute = new BufferAttribute( new Float32Array( positionAttribute.count * 3 ), 3 );\n\t\t\t\tthis.setAttribute( 'normal', normalAttribute );\n\n\t\t\t} else {\n\n\t\t\t\t// reset existing normals to zero\n\n\t\t\t\tfor ( let i = 0, il = normalAttribute.count; i < il; i ++ ) {\n\n\t\t\t\t\tnormalAttribute.setXYZ( i, 0, 0, 0 );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tconst pA = new Vector3(), pB = new Vector3(), pC = new Vector3();\n\t\t\tconst nA = new Vector3(), nB = new Vector3(), nC = new Vector3();\n\t\t\tconst cb = new Vector3(), ab = new Vector3();\n\n\t\t\t// indexed elements\n\n\t\t\tif ( index ) {\n\n\t\t\t\tfor ( let i = 0, il = index.count; i < il; i += 3 ) {\n\n\t\t\t\t\tconst vA = index.getX( i + 0 );\n\t\t\t\t\tconst vB = index.getX( i + 1 );\n\t\t\t\t\tconst vC = index.getX( i + 2 );\n\n\t\t\t\t\tpA.fromBufferAttribute( positionAttribute, vA );\n\t\t\t\t\tpB.fromBufferAttribute( positionAttribute, vB );\n\t\t\t\t\tpC.fromBufferAttribute( positionAttribute, vC );\n\n\t\t\t\t\tcb.subVectors( pC, pB );\n\t\t\t\t\tab.subVectors( pA, pB );\n\t\t\t\t\tcb.cross( ab );\n\n\t\t\t\t\tnA.fromBufferAttribute( normalAttribute, vA );\n\t\t\t\t\tnB.fromBufferAttribute( normalAttribute, vB );\n\t\t\t\t\tnC.fromBufferAttribute( normalAttribute, vC );\n\n\t\t\t\t\tnA.add( cb );\n\t\t\t\t\tnB.add( cb );\n\t\t\t\t\tnC.add( cb );\n\n\t\t\t\t\tnormalAttribute.setXYZ( vA, nA.x, nA.y, nA.z );\n\t\t\t\t\tnormalAttribute.setXYZ( vB, nB.x, nB.y, nB.z );\n\t\t\t\t\tnormalAttribute.setXYZ( vC, nC.x, nC.y, nC.z );\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\t// non-indexed elements (unconnected triangle soup)\n\n\t\t\t\tfor ( let i = 0, il = positionAttribute.count; i < il; i += 3 ) {\n\n\t\t\t\t\tpA.fromBufferAttribute( positionAttribute, i + 0 );\n\t\t\t\t\tpB.fromBufferAttribute( positionAttribute, i + 1 );\n\t\t\t\t\tpC.fromBufferAttribute( positionAttribute, i + 2 );\n\n\t\t\t\t\tcb.subVectors( pC, pB );\n\t\t\t\t\tab.subVectors( pA, pB );\n\t\t\t\t\tcb.cross( ab );\n\n\t\t\t\t\tnormalAttribute.setXYZ( i + 0, cb.x, cb.y, cb.z );\n\t\t\t\t\tnormalAttribute.setXYZ( i + 1, cb.x, cb.y, cb.z );\n\t\t\t\t\tnormalAttribute.setXYZ( i + 2, cb.x, cb.y, cb.z );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tthis.normalizeNormals();\n\n\t\t\tnormalAttribute.needsUpdate = true;\n\n\t\t}\n\n\t}\n\n\tnormalizeNormals() {\n\n\t\tconst normals = this.attributes.normal;\n\n\t\tfor ( let i = 0, il = normals.count; i < il; i ++ ) {\n\n\t\t\t_vector.fromBufferAttribute( normals, i );\n\n\t\t\t_vector.normalize();\n\n\t\t\tnormals.setXYZ( i, _vector.x, _vector.y, _vector.z );\n\n\t\t}\n\n\t}\n\n\ttoNonIndexed() {\n\n\t\tfunction convertBufferAttribute( attribute, indices ) {\n\n\t\t\tconst array = attribute.array;\n\t\t\tconst itemSize = attribute.itemSize;\n\t\t\tconst normalized = attribute.normalized;\n\n\t\t\tconst array2 = new array.constructor( indices.length * itemSize );\n\n\t\t\tlet index = 0, index2 = 0;\n\n\t\t\tfor ( let i = 0, l = indices.length; i < l; i ++ ) {\n\n\t\t\t\tif ( attribute.isInterleavedBufferAttribute ) {\n\n\t\t\t\t\tindex = indices[ i ] * attribute.data.stride + attribute.offset;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tindex = indices[ i ] * itemSize;\n\n\t\t\t\t}\n\n\t\t\t\tfor ( let j = 0; j < itemSize; j ++ ) {\n\n\t\t\t\t\tarray2[ index2 ++ ] = array[ index ++ ];\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn new BufferAttribute( array2, itemSize, normalized );\n\n\t\t}\n\n\t\t//\n\n\t\tif ( this.index === null ) {\n\n\t\t\tconsole.warn( 'THREE.BufferGeometry.toNonIndexed(): BufferGeometry is already non-indexed.' );\n\t\t\treturn this;\n\n\t\t}\n\n\t\tconst geometry2 = new BufferGeometry();\n\n\t\tconst indices = this.index.array;\n\t\tconst attributes = this.attributes;\n\n\t\t// attributes\n\n\t\tfor ( const name in attributes ) {\n\n\t\t\tconst attribute = attributes[ name ];\n\n\t\t\tconst newAttribute = convertBufferAttribute( attribute, indices );\n\n\t\t\tgeometry2.setAttribute( name, newAttribute );\n\n\t\t}\n\n\t\t// morph attributes\n\n\t\tconst morphAttributes = this.morphAttributes;\n\n\t\tfor ( const name in morphAttributes ) {\n\n\t\t\tconst morphArray = [];\n\t\t\tconst morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes\n\n\t\t\tfor ( let i = 0, il = morphAttribute.length; i < il; i ++ ) {\n\n\t\t\t\tconst attribute = morphAttribute[ i ];\n\n\t\t\t\tconst newAttribute = convertBufferAttribute( attribute, indices );\n\n\t\t\t\tmorphArray.push( newAttribute );\n\n\t\t\t}\n\n\t\t\tgeometry2.morphAttributes[ name ] = morphArray;\n\n\t\t}\n\n\t\tgeometry2.morphTargetsRelative = this.morphTargetsRelative;\n\n\t\t// groups\n\n\t\tconst groups = this.groups;\n\n\t\tfor ( let i = 0, l = groups.length; i < l; i ++ ) {\n\n\t\t\tconst group = groups[ i ];\n\t\t\tgeometry2.addGroup( group.start, group.count, group.materialIndex );\n\n\t\t}\n\n\t\treturn geometry2;\n\n\t}\n\n\ttoJSON() {\n\n\t\tconst data = {\n\t\t\tmetadata: {\n\t\t\t\tversion: 4.6,\n\t\t\t\ttype: 'BufferGeometry',\n\t\t\t\tgenerator: 'BufferGeometry.toJSON'\n\t\t\t}\n\t\t};\n\n\t\t// standard BufferGeometry serialization\n\n\t\tdata.uuid = this.uuid;\n\t\tdata.type = this.type;\n\t\tif ( this.name !== '' ) data.name = this.name;\n\t\tif ( Object.keys( this.userData ).length > 0 ) data.userData = this.userData;\n\n\t\tif ( this.parameters !== undefined ) {\n\n\t\t\tconst parameters = this.parameters;\n\n\t\t\tfor ( const key in parameters ) {\n\n\t\t\t\tif ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ];\n\n\t\t\t}\n\n\t\t\treturn data;\n\n\t\t}\n\n\t\t// for simplicity the code assumes attributes are not shared across geometries, see #15811\n\n\t\tdata.data = { attributes: {} };\n\n\t\tconst index = this.index;\n\n\t\tif ( index !== null ) {\n\n\t\t\tdata.data.index = {\n\t\t\t\ttype: index.array.constructor.name,\n\t\t\t\tarray: Array.prototype.slice.call( index.array )\n\t\t\t};\n\n\t\t}\n\n\t\tconst attributes = this.attributes;\n\n\t\tfor ( const key in attributes ) {\n\n\t\t\tconst attribute = attributes[ key ];\n\n\t\t\tdata.data.attributes[ key ] = attribute.toJSON( data.data );\n\n\t\t}\n\n\t\tconst morphAttributes = {};\n\t\tlet hasMorphAttributes = false;\n\n\t\tfor ( const key in this.morphAttributes ) {\n\n\t\t\tconst attributeArray = this.morphAttributes[ key ];\n\n\t\t\tconst array = [];\n\n\t\t\tfor ( let i = 0, il = attributeArray.length; i < il; i ++ ) {\n\n\t\t\t\tconst attribute = attributeArray[ i ];\n\n\t\t\t\tarray.push( attribute.toJSON( data.data ) );\n\n\t\t\t}\n\n\t\t\tif ( array.length > 0 ) {\n\n\t\t\t\tmorphAttributes[ key ] = array;\n\n\t\t\t\thasMorphAttributes = true;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( hasMorphAttributes ) {\n\n\t\t\tdata.data.morphAttributes = morphAttributes;\n\t\t\tdata.data.morphTargetsRelative = this.morphTargetsRelative;\n\n\t\t}\n\n\t\tconst groups = this.groups;\n\n\t\tif ( groups.length > 0 ) {\n\n\t\t\tdata.data.groups = JSON.parse( JSON.stringify( groups ) );\n\n\t\t}\n\n\t\tconst boundingSphere = this.boundingSphere;\n\n\t\tif ( boundingSphere !== null ) {\n\n\t\t\tdata.data.boundingSphere = {\n\t\t\t\tcenter: boundingSphere.center.toArray(),\n\t\t\t\tradius: boundingSphere.radius\n\t\t\t};\n\n\t\t}\n\n\t\treturn data;\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n\tcopy( source ) {\n\n\t\t// reset\n\n\t\tthis.index = null;\n\t\tthis.attributes = {};\n\t\tthis.morphAttributes = {};\n\t\tthis.groups = [];\n\t\tthis.boundingBox = null;\n\t\tthis.boundingSphere = null;\n\n\t\t// used for storing cloned, shared data\n\n\t\tconst data = {};\n\n\t\t// name\n\n\t\tthis.name = source.name;\n\n\t\t// index\n\n\t\tconst index = source.index;\n\n\t\tif ( index !== null ) {\n\n\t\t\tthis.setIndex( index.clone( data ) );\n\n\t\t}\n\n\t\t// attributes\n\n\t\tconst attributes = source.attributes;\n\n\t\tfor ( const name in attributes ) {\n\n\t\t\tconst attribute = attributes[ name ];\n\t\t\tthis.setAttribute( name, attribute.clone( data ) );\n\n\t\t}\n\n\t\t// morph attributes\n\n\t\tconst morphAttributes = source.morphAttributes;\n\n\t\tfor ( const name in morphAttributes ) {\n\n\t\t\tconst array = [];\n\t\t\tconst morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes\n\n\t\t\tfor ( let i = 0, l = morphAttribute.length; i < l; i ++ ) {\n\n\t\t\t\tarray.push( morphAttribute[ i ].clone( data ) );\n\n\t\t\t}\n\n\t\t\tthis.morphAttributes[ name ] = array;\n\n\t\t}\n\n\t\tthis.morphTargetsRelative = source.morphTargetsRelative;\n\n\t\t// groups\n\n\t\tconst groups = source.groups;\n\n\t\tfor ( let i = 0, l = groups.length; i < l; i ++ ) {\n\n\t\t\tconst group = groups[ i ];\n\t\t\tthis.addGroup( group.start, group.count, group.materialIndex );\n\n\t\t}\n\n\t\t// bounding box\n\n\t\tconst boundingBox = source.boundingBox;\n\n\t\tif ( boundingBox !== null ) {\n\n\t\t\tthis.boundingBox = boundingBox.clone();\n\n\t\t}\n\n\t\t// bounding sphere\n\n\t\tconst boundingSphere = source.boundingSphere;\n\n\t\tif ( boundingSphere !== null ) {\n\n\t\t\tthis.boundingSphere = boundingSphere.clone();\n\n\t\t}\n\n\t\t// draw range\n\n\t\tthis.drawRange.start = source.drawRange.start;\n\t\tthis.drawRange.count = source.drawRange.count;\n\n\t\t// user data\n\n\t\tthis.userData = source.userData;\n\n\t\treturn this;\n\n\t}\n\n\tdispose() {\n\n\t\tthis.dispatchEvent( { type: 'dispose' } );\n\n\t}\n\n}\n\nexport { BufferGeometry };\n","import { Vector3 } from '../math/Vector3.js';\nimport { Vector2 } from '../math/Vector2.js';\nimport { Sphere } from '../math/Sphere.js';\nimport { Ray } from '../math/Ray.js';\nimport { Matrix4 } from '../math/Matrix4.js';\nimport { Object3D } from '../core/Object3D.js';\nimport { Triangle } from '../math/Triangle.js';\nimport { BackSide, FrontSide } from '../constants.js';\nimport { MeshBasicMaterial } from '../materials/MeshBasicMaterial.js';\nimport { BufferGeometry } from '../core/BufferGeometry.js';\n\nconst _inverseMatrix = /*@__PURE__*/ new Matrix4();\nconst _ray = /*@__PURE__*/ new Ray();\nconst _sphere = /*@__PURE__*/ new Sphere();\nconst _sphereHitAt = /*@__PURE__*/ new Vector3();\n\nconst _vA = /*@__PURE__*/ new Vector3();\nconst _vB = /*@__PURE__*/ new Vector3();\nconst _vC = /*@__PURE__*/ new Vector3();\n\nconst _tempA = /*@__PURE__*/ new Vector3();\nconst _morphA = /*@__PURE__*/ new Vector3();\n\nconst _uvA = /*@__PURE__*/ new Vector2();\nconst _uvB = /*@__PURE__*/ new Vector2();\nconst _uvC = /*@__PURE__*/ new Vector2();\n\nconst _normalA = /*@__PURE__*/ new Vector3();\nconst _normalB = /*@__PURE__*/ new Vector3();\nconst _normalC = /*@__PURE__*/ new Vector3();\n\nconst _intersectionPoint = /*@__PURE__*/ new Vector3();\nconst _intersectionPointWorld = /*@__PURE__*/ new Vector3();\n\nclass Mesh extends Object3D {\n\n\tconstructor( geometry = new BufferGeometry(), material = new MeshBasicMaterial() ) {\n\n\t\tsuper();\n\n\t\tthis.isMesh = true;\n\n\t\tthis.type = 'Mesh';\n\n\t\tthis.geometry = geometry;\n\t\tthis.material = material;\n\n\t\tthis.updateMorphTargets();\n\n\t}\n\n\tcopy( source, recursive ) {\n\n\t\tsuper.copy( source, recursive );\n\n\t\tif ( source.morphTargetInfluences !== undefined ) {\n\n\t\t\tthis.morphTargetInfluences = source.morphTargetInfluences.slice();\n\n\t\t}\n\n\t\tif ( source.morphTargetDictionary !== undefined ) {\n\n\t\t\tthis.morphTargetDictionary = Object.assign( {}, source.morphTargetDictionary );\n\n\t\t}\n\n\t\tthis.material = Array.isArray( source.material ) ? source.material.slice() : source.material;\n\t\tthis.geometry = source.geometry;\n\n\t\treturn this;\n\n\t}\n\n\tupdateMorphTargets() {\n\n\t\tconst geometry = this.geometry;\n\n\t\tconst morphAttributes = geometry.morphAttributes;\n\t\tconst keys = Object.keys( morphAttributes );\n\n\t\tif ( keys.length > 0 ) {\n\n\t\t\tconst morphAttribute = morphAttributes[ keys[ 0 ] ];\n\n\t\t\tif ( morphAttribute !== undefined ) {\n\n\t\t\t\tthis.morphTargetInfluences = [];\n\t\t\t\tthis.morphTargetDictionary = {};\n\n\t\t\t\tfor ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) {\n\n\t\t\t\t\tconst name = morphAttribute[ m ].name || String( m );\n\n\t\t\t\t\tthis.morphTargetInfluences.push( 0 );\n\t\t\t\t\tthis.morphTargetDictionary[ name ] = m;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tgetVertexPosition( index, target ) {\n\n\t\tconst geometry = this.geometry;\n\t\tconst position = geometry.attributes.position;\n\t\tconst morphPosition = geometry.morphAttributes.position;\n\t\tconst morphTargetsRelative = geometry.morphTargetsRelative;\n\n\t\ttarget.fromBufferAttribute( position, index );\n\n\t\tconst morphInfluences = this.morphTargetInfluences;\n\n\t\tif ( morphPosition && morphInfluences ) {\n\n\t\t\t_morphA.set( 0, 0, 0 );\n\n\t\t\tfor ( let i = 0, il = morphPosition.length; i < il; i ++ ) {\n\n\t\t\t\tconst influence = morphInfluences[ i ];\n\t\t\t\tconst morphAttribute = morphPosition[ i ];\n\n\t\t\t\tif ( influence === 0 ) continue;\n\n\t\t\t\t_tempA.fromBufferAttribute( morphAttribute, index );\n\n\t\t\t\tif ( morphTargetsRelative ) {\n\n\t\t\t\t\t_morphA.addScaledVector( _tempA, influence );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t_morphA.addScaledVector( _tempA.sub( target ), influence );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\ttarget.add( _morphA );\n\n\t\t}\n\n\t\treturn target;\n\n\t}\n\n\traycast( raycaster, intersects ) {\n\n\t\tconst geometry = this.geometry;\n\t\tconst material = this.material;\n\t\tconst matrixWorld = this.matrixWorld;\n\n\t\tif ( material === undefined ) return;\n\n\t\t// test with bounding sphere in world space\n\n\t\tif ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();\n\n\t\t_sphere.copy( geometry.boundingSphere );\n\t\t_sphere.applyMatrix4( matrixWorld );\n\n\t\t// check distance from ray origin to bounding sphere\n\n\t\t_ray.copy( raycaster.ray ).recast( raycaster.near );\n\n\t\tif ( _sphere.containsPoint( _ray.origin ) === false ) {\n\n\t\t\tif ( _ray.intersectSphere( _sphere, _sphereHitAt ) === null ) return;\n\n\t\t\tif ( _ray.origin.distanceToSquared( _sphereHitAt ) > ( raycaster.far - raycaster.near ) ** 2 ) return;\n\n\t\t}\n\n\t\t// convert ray to local space of mesh\n\n\t\t_inverseMatrix.copy( matrixWorld ).invert();\n\t\t_ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix );\n\n\t\t// test with bounding box in local space\n\n\t\tif ( geometry.boundingBox !== null ) {\n\n\t\t\tif ( _ray.intersectsBox( geometry.boundingBox ) === false ) return;\n\n\t\t}\n\n\t\t// test for intersections with geometry\n\n\t\tthis._computeIntersections( raycaster, intersects, _ray );\n\n\t}\n\n\t_computeIntersections( raycaster, intersects, rayLocalSpace ) {\n\n\t\tlet intersection;\n\n\t\tconst geometry = this.geometry;\n\t\tconst material = this.material;\n\n\t\tconst index = geometry.index;\n\t\tconst position = geometry.attributes.position;\n\t\tconst uv = geometry.attributes.uv;\n\t\tconst uv1 = geometry.attributes.uv1;\n\t\tconst normal = geometry.attributes.normal;\n\t\tconst groups = geometry.groups;\n\t\tconst drawRange = geometry.drawRange;\n\n\t\tif ( index !== null ) {\n\n\t\t\t// indexed buffer geometry\n\n\t\t\tif ( Array.isArray( material ) ) {\n\n\t\t\t\tfor ( let i = 0, il = groups.length; i < il; i ++ ) {\n\n\t\t\t\t\tconst group = groups[ i ];\n\t\t\t\t\tconst groupMaterial = material[ group.materialIndex ];\n\n\t\t\t\t\tconst start = Math.max( group.start, drawRange.start );\n\t\t\t\t\tconst end = Math.min( index.count, Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ) );\n\n\t\t\t\t\tfor ( let j = start, jl = end; j < jl; j += 3 ) {\n\n\t\t\t\t\t\tconst a = index.getX( j );\n\t\t\t\t\t\tconst b = index.getX( j + 1 );\n\t\t\t\t\t\tconst c = index.getX( j + 2 );\n\n\t\t\t\t\t\tintersection = checkGeometryIntersection( this, groupMaterial, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c );\n\n\t\t\t\t\t\tif ( intersection ) {\n\n\t\t\t\t\t\t\tintersection.faceIndex = Math.floor( j / 3 ); // triangle number in indexed buffer semantics\n\t\t\t\t\t\t\tintersection.face.materialIndex = group.materialIndex;\n\t\t\t\t\t\t\tintersects.push( intersection );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tconst start = Math.max( 0, drawRange.start );\n\t\t\t\tconst end = Math.min( index.count, ( drawRange.start + drawRange.count ) );\n\n\t\t\t\tfor ( let i = start, il = end; i < il; i += 3 ) {\n\n\t\t\t\t\tconst a = index.getX( i );\n\t\t\t\t\tconst b = index.getX( i + 1 );\n\t\t\t\t\tconst c = index.getX( i + 2 );\n\n\t\t\t\t\tintersection = checkGeometryIntersection( this, material, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c );\n\n\t\t\t\t\tif ( intersection ) {\n\n\t\t\t\t\t\tintersection.faceIndex = Math.floor( i / 3 ); // triangle number in indexed buffer semantics\n\t\t\t\t\t\tintersects.push( intersection );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t} else if ( position !== undefined ) {\n\n\t\t\t// non-indexed buffer geometry\n\n\t\t\tif ( Array.isArray( material ) ) {\n\n\t\t\t\tfor ( let i = 0, il = groups.length; i < il; i ++ ) {\n\n\t\t\t\t\tconst group = groups[ i ];\n\t\t\t\t\tconst groupMaterial = material[ group.materialIndex ];\n\n\t\t\t\t\tconst start = Math.max( group.start, drawRange.start );\n\t\t\t\t\tconst end = Math.min( position.count, Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ) );\n\n\t\t\t\t\tfor ( let j = start, jl = end; j < jl; j += 3 ) {\n\n\t\t\t\t\t\tconst a = j;\n\t\t\t\t\t\tconst b = j + 1;\n\t\t\t\t\t\tconst c = j + 2;\n\n\t\t\t\t\t\tintersection = checkGeometryIntersection( this, groupMaterial, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c );\n\n\t\t\t\t\t\tif ( intersection ) {\n\n\t\t\t\t\t\t\tintersection.faceIndex = Math.floor( j / 3 ); // triangle number in non-indexed buffer semantics\n\t\t\t\t\t\t\tintersection.face.materialIndex = group.materialIndex;\n\t\t\t\t\t\t\tintersects.push( intersection );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tconst start = Math.max( 0, drawRange.start );\n\t\t\t\tconst end = Math.min( position.count, ( drawRange.start + drawRange.count ) );\n\n\t\t\t\tfor ( let i = start, il = end; i < il; i += 3 ) {\n\n\t\t\t\t\tconst a = i;\n\t\t\t\t\tconst b = i + 1;\n\t\t\t\t\tconst c = i + 2;\n\n\t\t\t\t\tintersection = checkGeometryIntersection( this, material, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c );\n\n\t\t\t\t\tif ( intersection ) {\n\n\t\t\t\t\t\tintersection.faceIndex = Math.floor( i / 3 ); // triangle number in non-indexed buffer semantics\n\t\t\t\t\t\tintersects.push( intersection );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n\nfunction checkIntersection( object, material, raycaster, ray, pA, pB, pC, point ) {\n\n\tlet intersect;\n\n\tif ( material.side === BackSide ) {\n\n\t\tintersect = ray.intersectTriangle( pC, pB, pA, true, point );\n\n\t} else {\n\n\t\tintersect = ray.intersectTriangle( pA, pB, pC, ( material.side === FrontSide ), point );\n\n\t}\n\n\tif ( intersect === null ) return null;\n\n\t_intersectionPointWorld.copy( point );\n\t_intersectionPointWorld.applyMatrix4( object.matrixWorld );\n\n\tconst distance = raycaster.ray.origin.distanceTo( _intersectionPointWorld );\n\n\tif ( distance < raycaster.near || distance > raycaster.far ) return null;\n\n\treturn {\n\t\tdistance: distance,\n\t\tpoint: _intersectionPointWorld.clone(),\n\t\tobject: object\n\t};\n\n}\n\nfunction checkGeometryIntersection( object, material, raycaster, ray, uv, uv1, normal, a, b, c ) {\n\n\tobject.getVertexPosition( a, _vA );\n\tobject.getVertexPosition( b, _vB );\n\tobject.getVertexPosition( c, _vC );\n\n\tconst intersection = checkIntersection( object, material, raycaster, ray, _vA, _vB, _vC, _intersectionPoint );\n\n\tif ( intersection ) {\n\n\t\tif ( uv ) {\n\n\t\t\t_uvA.fromBufferAttribute( uv, a );\n\t\t\t_uvB.fromBufferAttribute( uv, b );\n\t\t\t_uvC.fromBufferAttribute( uv, c );\n\n\t\t\tintersection.uv = Triangle.getInterpolation( _intersectionPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() );\n\n\t\t}\n\n\t\tif ( uv1 ) {\n\n\t\t\t_uvA.fromBufferAttribute( uv1, a );\n\t\t\t_uvB.fromBufferAttribute( uv1, b );\n\t\t\t_uvC.fromBufferAttribute( uv1, c );\n\n\t\t\tintersection.uv1 = Triangle.getInterpolation( _intersectionPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() );\n\t\t\tintersection.uv2 = intersection.uv1; // @deprecated, r152\n\n\t\t}\n\n\t\tif ( normal ) {\n\n\t\t\t_normalA.fromBufferAttribute( normal, a );\n\t\t\t_normalB.fromBufferAttribute( normal, b );\n\t\t\t_normalC.fromBufferAttribute( normal, c );\n\n\t\t\tintersection.normal = Triangle.getInterpolation( _intersectionPoint, _vA, _vB, _vC, _normalA, _normalB, _normalC, new Vector3() );\n\n\t\t\tif ( intersection.normal.dot( ray.direction ) > 0 ) {\n\n\t\t\t\tintersection.normal.multiplyScalar( - 1 );\n\n\t\t\t}\n\n\t\t}\n\n\t\tconst face = {\n\t\t\ta: a,\n\t\t\tb: b,\n\t\t\tc: c,\n\t\t\tnormal: new Vector3(),\n\t\t\tmaterialIndex: 0\n\t\t};\n\n\t\tTriangle.getNormal( _vA, _vB, _vC, face.normal );\n\n\t\tintersection.face = face;\n\n\t}\n\n\treturn intersection;\n\n}\n\nexport { Mesh };\n","import { BufferGeometry } from '../core/BufferGeometry.js';\nimport { Float32BufferAttribute } from '../core/BufferAttribute.js';\nimport { Vector3 } from '../math/Vector3.js';\n\nclass BoxGeometry extends BufferGeometry {\n\n\tconstructor( width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1 ) {\n\n\t\tsuper();\n\n\t\tthis.type = 'BoxGeometry';\n\n\t\tthis.parameters = {\n\t\t\twidth: width,\n\t\t\theight: height,\n\t\t\tdepth: depth,\n\t\t\twidthSegments: widthSegments,\n\t\t\theightSegments: heightSegments,\n\t\t\tdepthSegments: depthSegments\n\t\t};\n\n\t\tconst scope = this;\n\n\t\t// segments\n\n\t\twidthSegments = Math.floor( widthSegments );\n\t\theightSegments = Math.floor( heightSegments );\n\t\tdepthSegments = Math.floor( depthSegments );\n\n\t\t// buffers\n\n\t\tconst indices = [];\n\t\tconst vertices = [];\n\t\tconst normals = [];\n\t\tconst uvs = [];\n\n\t\t// helper variables\n\n\t\tlet numberOfVertices = 0;\n\t\tlet groupStart = 0;\n\n\t\t// build each side of the box geometry\n\n\t\tbuildPlane( 'z', 'y', 'x', - 1, - 1, depth, height, width, depthSegments, heightSegments, 0 ); // px\n\t\tbuildPlane( 'z', 'y', 'x', 1, - 1, depth, height, - width, depthSegments, heightSegments, 1 ); // nx\n\t\tbuildPlane( 'x', 'z', 'y', 1, 1, width, depth, height, widthSegments, depthSegments, 2 ); // py\n\t\tbuildPlane( 'x', 'z', 'y', 1, - 1, width, depth, - height, widthSegments, depthSegments, 3 ); // ny\n\t\tbuildPlane( 'x', 'y', 'z', 1, - 1, width, height, depth, widthSegments, heightSegments, 4 ); // pz\n\t\tbuildPlane( 'x', 'y', 'z', - 1, - 1, width, height, - depth, widthSegments, heightSegments, 5 ); // nz\n\n\t\t// build geometry\n\n\t\tthis.setIndex( indices );\n\t\tthis.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n\t\tthis.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n\t\tthis.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n\t\tfunction buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) {\n\n\t\t\tconst segmentWidth = width / gridX;\n\t\t\tconst segmentHeight = height / gridY;\n\n\t\t\tconst widthHalf = width / 2;\n\t\t\tconst heightHalf = height / 2;\n\t\t\tconst depthHalf = depth / 2;\n\n\t\t\tconst gridX1 = gridX + 1;\n\t\t\tconst gridY1 = gridY + 1;\n\n\t\t\tlet vertexCounter = 0;\n\t\t\tlet groupCount = 0;\n\n\t\t\tconst vector = new Vector3();\n\n\t\t\t// generate vertices, normals and uvs\n\n\t\t\tfor ( let iy = 0; iy < gridY1; iy ++ ) {\n\n\t\t\t\tconst y = iy * segmentHeight - heightHalf;\n\n\t\t\t\tfor ( let ix = 0; ix < gridX1; ix ++ ) {\n\n\t\t\t\t\tconst x = ix * segmentWidth - widthHalf;\n\n\t\t\t\t\t// set values to correct vector component\n\n\t\t\t\t\tvector[ u ] = x * udir;\n\t\t\t\t\tvector[ v ] = y * vdir;\n\t\t\t\t\tvector[ w ] = depthHalf;\n\n\t\t\t\t\t// now apply vector to vertex buffer\n\n\t\t\t\t\tvertices.push( vector.x, vector.y, vector.z );\n\n\t\t\t\t\t// set values to correct vector component\n\n\t\t\t\t\tvector[ u ] = 0;\n\t\t\t\t\tvector[ v ] = 0;\n\t\t\t\t\tvector[ w ] = depth > 0 ? 1 : - 1;\n\n\t\t\t\t\t// now apply vector to normal buffer\n\n\t\t\t\t\tnormals.push( vector.x, vector.y, vector.z );\n\n\t\t\t\t\t// uvs\n\n\t\t\t\t\tuvs.push( ix / gridX );\n\t\t\t\t\tuvs.push( 1 - ( iy / gridY ) );\n\n\t\t\t\t\t// counters\n\n\t\t\t\t\tvertexCounter += 1;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// indices\n\n\t\t\t// 1. you need three indices to draw a single face\n\t\t\t// 2. a single segment consists of two faces\n\t\t\t// 3. so we need to generate six (2*3) indices per segment\n\n\t\t\tfor ( let iy = 0; iy < gridY; iy ++ ) {\n\n\t\t\t\tfor ( let ix = 0; ix < gridX; ix ++ ) {\n\n\t\t\t\t\tconst a = numberOfVertices + ix + gridX1 * iy;\n\t\t\t\t\tconst b = numberOfVertices + ix + gridX1 * ( iy + 1 );\n\t\t\t\t\tconst c = numberOfVertices + ( ix + 1 ) + gridX1 * ( iy + 1 );\n\t\t\t\t\tconst d = numberOfVertices + ( ix + 1 ) + gridX1 * iy;\n\n\t\t\t\t\t// faces\n\n\t\t\t\t\tindices.push( a, b, d );\n\t\t\t\t\tindices.push( b, c, d );\n\n\t\t\t\t\t// increase counter\n\n\t\t\t\t\tgroupCount += 6;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// add a group to the geometry. this will ensure multi material support\n\n\t\t\tscope.addGroup( groupStart, groupCount, materialIndex );\n\n\t\t\t// calculate new start value for groups\n\n\t\t\tgroupStart += groupCount;\n\n\t\t\t// update total number of vertices\n\n\t\t\tnumberOfVertices += vertexCounter;\n\n\t\t}\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.parameters = Object.assign( {}, source.parameters );\n\n\t\treturn this;\n\n\t}\n\n\tstatic fromJSON( data ) {\n\n\t\treturn new BoxGeometry( data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments );\n\n\t}\n\n}\n\nexport { BoxGeometry };\n","import { ColorManagement } from '../../math/ColorManagement.js';\n\n/**\n * Uniform Utilities\n */\n\nexport function cloneUniforms( src ) {\n\n\tconst dst = {};\n\n\tfor ( const u in src ) {\n\n\t\tdst[ u ] = {};\n\n\t\tfor ( const p in src[ u ] ) {\n\n\t\t\tconst property = src[ u ][ p ];\n\n\t\t\tif ( property && ( property.isColor ||\n\t\t\t\tproperty.isMatrix3 || property.isMatrix4 ||\n\t\t\t\tproperty.isVector2 || property.isVector3 || property.isVector4 ||\n\t\t\t\tproperty.isTexture || property.isQuaternion ) ) {\n\n\t\t\t\tif ( property.isRenderTargetTexture ) {\n\n\t\t\t\t\tconsole.warn( 'UniformsUtils: Textures of render targets cannot be cloned via cloneUniforms() or mergeUniforms().' );\n\t\t\t\t\tdst[ u ][ p ] = null;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tdst[ u ][ p ] = property.clone();\n\n\t\t\t\t}\n\n\t\t\t} else if ( Array.isArray( property ) ) {\n\n\t\t\t\tdst[ u ][ p ] = property.slice();\n\n\t\t\t} else {\n\n\t\t\t\tdst[ u ][ p ] = property;\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\treturn dst;\n\n}\n\nexport function mergeUniforms( uniforms ) {\n\n\tconst merged = {};\n\n\tfor ( let u = 0; u < uniforms.length; u ++ ) {\n\n\t\tconst tmp = cloneUniforms( uniforms[ u ] );\n\n\t\tfor ( const p in tmp ) {\n\n\t\t\tmerged[ p ] = tmp[ p ];\n\n\t\t}\n\n\t}\n\n\treturn merged;\n\n}\n\nexport function cloneUniformsGroups( src ) {\n\n\tconst dst = [];\n\n\tfor ( let u = 0; u < src.length; u ++ ) {\n\n\t\tdst.push( src[ u ].clone() );\n\n\t}\n\n\treturn dst;\n\n}\n\nexport function getUnlitUniformColorSpace( renderer ) {\n\n\tif ( renderer.getRenderTarget() === null ) {\n\n\t\t// https://github.com/mrdoob/three.js/pull/23937#issuecomment-1111067398\n\t\treturn renderer.outputColorSpace;\n\n\t}\n\n\treturn ColorManagement.workingColorSpace;\n\n}\n\n// Legacy\n\nconst UniformsUtils = { clone: cloneUniforms, merge: mergeUniforms };\n\nexport { UniformsUtils };\n","import { Material } from './Material.js';\nimport { cloneUniforms, cloneUniformsGroups } from '../renderers/shaders/UniformsUtils.js';\n\nimport default_vertex from '../renderers/shaders/ShaderChunk/default_vertex.glsl.js';\nimport default_fragment from '../renderers/shaders/ShaderChunk/default_fragment.glsl.js';\n\nclass ShaderMaterial extends Material {\n\n\tconstructor( parameters ) {\n\n\t\tsuper();\n\n\t\tthis.isShaderMaterial = true;\n\n\t\tthis.type = 'ShaderMaterial';\n\n\t\tthis.defines = {};\n\t\tthis.uniforms = {};\n\t\tthis.uniformsGroups = [];\n\n\t\tthis.vertexShader = default_vertex;\n\t\tthis.fragmentShader = default_fragment;\n\n\t\tthis.linewidth = 1;\n\n\t\tthis.wireframe = false;\n\t\tthis.wireframeLinewidth = 1;\n\n\t\tthis.fog = false; // set to use scene fog\n\t\tthis.lights = false; // set to use scene lights\n\t\tthis.clipping = false; // set to use user-defined clipping planes\n\n\t\tthis.forceSinglePass = true;\n\n\t\tthis.extensions = {\n\t\t\tderivatives: false, // set to use derivatives\n\t\t\tfragDepth: false, // set to use fragment depth values\n\t\t\tdrawBuffers: false, // set to use draw buffers\n\t\t\tshaderTextureLOD: false // set to use shader texture LOD\n\t\t};\n\n\t\t// When rendered geometry doesn't include these attributes but the material does,\n\t\t// use these default values in WebGL. This avoids errors when buffer data is missing.\n\t\tthis.defaultAttributeValues = {\n\t\t\t'color': [ 1, 1, 1 ],\n\t\t\t'uv': [ 0, 0 ],\n\t\t\t'uv1': [ 0, 0 ]\n\t\t};\n\n\t\tthis.index0AttributeName = undefined;\n\t\tthis.uniformsNeedUpdate = false;\n\n\t\tthis.glslVersion = null;\n\n\t\tif ( parameters !== undefined ) {\n\n\t\t\tthis.setValues( parameters );\n\n\t\t}\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.fragmentShader = source.fragmentShader;\n\t\tthis.vertexShader = source.vertexShader;\n\n\t\tthis.uniforms = cloneUniforms( source.uniforms );\n\t\tthis.uniformsGroups = cloneUniformsGroups( source.uniformsGroups );\n\n\t\tthis.defines = Object.assign( {}, source.defines );\n\n\t\tthis.wireframe = source.wireframe;\n\t\tthis.wireframeLinewidth = source.wireframeLinewidth;\n\n\t\tthis.fog = source.fog;\n\t\tthis.lights = source.lights;\n\t\tthis.clipping = source.clipping;\n\n\t\tthis.extensions = Object.assign( {}, source.extensions );\n\n\t\tthis.glslVersion = source.glslVersion;\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON( meta ) {\n\n\t\tconst data = super.toJSON( meta );\n\n\t\tdata.glslVersion = this.glslVersion;\n\t\tdata.uniforms = {};\n\n\t\tfor ( const name in this.uniforms ) {\n\n\t\t\tconst uniform = this.uniforms[ name ];\n\t\t\tconst value = uniform.value;\n\n\t\t\tif ( value && value.isTexture ) {\n\n\t\t\t\tdata.uniforms[ name ] = {\n\t\t\t\t\ttype: 't',\n\t\t\t\t\tvalue: value.toJSON( meta ).uuid\n\t\t\t\t};\n\n\t\t\t} else if ( value && value.isColor ) {\n\n\t\t\t\tdata.uniforms[ name ] = {\n\t\t\t\t\ttype: 'c',\n\t\t\t\t\tvalue: value.getHex()\n\t\t\t\t};\n\n\t\t\t} else if ( value && value.isVector2 ) {\n\n\t\t\t\tdata.uniforms[ name ] = {\n\t\t\t\t\ttype: 'v2',\n\t\t\t\t\tvalue: value.toArray()\n\t\t\t\t};\n\n\t\t\t} else if ( value && value.isVector3 ) {\n\n\t\t\t\tdata.uniforms[ name ] = {\n\t\t\t\t\ttype: 'v3',\n\t\t\t\t\tvalue: value.toArray()\n\t\t\t\t};\n\n\t\t\t} else if ( value && value.isVector4 ) {\n\n\t\t\t\tdata.uniforms[ name ] = {\n\t\t\t\t\ttype: 'v4',\n\t\t\t\t\tvalue: value.toArray()\n\t\t\t\t};\n\n\t\t\t} else if ( value && value.isMatrix3 ) {\n\n\t\t\t\tdata.uniforms[ name ] = {\n\t\t\t\t\ttype: 'm3',\n\t\t\t\t\tvalue: value.toArray()\n\t\t\t\t};\n\n\t\t\t} else if ( value && value.isMatrix4 ) {\n\n\t\t\t\tdata.uniforms[ name ] = {\n\t\t\t\t\ttype: 'm4',\n\t\t\t\t\tvalue: value.toArray()\n\t\t\t\t};\n\n\t\t\t} else {\n\n\t\t\t\tdata.uniforms[ name ] = {\n\t\t\t\t\tvalue: value\n\t\t\t\t};\n\n\t\t\t\t// note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( Object.keys( this.defines ).length > 0 ) data.defines = this.defines;\n\n\t\tdata.vertexShader = this.vertexShader;\n\t\tdata.fragmentShader = this.fragmentShader;\n\n\t\tdata.lights = this.lights;\n\t\tdata.clipping = this.clipping;\n\n\t\tconst extensions = {};\n\n\t\tfor ( const key in this.extensions ) {\n\n\t\t\tif ( this.extensions[ key ] === true ) extensions[ key ] = true;\n\n\t\t}\n\n\t\tif ( Object.keys( extensions ).length > 0 ) data.extensions = extensions;\n\n\t\treturn data;\n\n\t}\n\n}\n\nexport { ShaderMaterial };\n","export default /* glsl */`\nvoid main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}\n`;\n","export default /* glsl */`\nvoid main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}\n`;\n","import { WebGLCoordinateSystem } from '../constants.js';\nimport { Matrix4 } from '../math/Matrix4.js';\nimport { Object3D } from '../core/Object3D.js';\n\nclass Camera extends Object3D {\n\n\tconstructor() {\n\n\t\tsuper();\n\n\t\tthis.isCamera = true;\n\n\t\tthis.type = 'Camera';\n\n\t\tthis.matrixWorldInverse = new Matrix4();\n\n\t\tthis.projectionMatrix = new Matrix4();\n\t\tthis.projectionMatrixInverse = new Matrix4();\n\n\t\tthis.coordinateSystem = WebGLCoordinateSystem;\n\n\t}\n\n\tcopy( source, recursive ) {\n\n\t\tsuper.copy( source, recursive );\n\n\t\tthis.matrixWorldInverse.copy( source.matrixWorldInverse );\n\n\t\tthis.projectionMatrix.copy( source.projectionMatrix );\n\t\tthis.projectionMatrixInverse.copy( source.projectionMatrixInverse );\n\n\t\tthis.coordinateSystem = source.coordinateSystem;\n\n\t\treturn this;\n\n\t}\n\n\tgetWorldDirection( target ) {\n\n\t\treturn super.getWorldDirection( target ).negate();\n\n\t}\n\n\tupdateMatrixWorld( force ) {\n\n\t\tsuper.updateMatrixWorld( force );\n\n\t\tthis.matrixWorldInverse.copy( this.matrixWorld ).invert();\n\n\t}\n\n\tupdateWorldMatrix( updateParents, updateChildren ) {\n\n\t\tsuper.updateWorldMatrix( updateParents, updateChildren );\n\n\t\tthis.matrixWorldInverse.copy( this.matrixWorld ).invert();\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n}\n\nexport { Camera };\n","import { Camera } from './Camera.js';\nimport * as MathUtils from '../math/MathUtils.js';\n\nclass PerspectiveCamera extends Camera {\n\n\tconstructor( fov = 50, aspect = 1, near = 0.1, far = 2000 ) {\n\n\t\tsuper();\n\n\t\tthis.isPerspectiveCamera = true;\n\n\t\tthis.type = 'PerspectiveCamera';\n\n\t\tthis.fov = fov;\n\t\tthis.zoom = 1;\n\n\t\tthis.near = near;\n\t\tthis.far = far;\n\t\tthis.focus = 10;\n\n\t\tthis.aspect = aspect;\n\t\tthis.view = null;\n\n\t\tthis.filmGauge = 35;\t// width of the film (default in millimeters)\n\t\tthis.filmOffset = 0;\t// horizontal film offset (same unit as gauge)\n\n\t\tthis.updateProjectionMatrix();\n\n\t}\n\n\tcopy( source, recursive ) {\n\n\t\tsuper.copy( source, recursive );\n\n\t\tthis.fov = source.fov;\n\t\tthis.zoom = source.zoom;\n\n\t\tthis.near = source.near;\n\t\tthis.far = source.far;\n\t\tthis.focus = source.focus;\n\n\t\tthis.aspect = source.aspect;\n\t\tthis.view = source.view === null ? null : Object.assign( {}, source.view );\n\n\t\tthis.filmGauge = source.filmGauge;\n\t\tthis.filmOffset = source.filmOffset;\n\n\t\treturn this;\n\n\t}\n\n\t/**\n\t * Sets the FOV by focal length in respect to the current .filmGauge.\n\t *\n\t * The default film gauge is 35, so that the focal length can be specified for\n\t * a 35mm (full frame) camera.\n\t *\n\t * Values for focal length and film gauge must have the same unit.\n\t */\n\tsetFocalLength( focalLength ) {\n\n\t\t/** see {@link http://www.bobatkins.com/photography/technical/field_of_view.html} */\n\t\tconst vExtentSlope = 0.5 * this.getFilmHeight() / focalLength;\n\n\t\tthis.fov = MathUtils.RAD2DEG * 2 * Math.atan( vExtentSlope );\n\t\tthis.updateProjectionMatrix();\n\n\t}\n\n\t/**\n\t * Calculates the focal length from the current .fov and .filmGauge.\n\t */\n\tgetFocalLength() {\n\n\t\tconst vExtentSlope = Math.tan( MathUtils.DEG2RAD * 0.5 * this.fov );\n\n\t\treturn 0.5 * this.getFilmHeight() / vExtentSlope;\n\n\t}\n\n\tgetEffectiveFOV() {\n\n\t\treturn MathUtils.RAD2DEG * 2 * Math.atan(\n\t\t\tMath.tan( MathUtils.DEG2RAD * 0.5 * this.fov ) / this.zoom );\n\n\t}\n\n\tgetFilmWidth() {\n\n\t\t// film not completely covered in portrait format (aspect < 1)\n\t\treturn this.filmGauge * Math.min( this.aspect, 1 );\n\n\t}\n\n\tgetFilmHeight() {\n\n\t\t// film not completely covered in landscape format (aspect > 1)\n\t\treturn this.filmGauge / Math.max( this.aspect, 1 );\n\n\t}\n\n\t/**\n\t * Sets an offset in a larger frustum. This is useful for multi-window or\n\t * multi-monitor/multi-machine setups.\n\t *\n\t * For example, if you have 3x2 monitors and each monitor is 1920x1080 and\n\t * the monitors are in grid like this\n\t *\n\t * +---+---+---+\n\t * | A | B | C |\n\t * +---+---+---+\n\t * | D | E | F |\n\t * +---+---+---+\n\t *\n\t * then for each monitor you would call it like this\n\t *\n\t * const w = 1920;\n\t * const h = 1080;\n\t * const fullWidth = w * 3;\n\t * const fullHeight = h * 2;\n\t *\n\t * --A--\n\t * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h );\n\t * --B--\n\t * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h );\n\t * --C--\n\t * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h );\n\t * --D--\n\t * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h );\n\t * --E--\n\t * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h );\n\t * --F--\n\t * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h );\n\t *\n\t * Note there is no reason monitors have to be the same size or in a grid.\n\t */\n\tsetViewOffset( fullWidth, fullHeight, x, y, width, height ) {\n\n\t\tthis.aspect = fullWidth / fullHeight;\n\n\t\tif ( this.view === null ) {\n\n\t\t\tthis.view = {\n\t\t\t\tenabled: true,\n\t\t\t\tfullWidth: 1,\n\t\t\t\tfullHeight: 1,\n\t\t\t\toffsetX: 0,\n\t\t\t\toffsetY: 0,\n\t\t\t\twidth: 1,\n\t\t\t\theight: 1\n\t\t\t};\n\n\t\t}\n\n\t\tthis.view.enabled = true;\n\t\tthis.view.fullWidth = fullWidth;\n\t\tthis.view.fullHeight = fullHeight;\n\t\tthis.view.offsetX = x;\n\t\tthis.view.offsetY = y;\n\t\tthis.view.width = width;\n\t\tthis.view.height = height;\n\n\t\tthis.updateProjectionMatrix();\n\n\t}\n\n\tclearViewOffset() {\n\n\t\tif ( this.view !== null ) {\n\n\t\t\tthis.view.enabled = false;\n\n\t\t}\n\n\t\tthis.updateProjectionMatrix();\n\n\t}\n\n\tupdateProjectionMatrix() {\n\n\t\tconst near = this.near;\n\t\tlet top = near * Math.tan( MathUtils.DEG2RAD * 0.5 * this.fov ) / this.zoom;\n\t\tlet height = 2 * top;\n\t\tlet width = this.aspect * height;\n\t\tlet left = - 0.5 * width;\n\t\tconst view = this.view;\n\n\t\tif ( this.view !== null && this.view.enabled ) {\n\n\t\t\tconst fullWidth = view.fullWidth,\n\t\t\t\tfullHeight = view.fullHeight;\n\n\t\t\tleft += view.offsetX * width / fullWidth;\n\t\t\ttop -= view.offsetY * height / fullHeight;\n\t\t\twidth *= view.width / fullWidth;\n\t\t\theight *= view.height / fullHeight;\n\n\t\t}\n\n\t\tconst skew = this.filmOffset;\n\t\tif ( skew !== 0 ) left += near * skew / this.getFilmWidth();\n\n\t\tthis.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far, this.coordinateSystem );\n\n\t\tthis.projectionMatrixInverse.copy( this.projectionMatrix ).invert();\n\n\t}\n\n\ttoJSON( meta ) {\n\n\t\tconst data = super.toJSON( meta );\n\n\t\tdata.object.fov = this.fov;\n\t\tdata.object.zoom = this.zoom;\n\n\t\tdata.object.near = this.near;\n\t\tdata.object.far = this.far;\n\t\tdata.object.focus = this.focus;\n\n\t\tdata.object.aspect = this.aspect;\n\n\t\tif ( this.view !== null ) data.object.view = Object.assign( {}, this.view );\n\n\t\tdata.object.filmGauge = this.filmGauge;\n\t\tdata.object.filmOffset = this.filmOffset;\n\n\t\treturn data;\n\n\t}\n\n}\n\nexport { PerspectiveCamera };\n","import { WebGLCoordinateSystem, WebGPUCoordinateSystem } from '../constants.js';\nimport { Object3D } from '../core/Object3D.js';\nimport { PerspectiveCamera } from './PerspectiveCamera.js';\n\nconst fov = - 90; // negative fov is not an error\nconst aspect = 1;\n\nclass CubeCamera extends Object3D {\n\n\tconstructor( near, far, renderTarget ) {\n\n\t\tsuper();\n\n\t\tthis.type = 'CubeCamera';\n\n\t\tthis.renderTarget = renderTarget;\n\t\tthis.coordinateSystem = null;\n\t\tthis.activeMipmapLevel = 0;\n\n\t\tconst cameraPX = new PerspectiveCamera( fov, aspect, near, far );\n\t\tcameraPX.layers = this.layers;\n\t\tthis.add( cameraPX );\n\n\t\tconst cameraNX = new PerspectiveCamera( fov, aspect, near, far );\n\t\tcameraNX.layers = this.layers;\n\t\tthis.add( cameraNX );\n\n\t\tconst cameraPY = new PerspectiveCamera( fov, aspect, near, far );\n\t\tcameraPY.layers = this.layers;\n\t\tthis.add( cameraPY );\n\n\t\tconst cameraNY = new PerspectiveCamera( fov, aspect, near, far );\n\t\tcameraNY.layers = this.layers;\n\t\tthis.add( cameraNY );\n\n\t\tconst cameraPZ = new PerspectiveCamera( fov, aspect, near, far );\n\t\tcameraPZ.layers = this.layers;\n\t\tthis.add( cameraPZ );\n\n\t\tconst cameraNZ = new PerspectiveCamera( fov, aspect, near, far );\n\t\tcameraNZ.layers = this.layers;\n\t\tthis.add( cameraNZ );\n\n\t}\n\n\tupdateCoordinateSystem() {\n\n\t\tconst coordinateSystem = this.coordinateSystem;\n\n\t\tconst cameras = this.children.concat();\n\n\t\tconst [ cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ ] = cameras;\n\n\t\tfor ( const camera of cameras ) this.remove( camera );\n\n\t\tif ( coordinateSystem === WebGLCoordinateSystem ) {\n\n\t\t\tcameraPX.up.set( 0, 1, 0 );\n\t\t\tcameraPX.lookAt( 1, 0, 0 );\n\n\t\t\tcameraNX.up.set( 0, 1, 0 );\n\t\t\tcameraNX.lookAt( - 1, 0, 0 );\n\n\t\t\tcameraPY.up.set( 0, 0, - 1 );\n\t\t\tcameraPY.lookAt( 0, 1, 0 );\n\n\t\t\tcameraNY.up.set( 0, 0, 1 );\n\t\t\tcameraNY.lookAt( 0, - 1, 0 );\n\n\t\t\tcameraPZ.up.set( 0, 1, 0 );\n\t\t\tcameraPZ.lookAt( 0, 0, 1 );\n\n\t\t\tcameraNZ.up.set( 0, 1, 0 );\n\t\t\tcameraNZ.lookAt( 0, 0, - 1 );\n\n\t\t} else if ( coordinateSystem === WebGPUCoordinateSystem ) {\n\n\t\t\tcameraPX.up.set( 0, - 1, 0 );\n\t\t\tcameraPX.lookAt( - 1, 0, 0 );\n\n\t\t\tcameraNX.up.set( 0, - 1, 0 );\n\t\t\tcameraNX.lookAt( 1, 0, 0 );\n\n\t\t\tcameraPY.up.set( 0, 0, 1 );\n\t\t\tcameraPY.lookAt( 0, 1, 0 );\n\n\t\t\tcameraNY.up.set( 0, 0, - 1 );\n\t\t\tcameraNY.lookAt( 0, - 1, 0 );\n\n\t\t\tcameraPZ.up.set( 0, - 1, 0 );\n\t\t\tcameraPZ.lookAt( 0, 0, 1 );\n\n\t\t\tcameraNZ.up.set( 0, - 1, 0 );\n\t\t\tcameraNZ.lookAt( 0, 0, - 1 );\n\n\t\t} else {\n\n\t\t\tthrow new Error( 'THREE.CubeCamera.updateCoordinateSystem(): Invalid coordinate system: ' + coordinateSystem );\n\n\t\t}\n\n\t\tfor ( const camera of cameras ) {\n\n\t\t\tthis.add( camera );\n\n\t\t\tcamera.updateMatrixWorld();\n\n\t\t}\n\n\t}\n\n\tupdate( renderer, scene ) {\n\n\t\tif ( this.parent === null ) this.updateMatrixWorld();\n\n\t\tconst { renderTarget, activeMipmapLevel } = this;\n\n\t\tif ( this.coordinateSystem !== renderer.coordinateSystem ) {\n\n\t\t\tthis.coordinateSystem = renderer.coordinateSystem;\n\n\t\t\tthis.updateCoordinateSystem();\n\n\t\t}\n\n\t\tconst [ cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ ] = this.children;\n\n\t\tconst currentRenderTarget = renderer.getRenderTarget();\n\t\tconst currentActiveCubeFace = renderer.getActiveCubeFace();\n\t\tconst currentActiveMipmapLevel = renderer.getActiveMipmapLevel();\n\n\t\tconst currentXrEnabled = renderer.xr.enabled;\n\n\t\trenderer.xr.enabled = false;\n\n\t\tconst generateMipmaps = renderTarget.texture.generateMipmaps;\n\n\t\trenderTarget.texture.generateMipmaps = false;\n\n\t\trenderer.setRenderTarget( renderTarget, 0, activeMipmapLevel );\n\t\trenderer.render( scene, cameraPX );\n\n\t\trenderer.setRenderTarget( renderTarget, 1, activeMipmapLevel );\n\t\trenderer.render( scene, cameraNX );\n\n\t\trenderer.setRenderTarget( renderTarget, 2, activeMipmapLevel );\n\t\trenderer.render( scene, cameraPY );\n\n\t\trenderer.setRenderTarget( renderTarget, 3, activeMipmapLevel );\n\t\trenderer.render( scene, cameraNY );\n\n\t\trenderer.setRenderTarget( renderTarget, 4, activeMipmapLevel );\n\t\trenderer.render( scene, cameraPZ );\n\n\t\t// mipmaps are generated during the last call of render()\n\t\t// at this point, all sides of the cube render target are defined\n\n\t\trenderTarget.texture.generateMipmaps = generateMipmaps;\n\n\t\trenderer.setRenderTarget( renderTarget, 5, activeMipmapLevel );\n\t\trenderer.render( scene, cameraNZ );\n\n\t\trenderer.setRenderTarget( currentRenderTarget, currentActiveCubeFace, currentActiveMipmapLevel );\n\n\t\trenderer.xr.enabled = currentXrEnabled;\n\n\t\trenderTarget.texture.needsPMREMUpdate = true;\n\n\t}\n\n}\n\nexport { CubeCamera };\n","import { Texture } from './Texture.js';\nimport { CubeReflectionMapping } from '../constants.js';\n\nclass CubeTexture extends Texture {\n\n\tconstructor( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace ) {\n\n\t\timages = images !== undefined ? images : [];\n\t\tmapping = mapping !== undefined ? mapping : CubeReflectionMapping;\n\n\t\tsuper( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace );\n\n\t\tthis.isCubeTexture = true;\n\n\t\tthis.flipY = false;\n\n\t}\n\n\tget images() {\n\n\t\treturn this.image;\n\n\t}\n\n\tset images( value ) {\n\n\t\tthis.image = value;\n\n\t}\n\n}\n\nexport { CubeTexture };\n","import { BackSide, LinearFilter, LinearMipmapLinearFilter, NoBlending, NoColorSpace, SRGBColorSpace, sRGBEncoding } from '../constants.js';\nimport { Mesh } from '../objects/Mesh.js';\nimport { BoxGeometry } from '../geometries/BoxGeometry.js';\nimport { ShaderMaterial } from '../materials/ShaderMaterial.js';\nimport { cloneUniforms } from './shaders/UniformsUtils.js';\nimport { WebGLRenderTarget } from './WebGLRenderTarget.js';\nimport { CubeCamera } from '../cameras/CubeCamera.js';\nimport { CubeTexture } from '../textures/CubeTexture.js';\nimport { warnOnce } from '../utils.js';\n\nclass WebGLCubeRenderTarget extends WebGLRenderTarget {\n\n\tconstructor( size = 1, options = {} ) {\n\n\t\tsuper( size, size, options );\n\n\t\tthis.isWebGLCubeRenderTarget = true;\n\n\t\tconst image = { width: size, height: size, depth: 1 };\n\t\tconst images = [ image, image, image, image, image, image ];\n\n\t\tif ( options.encoding !== undefined ) {\n\n\t\t\t// @deprecated, r152\n\t\t\twarnOnce( 'THREE.WebGLCubeRenderTarget: option.encoding has been replaced by option.colorSpace.' );\n\t\t\toptions.colorSpace = options.encoding === sRGBEncoding ? SRGBColorSpace : NoColorSpace;\n\n\t\t}\n\n\t\tthis.texture = new CubeTexture( images, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.colorSpace );\n\n\t\t// By convention -- likely based on the RenderMan spec from the 1990's -- cube maps are specified by WebGL (and three.js)\n\t\t// in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words,\n\t\t// in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly.\n\n\t\t// three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped\n\t\t// and the flag isRenderTargetTexture controls this conversion. The flip is not required when using WebGLCubeRenderTarget.texture\n\t\t// as a cube texture (this is detected when isRenderTargetTexture is set to true for cube textures).\n\n\t\tthis.texture.isRenderTargetTexture = true;\n\n\t\tthis.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false;\n\t\tthis.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter;\n\n\t}\n\n\tfromEquirectangularTexture( renderer, texture ) {\n\n\t\tthis.texture.type = texture.type;\n\t\tthis.texture.colorSpace = texture.colorSpace;\n\n\t\tthis.texture.generateMipmaps = texture.generateMipmaps;\n\t\tthis.texture.minFilter = texture.minFilter;\n\t\tthis.texture.magFilter = texture.magFilter;\n\n\t\tconst shader = {\n\n\t\t\tuniforms: {\n\t\t\t\ttEquirect: { value: null },\n\t\t\t},\n\n\t\t\tvertexShader: /* glsl */`\n\n\t\t\t\tvarying vec3 vWorldDirection;\n\n\t\t\t\tvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\n\t\t\t\t\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n\n\t\t\t\t}\n\n\t\t\t\tvoid main() {\n\n\t\t\t\t\tvWorldDirection = transformDirection( position, modelMatrix );\n\n\t\t\t\t\t#include \n\t\t\t\t\t#include \n\n\t\t\t\t}\n\t\t\t`,\n\n\t\t\tfragmentShader: /* glsl */`\n\n\t\t\t\tuniform sampler2D tEquirect;\n\n\t\t\t\tvarying vec3 vWorldDirection;\n\n\t\t\t\t#include \n\n\t\t\t\tvoid main() {\n\n\t\t\t\t\tvec3 direction = normalize( vWorldDirection );\n\n\t\t\t\t\tvec2 sampleUV = equirectUv( direction );\n\n\t\t\t\t\tgl_FragColor = texture2D( tEquirect, sampleUV );\n\n\t\t\t\t}\n\t\t\t`\n\t\t};\n\n\t\tconst geometry = new BoxGeometry( 5, 5, 5 );\n\n\t\tconst material = new ShaderMaterial( {\n\n\t\t\tname: 'CubemapFromEquirect',\n\n\t\t\tuniforms: cloneUniforms( shader.uniforms ),\n\t\t\tvertexShader: shader.vertexShader,\n\t\t\tfragmentShader: shader.fragmentShader,\n\t\t\tside: BackSide,\n\t\t\tblending: NoBlending\n\n\t\t} );\n\n\t\tmaterial.uniforms.tEquirect.value = texture;\n\n\t\tconst mesh = new Mesh( geometry, material );\n\n\t\tconst currentMinFilter = texture.minFilter;\n\n\t\t// Avoid blurred poles\n\t\tif ( texture.minFilter === LinearMipmapLinearFilter ) texture.minFilter = LinearFilter;\n\n\t\tconst camera = new CubeCamera( 1, 10, this );\n\t\tcamera.update( renderer, mesh );\n\n\t\ttexture.minFilter = currentMinFilter;\n\n\t\tmesh.geometry.dispose();\n\t\tmesh.material.dispose();\n\n\t\treturn this;\n\n\t}\n\n\tclear( renderer, color, depth, stencil ) {\n\n\t\tconst currentRenderTarget = renderer.getRenderTarget();\n\n\t\tfor ( let i = 0; i < 6; i ++ ) {\n\n\t\t\trenderer.setRenderTarget( this, i );\n\n\t\t\trenderer.clear( color, depth, stencil );\n\n\t\t}\n\n\t\trenderer.setRenderTarget( currentRenderTarget );\n\n\t}\n\n}\n\nexport { WebGLCubeRenderTarget };\n","import { Matrix3 } from './Matrix3.js';\nimport { Vector3 } from './Vector3.js';\n\nconst _vector1 = /*@__PURE__*/ new Vector3();\nconst _vector2 = /*@__PURE__*/ new Vector3();\nconst _normalMatrix = /*@__PURE__*/ new Matrix3();\n\nclass Plane {\n\n\tconstructor( normal = new Vector3( 1, 0, 0 ), constant = 0 ) {\n\n\t\tthis.isPlane = true;\n\n\t\t// normal is assumed to be normalized\n\n\t\tthis.normal = normal;\n\t\tthis.constant = constant;\n\n\t}\n\n\tset( normal, constant ) {\n\n\t\tthis.normal.copy( normal );\n\t\tthis.constant = constant;\n\n\t\treturn this;\n\n\t}\n\n\tsetComponents( x, y, z, w ) {\n\n\t\tthis.normal.set( x, y, z );\n\t\tthis.constant = w;\n\n\t\treturn this;\n\n\t}\n\n\tsetFromNormalAndCoplanarPoint( normal, point ) {\n\n\t\tthis.normal.copy( normal );\n\t\tthis.constant = - point.dot( this.normal );\n\n\t\treturn this;\n\n\t}\n\n\tsetFromCoplanarPoints( a, b, c ) {\n\n\t\tconst normal = _vector1.subVectors( c, b ).cross( _vector2.subVectors( a, b ) ).normalize();\n\n\t\t// Q: should an error be thrown if normal is zero (e.g. degenerate plane)?\n\n\t\tthis.setFromNormalAndCoplanarPoint( normal, a );\n\n\t\treturn this;\n\n\t}\n\n\tcopy( plane ) {\n\n\t\tthis.normal.copy( plane.normal );\n\t\tthis.constant = plane.constant;\n\n\t\treturn this;\n\n\t}\n\n\tnormalize() {\n\n\t\t// Note: will lead to a divide by zero if the plane is invalid.\n\n\t\tconst inverseNormalLength = 1.0 / this.normal.length();\n\t\tthis.normal.multiplyScalar( inverseNormalLength );\n\t\tthis.constant *= inverseNormalLength;\n\n\t\treturn this;\n\n\t}\n\n\tnegate() {\n\n\t\tthis.constant *= - 1;\n\t\tthis.normal.negate();\n\n\t\treturn this;\n\n\t}\n\n\tdistanceToPoint( point ) {\n\n\t\treturn this.normal.dot( point ) + this.constant;\n\n\t}\n\n\tdistanceToSphere( sphere ) {\n\n\t\treturn this.distanceToPoint( sphere.center ) - sphere.radius;\n\n\t}\n\n\tprojectPoint( point, target ) {\n\n\t\treturn target.copy( point ).addScaledVector( this.normal, - this.distanceToPoint( point ) );\n\n\t}\n\n\tintersectLine( line, target ) {\n\n\t\tconst direction = line.delta( _vector1 );\n\n\t\tconst denominator = this.normal.dot( direction );\n\n\t\tif ( denominator === 0 ) {\n\n\t\t\t// line is coplanar, return origin\n\t\t\tif ( this.distanceToPoint( line.start ) === 0 ) {\n\n\t\t\t\treturn target.copy( line.start );\n\n\t\t\t}\n\n\t\t\t// Unsure if this is the correct method to handle this case.\n\t\t\treturn null;\n\n\t\t}\n\n\t\tconst t = - ( line.start.dot( this.normal ) + this.constant ) / denominator;\n\n\t\tif ( t < 0 || t > 1 ) {\n\n\t\t\treturn null;\n\n\t\t}\n\n\t\treturn target.copy( line.start ).addScaledVector( direction, t );\n\n\t}\n\n\tintersectsLine( line ) {\n\n\t\t// Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it.\n\n\t\tconst startSign = this.distanceToPoint( line.start );\n\t\tconst endSign = this.distanceToPoint( line.end );\n\n\t\treturn ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 );\n\n\t}\n\n\tintersectsBox( box ) {\n\n\t\treturn box.intersectsPlane( this );\n\n\t}\n\n\tintersectsSphere( sphere ) {\n\n\t\treturn sphere.intersectsPlane( this );\n\n\t}\n\n\tcoplanarPoint( target ) {\n\n\t\treturn target.copy( this.normal ).multiplyScalar( - this.constant );\n\n\t}\n\n\tapplyMatrix4( matrix, optionalNormalMatrix ) {\n\n\t\tconst normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix( matrix );\n\n\t\tconst referencePoint = this.coplanarPoint( _vector1 ).applyMatrix4( matrix );\n\n\t\tconst normal = this.normal.applyMatrix3( normalMatrix ).normalize();\n\n\t\tthis.constant = - referencePoint.dot( normal );\n\n\t\treturn this;\n\n\t}\n\n\ttranslate( offset ) {\n\n\t\tthis.constant -= offset.dot( this.normal );\n\n\t\treturn this;\n\n\t}\n\n\tequals( plane ) {\n\n\t\treturn plane.normal.equals( this.normal ) && ( plane.constant === this.constant );\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n}\n\nexport { Plane };\n","import { WebGLCoordinateSystem, WebGPUCoordinateSystem } from '../constants.js';\nimport { Vector3 } from './Vector3.js';\nimport { Sphere } from './Sphere.js';\nimport { Plane } from './Plane.js';\n\nconst _sphere = /*@__PURE__*/ new Sphere();\nconst _vector = /*@__PURE__*/ new Vector3();\n\nclass Frustum {\n\n\tconstructor( p0 = new Plane(), p1 = new Plane(), p2 = new Plane(), p3 = new Plane(), p4 = new Plane(), p5 = new Plane() ) {\n\n\t\tthis.planes = [ p0, p1, p2, p3, p4, p5 ];\n\n\t}\n\n\tset( p0, p1, p2, p3, p4, p5 ) {\n\n\t\tconst planes = this.planes;\n\n\t\tplanes[ 0 ].copy( p0 );\n\t\tplanes[ 1 ].copy( p1 );\n\t\tplanes[ 2 ].copy( p2 );\n\t\tplanes[ 3 ].copy( p3 );\n\t\tplanes[ 4 ].copy( p4 );\n\t\tplanes[ 5 ].copy( p5 );\n\n\t\treturn this;\n\n\t}\n\n\tcopy( frustum ) {\n\n\t\tconst planes = this.planes;\n\n\t\tfor ( let i = 0; i < 6; i ++ ) {\n\n\t\t\tplanes[ i ].copy( frustum.planes[ i ] );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tsetFromProjectionMatrix( m, coordinateSystem = WebGLCoordinateSystem ) {\n\n\t\tconst planes = this.planes;\n\t\tconst me = m.elements;\n\t\tconst me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ];\n\t\tconst me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ];\n\t\tconst me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ];\n\t\tconst me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ];\n\n\t\tplanes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize();\n\t\tplanes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize();\n\t\tplanes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize();\n\t\tplanes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize();\n\t\tplanes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize();\n\n\t\tif ( coordinateSystem === WebGLCoordinateSystem ) {\n\n\t\t\tplanes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize();\n\n\t\t} else if ( coordinateSystem === WebGPUCoordinateSystem ) {\n\n\t\t\tplanes[ 5 ].setComponents( me2, me6, me10, me14 ).normalize();\n\n\t\t} else {\n\n\t\t\tthrow new Error( 'THREE.Frustum.setFromProjectionMatrix(): Invalid coordinate system: ' + coordinateSystem );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tintersectsObject( object ) {\n\n\t\tif ( object.boundingSphere !== undefined ) {\n\n\t\t\tif ( object.boundingSphere === null ) object.computeBoundingSphere();\n\n\t\t\t_sphere.copy( object.boundingSphere ).applyMatrix4( object.matrixWorld );\n\n\t\t} else {\n\n\t\t\tconst geometry = object.geometry;\n\n\t\t\tif ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();\n\n\t\t\t_sphere.copy( geometry.boundingSphere ).applyMatrix4( object.matrixWorld );\n\n\t\t}\n\n\t\treturn this.intersectsSphere( _sphere );\n\n\t}\n\n\tintersectsSprite( sprite ) {\n\n\t\t_sphere.center.set( 0, 0, 0 );\n\t\t_sphere.radius = 0.7071067811865476;\n\t\t_sphere.applyMatrix4( sprite.matrixWorld );\n\n\t\treturn this.intersectsSphere( _sphere );\n\n\t}\n\n\tintersectsSphere( sphere ) {\n\n\t\tconst planes = this.planes;\n\t\tconst center = sphere.center;\n\t\tconst negRadius = - sphere.radius;\n\n\t\tfor ( let i = 0; i < 6; i ++ ) {\n\n\t\t\tconst distance = planes[ i ].distanceToPoint( center );\n\n\t\t\tif ( distance < negRadius ) {\n\n\t\t\t\treturn false;\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn true;\n\n\t}\n\n\tintersectsBox( box ) {\n\n\t\tconst planes = this.planes;\n\n\t\tfor ( let i = 0; i < 6; i ++ ) {\n\n\t\t\tconst plane = planes[ i ];\n\n\t\t\t// corner at max distance\n\n\t\t\t_vector.x = plane.normal.x > 0 ? box.max.x : box.min.x;\n\t\t\t_vector.y = plane.normal.y > 0 ? box.max.y : box.min.y;\n\t\t\t_vector.z = plane.normal.z > 0 ? box.max.z : box.min.z;\n\n\t\t\tif ( plane.distanceToPoint( _vector ) < 0 ) {\n\n\t\t\t\treturn false;\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn true;\n\n\t}\n\n\tcontainsPoint( point ) {\n\n\t\tconst planes = this.planes;\n\n\t\tfor ( let i = 0; i < 6; i ++ ) {\n\n\t\t\tif ( planes[ i ].distanceToPoint( point ) < 0 ) {\n\n\t\t\t\treturn false;\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn true;\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n}\n\n\nexport { Frustum };\n","function WebGLAnimation() {\n\n\tlet context = null;\n\tlet isAnimating = false;\n\tlet animationLoop = null;\n\tlet requestId = null;\n\n\tfunction onAnimationFrame( time, frame ) {\n\n\t\tanimationLoop( time, frame );\n\n\t\trequestId = context.requestAnimationFrame( onAnimationFrame );\n\n\t}\n\n\treturn {\n\n\t\tstart: function () {\n\n\t\t\tif ( isAnimating === true ) return;\n\t\t\tif ( animationLoop === null ) return;\n\n\t\t\trequestId = context.requestAnimationFrame( onAnimationFrame );\n\n\t\t\tisAnimating = true;\n\n\t\t},\n\n\t\tstop: function () {\n\n\t\t\tcontext.cancelAnimationFrame( requestId );\n\n\t\t\tisAnimating = false;\n\n\t\t},\n\n\t\tsetAnimationLoop: function ( callback ) {\n\n\t\t\tanimationLoop = callback;\n\n\t\t},\n\n\t\tsetContext: function ( value ) {\n\n\t\t\tcontext = value;\n\n\t\t}\n\n\t};\n\n}\n\nexport { WebGLAnimation };\n","function WebGLAttributes( gl, capabilities ) {\n\n\tconst isWebGL2 = capabilities.isWebGL2;\n\n\tconst buffers = new WeakMap();\n\n\tfunction createBuffer( attribute, bufferType ) {\n\n\t\tconst array = attribute.array;\n\t\tconst usage = attribute.usage;\n\n\t\tconst buffer = gl.createBuffer();\n\n\t\tgl.bindBuffer( bufferType, buffer );\n\t\tgl.bufferData( bufferType, array, usage );\n\n\t\tattribute.onUploadCallback();\n\n\t\tlet type;\n\n\t\tif ( array instanceof Float32Array ) {\n\n\t\t\ttype = gl.FLOAT;\n\n\t\t} else if ( array instanceof Uint16Array ) {\n\n\t\t\tif ( attribute.isFloat16BufferAttribute ) {\n\n\t\t\t\tif ( isWebGL2 ) {\n\n\t\t\t\t\ttype = gl.HALF_FLOAT;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tthrow new Error( 'THREE.WebGLAttributes: Usage of Float16BufferAttribute requires WebGL2.' );\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\ttype = gl.UNSIGNED_SHORT;\n\n\t\t\t}\n\n\t\t} else if ( array instanceof Int16Array ) {\n\n\t\t\ttype = gl.SHORT;\n\n\t\t} else if ( array instanceof Uint32Array ) {\n\n\t\t\ttype = gl.UNSIGNED_INT;\n\n\t\t} else if ( array instanceof Int32Array ) {\n\n\t\t\ttype = gl.INT;\n\n\t\t} else if ( array instanceof Int8Array ) {\n\n\t\t\ttype = gl.BYTE;\n\n\t\t} else if ( array instanceof Uint8Array ) {\n\n\t\t\ttype = gl.UNSIGNED_BYTE;\n\n\t\t} else if ( array instanceof Uint8ClampedArray ) {\n\n\t\t\ttype = gl.UNSIGNED_BYTE;\n\n\t\t} else {\n\n\t\t\tthrow new Error( 'THREE.WebGLAttributes: Unsupported buffer data format: ' + array );\n\n\t\t}\n\n\t\treturn {\n\t\t\tbuffer: buffer,\n\t\t\ttype: type,\n\t\t\tbytesPerElement: array.BYTES_PER_ELEMENT,\n\t\t\tversion: attribute.version\n\t\t};\n\n\t}\n\n\tfunction updateBuffer( buffer, attribute, bufferType ) {\n\n\t\tconst array = attribute.array;\n\t\tconst updateRange = attribute.updateRange;\n\n\t\tgl.bindBuffer( bufferType, buffer );\n\n\t\tif ( updateRange.count === - 1 ) {\n\n\t\t\t// Not using update ranges\n\n\t\t\tgl.bufferSubData( bufferType, 0, array );\n\n\t\t} else {\n\n\t\t\tif ( isWebGL2 ) {\n\n\t\t\t\tgl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT,\n\t\t\t\t\tarray, updateRange.offset, updateRange.count );\n\n\t\t\t} else {\n\n\t\t\t\tgl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT,\n\t\t\t\t\tarray.subarray( updateRange.offset, updateRange.offset + updateRange.count ) );\n\n\t\t\t}\n\n\t\t\tupdateRange.count = - 1; // reset range\n\n\t\t}\n\n\t\tattribute.onUploadCallback();\n\n\t}\n\n\t//\n\n\tfunction get( attribute ) {\n\n\t\tif ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;\n\n\t\treturn buffers.get( attribute );\n\n\t}\n\n\tfunction remove( attribute ) {\n\n\t\tif ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;\n\n\t\tconst data = buffers.get( attribute );\n\n\t\tif ( data ) {\n\n\t\t\tgl.deleteBuffer( data.buffer );\n\n\t\t\tbuffers.delete( attribute );\n\n\t\t}\n\n\t}\n\n\tfunction update( attribute, bufferType ) {\n\n\t\tif ( attribute.isGLBufferAttribute ) {\n\n\t\t\tconst cached = buffers.get( attribute );\n\n\t\t\tif ( ! cached || cached.version < attribute.version ) {\n\n\t\t\t\tbuffers.set( attribute, {\n\t\t\t\t\tbuffer: attribute.buffer,\n\t\t\t\t\ttype: attribute.type,\n\t\t\t\t\tbytesPerElement: attribute.elementSize,\n\t\t\t\t\tversion: attribute.version\n\t\t\t\t} );\n\n\t\t\t}\n\n\t\t\treturn;\n\n\t\t}\n\n\t\tif ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;\n\n\t\tconst data = buffers.get( attribute );\n\n\t\tif ( data === undefined ) {\n\n\t\t\tbuffers.set( attribute, createBuffer( attribute, bufferType ) );\n\n\t\t} else if ( data.version < attribute.version ) {\n\n\t\t\tupdateBuffer( data.buffer, attribute, bufferType );\n\n\t\t\tdata.version = attribute.version;\n\n\t\t}\n\n\t}\n\n\treturn {\n\n\t\tget: get,\n\t\tremove: remove,\n\t\tupdate: update\n\n\t};\n\n}\n\n\nexport { WebGLAttributes };\n","import { BufferGeometry } from '../core/BufferGeometry.js';\nimport { Float32BufferAttribute } from '../core/BufferAttribute.js';\n\nclass PlaneGeometry extends BufferGeometry {\n\n\tconstructor( width = 1, height = 1, widthSegments = 1, heightSegments = 1 ) {\n\n\t\tsuper();\n\n\t\tthis.type = 'PlaneGeometry';\n\n\t\tthis.parameters = {\n\t\t\twidth: width,\n\t\t\theight: height,\n\t\t\twidthSegments: widthSegments,\n\t\t\theightSegments: heightSegments\n\t\t};\n\n\t\tconst width_half = width / 2;\n\t\tconst height_half = height / 2;\n\n\t\tconst gridX = Math.floor( widthSegments );\n\t\tconst gridY = Math.floor( heightSegments );\n\n\t\tconst gridX1 = gridX + 1;\n\t\tconst gridY1 = gridY + 1;\n\n\t\tconst segment_width = width / gridX;\n\t\tconst segment_height = height / gridY;\n\n\t\t//\n\n\t\tconst indices = [];\n\t\tconst vertices = [];\n\t\tconst normals = [];\n\t\tconst uvs = [];\n\n\t\tfor ( let iy = 0; iy < gridY1; iy ++ ) {\n\n\t\t\tconst y = iy * segment_height - height_half;\n\n\t\t\tfor ( let ix = 0; ix < gridX1; ix ++ ) {\n\n\t\t\t\tconst x = ix * segment_width - width_half;\n\n\t\t\t\tvertices.push( x, - y, 0 );\n\n\t\t\t\tnormals.push( 0, 0, 1 );\n\n\t\t\t\tuvs.push( ix / gridX );\n\t\t\t\tuvs.push( 1 - ( iy / gridY ) );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfor ( let iy = 0; iy < gridY; iy ++ ) {\n\n\t\t\tfor ( let ix = 0; ix < gridX; ix ++ ) {\n\n\t\t\t\tconst a = ix + gridX1 * iy;\n\t\t\t\tconst b = ix + gridX1 * ( iy + 1 );\n\t\t\t\tconst c = ( ix + 1 ) + gridX1 * ( iy + 1 );\n\t\t\t\tconst d = ( ix + 1 ) + gridX1 * iy;\n\n\t\t\t\tindices.push( a, b, d );\n\t\t\t\tindices.push( b, c, d );\n\n\t\t\t}\n\n\t\t}\n\n\t\tthis.setIndex( indices );\n\t\tthis.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n\t\tthis.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n\t\tthis.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.parameters = Object.assign( {}, source.parameters );\n\n\t\treturn this;\n\n\t}\n\n\tstatic fromJSON( data ) {\n\n\t\treturn new PlaneGeometry( data.width, data.height, data.widthSegments, data.heightSegments );\n\n\t}\n\n}\n\nexport { PlaneGeometry };\n","export const vertex = /* glsl */`\nvarying vec2 vUv;\nuniform mat3 uvTransform;\n\nvoid main() {\n\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n\n\tgl_Position = vec4( position.xy, 1.0, 1.0 );\n\n}\n`;\n\nexport const fragment = /* glsl */`\nuniform sampler2D t2D;\nuniform float backgroundIntensity;\n\nvarying vec2 vUv;\n\nvoid main() {\n\n\tvec4 texColor = texture2D( t2D, vUv );\n\n\t#ifdef DECODE_VIDEO_TEXTURE\n\n\t\t// use inline sRGB decode until browsers properly support SRGB8_APLHA8 with video textures\n\n\t\ttexColor = vec4( mix( pow( texColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), texColor.rgb * 0.0773993808, vec3( lessThanEqual( texColor.rgb, vec3( 0.04045 ) ) ) ), texColor.w );\n\n\t#endif\n\n\ttexColor.rgb *= backgroundIntensity;\n\n\tgl_FragColor = texColor;\n\n\t#include \n\t#include \n\n}\n`;\n","import alphahash_fragment from './ShaderChunk/alphahash_fragment.glsl.js';\nimport alphahash_pars_fragment from './ShaderChunk/alphahash_pars_fragment.glsl.js';\nimport alphamap_fragment from './ShaderChunk/alphamap_fragment.glsl.js';\nimport alphamap_pars_fragment from './ShaderChunk/alphamap_pars_fragment.glsl.js';\nimport alphatest_fragment from './ShaderChunk/alphatest_fragment.glsl.js';\nimport alphatest_pars_fragment from './ShaderChunk/alphatest_pars_fragment.glsl.js';\nimport aomap_fragment from './ShaderChunk/aomap_fragment.glsl.js';\nimport aomap_pars_fragment from './ShaderChunk/aomap_pars_fragment.glsl.js';\nimport begin_vertex from './ShaderChunk/begin_vertex.glsl.js';\nimport beginnormal_vertex from './ShaderChunk/beginnormal_vertex.glsl.js';\nimport bsdfs from './ShaderChunk/bsdfs.glsl.js';\nimport iridescence_fragment from './ShaderChunk/iridescence_fragment.glsl.js';\nimport bumpmap_pars_fragment from './ShaderChunk/bumpmap_pars_fragment.glsl.js';\nimport clipping_planes_fragment from './ShaderChunk/clipping_planes_fragment.glsl.js';\nimport clipping_planes_pars_fragment from './ShaderChunk/clipping_planes_pars_fragment.glsl.js';\nimport clipping_planes_pars_vertex from './ShaderChunk/clipping_planes_pars_vertex.glsl.js';\nimport clipping_planes_vertex from './ShaderChunk/clipping_planes_vertex.glsl.js';\nimport color_fragment from './ShaderChunk/color_fragment.glsl.js';\nimport color_pars_fragment from './ShaderChunk/color_pars_fragment.glsl.js';\nimport color_pars_vertex from './ShaderChunk/color_pars_vertex.glsl.js';\nimport color_vertex from './ShaderChunk/color_vertex.glsl.js';\nimport common from './ShaderChunk/common.glsl.js';\nimport cube_uv_reflection_fragment from './ShaderChunk/cube_uv_reflection_fragment.glsl.js';\nimport defaultnormal_vertex from './ShaderChunk/defaultnormal_vertex.glsl.js';\nimport displacementmap_pars_vertex from './ShaderChunk/displacementmap_pars_vertex.glsl.js';\nimport displacementmap_vertex from './ShaderChunk/displacementmap_vertex.glsl.js';\nimport emissivemap_fragment from './ShaderChunk/emissivemap_fragment.glsl.js';\nimport emissivemap_pars_fragment from './ShaderChunk/emissivemap_pars_fragment.glsl.js';\nimport colorspace_fragment from './ShaderChunk/colorspace_fragment.glsl.js';\nimport colorspace_pars_fragment from './ShaderChunk/colorspace_pars_fragment.glsl.js';\nimport envmap_fragment from './ShaderChunk/envmap_fragment.glsl.js';\nimport envmap_common_pars_fragment from './ShaderChunk/envmap_common_pars_fragment.glsl.js';\nimport envmap_pars_fragment from './ShaderChunk/envmap_pars_fragment.glsl.js';\nimport envmap_pars_vertex from './ShaderChunk/envmap_pars_vertex.glsl.js';\nimport envmap_vertex from './ShaderChunk/envmap_vertex.glsl.js';\nimport fog_vertex from './ShaderChunk/fog_vertex.glsl.js';\nimport fog_pars_vertex from './ShaderChunk/fog_pars_vertex.glsl.js';\nimport fog_fragment from './ShaderChunk/fog_fragment.glsl.js';\nimport fog_pars_fragment from './ShaderChunk/fog_pars_fragment.glsl.js';\nimport gradientmap_pars_fragment from './ShaderChunk/gradientmap_pars_fragment.glsl.js';\nimport lightmap_fragment from './ShaderChunk/lightmap_fragment.glsl.js';\nimport lightmap_pars_fragment from './ShaderChunk/lightmap_pars_fragment.glsl.js';\nimport lights_lambert_fragment from './ShaderChunk/lights_lambert_fragment.glsl.js';\nimport lights_lambert_pars_fragment from './ShaderChunk/lights_lambert_pars_fragment.glsl.js';\nimport lights_pars_begin from './ShaderChunk/lights_pars_begin.glsl.js';\nimport envmap_physical_pars_fragment from './ShaderChunk/envmap_physical_pars_fragment.glsl.js';\nimport lights_toon_fragment from './ShaderChunk/lights_toon_fragment.glsl.js';\nimport lights_toon_pars_fragment from './ShaderChunk/lights_toon_pars_fragment.glsl.js';\nimport lights_phong_fragment from './ShaderChunk/lights_phong_fragment.glsl.js';\nimport lights_phong_pars_fragment from './ShaderChunk/lights_phong_pars_fragment.glsl.js';\nimport lights_physical_fragment from './ShaderChunk/lights_physical_fragment.glsl.js';\nimport lights_physical_pars_fragment from './ShaderChunk/lights_physical_pars_fragment.glsl.js';\nimport lights_fragment_begin from './ShaderChunk/lights_fragment_begin.glsl.js';\nimport lights_fragment_maps from './ShaderChunk/lights_fragment_maps.glsl.js';\nimport lights_fragment_end from './ShaderChunk/lights_fragment_end.glsl.js';\nimport logdepthbuf_fragment from './ShaderChunk/logdepthbuf_fragment.glsl.js';\nimport logdepthbuf_pars_fragment from './ShaderChunk/logdepthbuf_pars_fragment.glsl.js';\nimport logdepthbuf_pars_vertex from './ShaderChunk/logdepthbuf_pars_vertex.glsl.js';\nimport logdepthbuf_vertex from './ShaderChunk/logdepthbuf_vertex.glsl.js';\nimport map_fragment from './ShaderChunk/map_fragment.glsl.js';\nimport map_pars_fragment from './ShaderChunk/map_pars_fragment.glsl.js';\nimport map_particle_fragment from './ShaderChunk/map_particle_fragment.glsl.js';\nimport map_particle_pars_fragment from './ShaderChunk/map_particle_pars_fragment.glsl.js';\nimport metalnessmap_fragment from './ShaderChunk/metalnessmap_fragment.glsl.js';\nimport metalnessmap_pars_fragment from './ShaderChunk/metalnessmap_pars_fragment.glsl.js';\nimport morphcolor_vertex from './ShaderChunk/morphcolor_vertex.glsl.js';\nimport morphnormal_vertex from './ShaderChunk/morphnormal_vertex.glsl.js';\nimport morphtarget_pars_vertex from './ShaderChunk/morphtarget_pars_vertex.glsl.js';\nimport morphtarget_vertex from './ShaderChunk/morphtarget_vertex.glsl.js';\nimport normal_fragment_begin from './ShaderChunk/normal_fragment_begin.glsl.js';\nimport normal_fragment_maps from './ShaderChunk/normal_fragment_maps.glsl.js';\nimport normal_pars_fragment from './ShaderChunk/normal_pars_fragment.glsl.js';\nimport normal_pars_vertex from './ShaderChunk/normal_pars_vertex.glsl.js';\nimport normal_vertex from './ShaderChunk/normal_vertex.glsl.js';\nimport normalmap_pars_fragment from './ShaderChunk/normalmap_pars_fragment.glsl.js';\nimport clearcoat_normal_fragment_begin from './ShaderChunk/clearcoat_normal_fragment_begin.glsl.js';\nimport clearcoat_normal_fragment_maps from './ShaderChunk/clearcoat_normal_fragment_maps.glsl.js';\nimport clearcoat_pars_fragment from './ShaderChunk/clearcoat_pars_fragment.glsl.js';\nimport iridescence_pars_fragment from './ShaderChunk/iridescence_pars_fragment.glsl.js';\nimport opaque_fragment from './ShaderChunk/opaque_fragment.glsl.js';\nimport packing from './ShaderChunk/packing.glsl.js';\nimport premultiplied_alpha_fragment from './ShaderChunk/premultiplied_alpha_fragment.glsl.js';\nimport project_vertex from './ShaderChunk/project_vertex.glsl.js';\nimport dithering_fragment from './ShaderChunk/dithering_fragment.glsl.js';\nimport dithering_pars_fragment from './ShaderChunk/dithering_pars_fragment.glsl.js';\nimport roughnessmap_fragment from './ShaderChunk/roughnessmap_fragment.glsl.js';\nimport roughnessmap_pars_fragment from './ShaderChunk/roughnessmap_pars_fragment.glsl.js';\nimport shadowmap_pars_fragment from './ShaderChunk/shadowmap_pars_fragment.glsl.js';\nimport shadowmap_pars_vertex from './ShaderChunk/shadowmap_pars_vertex.glsl.js';\nimport shadowmap_vertex from './ShaderChunk/shadowmap_vertex.glsl.js';\nimport shadowmask_pars_fragment from './ShaderChunk/shadowmask_pars_fragment.glsl.js';\nimport skinbase_vertex from './ShaderChunk/skinbase_vertex.glsl.js';\nimport skinning_pars_vertex from './ShaderChunk/skinning_pars_vertex.glsl.js';\nimport skinning_vertex from './ShaderChunk/skinning_vertex.glsl.js';\nimport skinnormal_vertex from './ShaderChunk/skinnormal_vertex.glsl.js';\nimport specularmap_fragment from './ShaderChunk/specularmap_fragment.glsl.js';\nimport specularmap_pars_fragment from './ShaderChunk/specularmap_pars_fragment.glsl.js';\nimport tonemapping_fragment from './ShaderChunk/tonemapping_fragment.glsl.js';\nimport tonemapping_pars_fragment from './ShaderChunk/tonemapping_pars_fragment.glsl.js';\nimport transmission_fragment from './ShaderChunk/transmission_fragment.glsl.js';\nimport transmission_pars_fragment from './ShaderChunk/transmission_pars_fragment.glsl.js';\nimport uv_pars_fragment from './ShaderChunk/uv_pars_fragment.glsl.js';\nimport uv_pars_vertex from './ShaderChunk/uv_pars_vertex.glsl.js';\nimport uv_vertex from './ShaderChunk/uv_vertex.glsl.js';\nimport worldpos_vertex from './ShaderChunk/worldpos_vertex.glsl.js';\n\nimport * as background from './ShaderLib/background.glsl.js';\nimport * as backgroundCube from './ShaderLib/backgroundCube.glsl.js';\nimport * as cube from './ShaderLib/cube.glsl.js';\nimport * as depth from './ShaderLib/depth.glsl.js';\nimport * as distanceRGBA from './ShaderLib/distanceRGBA.glsl.js';\nimport * as equirect from './ShaderLib/equirect.glsl.js';\nimport * as linedashed from './ShaderLib/linedashed.glsl.js';\nimport * as meshbasic from './ShaderLib/meshbasic.glsl.js';\nimport * as meshlambert from './ShaderLib/meshlambert.glsl.js';\nimport * as meshmatcap from './ShaderLib/meshmatcap.glsl.js';\nimport * as meshnormal from './ShaderLib/meshnormal.glsl.js';\nimport * as meshphong from './ShaderLib/meshphong.glsl.js';\nimport * as meshphysical from './ShaderLib/meshphysical.glsl.js';\nimport * as meshtoon from './ShaderLib/meshtoon.glsl.js';\nimport * as points from './ShaderLib/points.glsl.js';\nimport * as shadow from './ShaderLib/shadow.glsl.js';\nimport * as sprite from './ShaderLib/sprite.glsl.js';\n\nexport const ShaderChunk = {\n\talphahash_fragment: alphahash_fragment,\n\talphahash_pars_fragment: alphahash_pars_fragment,\n\talphamap_fragment: alphamap_fragment,\n\talphamap_pars_fragment: alphamap_pars_fragment,\n\talphatest_fragment: alphatest_fragment,\n\talphatest_pars_fragment: alphatest_pars_fragment,\n\taomap_fragment: aomap_fragment,\n\taomap_pars_fragment: aomap_pars_fragment,\n\tbegin_vertex: begin_vertex,\n\tbeginnormal_vertex: beginnormal_vertex,\n\tbsdfs: bsdfs,\n\tiridescence_fragment: iridescence_fragment,\n\tbumpmap_pars_fragment: bumpmap_pars_fragment,\n\tclipping_planes_fragment: clipping_planes_fragment,\n\tclipping_planes_pars_fragment: clipping_planes_pars_fragment,\n\tclipping_planes_pars_vertex: clipping_planes_pars_vertex,\n\tclipping_planes_vertex: clipping_planes_vertex,\n\tcolor_fragment: color_fragment,\n\tcolor_pars_fragment: color_pars_fragment,\n\tcolor_pars_vertex: color_pars_vertex,\n\tcolor_vertex: color_vertex,\n\tcommon: common,\n\tcube_uv_reflection_fragment: cube_uv_reflection_fragment,\n\tdefaultnormal_vertex: defaultnormal_vertex,\n\tdisplacementmap_pars_vertex: displacementmap_pars_vertex,\n\tdisplacementmap_vertex: displacementmap_vertex,\n\temissivemap_fragment: emissivemap_fragment,\n\temissivemap_pars_fragment: emissivemap_pars_fragment,\n\tcolorspace_fragment: colorspace_fragment,\n\tcolorspace_pars_fragment: colorspace_pars_fragment,\n\tenvmap_fragment: envmap_fragment,\n\tenvmap_common_pars_fragment: envmap_common_pars_fragment,\n\tenvmap_pars_fragment: envmap_pars_fragment,\n\tenvmap_pars_vertex: envmap_pars_vertex,\n\tenvmap_physical_pars_fragment: envmap_physical_pars_fragment,\n\tenvmap_vertex: envmap_vertex,\n\tfog_vertex: fog_vertex,\n\tfog_pars_vertex: fog_pars_vertex,\n\tfog_fragment: fog_fragment,\n\tfog_pars_fragment: fog_pars_fragment,\n\tgradientmap_pars_fragment: gradientmap_pars_fragment,\n\tlightmap_fragment: lightmap_fragment,\n\tlightmap_pars_fragment: lightmap_pars_fragment,\n\tlights_lambert_fragment: lights_lambert_fragment,\n\tlights_lambert_pars_fragment: lights_lambert_pars_fragment,\n\tlights_pars_begin: lights_pars_begin,\n\tlights_toon_fragment: lights_toon_fragment,\n\tlights_toon_pars_fragment: lights_toon_pars_fragment,\n\tlights_phong_fragment: lights_phong_fragment,\n\tlights_phong_pars_fragment: lights_phong_pars_fragment,\n\tlights_physical_fragment: lights_physical_fragment,\n\tlights_physical_pars_fragment: lights_physical_pars_fragment,\n\tlights_fragment_begin: lights_fragment_begin,\n\tlights_fragment_maps: lights_fragment_maps,\n\tlights_fragment_end: lights_fragment_end,\n\tlogdepthbuf_fragment: logdepthbuf_fragment,\n\tlogdepthbuf_pars_fragment: logdepthbuf_pars_fragment,\n\tlogdepthbuf_pars_vertex: logdepthbuf_pars_vertex,\n\tlogdepthbuf_vertex: logdepthbuf_vertex,\n\tmap_fragment: map_fragment,\n\tmap_pars_fragment: map_pars_fragment,\n\tmap_particle_fragment: map_particle_fragment,\n\tmap_particle_pars_fragment: map_particle_pars_fragment,\n\tmetalnessmap_fragment: metalnessmap_fragment,\n\tmetalnessmap_pars_fragment: metalnessmap_pars_fragment,\n\tmorphcolor_vertex: morphcolor_vertex,\n\tmorphnormal_vertex: morphnormal_vertex,\n\tmorphtarget_pars_vertex: morphtarget_pars_vertex,\n\tmorphtarget_vertex: morphtarget_vertex,\n\tnormal_fragment_begin: normal_fragment_begin,\n\tnormal_fragment_maps: normal_fragment_maps,\n\tnormal_pars_fragment: normal_pars_fragment,\n\tnormal_pars_vertex: normal_pars_vertex,\n\tnormal_vertex: normal_vertex,\n\tnormalmap_pars_fragment: normalmap_pars_fragment,\n\tclearcoat_normal_fragment_begin: clearcoat_normal_fragment_begin,\n\tclearcoat_normal_fragment_maps: clearcoat_normal_fragment_maps,\n\tclearcoat_pars_fragment: clearcoat_pars_fragment,\n\tiridescence_pars_fragment: iridescence_pars_fragment,\n\topaque_fragment: opaque_fragment,\n\tpacking: packing,\n\tpremultiplied_alpha_fragment: premultiplied_alpha_fragment,\n\tproject_vertex: project_vertex,\n\tdithering_fragment: dithering_fragment,\n\tdithering_pars_fragment: dithering_pars_fragment,\n\troughnessmap_fragment: roughnessmap_fragment,\n\troughnessmap_pars_fragment: roughnessmap_pars_fragment,\n\tshadowmap_pars_fragment: shadowmap_pars_fragment,\n\tshadowmap_pars_vertex: shadowmap_pars_vertex,\n\tshadowmap_vertex: shadowmap_vertex,\n\tshadowmask_pars_fragment: shadowmask_pars_fragment,\n\tskinbase_vertex: skinbase_vertex,\n\tskinning_pars_vertex: skinning_pars_vertex,\n\tskinning_vertex: skinning_vertex,\n\tskinnormal_vertex: skinnormal_vertex,\n\tspecularmap_fragment: specularmap_fragment,\n\tspecularmap_pars_fragment: specularmap_pars_fragment,\n\ttonemapping_fragment: tonemapping_fragment,\n\ttonemapping_pars_fragment: tonemapping_pars_fragment,\n\ttransmission_fragment: transmission_fragment,\n\ttransmission_pars_fragment: transmission_pars_fragment,\n\tuv_pars_fragment: uv_pars_fragment,\n\tuv_pars_vertex: uv_pars_vertex,\n\tuv_vertex: uv_vertex,\n\tworldpos_vertex: worldpos_vertex,\n\n\tbackground_vert: background.vertex,\n\tbackground_frag: background.fragment,\n\tbackgroundCube_vert: backgroundCube.vertex,\n\tbackgroundCube_frag: backgroundCube.fragment,\n\tcube_vert: cube.vertex,\n\tcube_frag: cube.fragment,\n\tdepth_vert: depth.vertex,\n\tdepth_frag: depth.fragment,\n\tdistanceRGBA_vert: distanceRGBA.vertex,\n\tdistanceRGBA_frag: distanceRGBA.fragment,\n\tequirect_vert: equirect.vertex,\n\tequirect_frag: equirect.fragment,\n\tlinedashed_vert: linedashed.vertex,\n\tlinedashed_frag: linedashed.fragment,\n\tmeshbasic_vert: meshbasic.vertex,\n\tmeshbasic_frag: meshbasic.fragment,\n\tmeshlambert_vert: meshlambert.vertex,\n\tmeshlambert_frag: meshlambert.fragment,\n\tmeshmatcap_vert: meshmatcap.vertex,\n\tmeshmatcap_frag: meshmatcap.fragment,\n\tmeshnormal_vert: meshnormal.vertex,\n\tmeshnormal_frag: meshnormal.fragment,\n\tmeshphong_vert: meshphong.vertex,\n\tmeshphong_frag: meshphong.fragment,\n\tmeshphysical_vert: meshphysical.vertex,\n\tmeshphysical_frag: meshphysical.fragment,\n\tmeshtoon_vert: meshtoon.vertex,\n\tmeshtoon_frag: meshtoon.fragment,\n\tpoints_vert: points.vertex,\n\tpoints_frag: points.fragment,\n\tshadow_vert: shadow.vertex,\n\tshadow_frag: shadow.fragment,\n\tsprite_vert: sprite.vertex,\n\tsprite_frag: sprite.fragment\n};\n","export default /* glsl */`\n#ifdef USE_ALPHAHASH\n\n\tif ( diffuseColor.a < getAlphaHashThreshold( vPosition ) ) discard;\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_ALPHAHASH\n\n\t/**\n\t * See: https://casual-effects.com/research/Wyman2017Hashed/index.html\n\t */\n\n\tconst float ALPHA_HASH_SCALE = 0.05; // Derived from trials only, and may be changed.\n\n\tfloat hash2D( vec2 value ) {\n\n\t\treturn fract( 1.0e4 * sin( 17.0 * value.x + 0.1 * value.y ) * ( 0.1 + abs( sin( 13.0 * value.y + value.x ) ) ) );\n\n\t}\n\n\tfloat hash3D( vec3 value ) {\n\n\t\treturn hash2D( vec2( hash2D( value.xy ), value.z ) );\n\n\t}\n\n\tfloat getAlphaHashThreshold( vec3 position ) {\n\n\t\t// Find the discretized derivatives of our coordinates\n\t\tfloat maxDeriv = max(\n\t\t\tlength( dFdx( position.xyz ) ),\n\t\t\tlength( dFdy( position.xyz ) )\n\t\t);\n\t\tfloat pixScale = 1.0 / ( ALPHA_HASH_SCALE * maxDeriv );\n\n\t\t// Find two nearest log-discretized noise scales\n\t\tvec2 pixScales = vec2(\n\t\t\texp2( floor( log2( pixScale ) ) ),\n\t\t\texp2( ceil( log2( pixScale ) ) )\n\t\t);\n\n\t\t// Compute alpha thresholds at our two noise scales\n\t\tvec2 alpha = vec2(\n\t\t\thash3D( floor( pixScales.x * position.xyz ) ),\n\t\t\thash3D( floor( pixScales.y * position.xyz ) )\n\t\t);\n\n\t\t// Factor to interpolate lerp with\n\t\tfloat lerpFactor = fract( log2( pixScale ) );\n\n\t\t// Interpolate alpha threshold from noise at two scales\n\t\tfloat x = ( 1.0 - lerpFactor ) * alpha.x + lerpFactor * alpha.y;\n\n\t\t// Pass into CDF to compute uniformly distrib threshold\n\t\tfloat a = min( lerpFactor, 1.0 - lerpFactor );\n\t\tvec3 cases = vec3(\n\t\t\tx * x / ( 2.0 * a * ( 1.0 - a ) ),\n\t\t\t( x - 0.5 * a ) / ( 1.0 - a ),\n\t\t\t1.0 - ( ( 1.0 - x ) * ( 1.0 - x ) / ( 2.0 * a * ( 1.0 - a ) ) )\n\t\t);\n\n\t\t// Find our final, uniformly distributed alpha threshold (ατ)\n\t\tfloat threshold = ( x < ( 1.0 - a ) )\n\t\t\t? ( ( x < a ) ? cases.x : cases.y )\n\t\t\t: cases.z;\n\n\t\t// Avoids ατ == 0. Could also do ατ =1-ατ\n\t\treturn clamp( threshold , 1.0e-6, 1.0 );\n\n\t}\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_ALPHAMAP\n\n\tdiffuseColor.a *= texture2D( alphaMap, vAlphaMapUv ).g;\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_ALPHAMAP\n\n\tuniform sampler2D alphaMap;\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_ALPHATEST\n\n\tif ( diffuseColor.a < alphaTest ) discard;\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_ALPHATEST\n\tuniform float alphaTest;\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_AOMAP\n\n\t// reads channel R, compatible with a combined OcclusionRoughnessMetallic (RGB) texture\n\tfloat ambientOcclusion = ( texture2D( aoMap, vAoMapUv ).r - 1.0 ) * aoMapIntensity + 1.0;\n\n\treflectedLight.indirectDiffuse *= ambientOcclusion;\n\n\t#if defined( USE_CLEARCOAT ) \n\t\tclearcoatSpecularIndirect *= ambientOcclusion;\n\t#endif\n\n\t#if defined( USE_SHEEN ) \n\t\tsheenSpecularIndirect *= ambientOcclusion;\n\t#endif\n\n\t#if defined( USE_ENVMAP ) && defined( STANDARD )\n\n\t\tfloat dotNV = saturate( dot( geometryNormal, geometryViewDir ) );\n\n\t\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness );\n\n\t#endif\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_AOMAP\n\n\tuniform sampler2D aoMap;\n\tuniform float aoMapIntensity;\n\n#endif\n`;\n","export default /* glsl */`\nvec3 transformed = vec3( position );\n\n#ifdef USE_ALPHAHASH\n\n\tvPosition = vec3( position );\n\n#endif\n`;\n","export default /* glsl */`\nvec3 objectNormal = vec3( normal );\n\n#ifdef USE_TANGENT\n\n\tvec3 objectTangent = vec3( tangent.xyz );\n\n#endif\n`;\n","export default /* glsl */`\n\nfloat G_BlinnPhong_Implicit( /* const in float dotNL, const in float dotNV */ ) {\n\n\t// geometry term is (n dot l)(n dot v) / 4(n dot l)(n dot v)\n\treturn 0.25;\n\n}\n\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\n\n\treturn RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n\n}\n\nvec3 BRDF_BlinnPhong( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float shininess ) {\n\n\tvec3 halfDir = normalize( lightDir + viewDir );\n\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat dotVH = saturate( dot( viewDir, halfDir ) );\n\n\tvec3 F = F_Schlick( specularColor, 1.0, dotVH );\n\n\tfloat G = G_BlinnPhong_Implicit( /* dotNL, dotNV */ );\n\n\tfloat D = D_BlinnPhong( shininess, dotNH );\n\n\treturn F * ( G * D );\n\n} // validated\n\n`;\n","export default /* glsl */`\n\n#ifdef USE_IRIDESCENCE\n\n\t// XYZ to linear-sRGB color space\n\tconst mat3 XYZ_TO_REC709 = mat3(\n\t\t 3.2404542, -0.9692660, 0.0556434,\n\t\t-1.5371385, 1.8760108, -0.2040259,\n\t\t-0.4985314, 0.0415560, 1.0572252\n\t);\n\n\t// Assume air interface for top\n\t// Note: We don't handle the case fresnel0 == 1\n\tvec3 Fresnel0ToIor( vec3 fresnel0 ) {\n\n\t\tvec3 sqrtF0 = sqrt( fresnel0 );\n\t\treturn ( vec3( 1.0 ) + sqrtF0 ) / ( vec3( 1.0 ) - sqrtF0 );\n\n\t}\n\n\t// Conversion FO/IOR\n\tvec3 IorToFresnel0( vec3 transmittedIor, float incidentIor ) {\n\n\t\treturn pow2( ( transmittedIor - vec3( incidentIor ) ) / ( transmittedIor + vec3( incidentIor ) ) );\n\n\t}\n\n\t// ior is a value between 1.0 and 3.0. 1.0 is air interface\n\tfloat IorToFresnel0( float transmittedIor, float incidentIor ) {\n\n\t\treturn pow2( ( transmittedIor - incidentIor ) / ( transmittedIor + incidentIor ));\n\n\t}\n\n\t// Fresnel equations for dielectric/dielectric interfaces.\n\t// Ref: https://belcour.github.io/blog/research/2017/05/01/brdf-thin-film.html\n\t// Evaluation XYZ sensitivity curves in Fourier space\n\tvec3 evalSensitivity( float OPD, vec3 shift ) {\n\n\t\tfloat phase = 2.0 * PI * OPD * 1.0e-9;\n\t\tvec3 val = vec3( 5.4856e-13, 4.4201e-13, 5.2481e-13 );\n\t\tvec3 pos = vec3( 1.6810e+06, 1.7953e+06, 2.2084e+06 );\n\t\tvec3 var = vec3( 4.3278e+09, 9.3046e+09, 6.6121e+09 );\n\n\t\tvec3 xyz = val * sqrt( 2.0 * PI * var ) * cos( pos * phase + shift ) * exp( - pow2( phase ) * var );\n\t\txyz.x += 9.7470e-14 * sqrt( 2.0 * PI * 4.5282e+09 ) * cos( 2.2399e+06 * phase + shift[ 0 ] ) * exp( - 4.5282e+09 * pow2( phase ) );\n\t\txyz /= 1.0685e-7;\n\n\t\tvec3 rgb = XYZ_TO_REC709 * xyz;\n\t\treturn rgb;\n\n\t}\n\n\tvec3 evalIridescence( float outsideIOR, float eta2, float cosTheta1, float thinFilmThickness, vec3 baseF0 ) {\n\n\t\tvec3 I;\n\n\t\t// Force iridescenceIOR -> outsideIOR when thinFilmThickness -> 0.0\n\t\tfloat iridescenceIOR = mix( outsideIOR, eta2, smoothstep( 0.0, 0.03, thinFilmThickness ) );\n\t\t// Evaluate the cosTheta on the base layer (Snell law)\n\t\tfloat sinTheta2Sq = pow2( outsideIOR / iridescenceIOR ) * ( 1.0 - pow2( cosTheta1 ) );\n\n\t\t// Handle TIR:\n\t\tfloat cosTheta2Sq = 1.0 - sinTheta2Sq;\n\t\tif ( cosTheta2Sq < 0.0 ) {\n\n\t\t\treturn vec3( 1.0 );\n\n\t\t}\n\n\t\tfloat cosTheta2 = sqrt( cosTheta2Sq );\n\n\t\t// First interface\n\t\tfloat R0 = IorToFresnel0( iridescenceIOR, outsideIOR );\n\t\tfloat R12 = F_Schlick( R0, 1.0, cosTheta1 );\n\t\tfloat T121 = 1.0 - R12;\n\t\tfloat phi12 = 0.0;\n\t\tif ( iridescenceIOR < outsideIOR ) phi12 = PI;\n\t\tfloat phi21 = PI - phi12;\n\n\t\t// Second interface\n\t\tvec3 baseIOR = Fresnel0ToIor( clamp( baseF0, 0.0, 0.9999 ) ); // guard against 1.0\n\t\tvec3 R1 = IorToFresnel0( baseIOR, iridescenceIOR );\n\t\tvec3 R23 = F_Schlick( R1, 1.0, cosTheta2 );\n\t\tvec3 phi23 = vec3( 0.0 );\n\t\tif ( baseIOR[ 0 ] < iridescenceIOR ) phi23[ 0 ] = PI;\n\t\tif ( baseIOR[ 1 ] < iridescenceIOR ) phi23[ 1 ] = PI;\n\t\tif ( baseIOR[ 2 ] < iridescenceIOR ) phi23[ 2 ] = PI;\n\n\t\t// Phase shift\n\t\tfloat OPD = 2.0 * iridescenceIOR * thinFilmThickness * cosTheta2;\n\t\tvec3 phi = vec3( phi21 ) + phi23;\n\n\t\t// Compound terms\n\t\tvec3 R123 = clamp( R12 * R23, 1e-5, 0.9999 );\n\t\tvec3 r123 = sqrt( R123 );\n\t\tvec3 Rs = pow2( T121 ) * R23 / ( vec3( 1.0 ) - R123 );\n\n\t\t// Reflectance term for m = 0 (DC term amplitude)\n\t\tvec3 C0 = R12 + Rs;\n\t\tI = C0;\n\n\t\t// Reflectance term for m > 0 (pairs of diracs)\n\t\tvec3 Cm = Rs - T121;\n\t\tfor ( int m = 1; m <= 2; ++ m ) {\n\n\t\t\tCm *= r123;\n\t\t\tvec3 Sm = 2.0 * evalSensitivity( float( m ) * OPD, float( m ) * phi );\n\t\t\tI += Cm * Sm;\n\n\t\t}\n\n\t\t// Since out of gamut colors might be produced, negative color values are clamped to 0.\n\t\treturn max( I, vec3( 0.0 ) );\n\n\t}\n\n#endif\n\n`;\n","export default /* glsl */`\n#ifdef USE_BUMPMAP\n\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\n\t// Bump Mapping Unparametrized Surfaces on the GPU by Morten S. Mikkelsen\n\t// https://mmikk.github.io/papers3d/mm_sfgrad_bump.pdf\n\n\t// Evaluate the derivative of the height w.r.t. screen-space using forward differencing (listing 2)\n\n\tvec2 dHdxy_fwd() {\n\n\t\tvec2 dSTdx = dFdx( vBumpMapUv );\n\t\tvec2 dSTdy = dFdy( vBumpMapUv );\n\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vBumpMapUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vBumpMapUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vBumpMapUv + dSTdy ).x - Hll;\n\n\t\treturn vec2( dBx, dBy );\n\n\t}\n\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection ) {\n\n\t\t// normalize is done to ensure that the bump map looks the same regardless of the texture's scale\n\t\tvec3 vSigmaX = normalize( dFdx( surf_pos.xyz ) );\n\t\tvec3 vSigmaY = normalize( dFdy( surf_pos.xyz ) );\n\t\tvec3 vN = surf_norm; // normalized\n\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\n\t\tfloat fDet = dot( vSigmaX, R1 ) * faceDirection;\n\n\t\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n\t\treturn normalize( abs( fDet ) * surf_norm - vGrad );\n\n\t}\n\n#endif\n`;\n","export default /* glsl */`\n#if NUM_CLIPPING_PLANES > 0\n\n\tvec4 plane;\n\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {\n\n\t\tplane = clippingPlanes[ i ];\n\t\tif ( dot( vClipPosition, plane.xyz ) > plane.w ) discard;\n\n\t}\n\t#pragma unroll_loop_end\n\n\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\n\n\t\tbool clipped = true;\n\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {\n\n\t\t\tplane = clippingPlanes[ i ];\n\t\t\tclipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped;\n\n\t\t}\n\t\t#pragma unroll_loop_end\n\n\t\tif ( clipped ) discard;\n\n\t#endif\n\n#endif\n`;\n","export default /* glsl */`\n#if NUM_CLIPPING_PLANES > 0\n\n\tvarying vec3 vClipPosition;\n\n\tuniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\n\n#endif\n`;\n","export default /* glsl */`\n#if NUM_CLIPPING_PLANES > 0\n\n\tvarying vec3 vClipPosition;\n\n#endif\n`;\n","export default /* glsl */`\n#if NUM_CLIPPING_PLANES > 0\n\n\tvClipPosition = - mvPosition.xyz;\n\n#endif\n`;\n","export default /* glsl */`\n#if defined( USE_COLOR_ALPHA )\n\n\tdiffuseColor *= vColor;\n\n#elif defined( USE_COLOR )\n\n\tdiffuseColor.rgb *= vColor;\n\n#endif\n`;\n","export default /* glsl */`\n#if defined( USE_COLOR_ALPHA )\n\n\tvarying vec4 vColor;\n\n#elif defined( USE_COLOR )\n\n\tvarying vec3 vColor;\n\n#endif\n`;\n","export default /* glsl */`\n#if defined( USE_COLOR_ALPHA )\n\n\tvarying vec4 vColor;\n\n#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\n\n\tvarying vec3 vColor;\n\n#endif\n`;\n","export default /* glsl */`\n#if defined( USE_COLOR_ALPHA )\n\n\tvColor = vec4( 1.0 );\n\n#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\n\n\tvColor = vec3( 1.0 );\n\n#endif\n\n#ifdef USE_COLOR\n\n\tvColor *= color;\n\n#endif\n\n#ifdef USE_INSTANCING_COLOR\n\n\tvColor.xyz *= instanceColor.xyz;\n\n#endif\n`;\n","export default /* glsl */`\n#define PI 3.141592653589793\n#define PI2 6.283185307179586\n#define PI_HALF 1.5707963267948966\n#define RECIPROCAL_PI 0.3183098861837907\n#define RECIPROCAL_PI2 0.15915494309189535\n#define EPSILON 1e-6\n\n#ifndef saturate\n// may have defined saturate() already\n#define saturate( a ) clamp( a, 0.0, 1.0 )\n#endif\n#define whiteComplement( a ) ( 1.0 - saturate( a ) )\n\nfloat pow2( const in float x ) { return x*x; }\nvec3 pow2( const in vec3 x ) { return x*x; }\nfloat pow3( const in float x ) { return x*x*x; }\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\nfloat max3( const in vec3 v ) { return max( max( v.x, v.y ), v.z ); }\nfloat average( const in vec3 v ) { return dot( v, vec3( 0.3333333 ) ); }\n\n// expects values in the range of [0,1]x[0,1], returns values in the [0,1] range.\n// do not collapse into a single function per: http://byteblacksmith.com/improvements-to-the-canonical-one-liner-glsl-rand-for-opengl-es-2-0/\nhighp float rand( const in vec2 uv ) {\n\n\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\n\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\n\n\treturn fract( sin( sn ) * c );\n\n}\n\n#ifdef HIGH_PRECISION\n\tfloat precisionSafeLength( vec3 v ) { return length( v ); }\n#else\n\tfloat precisionSafeLength( vec3 v ) {\n\t\tfloat maxComponent = max3( abs( v ) );\n\t\treturn length( v / maxComponent ) * maxComponent;\n\t}\n#endif\n\nstruct IncidentLight {\n\tvec3 color;\n\tvec3 direction;\n\tbool visible;\n};\n\nstruct ReflectedLight {\n\tvec3 directDiffuse;\n\tvec3 directSpecular;\n\tvec3 indirectDiffuse;\n\tvec3 indirectSpecular;\n};\n\n#ifdef USE_ALPHAHASH\n\n\tvarying vec3 vPosition;\n\n#endif\n\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\n\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n\n}\n\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n\n\t// dir can be either a direction vector or a normal vector\n\t// upper-left 3x3 of matrix is assumed to be orthogonal\n\n\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n\n}\n\nmat3 transposeMat3( const in mat3 m ) {\n\n\tmat3 tmp;\n\n\ttmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x );\n\ttmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y );\n\ttmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z );\n\n\treturn tmp;\n\n}\n\nfloat luminance( const in vec3 rgb ) {\n\n\t// assumes rgb is in linear color space with sRGB primaries and D65 white point\n\n\tconst vec3 weights = vec3( 0.2126729, 0.7151522, 0.0721750 );\n\n\treturn dot( weights, rgb );\n\n}\n\nbool isPerspectiveMatrix( mat4 m ) {\n\n\treturn m[ 2 ][ 3 ] == - 1.0;\n\n}\n\nvec2 equirectUv( in vec3 dir ) {\n\n\t// dir is assumed to be unit length\n\n\tfloat u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5;\n\n\tfloat v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\n\treturn vec2( u, v );\n\n}\n\nvec3 BRDF_Lambert( const in vec3 diffuseColor ) {\n\n\treturn RECIPROCAL_PI * diffuseColor;\n\n} // validated\n\nvec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) {\n\n\t// Original approximation by Christophe Schlick '94\n\t// float fresnel = pow( 1.0 - dotVH, 5.0 );\n\n\t// Optimized variant (presented by Epic at SIGGRAPH '13)\n\t// https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf\n\tfloat fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH );\n\n\treturn f0 * ( 1.0 - fresnel ) + ( f90 * fresnel );\n\n} // validated\n\nfloat F_Schlick( const in float f0, const in float f90, const in float dotVH ) {\n\n\t// Original approximation by Christophe Schlick '94\n\t// float fresnel = pow( 1.0 - dotVH, 5.0 );\n\n\t// Optimized variant (presented by Epic at SIGGRAPH '13)\n\t// https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf\n\tfloat fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH );\n\n\treturn f0 * ( 1.0 - fresnel ) + ( f90 * fresnel );\n\n} // validated\n`;\n","export default /* glsl */`\n#ifdef ENVMAP_TYPE_CUBE_UV\n\n\t#define cubeUV_minMipLevel 4.0\n\t#define cubeUV_minTileSize 16.0\n\n\t// These shader functions convert between the UV coordinates of a single face of\n\t// a cubemap, the 0-5 integer index of a cube face, and the direction vector for\n\t// sampling a textureCube (not generally normalized ).\n\n\tfloat getFace( vec3 direction ) {\n\n\t\tvec3 absDirection = abs( direction );\n\n\t\tfloat face = - 1.0;\n\n\t\tif ( absDirection.x > absDirection.z ) {\n\n\t\t\tif ( absDirection.x > absDirection.y )\n\n\t\t\t\tface = direction.x > 0.0 ? 0.0 : 3.0;\n\n\t\t\telse\n\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\n\t\t} else {\n\n\t\t\tif ( absDirection.z > absDirection.y )\n\n\t\t\t\tface = direction.z > 0.0 ? 2.0 : 5.0;\n\n\t\t\telse\n\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\n\t\t}\n\n\t\treturn face;\n\n\t}\n\n\t// RH coordinate system; PMREM face-indexing convention\n\tvec2 getUV( vec3 direction, float face ) {\n\n\t\tvec2 uv;\n\n\t\tif ( face == 0.0 ) {\n\n\t\t\tuv = vec2( direction.z, direction.y ) / abs( direction.x ); // pos x\n\n\t\t} else if ( face == 1.0 ) {\n\n\t\t\tuv = vec2( - direction.x, - direction.z ) / abs( direction.y ); // pos y\n\n\t\t} else if ( face == 2.0 ) {\n\n\t\t\tuv = vec2( - direction.x, direction.y ) / abs( direction.z ); // pos z\n\n\t\t} else if ( face == 3.0 ) {\n\n\t\t\tuv = vec2( - direction.z, direction.y ) / abs( direction.x ); // neg x\n\n\t\t} else if ( face == 4.0 ) {\n\n\t\t\tuv = vec2( - direction.x, direction.z ) / abs( direction.y ); // neg y\n\n\t\t} else {\n\n\t\t\tuv = vec2( direction.x, direction.y ) / abs( direction.z ); // neg z\n\n\t\t}\n\n\t\treturn 0.5 * ( uv + 1.0 );\n\n\t}\n\n\tvec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) {\n\n\t\tfloat face = getFace( direction );\n\n\t\tfloat filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 );\n\n\t\tmipInt = max( mipInt, cubeUV_minMipLevel );\n\n\t\tfloat faceSize = exp2( mipInt );\n\n\t\thighp vec2 uv = getUV( direction, face ) * ( faceSize - 2.0 ) + 1.0; // #25071\n\n\t\tif ( face > 2.0 ) {\n\n\t\t\tuv.y += faceSize;\n\n\t\t\tface -= 3.0;\n\n\t\t}\n\n\t\tuv.x += face * faceSize;\n\n\t\tuv.x += filterInt * 3.0 * cubeUV_minTileSize;\n\n\t\tuv.y += 4.0 * ( exp2( CUBEUV_MAX_MIP ) - faceSize );\n\n\t\tuv.x *= CUBEUV_TEXEL_WIDTH;\n\t\tuv.y *= CUBEUV_TEXEL_HEIGHT;\n\n\t\t#ifdef texture2DGradEXT\n\n\t\t\treturn texture2DGradEXT( envMap, uv, vec2( 0.0 ), vec2( 0.0 ) ).rgb; // disable anisotropic filtering\n\n\t\t#else\n\n\t\t\treturn texture2D( envMap, uv ).rgb;\n\n\t\t#endif\n\n\t}\n\n\t// These defines must match with PMREMGenerator\n\n\t#define cubeUV_r0 1.0\n\t#define cubeUV_v0 0.339\n\t#define cubeUV_m0 - 2.0\n\t#define cubeUV_r1 0.8\n\t#define cubeUV_v1 0.276\n\t#define cubeUV_m1 - 1.0\n\t#define cubeUV_r4 0.4\n\t#define cubeUV_v4 0.046\n\t#define cubeUV_m4 2.0\n\t#define cubeUV_r5 0.305\n\t#define cubeUV_v5 0.016\n\t#define cubeUV_m5 3.0\n\t#define cubeUV_r6 0.21\n\t#define cubeUV_v6 0.0038\n\t#define cubeUV_m6 4.0\n\n\tfloat roughnessToMip( float roughness ) {\n\n\t\tfloat mip = 0.0;\n\n\t\tif ( roughness >= cubeUV_r1 ) {\n\n\t\t\tmip = ( cubeUV_r0 - roughness ) * ( cubeUV_m1 - cubeUV_m0 ) / ( cubeUV_r0 - cubeUV_r1 ) + cubeUV_m0;\n\n\t\t} else if ( roughness >= cubeUV_r4 ) {\n\n\t\t\tmip = ( cubeUV_r1 - roughness ) * ( cubeUV_m4 - cubeUV_m1 ) / ( cubeUV_r1 - cubeUV_r4 ) + cubeUV_m1;\n\n\t\t} else if ( roughness >= cubeUV_r5 ) {\n\n\t\t\tmip = ( cubeUV_r4 - roughness ) * ( cubeUV_m5 - cubeUV_m4 ) / ( cubeUV_r4 - cubeUV_r5 ) + cubeUV_m4;\n\n\t\t} else if ( roughness >= cubeUV_r6 ) {\n\n\t\t\tmip = ( cubeUV_r5 - roughness ) * ( cubeUV_m6 - cubeUV_m5 ) / ( cubeUV_r5 - cubeUV_r6 ) + cubeUV_m5;\n\n\t\t} else {\n\n\t\t\tmip = - 2.0 * log2( 1.16 * roughness ); // 1.16 = 1.79^0.25\n\t\t}\n\n\t\treturn mip;\n\n\t}\n\n\tvec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) {\n\n\t\tfloat mip = clamp( roughnessToMip( roughness ), cubeUV_m0, CUBEUV_MAX_MIP );\n\n\t\tfloat mipF = fract( mip );\n\n\t\tfloat mipInt = floor( mip );\n\n\t\tvec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt );\n\n\t\tif ( mipF == 0.0 ) {\n\n\t\t\treturn vec4( color0, 1.0 );\n\n\t\t} else {\n\n\t\t\tvec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 );\n\n\t\t\treturn vec4( mix( color0, color1, mipF ), 1.0 );\n\n\t\t}\n\n\t}\n\n#endif\n`;\n","export default /* glsl */`\nvec3 transformedNormal = objectNormal;\n\n#ifdef USE_INSTANCING\n\n\t// this is in lieu of a per-instance normal-matrix\n\t// shear transforms in the instance matrix are not supported\n\n\tmat3 m = mat3( instanceMatrix );\n\n\ttransformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) );\n\n\ttransformedNormal = m * transformedNormal;\n\n#endif\n\ntransformedNormal = normalMatrix * transformedNormal;\n\n#ifdef FLIP_SIDED\n\n\ttransformedNormal = - transformedNormal;\n\n#endif\n\n#ifdef USE_TANGENT\n\n\tvec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\n\t#ifdef FLIP_SIDED\n\n\t\ttransformedTangent = - transformedTangent;\n\n\t#endif\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_DISPLACEMENTMAP\n\n\tuniform sampler2D displacementMap;\n\tuniform float displacementScale;\n\tuniform float displacementBias;\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_DISPLACEMENTMAP\n\n\ttransformed += normalize( objectNormal ) * ( texture2D( displacementMap, vDisplacementMapUv ).x * displacementScale + displacementBias );\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_EMISSIVEMAP\n\n\tvec4 emissiveColor = texture2D( emissiveMap, vEmissiveMapUv );\n\n\ttotalEmissiveRadiance *= emissiveColor.rgb;\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_EMISSIVEMAP\n\n\tuniform sampler2D emissiveMap;\n\n#endif\n`;\n","export default /* glsl */`\ngl_FragColor = linearToOutputTexel( gl_FragColor );\n`;\n","export default /* glsl */`\n\n// http://www.russellcottrell.com/photo/matrixCalculator.htm\n\n// Linear sRGB => XYZ => Linear Display P3\nconst mat3 LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 = mat3(\n\tvec3( 0.8224621, 0.177538, 0.0 ),\n\tvec3( 0.0331941, 0.9668058, 0.0 ),\n\tvec3( 0.0170827, 0.0723974, 0.9105199 )\n);\n\n// Linear Display P3 => XYZ => Linear sRGB\nconst mat3 LINEAR_DISPLAY_P3_TO_LINEAR_SRGB = mat3(\n\tvec3( 1.2249401, - 0.2249404, 0.0 ),\n\tvec3( - 0.0420569, 1.0420571, 0.0 ),\n\tvec3( - 0.0196376, - 0.0786361, 1.0982735 )\n);\n\nvec4 LinearSRGBToLinearDisplayP3( in vec4 value ) {\n\treturn vec4( value.rgb * LINEAR_SRGB_TO_LINEAR_DISPLAY_P3, value.a );\n}\n\nvec4 LinearDisplayP3ToLinearSRGB( in vec4 value ) {\n\treturn vec4( value.rgb * LINEAR_DISPLAY_P3_TO_LINEAR_SRGB, value.a );\n}\n\nvec4 LinearTransferOETF( in vec4 value ) {\n\treturn value;\n}\n\nvec4 sRGBTransferOETF( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a );\n}\n\n// @deprecated, r156\nvec4 LinearToLinear( in vec4 value ) {\n\treturn value;\n}\n\n// @deprecated, r156\nvec4 LinearTosRGB( in vec4 value ) {\n\treturn sRGBTransferOETF( value );\n}\n`;\n","export default /* glsl */`\n#ifdef USE_ENVMAP\n\n\t#ifdef ENV_WORLDPOS\n\n\t\tvec3 cameraToFrag;\n\n\t\tif ( isOrthographic ) {\n\n\t\t\tcameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\n\t\t} else {\n\n\t\t\tcameraToFrag = normalize( vWorldPosition - cameraPosition );\n\n\t\t}\n\n\t\t// Transforming Normal Vectors with the Inverse Transformation\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\n\t\t\tvec3 reflectVec = reflect( cameraToFrag, worldNormal );\n\n\t\t#else\n\n\t\t\tvec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio );\n\n\t\t#endif\n\n\t#else\n\n\t\tvec3 reflectVec = vReflect;\n\n\t#endif\n\n\t#ifdef ENVMAP_TYPE_CUBE\n\n\t\tvec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\n\t#else\n\n\t\tvec4 envColor = vec4( 0.0 );\n\n\t#endif\n\n\t#ifdef ENVMAP_BLENDING_MULTIPLY\n\n\t\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\n\t#elif defined( ENVMAP_BLENDING_MIX )\n\n\t\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\n\t#elif defined( ENVMAP_BLENDING_ADD )\n\n\t\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\n\n\t#endif\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_ENVMAP\n\n\tuniform float envMapIntensity;\n\tuniform float flipEnvMap;\n\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\t\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_ENVMAP\n\n\tuniform float reflectivity;\n\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT )\n\n\t\t#define ENV_WORLDPOS\n\n\t#endif\n\n\t#ifdef ENV_WORLDPOS\n\n\t\tvarying vec3 vWorldPosition;\n\t\tuniform float refractionRatio;\n\t#else\n\t\tvarying vec3 vReflect;\n\t#endif\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_ENVMAP\n\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT )\n\n\t\t#define ENV_WORLDPOS\n\n\t#endif\n\n\t#ifdef ENV_WORLDPOS\n\t\t\n\t\tvarying vec3 vWorldPosition;\n\n\t#else\n\n\t\tvarying vec3 vReflect;\n\t\tuniform float refractionRatio;\n\n\t#endif\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_ENVMAP\n\n\tvec3 getIBLIrradiance( const in vec3 normal ) {\n\n\t\t#ifdef ENVMAP_TYPE_CUBE_UV\n\n\t\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 );\n\n\t\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\n\t\t#else\n\n\t\t\treturn vec3( 0.0 );\n\n\t\t#endif\n\n\t}\n\n\tvec3 getIBLRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness ) {\n\n\t\t#ifdef ENVMAP_TYPE_CUBE_UV\n\n\t\t\tvec3 reflectVec = reflect( - viewDir, normal );\n\n\t\t\t// Mixing the reflection with the normal is more accurate and keeps rough objects from gathering light from behind their tangent plane.\n\t\t\treflectVec = normalize( mix( reflectVec, normal, roughness * roughness) );\n\n\t\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness );\n\n\t\t\treturn envMapColor.rgb * envMapIntensity;\n\n\t\t#else\n\n\t\t\treturn vec3( 0.0 );\n\n\t\t#endif\n\n\t}\n\n\t#ifdef USE_ANISOTROPY\n\n\t\tvec3 getIBLAnisotropyRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness, const in vec3 bitangent, const in float anisotropy ) {\n\n\t\t\t#ifdef ENVMAP_TYPE_CUBE_UV\n\n\t\t\t // https://google.github.io/filament/Filament.md.html#lighting/imagebasedlights/anisotropy\n\t\t\t\tvec3 bentNormal = cross( bitangent, viewDir );\n\t\t\t\tbentNormal = normalize( cross( bentNormal, bitangent ) );\n\t\t\t\tbentNormal = normalize( mix( bentNormal, normal, pow2( pow2( 1.0 - anisotropy * ( 1.0 - roughness ) ) ) ) );\n\n\t\t\t\treturn getIBLRadiance( viewDir, bentNormal, roughness );\n\n\t\t\t#else\n\n\t\t\t\treturn vec3( 0.0 );\n\n\t\t\t#endif\n\n\t\t}\n\n\t#endif\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_ENVMAP\n\n\t#ifdef ENV_WORLDPOS\n\n\t\tvWorldPosition = worldPosition.xyz;\n\n\t#else\n\n\t\tvec3 cameraToVertex;\n\n\t\tif ( isOrthographic ) {\n\n\t\t\tcameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\n\t\t} else {\n\n\t\t\tcameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\n\t\t}\n\n\t\tvec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\n\t\t\tvReflect = reflect( cameraToVertex, worldNormal );\n\n\t\t#else\n\n\t\t\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\n\t\t#endif\n\n\t#endif\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_FOG\n\n\tvFogDepth = - mvPosition.z;\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_FOG\n\n\tvarying float vFogDepth;\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_FOG\n\n\t#ifdef FOG_EXP2\n\n\t\tfloat fogFactor = 1.0 - exp( - fogDensity * fogDensity * vFogDepth * vFogDepth );\n\n\t#else\n\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, vFogDepth );\n\n\t#endif\n\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_FOG\n\n\tuniform vec3 fogColor;\n\tvarying float vFogDepth;\n\n\t#ifdef FOG_EXP2\n\n\t\tuniform float fogDensity;\n\n\t#else\n\n\t\tuniform float fogNear;\n\t\tuniform float fogFar;\n\n\t#endif\n\n#endif\n`;\n","export default /* glsl */`\n\n#ifdef USE_GRADIENTMAP\n\n\tuniform sampler2D gradientMap;\n\n#endif\n\nvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\n\t// dotNL will be from -1.0 to 1.0\n\tfloat dotNL = dot( normal, lightDirection );\n\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\n\t#ifdef USE_GRADIENTMAP\n\n\t\treturn vec3( texture2D( gradientMap, coord ).r );\n\n\t#else\n\n\t\tvec2 fw = fwidth( coord ) * 0.5;\n\t\treturn mix( vec3( 0.7 ), vec3( 1.0 ), smoothstep( 0.7 - fw.x, 0.7 + fw.x, coord.x ) );\n\n\t#endif\n\n}\n`;\n","export default /* glsl */`\n#ifdef USE_LIGHTMAP\n\n\tvec4 lightMapTexel = texture2D( lightMap, vLightMapUv );\n\tvec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity;\n\n\treflectedLight.indirectDiffuse += lightMapIrradiance;\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_LIGHTMAP\n\n\tuniform sampler2D lightMap;\n\tuniform float lightMapIntensity;\n\n#endif\n`;\n","export default /* glsl */`\nLambertMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularStrength = specularStrength;\n`;\n","export default /* glsl */`\nvarying vec3 vViewPosition;\n\nstruct LambertMaterial {\n\n\tvec3 diffuseColor;\n\tfloat specularStrength;\n\n};\n\nvoid RE_Direct_Lambert( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in LambertMaterial material, inout ReflectedLight reflectedLight ) {\n\n\tfloat dotNL = saturate( dot( geometryNormal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\n\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n\n}\n\nvoid RE_IndirectDiffuse_Lambert( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in LambertMaterial material, inout ReflectedLight reflectedLight ) {\n\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n\n}\n\n#define RE_Direct\t\t\t\tRE_Direct_Lambert\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Lambert\n`;\n","export default /* glsl */`\nuniform bool receiveShadow;\nuniform vec3 ambientLightColor;\n\n#if defined( USE_LIGHT_PROBES )\n\n\tuniform vec3 lightProbe[ 9 ];\n\n#endif\n\n// get the irradiance (radiance convolved with cosine lobe) at the point 'normal' on the unit sphere\n// source: https://graphics.stanford.edu/papers/envmap/envmap.pdf\nvec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) {\n\n\t// normal is assumed to have unit length\n\n\tfloat x = normal.x, y = normal.y, z = normal.z;\n\n\t// band 0\n\tvec3 result = shCoefficients[ 0 ] * 0.886227;\n\n\t// band 1\n\tresult += shCoefficients[ 1 ] * 2.0 * 0.511664 * y;\n\tresult += shCoefficients[ 2 ] * 2.0 * 0.511664 * z;\n\tresult += shCoefficients[ 3 ] * 2.0 * 0.511664 * x;\n\n\t// band 2\n\tresult += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y;\n\tresult += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z;\n\tresult += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 );\n\tresult += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z;\n\tresult += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y );\n\n\treturn result;\n\n}\n\nvec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in vec3 normal ) {\n\n\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\n\tvec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe );\n\n\treturn irradiance;\n\n}\n\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n\n\tvec3 irradiance = ambientLightColor;\n\n\treturn irradiance;\n\n}\n\nfloat getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n\n\t#if defined ( LEGACY_LIGHTS )\n\n\t\tif ( cutoffDistance > 0.0 && decayExponent > 0.0 ) {\n\n\t\t\treturn pow( saturate( - lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\n\t\t}\n\n\t\treturn 1.0;\n\n\t#else\n\n\t\t// based upon Frostbite 3 Moving to Physically-based Rendering\n\t\t// page 32, equation 26: E[window1]\n\t\t// https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf\n\t\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n\n\t\tif ( cutoffDistance > 0.0 ) {\n\n\t\t\tdistanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\n\t\t}\n\n\t\treturn distanceFalloff;\n\n\t#endif\n\n}\n\nfloat getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) {\n\n\treturn smoothstep( coneCosine, penumbraCosine, angleCosine );\n\n}\n\n#if NUM_DIR_LIGHTS > 0\n\n\tstruct DirectionalLight {\n\t\tvec3 direction;\n\t\tvec3 color;\n\t};\n\n\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n\n\tvoid getDirectionalLightInfo( const in DirectionalLight directionalLight, out IncidentLight light ) {\n\n\t\tlight.color = directionalLight.color;\n\t\tlight.direction = directionalLight.direction;\n\t\tlight.visible = true;\n\n\t}\n\n#endif\n\n\n#if NUM_POINT_LIGHTS > 0\n\n\tstruct PointLight {\n\t\tvec3 position;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t};\n\n\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n\n\t// light is an out parameter as having it as a return value caused compiler errors on some devices\n\tvoid getPointLightInfo( const in PointLight pointLight, const in vec3 geometryPosition, out IncidentLight light ) {\n\n\t\tvec3 lVector = pointLight.position - geometryPosition;\n\n\t\tlight.direction = normalize( lVector );\n\n\t\tfloat lightDistance = length( lVector );\n\n\t\tlight.color = pointLight.color;\n\t\tlight.color *= getDistanceAttenuation( lightDistance, pointLight.distance, pointLight.decay );\n\t\tlight.visible = ( light.color != vec3( 0.0 ) );\n\n\t}\n\n#endif\n\n\n#if NUM_SPOT_LIGHTS > 0\n\n\tstruct SpotLight {\n\t\tvec3 position;\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tfloat coneCos;\n\t\tfloat penumbraCos;\n\t};\n\n\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n\n\t// light is an out parameter as having it as a return value caused compiler errors on some devices\n\tvoid getSpotLightInfo( const in SpotLight spotLight, const in vec3 geometryPosition, out IncidentLight light ) {\n\n\t\tvec3 lVector = spotLight.position - geometryPosition;\n\n\t\tlight.direction = normalize( lVector );\n\n\t\tfloat angleCos = dot( light.direction, spotLight.direction );\n\n\t\tfloat spotAttenuation = getSpotAttenuation( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n\n\t\tif ( spotAttenuation > 0.0 ) {\n\n\t\t\tfloat lightDistance = length( lVector );\n\n\t\t\tlight.color = spotLight.color * spotAttenuation;\n\t\t\tlight.color *= getDistanceAttenuation( lightDistance, spotLight.distance, spotLight.decay );\n\t\t\tlight.visible = ( light.color != vec3( 0.0 ) );\n\n\t\t} else {\n\n\t\t\tlight.color = vec3( 0.0 );\n\t\t\tlight.visible = false;\n\n\t\t}\n\n\t}\n\n#endif\n\n\n#if NUM_RECT_AREA_LIGHTS > 0\n\n\tstruct RectAreaLight {\n\t\tvec3 color;\n\t\tvec3 position;\n\t\tvec3 halfWidth;\n\t\tvec3 halfHeight;\n\t};\n\n\t// Pre-computed values of LinearTransformedCosine approximation of BRDF\n\t// BRDF approximation Texture is 64x64\n\tuniform sampler2D ltc_1; // RGBA Float\n\tuniform sampler2D ltc_2; // RGBA Float\n\n\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\n\n#endif\n\n\n#if NUM_HEMI_LIGHTS > 0\n\n\tstruct HemisphereLight {\n\t\tvec3 direction;\n\t\tvec3 skyColor;\n\t\tvec3 groundColor;\n\t};\n\n\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n\n\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in vec3 normal ) {\n\n\t\tfloat dotNL = dot( normal, hemiLight.direction );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n\n\t\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n\n\t\treturn irradiance;\n\n\t}\n\n#endif\n`;\n","export default /* glsl */`\nToonMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\n`;\n","export default /* glsl */`\nvarying vec3 vViewPosition;\n\nstruct ToonMaterial {\n\n\tvec3 diffuseColor;\n\n};\n\nvoid RE_Direct_Toon( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\n\tvec3 irradiance = getGradientIrradiance( geometryNormal, directLight.direction ) * directLight.color;\n\n\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n\n}\n\nvoid RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n\n}\n\n#define RE_Direct\t\t\t\tRE_Direct_Toon\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Toon\n`;\n","export default /* glsl */`\nBlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;\n`;\n","export default /* glsl */`\nvarying vec3 vViewPosition;\n\nstruct BlinnPhongMaterial {\n\n\tvec3 diffuseColor;\n\tvec3 specularColor;\n\tfloat specularShininess;\n\tfloat specularStrength;\n\n};\n\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\n\tfloat dotNL = saturate( dot( geometryNormal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\n\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n\n\treflectedLight.directSpecular += irradiance * BRDF_BlinnPhong( directLight.direction, geometryViewDir, geometryNormal, material.specularColor, material.specularShininess ) * material.specularStrength;\n\n}\n\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n\n}\n\n#define RE_Direct\t\t\t\tRE_Direct_BlinnPhong\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_BlinnPhong\n`;\n","export default /* glsl */`\nPhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\n\nvec3 dxy = max( abs( dFdx( nonPerturbedNormal ) ), abs( dFdy( nonPerturbedNormal ) ) );\nfloat geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );\n\nmaterial.roughness = max( roughnessFactor, 0.0525 );// 0.0525 corresponds to the base mip of a 256 cubemap.\nmaterial.roughness += geometryRoughness;\nmaterial.roughness = min( material.roughness, 1.0 );\n\n#ifdef IOR\n\n\tmaterial.ior = ior;\n\n\t#ifdef USE_SPECULAR\n\n\t\tfloat specularIntensityFactor = specularIntensity;\n\t\tvec3 specularColorFactor = specularColor;\n\n\t\t#ifdef USE_SPECULAR_COLORMAP\n\n\t\t\tspecularColorFactor *= texture2D( specularColorMap, vSpecularColorMapUv ).rgb;\n\n\t\t#endif\n\n\t\t#ifdef USE_SPECULAR_INTENSITYMAP\n\n\t\t\tspecularIntensityFactor *= texture2D( specularIntensityMap, vSpecularIntensityMapUv ).a;\n\n\t\t#endif\n\n\t\tmaterial.specularF90 = mix( specularIntensityFactor, 1.0, metalnessFactor );\n\n\t#else\n\n\t\tfloat specularIntensityFactor = 1.0;\n\t\tvec3 specularColorFactor = vec3( 1.0 );\n\t\tmaterial.specularF90 = 1.0;\n\n\t#endif\n\n\tmaterial.specularColor = mix( min( pow2( ( material.ior - 1.0 ) / ( material.ior + 1.0 ) ) * specularColorFactor, vec3( 1.0 ) ) * specularIntensityFactor, diffuseColor.rgb, metalnessFactor );\n\n#else\n\n\tmaterial.specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor );\n\tmaterial.specularF90 = 1.0;\n\n#endif\n\n#ifdef USE_CLEARCOAT\n\n\tmaterial.clearcoat = clearcoat;\n\tmaterial.clearcoatRoughness = clearcoatRoughness;\n\tmaterial.clearcoatF0 = vec3( 0.04 );\n\tmaterial.clearcoatF90 = 1.0;\n\n\t#ifdef USE_CLEARCOATMAP\n\n\t\tmaterial.clearcoat *= texture2D( clearcoatMap, vClearcoatMapUv ).x;\n\n\t#endif\n\n\t#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\n\t\tmaterial.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vClearcoatRoughnessMapUv ).y;\n\n\t#endif\n\n\tmaterial.clearcoat = saturate( material.clearcoat ); // Burley clearcoat model\n\tmaterial.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 );\n\tmaterial.clearcoatRoughness += geometryRoughness;\n\tmaterial.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 );\n\n#endif\n\n#ifdef USE_IRIDESCENCE\n\n\tmaterial.iridescence = iridescence;\n\tmaterial.iridescenceIOR = iridescenceIOR;\n\n\t#ifdef USE_IRIDESCENCEMAP\n\n\t\tmaterial.iridescence *= texture2D( iridescenceMap, vIridescenceMapUv ).r;\n\n\t#endif\n\n\t#ifdef USE_IRIDESCENCE_THICKNESSMAP\n\n\t\tmaterial.iridescenceThickness = (iridescenceThicknessMaximum - iridescenceThicknessMinimum) * texture2D( iridescenceThicknessMap, vIridescenceThicknessMapUv ).g + iridescenceThicknessMinimum;\n\n\t#else\n\n\t\tmaterial.iridescenceThickness = iridescenceThicknessMaximum;\n\n\t#endif\n\n#endif\n\n#ifdef USE_SHEEN\n\n\tmaterial.sheenColor = sheenColor;\n\n\t#ifdef USE_SHEEN_COLORMAP\n\n\t\tmaterial.sheenColor *= texture2D( sheenColorMap, vSheenColorMapUv ).rgb;\n\n\t#endif\n\n\tmaterial.sheenRoughness = clamp( sheenRoughness, 0.07, 1.0 );\n\n\t#ifdef USE_SHEEN_ROUGHNESSMAP\n\n\t\tmaterial.sheenRoughness *= texture2D( sheenRoughnessMap, vSheenRoughnessMapUv ).a;\n\n\t#endif\n\n#endif\n\n#ifdef USE_ANISOTROPY\n\n\t#ifdef USE_ANISOTROPYMAP\n\n\t\tmat2 anisotropyMat = mat2( anisotropyVector.x, anisotropyVector.y, - anisotropyVector.y, anisotropyVector.x );\n\t\tvec3 anisotropyPolar = texture2D( anisotropyMap, vAnisotropyMapUv ).rgb;\n\t\tvec2 anisotropyV = anisotropyMat * normalize( 2.0 * anisotropyPolar.rg - vec2( 1.0 ) ) * anisotropyPolar.b;\n\n\t#else\n\n\t\tvec2 anisotropyV = anisotropyVector;\n\n\t#endif\n\n\tmaterial.anisotropy = length( anisotropyV );\n\tanisotropyV /= material.anisotropy;\n\tmaterial.anisotropy = saturate( material.anisotropy );\n\n\t// Roughness along the anisotropy bitangent is the material roughness, while the tangent roughness increases with anisotropy.\n\tmaterial.alphaT = mix( pow2( material.roughness ), 1.0, pow2( material.anisotropy ) );\n\n\tmaterial.anisotropyT = tbn[ 0 ] * anisotropyV.x - tbn[ 1 ] * anisotropyV.y;\n\tmaterial.anisotropyB = tbn[ 1 ] * anisotropyV.x + tbn[ 0 ] * anisotropyV.y;\n\n#endif\n`;\n","export default /* glsl */`\n\nstruct PhysicalMaterial {\n\n\tvec3 diffuseColor;\n\tfloat roughness;\n\tvec3 specularColor;\n\tfloat specularF90;\n\n\t#ifdef USE_CLEARCOAT\n\t\tfloat clearcoat;\n\t\tfloat clearcoatRoughness;\n\t\tvec3 clearcoatF0;\n\t\tfloat clearcoatF90;\n\t#endif\n\n\t#ifdef USE_IRIDESCENCE\n\t\tfloat iridescence;\n\t\tfloat iridescenceIOR;\n\t\tfloat iridescenceThickness;\n\t\tvec3 iridescenceFresnel;\n\t\tvec3 iridescenceF0;\n\t#endif\n\n\t#ifdef USE_SHEEN\n\t\tvec3 sheenColor;\n\t\tfloat sheenRoughness;\n\t#endif\n\n\t#ifdef IOR\n\t\tfloat ior;\n\t#endif\n\n\t#ifdef USE_TRANSMISSION\n\t\tfloat transmission;\n\t\tfloat transmissionAlpha;\n\t\tfloat thickness;\n\t\tfloat attenuationDistance;\n\t\tvec3 attenuationColor;\n\t#endif\n\n\t#ifdef USE_ANISOTROPY\n\t\tfloat anisotropy;\n\t\tfloat alphaT;\n\t\tvec3 anisotropyT;\n\t\tvec3 anisotropyB;\n\t#endif\n\n};\n\n// temporary\nvec3 clearcoatSpecularDirect = vec3( 0.0 );\nvec3 clearcoatSpecularIndirect = vec3( 0.0 );\nvec3 sheenSpecularDirect = vec3( 0.0 );\nvec3 sheenSpecularIndirect = vec3(0.0 );\n\nvec3 Schlick_to_F0( const in vec3 f, const in float f90, const in float dotVH ) {\n float x = clamp( 1.0 - dotVH, 0.0, 1.0 );\n float x2 = x * x;\n float x5 = clamp( x * x2 * x2, 0.0, 0.9999 );\n\n return ( f - vec3( f90 ) * x5 ) / ( 1.0 - x5 );\n}\n\n// Moving Frostbite to Physically Based Rendering 3.0 - page 12, listing 2\n// https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf\nfloat V_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\n\n\tfloat a2 = pow2( alpha );\n\n\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\n\treturn 0.5 / max( gv + gl, EPSILON );\n\n}\n\n// Microfacet Models for Refraction through Rough Surfaces - equation (33)\n// http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html\n// alpha is \"roughness squared\" in Disney’s reparameterization\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n\n\tfloat a2 = pow2( alpha );\n\n\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0; // avoid alpha = 0 with dotNH = 1\n\n\treturn RECIPROCAL_PI * a2 / pow2( denom );\n\n}\n\n// https://google.github.io/filament/Filament.md.html#materialsystem/anisotropicmodel/anisotropicspecularbrdf\n#ifdef USE_ANISOTROPY\n\n\tfloat V_GGX_SmithCorrelated_Anisotropic( const in float alphaT, const in float alphaB, const in float dotTV, const in float dotBV, const in float dotTL, const in float dotBL, const in float dotNV, const in float dotNL ) {\n\n\t\tfloat gv = dotNL * length( vec3( alphaT * dotTV, alphaB * dotBV, dotNV ) );\n\t\tfloat gl = dotNV * length( vec3( alphaT * dotTL, alphaB * dotBL, dotNL ) );\n\t\tfloat v = 0.5 / ( gv + gl );\n\n\t\treturn saturate(v);\n\n\t}\n\n\tfloat D_GGX_Anisotropic( const in float alphaT, const in float alphaB, const in float dotNH, const in float dotTH, const in float dotBH ) {\n\n\t\tfloat a2 = alphaT * alphaB;\n\t\thighp vec3 v = vec3( alphaB * dotTH, alphaT * dotBH, a2 * dotNH );\n\t\thighp float v2 = dot( v, v );\n\t\tfloat w2 = a2 / v2;\n\n\t\treturn RECIPROCAL_PI * a2 * pow2 ( w2 );\n\n\t}\n\n#endif\n\n#ifdef USE_CLEARCOAT\n\n\t// GGX Distribution, Schlick Fresnel, GGX_SmithCorrelated Visibility\n\tvec3 BRDF_GGX_Clearcoat( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material) {\n\n\t\tvec3 f0 = material.clearcoatF0;\n\t\tfloat f90 = material.clearcoatF90;\n\t\tfloat roughness = material.clearcoatRoughness;\n\n\t\tfloat alpha = pow2( roughness ); // UE4's roughness\n\n\t\tvec3 halfDir = normalize( lightDir + viewDir );\n\n\t\tfloat dotNL = saturate( dot( normal, lightDir ) );\n\t\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\t\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\t\tfloat dotVH = saturate( dot( viewDir, halfDir ) );\n\n\t\tvec3 F = F_Schlick( f0, f90, dotVH );\n\n\t\tfloat V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\n\t\tfloat D = D_GGX( alpha, dotNH );\n\n\t\treturn F * ( V * D );\n\n\t}\n\n#endif\n\nvec3 BRDF_GGX( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material ) {\n\n\tvec3 f0 = material.specularColor;\n\tfloat f90 = material.specularF90;\n\tfloat roughness = material.roughness;\n\n\tfloat alpha = pow2( roughness ); // UE4's roughness\n\n\tvec3 halfDir = normalize( lightDir + viewDir );\n\n\tfloat dotNL = saturate( dot( normal, lightDir ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat dotVH = saturate( dot( viewDir, halfDir ) );\n\n\tvec3 F = F_Schlick( f0, f90, dotVH );\n\n\t#ifdef USE_IRIDESCENCE\n\n\t\tF = mix( F, material.iridescenceFresnel, material.iridescence );\n\n\t#endif\n\n\t#ifdef USE_ANISOTROPY\n\n\t\tfloat dotTL = dot( material.anisotropyT, lightDir );\n\t\tfloat dotTV = dot( material.anisotropyT, viewDir );\n\t\tfloat dotTH = dot( material.anisotropyT, halfDir );\n\t\tfloat dotBL = dot( material.anisotropyB, lightDir );\n\t\tfloat dotBV = dot( material.anisotropyB, viewDir );\n\t\tfloat dotBH = dot( material.anisotropyB, halfDir );\n\n\t\tfloat V = V_GGX_SmithCorrelated_Anisotropic( material.alphaT, alpha, dotTV, dotBV, dotTL, dotBL, dotNV, dotNL );\n\n\t\tfloat D = D_GGX_Anisotropic( material.alphaT, alpha, dotNH, dotTH, dotBH );\n\n\t#else\n\n\t\tfloat V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\n\t\tfloat D = D_GGX( alpha, dotNH );\n\n\t#endif\n\n\treturn F * ( V * D );\n\n}\n\n// Rect Area Light\n\n// Real-Time Polygonal-Light Shading with Linearly Transformed Cosines\n// by Eric Heitz, Jonathan Dupuy, Stephen Hill and David Neubelt\n// code: https://github.com/selfshadow/ltc_code/\n\nvec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {\n\n\tconst float LUT_SIZE = 64.0;\n\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\n\tconst float LUT_BIAS = 0.5 / LUT_SIZE;\n\n\tfloat dotNV = saturate( dot( N, V ) );\n\n\t// texture parameterized by sqrt( GGX alpha ) and sqrt( 1 - cos( theta ) )\n\tvec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );\n\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\n\treturn uv;\n\n}\n\nfloat LTC_ClippedSphereFormFactor( const in vec3 f ) {\n\n\t// Real-Time Area Lighting: a Journey from Research to Production (p.102)\n\t// An approximation of the form factor of a horizon-clipped rectangle.\n\n\tfloat l = length( f );\n\n\treturn max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\n\n}\n\nvec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {\n\n\tfloat x = dot( v1, v2 );\n\n\tfloat y = abs( x );\n\n\t// rational polynomial approximation to theta / sin( theta ) / 2PI\n\tfloat a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;\n\tfloat b = 3.4175940 + ( 4.1616724 + y ) * y;\n\tfloat v = a / b;\n\n\tfloat theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;\n\n\treturn cross( v1, v2 ) * theta_sintheta;\n\n}\n\nvec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {\n\n\t// bail if point is on back side of plane of light\n\t// assumes ccw winding order of light vertices\n\tvec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];\n\tvec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];\n\tvec3 lightNormal = cross( v1, v2 );\n\n\tif( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );\n\n\t// construct orthonormal basis around N\n\tvec3 T1, T2;\n\tT1 = normalize( V - N * dot( V, N ) );\n\tT2 = - cross( N, T1 ); // negated from paper; possibly due to a different handedness of world coordinate system\n\n\t// compute transform\n\tmat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );\n\n\t// transform rect\n\tvec3 coords[ 4 ];\n\tcoords[ 0 ] = mat * ( rectCoords[ 0 ] - P );\n\tcoords[ 1 ] = mat * ( rectCoords[ 1 ] - P );\n\tcoords[ 2 ] = mat * ( rectCoords[ 2 ] - P );\n\tcoords[ 3 ] = mat * ( rectCoords[ 3 ] - P );\n\n\t// project rect onto sphere\n\tcoords[ 0 ] = normalize( coords[ 0 ] );\n\tcoords[ 1 ] = normalize( coords[ 1 ] );\n\tcoords[ 2 ] = normalize( coords[ 2 ] );\n\tcoords[ 3 ] = normalize( coords[ 3 ] );\n\n\t// calculate vector form factor\n\tvec3 vectorFormFactor = vec3( 0.0 );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );\n\n\t// adjust for horizon clipping\n\tfloat result = LTC_ClippedSphereFormFactor( vectorFormFactor );\n\n/*\n\t// alternate method of adjusting for horizon clipping (see referece)\n\t// refactoring required\n\tfloat len = length( vectorFormFactor );\n\tfloat z = vectorFormFactor.z / len;\n\n\tconst float LUT_SIZE = 64.0;\n\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\n\tconst float LUT_BIAS = 0.5 / LUT_SIZE;\n\n\t// tabulated horizon-clipped sphere, apparently...\n\tvec2 uv = vec2( z * 0.5 + 0.5, len );\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\n\tfloat scale = texture2D( ltc_2, uv ).w;\n\n\tfloat result = len * scale;\n*/\n\n\treturn vec3( result );\n\n}\n\n// End Rect Area Light\n\n#if defined( USE_SHEEN )\n\n// https://github.com/google/filament/blob/master/shaders/src/brdf.fs\nfloat D_Charlie( float roughness, float dotNH ) {\n\n\tfloat alpha = pow2( roughness );\n\n\t// Estevez and Kulla 2017, \"Production Friendly Microfacet Sheen BRDF\"\n\tfloat invAlpha = 1.0 / alpha;\n\tfloat cos2h = dotNH * dotNH;\n\tfloat sin2h = max( 1.0 - cos2h, 0.0078125 ); // 2^(-14/2), so sin2h^2 > 0 in fp16\n\n\treturn ( 2.0 + invAlpha ) * pow( sin2h, invAlpha * 0.5 ) / ( 2.0 * PI );\n\n}\n\n// https://github.com/google/filament/blob/master/shaders/src/brdf.fs\nfloat V_Neubelt( float dotNV, float dotNL ) {\n\n\t// Neubelt and Pettineo 2013, \"Crafting a Next-gen Material Pipeline for The Order: 1886\"\n\treturn saturate( 1.0 / ( 4.0 * ( dotNL + dotNV - dotNL * dotNV ) ) );\n\n}\n\nvec3 BRDF_Sheen( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, vec3 sheenColor, const in float sheenRoughness ) {\n\n\tvec3 halfDir = normalize( lightDir + viewDir );\n\n\tfloat dotNL = saturate( dot( normal, lightDir ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\n\tfloat D = D_Charlie( sheenRoughness, dotNH );\n\tfloat V = V_Neubelt( dotNV, dotNL );\n\n\treturn sheenColor * ( D * V );\n\n}\n\n#endif\n\n// This is a curve-fit approxmation to the \"Charlie sheen\" BRDF integrated over the hemisphere from \n// Estevez and Kulla 2017, \"Production Friendly Microfacet Sheen BRDF\". The analysis can be found\n// in the Sheen section of https://drive.google.com/file/d/1T0D1VSyR4AllqIJTQAraEIzjlb5h4FKH/view?usp=sharing\nfloat IBLSheenBRDF( const in vec3 normal, const in vec3 viewDir, const in float roughness ) {\n\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\n\tfloat r2 = roughness * roughness;\n\n\tfloat a = roughness < 0.25 ? -339.2 * r2 + 161.4 * roughness - 25.9 : -8.48 * r2 + 14.3 * roughness - 9.95;\n\n\tfloat b = roughness < 0.25 ? 44.0 * r2 - 23.7 * roughness + 3.26 : 1.97 * r2 - 3.27 * roughness + 0.72;\n\n\tfloat DG = exp( a * dotNV + b ) + ( roughness < 0.25 ? 0.0 : 0.1 * ( roughness - 0.25 ) );\n\n\treturn saturate( DG * RECIPROCAL_PI );\n\n}\n\n// Analytical approximation of the DFG LUT, one half of the\n// split-sum approximation used in indirect specular lighting.\n// via 'environmentBRDF' from \"Physically Based Shading on Mobile\"\n// https://www.unrealengine.com/blog/physically-based-shading-on-mobile\nvec2 DFGApprox( const in vec3 normal, const in vec3 viewDir, const in float roughness ) {\n\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\n\tconst vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\n\n\tconst vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\n\n\tvec4 r = roughness * c0 + c1;\n\n\tfloat a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\n\n\tvec2 fab = vec2( - 1.04, 1.04 ) * a004 + r.zw;\n\n\treturn fab;\n\n}\n\nvec3 EnvironmentBRDF( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness ) {\n\n\tvec2 fab = DFGApprox( normal, viewDir, roughness );\n\n\treturn specularColor * fab.x + specularF90 * fab.y;\n\n}\n\n// Fdez-Agüera's \"Multiple-Scattering Microfacet Model for Real-Time Image Based Lighting\"\n// Approximates multiscattering in order to preserve energy.\n// http://www.jcgt.org/published/0008/01/03/\n#ifdef USE_IRIDESCENCE\nvoid computeMultiscatteringIridescence( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float iridescence, const in vec3 iridescenceF0, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\n#else\nvoid computeMultiscattering( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\n#endif\n\n\tvec2 fab = DFGApprox( normal, viewDir, roughness );\n\n\t#ifdef USE_IRIDESCENCE\n\n\t\tvec3 Fr = mix( specularColor, iridescenceF0, iridescence );\n\n\t#else\n\n\t\tvec3 Fr = specularColor;\n\n\t#endif\n\n\tvec3 FssEss = Fr * fab.x + specularF90 * fab.y;\n\n\tfloat Ess = fab.x + fab.y;\n\tfloat Ems = 1.0 - Ess;\n\n\tvec3 Favg = Fr + ( 1.0 - Fr ) * 0.047619; // 1/21\n\tvec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg );\n\n\tsingleScatter += FssEss;\n\tmultiScatter += Fms * Ems;\n\n}\n\n#if NUM_RECT_AREA_LIGHTS > 0\n\n\tvoid RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\n\t\tvec3 normal = geometryNormal;\n\t\tvec3 viewDir = geometryViewDir;\n\t\tvec3 position = geometryPosition;\n\t\tvec3 lightPos = rectAreaLight.position;\n\t\tvec3 halfWidth = rectAreaLight.halfWidth;\n\t\tvec3 halfHeight = rectAreaLight.halfHeight;\n\t\tvec3 lightColor = rectAreaLight.color;\n\t\tfloat roughness = material.roughness;\n\n\t\tvec3 rectCoords[ 4 ];\n\t\trectCoords[ 0 ] = lightPos + halfWidth - halfHeight; // counterclockwise; light shines in local neg z direction\n\t\trectCoords[ 1 ] = lightPos - halfWidth - halfHeight;\n\t\trectCoords[ 2 ] = lightPos - halfWidth + halfHeight;\n\t\trectCoords[ 3 ] = lightPos + halfWidth + halfHeight;\n\n\t\tvec2 uv = LTC_Uv( normal, viewDir, roughness );\n\n\t\tvec4 t1 = texture2D( ltc_1, uv );\n\t\tvec4 t2 = texture2D( ltc_2, uv );\n\n\t\tmat3 mInv = mat3(\n\t\t\tvec3( t1.x, 0, t1.y ),\n\t\t\tvec3( 0, 1, 0 ),\n\t\t\tvec3( t1.z, 0, t1.w )\n\t\t);\n\n\t\t// LTC Fresnel Approximation by Stephen Hill\n\t\t// http://blog.selfshadow.com/publications/s2016-advances/s2016_ltc_fresnel.pdf\n\t\tvec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y );\n\n\t\treflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );\n\n\t\treflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );\n\n\t}\n\n#endif\n\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\n\tfloat dotNL = saturate( dot( geometryNormal, directLight.direction ) );\n\n\tvec3 irradiance = dotNL * directLight.color;\n\n\t#ifdef USE_CLEARCOAT\n\n\t\tfloat dotNLcc = saturate( dot( geometryClearcoatNormal, directLight.direction ) );\n\n\t\tvec3 ccIrradiance = dotNLcc * directLight.color;\n\n\t\tclearcoatSpecularDirect += ccIrradiance * BRDF_GGX_Clearcoat( directLight.direction, geometryViewDir, geometryClearcoatNormal, material );\n\n\t#endif\n\n\t#ifdef USE_SHEEN\n\n\t\tsheenSpecularDirect += irradiance * BRDF_Sheen( directLight.direction, geometryViewDir, geometryNormal, material.sheenColor, material.sheenRoughness );\n\n\t#endif\n\n\treflectedLight.directSpecular += irradiance * BRDF_GGX( directLight.direction, geometryViewDir, geometryNormal, material );\n\n\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\n\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n\n}\n\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {\n\n\t#ifdef USE_CLEARCOAT\n\n\t\tclearcoatSpecularIndirect += clearcoatRadiance * EnvironmentBRDF( geometryClearcoatNormal, geometryViewDir, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness );\n\n\t#endif\n\n\t#ifdef USE_SHEEN\n\n\t\tsheenSpecularIndirect += irradiance * material.sheenColor * IBLSheenBRDF( geometryNormal, geometryViewDir, material.sheenRoughness );\n\n\t#endif\n\n\t// Both indirect specular and indirect diffuse light accumulate here\n\n\tvec3 singleScattering = vec3( 0.0 );\n\tvec3 multiScattering = vec3( 0.0 );\n\tvec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI;\n\n\t#ifdef USE_IRIDESCENCE\n\n\t\tcomputeMultiscatteringIridescence( geometryNormal, geometryViewDir, material.specularColor, material.specularF90, material.iridescence, material.iridescenceFresnel, material.roughness, singleScattering, multiScattering );\n\n\t#else\n\n\t\tcomputeMultiscattering( geometryNormal, geometryViewDir, material.specularColor, material.specularF90, material.roughness, singleScattering, multiScattering );\n\n\t#endif\n\n\tvec3 totalScattering = singleScattering + multiScattering;\n\tvec3 diffuse = material.diffuseColor * ( 1.0 - max( max( totalScattering.r, totalScattering.g ), totalScattering.b ) );\n\n\treflectedLight.indirectSpecular += radiance * singleScattering;\n\treflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance;\n\n\treflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance;\n\n}\n\n#define RE_Direct\t\t\t\tRE_Direct_Physical\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_Physical\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Physical\n#define RE_IndirectSpecular\t\tRE_IndirectSpecular_Physical\n\n// ref: https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\n\n\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\n\n}\n`;\n","export default /* glsl */`\n/**\n * This is a template that can be used to light a material, it uses pluggable\n * RenderEquations (RE)for specific lighting scenarios.\n *\n * Instructions for use:\n * - Ensure that both RE_Direct, RE_IndirectDiffuse and RE_IndirectSpecular are defined\n * - Create a material parameter that is to be passed as the third parameter to your lighting functions.\n *\n * TODO:\n * - Add area light support.\n * - Add sphere light support.\n * - Add diffuse light probe (irradiance cubemap) support.\n */\n\nvec3 geometryPosition = - vViewPosition;\nvec3 geometryNormal = normal;\nvec3 geometryViewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition );\n\nvec3 geometryClearcoatNormal = vec3( 0.0 );\n\n#ifdef USE_CLEARCOAT\n\n\tgeometryClearcoatNormal = clearcoatNormal;\n\n#endif\n\n#ifdef USE_IRIDESCENCE\n\n\tfloat dotNVi = saturate( dot( normal, geometryViewDir ) );\n\n\tif ( material.iridescenceThickness == 0.0 ) {\n\n\t\tmaterial.iridescence = 0.0;\n\n\t} else {\n\n\t\tmaterial.iridescence = saturate( material.iridescence );\n\n\t}\n\n\tif ( material.iridescence > 0.0 ) {\n\n\t\tmaterial.iridescenceFresnel = evalIridescence( 1.0, material.iridescenceIOR, dotNVi, material.iridescenceThickness, material.specularColor );\n\n\t\t// Iridescence F0 approximation\n\t\tmaterial.iridescenceF0 = Schlick_to_F0( material.iridescenceFresnel, 1.0, dotNVi );\n\n\t}\n\n#endif\n\nIncidentLight directLight;\n\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\n\tPointLight pointLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLightShadow;\n\t#endif\n\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\n\t\tpointLight = pointLights[ i ];\n\n\t\tgetPointLightInfo( pointLight, geometryPosition, directLight );\n\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )\n\t\tpointLightShadow = pointLightShadows[ i ];\n\t\tdirectLight.color *= ( directLight.visible && receiveShadow ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0;\n\t\t#endif\n\n\t\tRE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\n\n\t}\n\t#pragma unroll_loop_end\n\n#endif\n\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\n\tSpotLight spotLight;\n\tvec4 spotColor;\n\tvec3 spotLightCoord;\n\tbool inSpotLightMap;\n\n\t#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLightShadow;\n\t#endif\n\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\n\t\tspotLight = spotLights[ i ];\n\n\t\tgetSpotLightInfo( spotLight, geometryPosition, directLight );\n\n\t\t// spot lights are ordered [shadows with maps, shadows without maps, maps without shadows, none]\n\t\t#if ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS )\n\t\t#define SPOT_LIGHT_MAP_INDEX UNROLLED_LOOP_INDEX\n\t\t#elif ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\t#define SPOT_LIGHT_MAP_INDEX NUM_SPOT_LIGHT_MAPS\n\t\t#else\n\t\t#define SPOT_LIGHT_MAP_INDEX ( UNROLLED_LOOP_INDEX - NUM_SPOT_LIGHT_SHADOWS + NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS )\n\t\t#endif\n\n\t\t#if ( SPOT_LIGHT_MAP_INDEX < NUM_SPOT_LIGHT_MAPS )\n\t\t\tspotLightCoord = vSpotLightCoord[ i ].xyz / vSpotLightCoord[ i ].w;\n\t\t\tinSpotLightMap = all( lessThan( abs( spotLightCoord * 2. - 1. ), vec3( 1.0 ) ) );\n\t\t\tspotColor = texture2D( spotLightMap[ SPOT_LIGHT_MAP_INDEX ], spotLightCoord.xy );\n\t\t\tdirectLight.color = inSpotLightMap ? directLight.color * spotColor.rgb : directLight.color;\n\t\t#endif\n\n\t\t#undef SPOT_LIGHT_MAP_INDEX\n\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\tspotLightShadow = spotLightShadows[ i ];\n\t\tdirectLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotLightCoord[ i ] ) : 1.0;\n\t\t#endif\n\n\t\tRE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\n\n\t}\n\t#pragma unroll_loop_end\n\n#endif\n\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\n\tDirectionalLight directionalLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLightShadow;\n\t#endif\n\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\n\t\tdirectionalLight = directionalLights[ i ];\n\n\t\tgetDirectionalLightInfo( directionalLight, directLight );\n\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )\n\t\tdirectionalLightShadow = directionalLightShadows[ i ];\n\t\tdirectLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\n\t\tRE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\n\n\t}\n\t#pragma unroll_loop_end\n\n#endif\n\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\n\tRectAreaLight rectAreaLight;\n\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\n\t\trectAreaLight = rectAreaLights[ i ];\n\t\tRE_Direct_RectArea( rectAreaLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\n\n\t}\n\t#pragma unroll_loop_end\n\n#endif\n\n#if defined( RE_IndirectDiffuse )\n\n\tvec3 iblIrradiance = vec3( 0.0 );\n\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\n\t#if defined( USE_LIGHT_PROBES )\n\n\t\tirradiance += getLightProbeIrradiance( lightProbe, geometryNormal );\n\n\t#endif\n\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometryNormal );\n\n\t\t}\n\t\t#pragma unroll_loop_end\n\n\t#endif\n\n#endif\n\n#if defined( RE_IndirectSpecular )\n\n\tvec3 radiance = vec3( 0.0 );\n\tvec3 clearcoatRadiance = vec3( 0.0 );\n\n#endif\n`;\n","export default /* glsl */`\n#if defined( RE_IndirectDiffuse )\n\n\t#ifdef USE_LIGHTMAP\n\n\t\tvec4 lightMapTexel = texture2D( lightMap, vLightMapUv );\n\t\tvec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity;\n\n\t\tirradiance += lightMapIrradiance;\n\n\t#endif\n\n\t#if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV )\n\n\t\tiblIrradiance += getIBLIrradiance( geometryNormal );\n\n\t#endif\n\n#endif\n\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\n\t#ifdef USE_ANISOTROPY\n\n\t\tradiance += getIBLAnisotropyRadiance( geometryViewDir, geometryNormal, material.roughness, material.anisotropyB, material.anisotropy );\n\n\t#else\n\n\t\tradiance += getIBLRadiance( geometryViewDir, geometryNormal, material.roughness );\n\n\t#endif\n\n\t#ifdef USE_CLEARCOAT\n\n\t\tclearcoatRadiance += getIBLRadiance( geometryViewDir, geometryClearcoatNormal, material.clearcoatRoughness );\n\n\t#endif\n\n#endif\n`;\n","export default /* glsl */`\n#if defined( RE_IndirectDiffuse )\n\n\tRE_IndirectDiffuse( irradiance, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\n\n#endif\n\n#if defined( RE_IndirectSpecular )\n\n\tRE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\n\n#endif\n`;\n","export default /* glsl */`\n#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\n\t// Doing a strict comparison with == 1.0 can cause noise artifacts\n\t// on some platforms. See issue #17623.\n\tgl_FragDepthEXT = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5;\n\n#endif\n`;\n","export default /* glsl */`\n#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\n\tuniform float logDepthBufFC;\n\tvarying float vFragDepth;\n\tvarying float vIsPerspective;\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_LOGDEPTHBUF\n\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\n\t\tvarying float vFragDepth;\n\t\tvarying float vIsPerspective;\n\n\t#else\n\n\t\tuniform float logDepthBufFC;\n\n\t#endif\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_LOGDEPTHBUF\n\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\t\tvIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) );\n\n\t#else\n\n\t\tif ( isPerspectiveMatrix( projectionMatrix ) ) {\n\n\t\t\tgl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0;\n\n\t\t\tgl_Position.z *= gl_Position.w;\n\n\t\t}\n\n\t#endif\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_MAP\n\n\tvec4 sampledDiffuseColor = texture2D( map, vMapUv );\n\n\t#ifdef DECODE_VIDEO_TEXTURE\n\n\t\t// use inline sRGB decode until browsers properly support SRGB8_APLHA8 with video textures\n\n\t\tsampledDiffuseColor = vec4( mix( pow( sampledDiffuseColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), sampledDiffuseColor.rgb * 0.0773993808, vec3( lessThanEqual( sampledDiffuseColor.rgb, vec3( 0.04045 ) ) ) ), sampledDiffuseColor.w );\n\t\n\t#endif\n\n\tdiffuseColor *= sampledDiffuseColor;\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_MAP\n\n\tuniform sampler2D map;\n\n#endif\n`;\n","export default /* glsl */`\n#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\n\t#if defined( USE_POINTS_UV )\n\n\t\tvec2 uv = vUv;\n\n\t#else\n\n\t\tvec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy;\n\n\t#endif\n\n#endif\n\n#ifdef USE_MAP\n\n\tdiffuseColor *= texture2D( map, uv );\n\n#endif\n\n#ifdef USE_ALPHAMAP\n\n\tdiffuseColor.a *= texture2D( alphaMap, uv ).g;\n\n#endif\n`;\n","export default /* glsl */`\n#if defined( USE_POINTS_UV )\n\n\tvarying vec2 vUv;\n\n#else\n\n\t#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\n\t\tuniform mat3 uvTransform;\n\n\t#endif\n\n#endif\n\n#ifdef USE_MAP\n\n\tuniform sampler2D map;\n\n#endif\n\n#ifdef USE_ALPHAMAP\n\n\tuniform sampler2D alphaMap;\n\n#endif\n`;\n","export default /* glsl */`\nfloat metalnessFactor = metalness;\n\n#ifdef USE_METALNESSMAP\n\n\tvec4 texelMetalness = texture2D( metalnessMap, vMetalnessMapUv );\n\n\t// reads channel B, compatible with a combined OcclusionRoughnessMetallic (RGB) texture\n\tmetalnessFactor *= texelMetalness.b;\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_METALNESSMAP\n\n\tuniform sampler2D metalnessMap;\n\n#endif\n`;\n","export default /* glsl */`\n#if defined( USE_MORPHCOLORS ) && defined( MORPHTARGETS_TEXTURE )\n\n\t// morphTargetBaseInfluence is set based on BufferGeometry.morphTargetsRelative value:\n\t// When morphTargetsRelative is false, this is set to 1 - sum(influences); this results in normal = sum((target - base) * influence)\n\t// When morphTargetsRelative is true, this is set to 1; as a result, all morph targets are simply added to the base after weighting\n\tvColor *= morphTargetBaseInfluence;\n\n\tfor ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) {\n\n\t\t#if defined( USE_COLOR_ALPHA )\n\n\t\t\tif ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ) * morphTargetInfluences[ i ];\n\n\t\t#elif defined( USE_COLOR )\n\n\t\t\tif ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ).rgb * morphTargetInfluences[ i ];\n\n\t\t#endif\n\n\t}\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_MORPHNORMALS\n\n\t// morphTargetBaseInfluence is set based on BufferGeometry.morphTargetsRelative value:\n\t// When morphTargetsRelative is false, this is set to 1 - sum(influences); this results in normal = sum((target - base) * influence)\n\t// When morphTargetsRelative is true, this is set to 1; as a result, all morph targets are simply added to the base after weighting\n\tobjectNormal *= morphTargetBaseInfluence;\n\n\t#ifdef MORPHTARGETS_TEXTURE\n\n\t\tfor ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) {\n\n\t\t\tif ( morphTargetInfluences[ i ] != 0.0 ) objectNormal += getMorph( gl_VertexID, i, 1 ).xyz * morphTargetInfluences[ i ];\n\n\t\t}\n\n\t#else\n\n\t\tobjectNormal += morphNormal0 * morphTargetInfluences[ 0 ];\n\t\tobjectNormal += morphNormal1 * morphTargetInfluences[ 1 ];\n\t\tobjectNormal += morphNormal2 * morphTargetInfluences[ 2 ];\n\t\tobjectNormal += morphNormal3 * morphTargetInfluences[ 3 ];\n\n\t#endif\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_MORPHTARGETS\n\n\tuniform float morphTargetBaseInfluence;\n\n\t#ifdef MORPHTARGETS_TEXTURE\n\n\t\tuniform float morphTargetInfluences[ MORPHTARGETS_COUNT ];\n\t\tuniform sampler2DArray morphTargetsTexture;\n\t\tuniform ivec2 morphTargetsTextureSize;\n\n\t\tvec4 getMorph( const in int vertexIndex, const in int morphTargetIndex, const in int offset ) {\n\n\t\t\tint texelIndex = vertexIndex * MORPHTARGETS_TEXTURE_STRIDE + offset;\n\t\t\tint y = texelIndex / morphTargetsTextureSize.x;\n\t\t\tint x = texelIndex - y * morphTargetsTextureSize.x;\n\n\t\t\tivec3 morphUV = ivec3( x, y, morphTargetIndex );\n\t\t\treturn texelFetch( morphTargetsTexture, morphUV, 0 );\n\n\t\t}\n\n\t#else\n\n\t\t#ifndef USE_MORPHNORMALS\n\n\t\t\tuniform float morphTargetInfluences[ 8 ];\n\n\t\t#else\n\n\t\t\tuniform float morphTargetInfluences[ 4 ];\n\n\t\t#endif\n\n\t#endif\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_MORPHTARGETS\n\n\t// morphTargetBaseInfluence is set based on BufferGeometry.morphTargetsRelative value:\n\t// When morphTargetsRelative is false, this is set to 1 - sum(influences); this results in position = sum((target - base) * influence)\n\t// When morphTargetsRelative is true, this is set to 1; as a result, all morph targets are simply added to the base after weighting\n\ttransformed *= morphTargetBaseInfluence;\n\n\t#ifdef MORPHTARGETS_TEXTURE\n\n\t\tfor ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) {\n\n\t\t\tif ( morphTargetInfluences[ i ] != 0.0 ) transformed += getMorph( gl_VertexID, i, 0 ).xyz * morphTargetInfluences[ i ];\n\n\t\t}\n\n\t#else\n\n\t\ttransformed += morphTarget0 * morphTargetInfluences[ 0 ];\n\t\ttransformed += morphTarget1 * morphTargetInfluences[ 1 ];\n\t\ttransformed += morphTarget2 * morphTargetInfluences[ 2 ];\n\t\ttransformed += morphTarget3 * morphTargetInfluences[ 3 ];\n\n\t\t#ifndef USE_MORPHNORMALS\n\n\t\t\ttransformed += morphTarget4 * morphTargetInfluences[ 4 ];\n\t\t\ttransformed += morphTarget5 * morphTargetInfluences[ 5 ];\n\t\t\ttransformed += morphTarget6 * morphTargetInfluences[ 6 ];\n\t\t\ttransformed += morphTarget7 * morphTargetInfluences[ 7 ];\n\n\t\t#endif\n\n\t#endif\n\n#endif\n`;\n","export default /* glsl */`\nfloat faceDirection = gl_FrontFacing ? 1.0 : - 1.0;\n\n#ifdef FLAT_SHADED\n\n\tvec3 fdx = dFdx( vViewPosition );\n\tvec3 fdy = dFdy( vViewPosition );\n\tvec3 normal = normalize( cross( fdx, fdy ) );\n\n#else\n\n\tvec3 normal = normalize( vNormal );\n\n\t#ifdef DOUBLE_SIDED\n\n\t\tnormal *= faceDirection;\n\n\t#endif\n\n#endif\n\n#if defined( USE_NORMALMAP_TANGENTSPACE ) || defined( USE_CLEARCOAT_NORMALMAP ) || defined( USE_ANISOTROPY )\n\n\t#ifdef USE_TANGENT\n\n\t\tmat3 tbn = mat3( normalize( vTangent ), normalize( vBitangent ), normal );\n\n\t#else\n\n\t\tmat3 tbn = getTangentFrame( - vViewPosition, normal,\n\t\t#if defined( USE_NORMALMAP )\n\t\t\tvNormalMapUv\n\t\t#elif defined( USE_CLEARCOAT_NORMALMAP )\n\t\t\tvClearcoatNormalMapUv\n\t\t#else\n\t\t\tvUv\n\t\t#endif\n\t\t);\n\n\t#endif\n\n\t#if defined( DOUBLE_SIDED ) && ! defined( FLAT_SHADED )\n\n\t\ttbn[0] *= faceDirection;\n\t\ttbn[1] *= faceDirection;\n\n\t#endif\n\n#endif\n\n#ifdef USE_CLEARCOAT_NORMALMAP\n\n\t#ifdef USE_TANGENT\n\n\t\tmat3 tbn2 = mat3( normalize( vTangent ), normalize( vBitangent ), normal );\n\n\t#else\n\n\t\tmat3 tbn2 = getTangentFrame( - vViewPosition, normal, vClearcoatNormalMapUv );\n\n\t#endif\n\n\t#if defined( DOUBLE_SIDED ) && ! defined( FLAT_SHADED )\n\n\t\ttbn2[0] *= faceDirection;\n\t\ttbn2[1] *= faceDirection;\n\n\t#endif\n\n#endif\n\n// non perturbed normal for clearcoat among others\n\nvec3 nonPerturbedNormal = normal;\n\n`;\n","export default /* glsl */`\n\n#ifdef USE_NORMALMAP_OBJECTSPACE\n\n\tnormal = texture2D( normalMap, vNormalMapUv ).xyz * 2.0 - 1.0; // overrides both flatShading and attribute normals\n\n\t#ifdef FLIP_SIDED\n\n\t\tnormal = - normal;\n\n\t#endif\n\n\t#ifdef DOUBLE_SIDED\n\n\t\tnormal = normal * faceDirection;\n\n\t#endif\n\n\tnormal = normalize( normalMatrix * normal );\n\n#elif defined( USE_NORMALMAP_TANGENTSPACE )\n\n\tvec3 mapN = texture2D( normalMap, vNormalMapUv ).xyz * 2.0 - 1.0;\n\tmapN.xy *= normalScale;\n\n\tnormal = normalize( tbn * mapN );\n\n#elif defined( USE_BUMPMAP )\n\n\tnormal = perturbNormalArb( - vViewPosition, normal, dHdxy_fwd(), faceDirection );\n\n#endif\n`;\n","export default /* glsl */`\n#ifndef FLAT_SHADED\n\n\tvarying vec3 vNormal;\n\n\t#ifdef USE_TANGENT\n\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\n\t#endif\n\n#endif\n`;\n","export default /* glsl */`\n#ifndef FLAT_SHADED\n\n\tvarying vec3 vNormal;\n\n\t#ifdef USE_TANGENT\n\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\n\t#endif\n\n#endif\n`;\n","export default /* glsl */`\n#ifndef FLAT_SHADED // normal is computed with derivatives when FLAT_SHADED\n\n\tvNormal = normalize( transformedNormal );\n\n\t#ifdef USE_TANGENT\n\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\n\t#endif\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_NORMALMAP\n\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n\n#endif\n\n#ifdef USE_NORMALMAP_OBJECTSPACE\n\n\tuniform mat3 normalMatrix;\n\n#endif\n\n#if ! defined ( USE_TANGENT ) && ( defined ( USE_NORMALMAP_TANGENTSPACE ) || defined ( USE_CLEARCOAT_NORMALMAP ) || defined( USE_ANISOTROPY ) )\n\n\t// Normal Mapping Without Precomputed Tangents\n\t// http://www.thetenthplanet.de/archives/1180\n\n\tmat3 getTangentFrame( vec3 eye_pos, vec3 surf_norm, vec2 uv ) {\n\n\t\tvec3 q0 = dFdx( eye_pos.xyz );\n\t\tvec3 q1 = dFdy( eye_pos.xyz );\n\t\tvec2 st0 = dFdx( uv.st );\n\t\tvec2 st1 = dFdy( uv.st );\n\n\t\tvec3 N = surf_norm; // normalized\n\n\t\tvec3 q1perp = cross( q1, N );\n\t\tvec3 q0perp = cross( N, q0 );\n\n\t\tvec3 T = q1perp * st0.x + q0perp * st1.x;\n\t\tvec3 B = q1perp * st0.y + q0perp * st1.y;\n\n\t\tfloat det = max( dot( T, T ), dot( B, B ) );\n\t\tfloat scale = ( det == 0.0 ) ? 0.0 : inversesqrt( det );\n\n\t\treturn mat3( T * scale, B * scale, N );\n\n\t}\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_CLEARCOAT\n\n\tvec3 clearcoatNormal = nonPerturbedNormal;\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_CLEARCOAT_NORMALMAP\n\n\tvec3 clearcoatMapN = texture2D( clearcoatNormalMap, vClearcoatNormalMapUv ).xyz * 2.0 - 1.0;\n\tclearcoatMapN.xy *= clearcoatNormalScale;\n\n\tclearcoatNormal = normalize( tbn2 * clearcoatMapN );\n\n#endif\n`;\n","export default /* glsl */`\n\n#ifdef USE_CLEARCOATMAP\n\n\tuniform sampler2D clearcoatMap;\n\n#endif\n\n#ifdef USE_CLEARCOAT_NORMALMAP\n\n\tuniform sampler2D clearcoatNormalMap;\n\tuniform vec2 clearcoatNormalScale;\n\n#endif\n\n#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\n\tuniform sampler2D clearcoatRoughnessMap;\n\n#endif\n`;\n","export default /* glsl */`\n\n#ifdef USE_IRIDESCENCEMAP\n\n\tuniform sampler2D iridescenceMap;\n\n#endif\n\n#ifdef USE_IRIDESCENCE_THICKNESSMAP\n\n\tuniform sampler2D iridescenceThicknessMap;\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef OPAQUE\ndiffuseColor.a = 1.0;\n#endif\n\n#ifdef USE_TRANSMISSION\ndiffuseColor.a *= material.transmissionAlpha;\n#endif\n\ngl_FragColor = vec4( outgoingLight, diffuseColor.a );\n`;\n","export default /* glsl */`\nvec3 packNormalToRGB( const in vec3 normal ) {\n\treturn normalize( normal ) * 0.5 + 0.5;\n}\n\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\n\treturn 2.0 * rgb.xyz - 1.0;\n}\n\nconst float PackUpscale = 256. / 255.; // fraction -> 0..1 (including 1)\nconst float UnpackDownscale = 255. / 256.; // 0..1 -> fraction (excluding 1)\n\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\n\nconst float ShiftRight8 = 1. / 256.;\n\nvec4 packDepthToRGBA( const in float v ) {\n\tvec4 r = vec4( fract( v * PackFactors ), v );\n\tr.yzw -= r.xyz * ShiftRight8; // tidy overflow\n\treturn r * PackUpscale;\n}\n\nfloat unpackRGBAToDepth( const in vec4 v ) {\n\treturn dot( v, UnpackFactors );\n}\n\nvec2 packDepthToRG( in highp float v ) {\n\treturn packDepthToRGBA( v ).yx;\n}\n\nfloat unpackRGToDepth( const in highp vec2 v ) {\n\treturn unpackRGBAToDepth( vec4( v.xy, 0.0, 0.0 ) );\n}\n\nvec4 pack2HalfToRGBA( vec2 v ) {\n\tvec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ) );\n\treturn vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w );\n}\n\nvec2 unpackRGBATo2Half( vec4 v ) {\n\treturn vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) );\n}\n\n// NOTE: viewZ, the z-coordinate in camera space, is negative for points in front of the camera\n\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\n\t// -near maps to 0; -far maps to 1\n\treturn ( viewZ + near ) / ( near - far );\n}\n\nfloat orthographicDepthToViewZ( const in float depth, const in float near, const in float far ) {\n\t// maps orthographic depth in [ 0, 1 ] to viewZ\n\treturn depth * ( near - far ) - near;\n}\n\n// NOTE: https://twitter.com/gonnavis/status/1377183786949959682\n\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n\t// -near maps to 0; -far maps to 1\n\treturn ( ( near + viewZ ) * far ) / ( ( far - near ) * viewZ );\n}\n\nfloat perspectiveDepthToViewZ( const in float depth, const in float near, const in float far ) {\n\t// maps perspective depth in [ 0, 1 ] to viewZ\n\treturn ( near * far ) / ( ( far - near ) * depth - far );\n}\n`;\n","export default /* glsl */`\n#ifdef PREMULTIPLIED_ALPHA\n\n\t// Get get normal blending with premultipled, use with CustomBlending, OneFactor, OneMinusSrcAlphaFactor, AddEquation.\n\tgl_FragColor.rgb *= gl_FragColor.a;\n\n#endif\n`;\n","export default /* glsl */`\nvec4 mvPosition = vec4( transformed, 1.0 );\n\n#ifdef USE_INSTANCING\n\n\tmvPosition = instanceMatrix * mvPosition;\n\n#endif\n\nmvPosition = modelViewMatrix * mvPosition;\n\ngl_Position = projectionMatrix * mvPosition;\n`;\n","export default /* glsl */`\n#ifdef DITHERING\n\n\tgl_FragColor.rgb = dithering( gl_FragColor.rgb );\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef DITHERING\n\n\t// based on https://www.shadertoy.com/view/MslGR8\n\tvec3 dithering( vec3 color ) {\n\t\t//Calculate grid position\n\t\tfloat grid_position = rand( gl_FragCoord.xy );\n\n\t\t//Shift the individual colors differently, thus making it even harder to see the dithering pattern\n\t\tvec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );\n\n\t\t//modify shift according to grid position.\n\t\tdither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );\n\n\t\t//shift the color by dither_shift\n\t\treturn color + dither_shift_RGB;\n\t}\n\n#endif\n`;\n","export default /* glsl */`\nfloat roughnessFactor = roughness;\n\n#ifdef USE_ROUGHNESSMAP\n\n\tvec4 texelRoughness = texture2D( roughnessMap, vRoughnessMapUv );\n\n\t// reads channel G, compatible with a combined OcclusionRoughnessMetallic (RGB) texture\n\troughnessFactor *= texelRoughness.g;\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_ROUGHNESSMAP\n\n\tuniform sampler2D roughnessMap;\n\n#endif\n`;\n","export default /* glsl */`\n#if NUM_SPOT_LIGHT_COORDS > 0\n\n\tvarying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ];\n\n#endif\n\n#if NUM_SPOT_LIGHT_MAPS > 0\n\n\tuniform sampler2D spotLightMap[ NUM_SPOT_LIGHT_MAPS ];\n\n#endif\n\n#ifdef USE_SHADOWMAP\n\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\n\t\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\n\t\tstruct DirectionalLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\n\t\tuniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];\n\n\t#endif\n\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\n\t\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ];\n\n\t\tstruct SpotLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\n\t\tuniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];\n\n\t#endif\n\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\n\t\tuniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\n\t\tstruct PointLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t\tfloat shadowCameraNear;\n\t\t\tfloat shadowCameraFar;\n\t\t};\n\n\t\tuniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];\n\n\t#endif\n\n\t/*\n\t#if NUM_RECT_AREA_LIGHTS > 0\n\n\t\t// TODO (abelnation): create uniforms for area light shadows\n\n\t#endif\n\t*/\n\n\tfloat texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\n\n\t\treturn step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );\n\n\t}\n\n\tvec2 texture2DDistribution( sampler2D shadow, vec2 uv ) {\n\n\t\treturn unpackRGBATo2Half( texture2D( shadow, uv ) );\n\n\t}\n\n\tfloat VSMShadow (sampler2D shadow, vec2 uv, float compare ){\n\n\t\tfloat occlusion = 1.0;\n\n\t\tvec2 distribution = texture2DDistribution( shadow, uv );\n\n\t\tfloat hard_shadow = step( compare , distribution.x ); // Hard Shadow\n\n\t\tif (hard_shadow != 1.0 ) {\n\n\t\t\tfloat distance = compare - distribution.x ;\n\t\t\tfloat variance = max( 0.00000, distribution.y * distribution.y );\n\t\t\tfloat softness_probability = variance / (variance + distance * distance ); // Chebeyshevs inequality\n\t\t\tsoftness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 ); // 0.3 reduces light bleed\n\t\t\tocclusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 );\n\n\t\t}\n\t\treturn occlusion;\n\n\t}\n\n\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\n\t\tfloat shadow = 1.0;\n\n\t\tshadowCoord.xyz /= shadowCoord.w;\n\t\tshadowCoord.z += shadowBias;\n\n\t\tbool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0;\n\t\tbool frustumTest = inFrustum && shadowCoord.z <= 1.0;\n\n\t\tif ( frustumTest ) {\n\n\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tfloat dx2 = dx0 / 2.0;\n\t\t\tfloat dy2 = dy0 / 2.0;\n\t\t\tfloat dx3 = dx1 / 2.0;\n\t\t\tfloat dy3 = dy1 / 2.0;\n\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 17.0 );\n\n\t\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx = texelSize.x;\n\t\t\tfloat dy = texelSize.y;\n\n\t\t\tvec2 uv = shadowCoord.xy;\n\t\t\tvec2 f = fract( uv * shadowMapSize + 0.5 );\n\t\t\tuv -= f * texelSize;\n\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, uv, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ),\n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ),\n\t\t\t\t\t f.x ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ),\n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ),\n\t\t\t\t\t f.x ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ),\n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t f.y ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ),\n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t f.y ) +\n\t\t\t\tmix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ),\n\t\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ),\n\t\t\t\t\t\t f.x ),\n\t\t\t\t\t mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t\t f.x ),\n\t\t\t\t\t f.y )\n\t\t\t) * ( 1.0 / 9.0 );\n\n\t\t#elif defined( SHADOWMAP_TYPE_VSM )\n\n\t\t\tshadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z );\n\n\t\t#else // no percentage-closer filtering:\n\n\t\t\tshadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\n\n\t\t#endif\n\n\t\t}\n\n\t\treturn shadow;\n\n\t}\n\n\t// cubeToUV() maps a 3D direction vector suitable for cube texture mapping to a 2D\n\t// vector suitable for 2D texture mapping. This code uses the following layout for the\n\t// 2D texture:\n\t//\n\t// xzXZ\n\t// y Y\n\t//\n\t// Y - Positive y direction\n\t// y - Negative y direction\n\t// X - Positive x direction\n\t// x - Negative x direction\n\t// Z - Positive z direction\n\t// z - Negative z direction\n\t//\n\t// Source and test bed:\n\t// https://gist.github.com/tschw/da10c43c467ce8afd0c4\n\n\tvec2 cubeToUV( vec3 v, float texelSizeY ) {\n\n\t\t// Number of texels to avoid at the edge of each square\n\n\t\tvec3 absV = abs( v );\n\n\t\t// Intersect unit cube\n\n\t\tfloat scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n\t\tabsV *= scaleToCube;\n\n\t\t// Apply scale to avoid seams\n\n\t\t// two texels less per square (one texel will do for NEAREST)\n\t\tv *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\n\t\t// Unwrap\n\n\t\t// space: -1 ... 1 range for each square\n\t\t//\n\t\t// #X##\t\tdim := ( 4 , 2 )\n\t\t// # #\t\tcenter := ( 1 , 1 )\n\n\t\tvec2 planar = v.xy;\n\n\t\tfloat almostATexel = 1.5 * texelSizeY;\n\t\tfloat almostOne = 1.0 - almostATexel;\n\n\t\tif ( absV.z >= almostOne ) {\n\n\t\t\tif ( v.z > 0.0 )\n\t\t\t\tplanar.x = 4.0 - v.x;\n\n\t\t} else if ( absV.x >= almostOne ) {\n\n\t\t\tfloat signX = sign( v.x );\n\t\t\tplanar.x = v.z * signX + 2.0 * signX;\n\n\t\t} else if ( absV.y >= almostOne ) {\n\n\t\t\tfloat signY = sign( v.y );\n\t\t\tplanar.x = v.x + 2.0 * signY + 2.0;\n\t\t\tplanar.y = v.z * signY - 2.0;\n\n\t\t}\n\n\t\t// Transform to UV space\n\n\t\t// scale := 0.5 / dim\n\t\t// translate := ( center + 0.5 ) / dim\n\t\treturn vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\n\t}\n\n\tfloat getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {\n\n\t\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\n\n\t\t// for point lights, the uniform @vShadowCoord is re-purposed to hold\n\t\t// the vector from the light to the world-space position of the fragment.\n\t\tvec3 lightToPosition = shadowCoord.xyz;\n\n\t\t// dp = normalized distance from light to fragment position\n\t\tfloat dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear ); // need to clamp?\n\t\tdp += shadowBias;\n\n\t\t// bd3D = base direction 3D\n\t\tvec3 bd3D = normalize( lightToPosition );\n\n\t\t#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM )\n\n\t\t\tvec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\n\n\t\t\treturn (\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\n\t\t\t) * ( 1.0 / 9.0 );\n\n\t\t#else // no percentage-closer filtering\n\n\t\t\treturn texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\n\n\t\t#endif\n\n\t}\n\n#endif\n`;\n","export default /* glsl */`\n\n#if NUM_SPOT_LIGHT_COORDS > 0\n\n\tuniform mat4 spotLightMatrix[ NUM_SPOT_LIGHT_COORDS ];\n\tvarying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ];\n\n#endif\n\n#ifdef USE_SHADOWMAP\n\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\n\t\tuniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\n\t\tstruct DirectionalLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\n\t\tuniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];\n\n\t#endif\n\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\n\t\tstruct SpotLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\n\t\tuniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];\n\n\t#endif\n\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\n\t\tuniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\n\t\tstruct PointLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t\tfloat shadowCameraNear;\n\t\t\tfloat shadowCameraFar;\n\t\t};\n\n\t\tuniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];\n\n\t#endif\n\n\t/*\n\t#if NUM_RECT_AREA_LIGHTS > 0\n\n\t\t// TODO (abelnation): uniforms for area light shadows\n\n\t#endif\n\t*/\n\n#endif\n`;\n","export default /* glsl */`\n\n#if ( defined( USE_SHADOWMAP ) && ( NUM_DIR_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0 ) ) || ( NUM_SPOT_LIGHT_COORDS > 0 )\n\n\t// Offsetting the position used for querying occlusion along the world normal can be used to reduce shadow acne.\n\tvec3 shadowWorldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\tvec4 shadowWorldPosition;\n\n#endif\n\n#if defined( USE_SHADOWMAP )\n\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\n\t\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 );\n\t\t\tvDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition;\n\n\t\t}\n\t\t#pragma unroll_loop_end\n\n\t#endif\n\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\n\t\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 );\n\t\t\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition;\n\n\t\t}\n\t\t#pragma unroll_loop_end\n\n\t#endif\n\n\t/*\n\t#if NUM_RECT_AREA_LIGHTS > 0\n\n\t\t// TODO (abelnation): update vAreaShadowCoord with area light info\n\n\t#endif\n\t*/\n\n#endif\n\n// spot lights can be evaluated without active shadow mapping (when SpotLight.map is used)\n\n#if NUM_SPOT_LIGHT_COORDS > 0\n\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_COORDS; i ++ ) {\n\n\t\tshadowWorldPosition = worldPosition;\n\t\t#if ( defined( USE_SHADOWMAP ) && UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\t\tshadowWorldPosition.xyz += shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias;\n\t\t#endif\n\t\tvSpotLightCoord[ i ] = spotLightMatrix[ i ] * shadowWorldPosition;\n\n\t}\n\t#pragma unroll_loop_end\n\n#endif\n\n\n`;\n","export default /* glsl */`\nfloat getShadowMask() {\n\n\tfloat shadow = 1.0;\n\n\t#ifdef USE_SHADOWMAP\n\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\n\tDirectionalLightShadow directionalLight;\n\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\n\t\tdirectionalLight = directionalLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\n\t}\n\t#pragma unroll_loop_end\n\n\t#endif\n\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\n\tSpotLightShadow spotLight;\n\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) {\n\n\t\tspotLight = spotLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotLightCoord[ i ] ) : 1.0;\n\n\t}\n\t#pragma unroll_loop_end\n\n\t#endif\n\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\n\tPointLightShadow pointLight;\n\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\n\t\tpointLight = pointLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\n\t}\n\t#pragma unroll_loop_end\n\n\t#endif\n\n\t/*\n\t#if NUM_RECT_AREA_LIGHTS > 0\n\n\t\t// TODO (abelnation): update shadow for Area light\n\n\t#endif\n\t*/\n\n\t#endif\n\n\treturn shadow;\n\n}\n`;\n","export default /* glsl */`\n#ifdef USE_SKINNING\n\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_SKINNING\n\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\n\tuniform highp sampler2D boneTexture;\n\tuniform int boneTextureSize;\n\n\tmat4 getBoneMatrix( const in float i ) {\n\n\t\tfloat j = i * 4.0;\n\t\tfloat x = mod( j, float( boneTextureSize ) );\n\t\tfloat y = floor( j / float( boneTextureSize ) );\n\n\t\tfloat dx = 1.0 / float( boneTextureSize );\n\t\tfloat dy = 1.0 / float( boneTextureSize );\n\n\t\ty = dy * ( y + 0.5 );\n\n\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\n\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\n\t\treturn bone;\n\n\t}\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_SKINNING\n\n\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\n\ttransformed = ( bindMatrixInverse * skinned ).xyz;\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_SKINNING\n\n\tmat4 skinMatrix = mat4( 0.0 );\n\tskinMatrix += skinWeight.x * boneMatX;\n\tskinMatrix += skinWeight.y * boneMatY;\n\tskinMatrix += skinWeight.z * boneMatZ;\n\tskinMatrix += skinWeight.w * boneMatW;\n\tskinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\n\tobjectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n\n\t#ifdef USE_TANGENT\n\n\t\tobjectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\n\t#endif\n\n#endif\n`;\n","export default /* glsl */`\nfloat specularStrength;\n\n#ifdef USE_SPECULARMAP\n\n\tvec4 texelSpecular = texture2D( specularMap, vSpecularMapUv );\n\tspecularStrength = texelSpecular.r;\n\n#else\n\n\tspecularStrength = 1.0;\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_SPECULARMAP\n\n\tuniform sampler2D specularMap;\n\n#endif\n`;\n","export default /* glsl */`\n#if defined( TONE_MAPPING )\n\n\tgl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n\n#endif\n`;\n","export default /* glsl */`\n#ifndef saturate\n// may have defined saturate() already\n#define saturate( a ) clamp( a, 0.0, 1.0 )\n#endif\n\nuniform float toneMappingExposure;\n\n// exposure only\nvec3 LinearToneMapping( vec3 color ) {\n\n\treturn saturate( toneMappingExposure * color );\n\n}\n\n// source: https://www.cs.utah.edu/docs/techreports/2002/pdf/UUCS-02-001.pdf\nvec3 ReinhardToneMapping( vec3 color ) {\n\n\tcolor *= toneMappingExposure;\n\treturn saturate( color / ( vec3( 1.0 ) + color ) );\n\n}\n\n// source: http://filmicworlds.com/blog/filmic-tonemapping-operators/\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n\n\t// optimized filmic operator by Jim Hejl and Richard Burgess-Dawson\n\tcolor *= toneMappingExposure;\n\tcolor = max( vec3( 0.0 ), color - 0.004 );\n\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n\n}\n\n// source: https://github.com/selfshadow/ltc_code/blob/master/webgl/shaders/ltc/ltc_blit.fs\nvec3 RRTAndODTFit( vec3 v ) {\n\n\tvec3 a = v * ( v + 0.0245786 ) - 0.000090537;\n\tvec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081;\n\treturn a / b;\n\n}\n\n// this implementation of ACES is modified to accommodate a brighter viewing environment.\n// the scale factor of 1/0.6 is subjective. see discussion in #19621.\n\nvec3 ACESFilmicToneMapping( vec3 color ) {\n\n\t// sRGB => XYZ => D65_2_D60 => AP1 => RRT_SAT\n\tconst mat3 ACESInputMat = mat3(\n\t\tvec3( 0.59719, 0.07600, 0.02840 ), // transposed from source\n\t\tvec3( 0.35458, 0.90834, 0.13383 ),\n\t\tvec3( 0.04823, 0.01566, 0.83777 )\n\t);\n\n\t// ODT_SAT => XYZ => D60_2_D65 => sRGB\n\tconst mat3 ACESOutputMat = mat3(\n\t\tvec3( 1.60475, -0.10208, -0.00327 ), // transposed from source\n\t\tvec3( -0.53108, 1.10813, -0.07276 ),\n\t\tvec3( -0.07367, -0.00605, 1.07602 )\n\t);\n\n\tcolor *= toneMappingExposure / 0.6;\n\n\tcolor = ACESInputMat * color;\n\n\t// Apply RRT and ODT\n\tcolor = RRTAndODTFit( color );\n\n\tcolor = ACESOutputMat * color;\n\n\t// Clamp to [0, 1]\n\treturn saturate( color );\n\n}\n\nvec3 CustomToneMapping( vec3 color ) { return color; }\n`;\n","export default /* glsl */`\n#ifdef USE_TRANSMISSION\n\n\tmaterial.transmission = transmission;\n\tmaterial.transmissionAlpha = 1.0;\n\tmaterial.thickness = thickness;\n\tmaterial.attenuationDistance = attenuationDistance;\n\tmaterial.attenuationColor = attenuationColor;\n\n\t#ifdef USE_TRANSMISSIONMAP\n\n\t\tmaterial.transmission *= texture2D( transmissionMap, vTransmissionMapUv ).r;\n\n\t#endif\n\n\t#ifdef USE_THICKNESSMAP\n\n\t\tmaterial.thickness *= texture2D( thicknessMap, vThicknessMapUv ).g;\n\n\t#endif\n\n\tvec3 pos = vWorldPosition;\n\tvec3 v = normalize( cameraPosition - pos );\n\tvec3 n = inverseTransformDirection( normal, viewMatrix );\n\n\tvec4 transmitted = getIBLVolumeRefraction(\n\t\tn, v, material.roughness, material.diffuseColor, material.specularColor, material.specularF90,\n\t\tpos, modelMatrix, viewMatrix, projectionMatrix, material.ior, material.thickness,\n\t\tmaterial.attenuationColor, material.attenuationDistance );\n\n\tmaterial.transmissionAlpha = mix( material.transmissionAlpha, transmitted.a, material.transmission );\n\n\ttotalDiffuse = mix( totalDiffuse, transmitted.rgb, material.transmission );\n\n#endif\n`;\n","export default /* glsl */`\n#ifdef USE_TRANSMISSION\n\n\t// Transmission code is based on glTF-Sampler-Viewer\n\t// https://github.com/KhronosGroup/glTF-Sample-Viewer\n\n\tuniform float transmission;\n\tuniform float thickness;\n\tuniform float attenuationDistance;\n\tuniform vec3 attenuationColor;\n\n\t#ifdef USE_TRANSMISSIONMAP\n\n\t\tuniform sampler2D transmissionMap;\n\n\t#endif\n\n\t#ifdef USE_THICKNESSMAP\n\n\t\tuniform sampler2D thicknessMap;\n\n\t#endif\n\n\tuniform vec2 transmissionSamplerSize;\n\tuniform sampler2D transmissionSamplerMap;\n\n\tuniform mat4 modelMatrix;\n\tuniform mat4 projectionMatrix;\n\n\tvarying vec3 vWorldPosition;\n\n\t// Mipped Bicubic Texture Filtering by N8\n\t// https://www.shadertoy.com/view/Dl2SDW\n\n\tfloat w0( float a ) {\n\n\t\treturn ( 1.0 / 6.0 ) * ( a * ( a * ( - a + 3.0 ) - 3.0 ) + 1.0 );\n\n\t}\n\n\tfloat w1( float a ) {\n\n\t\treturn ( 1.0 / 6.0 ) * ( a * a * ( 3.0 * a - 6.0 ) + 4.0 );\n\n\t}\n\n\tfloat w2( float a ){\n\n\t\treturn ( 1.0 / 6.0 ) * ( a * ( a * ( - 3.0 * a + 3.0 ) + 3.0 ) + 1.0 );\n\n\t}\n\n\tfloat w3( float a ) {\n\n\t\treturn ( 1.0 / 6.0 ) * ( a * a * a );\n\n\t}\n\n\t// g0 and g1 are the two amplitude functions\n\tfloat g0( float a ) {\n\n\t\treturn w0( a ) + w1( a );\n\n\t}\n\n\tfloat g1( float a ) {\n\n\t\treturn w2( a ) + w3( a );\n\n\t}\n\n\t// h0 and h1 are the two offset functions\n\tfloat h0( float a ) {\n\n\t\treturn - 1.0 + w1( a ) / ( w0( a ) + w1( a ) );\n\n\t}\n\n\tfloat h1( float a ) {\n\n\t\treturn 1.0 + w3( a ) / ( w2( a ) + w3( a ) );\n\n\t}\n\n\tvec4 bicubic( sampler2D tex, vec2 uv, vec4 texelSize, float lod ) {\n\n\t\tuv = uv * texelSize.zw + 0.5;\n\n\t\tvec2 iuv = floor( uv );\n\t\tvec2 fuv = fract( uv );\n\n\t\tfloat g0x = g0( fuv.x );\n\t\tfloat g1x = g1( fuv.x );\n\t\tfloat h0x = h0( fuv.x );\n\t\tfloat h1x = h1( fuv.x );\n\t\tfloat h0y = h0( fuv.y );\n\t\tfloat h1y = h1( fuv.y );\n\n\t\tvec2 p0 = ( vec2( iuv.x + h0x, iuv.y + h0y ) - 0.5 ) * texelSize.xy;\n\t\tvec2 p1 = ( vec2( iuv.x + h1x, iuv.y + h0y ) - 0.5 ) * texelSize.xy;\n\t\tvec2 p2 = ( vec2( iuv.x + h0x, iuv.y + h1y ) - 0.5 ) * texelSize.xy;\n\t\tvec2 p3 = ( vec2( iuv.x + h1x, iuv.y + h1y ) - 0.5 ) * texelSize.xy;\n\n\t\treturn g0( fuv.y ) * ( g0x * textureLod( tex, p0, lod ) + g1x * textureLod( tex, p1, lod ) ) +\n\t\t\tg1( fuv.y ) * ( g0x * textureLod( tex, p2, lod ) + g1x * textureLod( tex, p3, lod ) );\n\n\t}\n\n\tvec4 textureBicubic( sampler2D sampler, vec2 uv, float lod ) {\n\n\t\tvec2 fLodSize = vec2( textureSize( sampler, int( lod ) ) );\n\t\tvec2 cLodSize = vec2( textureSize( sampler, int( lod + 1.0 ) ) );\n\t\tvec2 fLodSizeInv = 1.0 / fLodSize;\n\t\tvec2 cLodSizeInv = 1.0 / cLodSize;\n\t\tvec4 fSample = bicubic( sampler, uv, vec4( fLodSizeInv, fLodSize ), floor( lod ) );\n\t\tvec4 cSample = bicubic( sampler, uv, vec4( cLodSizeInv, cLodSize ), ceil( lod ) );\n\t\treturn mix( fSample, cSample, fract( lod ) );\n\n\t}\n\n\tvec3 getVolumeTransmissionRay( const in vec3 n, const in vec3 v, const in float thickness, const in float ior, const in mat4 modelMatrix ) {\n\n\t\t// Direction of refracted light.\n\t\tvec3 refractionVector = refract( - v, normalize( n ), 1.0 / ior );\n\n\t\t// Compute rotation-independant scaling of the model matrix.\n\t\tvec3 modelScale;\n\t\tmodelScale.x = length( vec3( modelMatrix[ 0 ].xyz ) );\n\t\tmodelScale.y = length( vec3( modelMatrix[ 1 ].xyz ) );\n\t\tmodelScale.z = length( vec3( modelMatrix[ 2 ].xyz ) );\n\n\t\t// The thickness is specified in local space.\n\t\treturn normalize( refractionVector ) * thickness * modelScale;\n\n\t}\n\n\tfloat applyIorToRoughness( const in float roughness, const in float ior ) {\n\n\t\t// Scale roughness with IOR so that an IOR of 1.0 results in no microfacet refraction and\n\t\t// an IOR of 1.5 results in the default amount of microfacet refraction.\n\t\treturn roughness * clamp( ior * 2.0 - 2.0, 0.0, 1.0 );\n\n\t}\n\n\tvec4 getTransmissionSample( const in vec2 fragCoord, const in float roughness, const in float ior ) {\n\n\t\tfloat lod = log2( transmissionSamplerSize.x ) * applyIorToRoughness( roughness, ior );\n\t\treturn textureBicubic( transmissionSamplerMap, fragCoord.xy, lod );\n\n\t}\n\n\tvec3 volumeAttenuation( const in float transmissionDistance, const in vec3 attenuationColor, const in float attenuationDistance ) {\n\n\t\tif ( isinf( attenuationDistance ) ) {\n\n\t\t\t// Attenuation distance is +∞, i.e. the transmitted color is not attenuated at all.\n\t\t\treturn vec3( 1.0 );\n\n\t\t} else {\n\n\t\t\t// Compute light attenuation using Beer's law.\n\t\t\tvec3 attenuationCoefficient = -log( attenuationColor ) / attenuationDistance;\n\t\t\tvec3 transmittance = exp( - attenuationCoefficient * transmissionDistance ); // Beer's law\n\t\t\treturn transmittance;\n\n\t\t}\n\n\t}\n\n\tvec4 getIBLVolumeRefraction( const in vec3 n, const in vec3 v, const in float roughness, const in vec3 diffuseColor,\n\t\tconst in vec3 specularColor, const in float specularF90, const in vec3 position, const in mat4 modelMatrix,\n\t\tconst in mat4 viewMatrix, const in mat4 projMatrix, const in float ior, const in float thickness,\n\t\tconst in vec3 attenuationColor, const in float attenuationDistance ) {\n\n\t\tvec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, ior, modelMatrix );\n\t\tvec3 refractedRayExit = position + transmissionRay;\n\n\t\t// Project refracted vector on the framebuffer, while mapping to normalized device coordinates.\n\t\tvec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 );\n\t\tvec2 refractionCoords = ndcPos.xy / ndcPos.w;\n\t\trefractionCoords += 1.0;\n\t\trefractionCoords /= 2.0;\n\n\t\t// Sample framebuffer to get pixel the refracted ray hits.\n\t\tvec4 transmittedLight = getTransmissionSample( refractionCoords, roughness, ior );\n\n\t\tvec3 transmittance = diffuseColor * volumeAttenuation( length( transmissionRay ), attenuationColor, attenuationDistance );\n\t\tvec3 attenuatedColor = transmittance * transmittedLight.rgb;\n\n\t\t// Get the specular component.\n\t\tvec3 F = EnvironmentBRDF( n, v, specularColor, specularF90, roughness );\n\n\t\t// As less light is transmitted, the opacity should be increased. This simple approximation does a decent job \n\t\t// of modulating a CSS background, and has no effect when the buffer is opaque, due to a solid object or clear color.\n\t\tfloat transmittanceFactor = ( transmittance.r + transmittance.g + transmittance.b ) / 3.0;\n\n\t\treturn vec4( ( 1.0 - F ) * attenuatedColor, 1.0 - ( 1.0 - transmittedLight.a ) * transmittanceFactor );\n\n\t}\n#endif\n`;\n","export default /* glsl */`\n#if defined( USE_UV ) || defined( USE_ANISOTROPY )\n\n\tvarying vec2 vUv;\n\n#endif\n#ifdef USE_MAP\n\n\tvarying vec2 vMapUv;\n\n#endif\n#ifdef USE_ALPHAMAP\n\n\tvarying vec2 vAlphaMapUv;\n\n#endif\n#ifdef USE_LIGHTMAP\n\n\tvarying vec2 vLightMapUv;\n\n#endif\n#ifdef USE_AOMAP\n\n\tvarying vec2 vAoMapUv;\n\n#endif\n#ifdef USE_BUMPMAP\n\n\tvarying vec2 vBumpMapUv;\n\n#endif\n#ifdef USE_NORMALMAP\n\n\tvarying vec2 vNormalMapUv;\n\n#endif\n#ifdef USE_EMISSIVEMAP\n\n\tvarying vec2 vEmissiveMapUv;\n\n#endif\n#ifdef USE_METALNESSMAP\n\n\tvarying vec2 vMetalnessMapUv;\n\n#endif\n#ifdef USE_ROUGHNESSMAP\n\n\tvarying vec2 vRoughnessMapUv;\n\n#endif\n#ifdef USE_ANISOTROPYMAP\n\n\tvarying vec2 vAnisotropyMapUv;\n\n#endif\n#ifdef USE_CLEARCOATMAP\n\n\tvarying vec2 vClearcoatMapUv;\n\n#endif\n#ifdef USE_CLEARCOAT_NORMALMAP\n\n\tvarying vec2 vClearcoatNormalMapUv;\n\n#endif\n#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\n\tvarying vec2 vClearcoatRoughnessMapUv;\n\n#endif\n#ifdef USE_IRIDESCENCEMAP\n\n\tvarying vec2 vIridescenceMapUv;\n\n#endif\n#ifdef USE_IRIDESCENCE_THICKNESSMAP\n\n\tvarying vec2 vIridescenceThicknessMapUv;\n\n#endif\n#ifdef USE_SHEEN_COLORMAP\n\n\tvarying vec2 vSheenColorMapUv;\n\n#endif\n#ifdef USE_SHEEN_ROUGHNESSMAP\n\n\tvarying vec2 vSheenRoughnessMapUv;\n\n#endif\n#ifdef USE_SPECULARMAP\n\n\tvarying vec2 vSpecularMapUv;\n\n#endif\n#ifdef USE_SPECULAR_COLORMAP\n\n\tvarying vec2 vSpecularColorMapUv;\n\n#endif\n#ifdef USE_SPECULAR_INTENSITYMAP\n\n\tvarying vec2 vSpecularIntensityMapUv;\n\n#endif\n#ifdef USE_TRANSMISSIONMAP\n\n\tuniform mat3 transmissionMapTransform;\n\tvarying vec2 vTransmissionMapUv;\n\n#endif\n#ifdef USE_THICKNESSMAP\n\n\tuniform mat3 thicknessMapTransform;\n\tvarying vec2 vThicknessMapUv;\n\n#endif\n`;\n","export default /* glsl */`\n#if defined( USE_UV ) || defined( USE_ANISOTROPY )\n\n\tvarying vec2 vUv;\n\n#endif\n#ifdef USE_MAP\n\n\tuniform mat3 mapTransform;\n\tvarying vec2 vMapUv;\n\n#endif\n#ifdef USE_ALPHAMAP\n\n\tuniform mat3 alphaMapTransform;\n\tvarying vec2 vAlphaMapUv;\n\n#endif\n#ifdef USE_LIGHTMAP\n\n\tuniform mat3 lightMapTransform;\n\tvarying vec2 vLightMapUv;\n\n#endif\n#ifdef USE_AOMAP\n\n\tuniform mat3 aoMapTransform;\n\tvarying vec2 vAoMapUv;\n\n#endif\n#ifdef USE_BUMPMAP\n\n\tuniform mat3 bumpMapTransform;\n\tvarying vec2 vBumpMapUv;\n\n#endif\n#ifdef USE_NORMALMAP\n\n\tuniform mat3 normalMapTransform;\n\tvarying vec2 vNormalMapUv;\n\n#endif\n#ifdef USE_DISPLACEMENTMAP\n\n\tuniform mat3 displacementMapTransform;\n\tvarying vec2 vDisplacementMapUv;\n\n#endif\n#ifdef USE_EMISSIVEMAP\n\n\tuniform mat3 emissiveMapTransform;\n\tvarying vec2 vEmissiveMapUv;\n\n#endif\n#ifdef USE_METALNESSMAP\n\n\tuniform mat3 metalnessMapTransform;\n\tvarying vec2 vMetalnessMapUv;\n\n#endif\n#ifdef USE_ROUGHNESSMAP\n\n\tuniform mat3 roughnessMapTransform;\n\tvarying vec2 vRoughnessMapUv;\n\n#endif\n#ifdef USE_ANISOTROPYMAP\n\n\tuniform mat3 anisotropyMapTransform;\n\tvarying vec2 vAnisotropyMapUv;\n\n#endif\n#ifdef USE_CLEARCOATMAP\n\n\tuniform mat3 clearcoatMapTransform;\n\tvarying vec2 vClearcoatMapUv;\n\n#endif\n#ifdef USE_CLEARCOAT_NORMALMAP\n\n\tuniform mat3 clearcoatNormalMapTransform;\n\tvarying vec2 vClearcoatNormalMapUv;\n\n#endif\n#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\n\tuniform mat3 clearcoatRoughnessMapTransform;\n\tvarying vec2 vClearcoatRoughnessMapUv;\n\n#endif\n#ifdef USE_SHEEN_COLORMAP\n\n\tuniform mat3 sheenColorMapTransform;\n\tvarying vec2 vSheenColorMapUv;\n\n#endif\n#ifdef USE_SHEEN_ROUGHNESSMAP\n\n\tuniform mat3 sheenRoughnessMapTransform;\n\tvarying vec2 vSheenRoughnessMapUv;\n\n#endif\n#ifdef USE_IRIDESCENCEMAP\n\n\tuniform mat3 iridescenceMapTransform;\n\tvarying vec2 vIridescenceMapUv;\n\n#endif\n#ifdef USE_IRIDESCENCE_THICKNESSMAP\n\n\tuniform mat3 iridescenceThicknessMapTransform;\n\tvarying vec2 vIridescenceThicknessMapUv;\n\n#endif\n#ifdef USE_SPECULARMAP\n\n\tuniform mat3 specularMapTransform;\n\tvarying vec2 vSpecularMapUv;\n\n#endif\n#ifdef USE_SPECULAR_COLORMAP\n\n\tuniform mat3 specularColorMapTransform;\n\tvarying vec2 vSpecularColorMapUv;\n\n#endif\n#ifdef USE_SPECULAR_INTENSITYMAP\n\n\tuniform mat3 specularIntensityMapTransform;\n\tvarying vec2 vSpecularIntensityMapUv;\n\n#endif\n#ifdef USE_TRANSMISSIONMAP\n\n\tuniform mat3 transmissionMapTransform;\n\tvarying vec2 vTransmissionMapUv;\n\n#endif\n#ifdef USE_THICKNESSMAP\n\n\tuniform mat3 thicknessMapTransform;\n\tvarying vec2 vThicknessMapUv;\n\n#endif\n`;\n","export default /* glsl */`\n#if defined( USE_UV ) || defined( USE_ANISOTROPY )\n\n\tvUv = vec3( uv, 1 ).xy;\n\n#endif\n#ifdef USE_MAP\n\n\tvMapUv = ( mapTransform * vec3( MAP_UV, 1 ) ).xy;\n\n#endif\n#ifdef USE_ALPHAMAP\n\n\tvAlphaMapUv = ( alphaMapTransform * vec3( ALPHAMAP_UV, 1 ) ).xy;\n\n#endif\n#ifdef USE_LIGHTMAP\n\n\tvLightMapUv = ( lightMapTransform * vec3( LIGHTMAP_UV, 1 ) ).xy;\n\n#endif\n#ifdef USE_AOMAP\n\n\tvAoMapUv = ( aoMapTransform * vec3( AOMAP_UV, 1 ) ).xy;\n\n#endif\n#ifdef USE_BUMPMAP\n\n\tvBumpMapUv = ( bumpMapTransform * vec3( BUMPMAP_UV, 1 ) ).xy;\n\n#endif\n#ifdef USE_NORMALMAP\n\n\tvNormalMapUv = ( normalMapTransform * vec3( NORMALMAP_UV, 1 ) ).xy;\n\n#endif\n#ifdef USE_DISPLACEMENTMAP\n\n\tvDisplacementMapUv = ( displacementMapTransform * vec3( DISPLACEMENTMAP_UV, 1 ) ).xy;\n\n#endif\n#ifdef USE_EMISSIVEMAP\n\n\tvEmissiveMapUv = ( emissiveMapTransform * vec3( EMISSIVEMAP_UV, 1 ) ).xy;\n\n#endif\n#ifdef USE_METALNESSMAP\n\n\tvMetalnessMapUv = ( metalnessMapTransform * vec3( METALNESSMAP_UV, 1 ) ).xy;\n\n#endif\n#ifdef USE_ROUGHNESSMAP\n\n\tvRoughnessMapUv = ( roughnessMapTransform * vec3( ROUGHNESSMAP_UV, 1 ) ).xy;\n\n#endif\n#ifdef USE_ANISOTROPYMAP\n\n\tvAnisotropyMapUv = ( anisotropyMapTransform * vec3( ANISOTROPYMAP_UV, 1 ) ).xy;\n\n#endif\n#ifdef USE_CLEARCOATMAP\n\n\tvClearcoatMapUv = ( clearcoatMapTransform * vec3( CLEARCOATMAP_UV, 1 ) ).xy;\n\n#endif\n#ifdef USE_CLEARCOAT_NORMALMAP\n\n\tvClearcoatNormalMapUv = ( clearcoatNormalMapTransform * vec3( CLEARCOAT_NORMALMAP_UV, 1 ) ).xy;\n\n#endif\n#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\n\tvClearcoatRoughnessMapUv = ( clearcoatRoughnessMapTransform * vec3( CLEARCOAT_ROUGHNESSMAP_UV, 1 ) ).xy;\n\n#endif\n#ifdef USE_IRIDESCENCEMAP\n\n\tvIridescenceMapUv = ( iridescenceMapTransform * vec3( IRIDESCENCEMAP_UV, 1 ) ).xy;\n\n#endif\n#ifdef USE_IRIDESCENCE_THICKNESSMAP\n\n\tvIridescenceThicknessMapUv = ( iridescenceThicknessMapTransform * vec3( IRIDESCENCE_THICKNESSMAP_UV, 1 ) ).xy;\n\n#endif\n#ifdef USE_SHEEN_COLORMAP\n\n\tvSheenColorMapUv = ( sheenColorMapTransform * vec3( SHEEN_COLORMAP_UV, 1 ) ).xy;\n\n#endif\n#ifdef USE_SHEEN_ROUGHNESSMAP\n\n\tvSheenRoughnessMapUv = ( sheenRoughnessMapTransform * vec3( SHEEN_ROUGHNESSMAP_UV, 1 ) ).xy;\n\n#endif\n#ifdef USE_SPECULARMAP\n\n\tvSpecularMapUv = ( specularMapTransform * vec3( SPECULARMAP_UV, 1 ) ).xy;\n\n#endif\n#ifdef USE_SPECULAR_COLORMAP\n\n\tvSpecularColorMapUv = ( specularColorMapTransform * vec3( SPECULAR_COLORMAP_UV, 1 ) ).xy;\n\n#endif\n#ifdef USE_SPECULAR_INTENSITYMAP\n\n\tvSpecularIntensityMapUv = ( specularIntensityMapTransform * vec3( SPECULAR_INTENSITYMAP_UV, 1 ) ).xy;\n\n#endif\n#ifdef USE_TRANSMISSIONMAP\n\n\tvTransmissionMapUv = ( transmissionMapTransform * vec3( TRANSMISSIONMAP_UV, 1 ) ).xy;\n\n#endif\n#ifdef USE_THICKNESSMAP\n\n\tvThicknessMapUv = ( thicknessMapTransform * vec3( THICKNESSMAP_UV, 1 ) ).xy;\n\n#endif\n`;\n","export default /* glsl */`\n#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION ) || NUM_SPOT_LIGHT_COORDS > 0\n\n\tvec4 worldPosition = vec4( transformed, 1.0 );\n\n\t#ifdef USE_INSTANCING\n\n\t\tworldPosition = instanceMatrix * worldPosition;\n\n\t#endif\n\n\tworldPosition = modelMatrix * worldPosition;\n\n#endif\n`;\n","export const vertex = /* glsl */`\nvarying vec3 vWorldDirection;\n\n#include \n\nvoid main() {\n\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\n\t#include \n\t#include \n\n\tgl_Position.z = gl_Position.w; // set z to camera.far\n\n}\n`;\n\nexport const fragment = /* glsl */`\n\n#ifdef ENVMAP_TYPE_CUBE\n\n\tuniform samplerCube envMap;\n\n#elif defined( ENVMAP_TYPE_CUBE_UV )\n\n\tuniform sampler2D envMap;\n\n#endif\n\nuniform float flipEnvMap;\nuniform float backgroundBlurriness;\nuniform float backgroundIntensity;\n\nvarying vec3 vWorldDirection;\n\n#include \n\nvoid main() {\n\n\t#ifdef ENVMAP_TYPE_CUBE\n\n\t\tvec4 texColor = textureCube( envMap, vec3( flipEnvMap * vWorldDirection.x, vWorldDirection.yz ) );\n\n\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\n\t\tvec4 texColor = textureCubeUV( envMap, vWorldDirection, backgroundBlurriness );\n\n\t#else\n\n\t\tvec4 texColor = vec4( 0.0, 0.0, 0.0, 1.0 );\n\n\t#endif\n\n\ttexColor.rgb *= backgroundIntensity;\n\n\tgl_FragColor = texColor;\n\n\t#include \n\t#include \n\n}\n`;\n","export const vertex = /* glsl */`\nvarying vec3 vWorldDirection;\n\n#include \n\nvoid main() {\n\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\n\t#include \n\t#include \n\n\tgl_Position.z = gl_Position.w; // set z to camera.far\n\n}\n`;\n\nexport const fragment = /* glsl */`\nuniform samplerCube tCube;\nuniform float tFlip;\nuniform float opacity;\n\nvarying vec3 vWorldDirection;\n\nvoid main() {\n\n\tvec4 texColor = textureCube( tCube, vec3( tFlip * vWorldDirection.x, vWorldDirection.yz ) );\n\n\tgl_FragColor = texColor;\n\tgl_FragColor.a *= opacity;\n\n\t#include \n\t#include \n\n}\n`;\n","export const vertex = /* glsl */`\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n\n// This is used for computing an equivalent of gl_FragCoord.z that is as high precision as possible.\n// Some platforms compute gl_FragCoord at a lower precision which makes the manually computed value better for\n// depth-based postprocessing effects. Reproduced on iPad with A10 processor / iPadOS 13.3.1.\nvarying vec2 vHighPrecisionZW;\n\nvoid main() {\n\n\t#include \n\n\t#include \n\n\t#ifdef USE_DISPLACEMENTMAP\n\n\t\t#include \n\t\t#include \n\t\t#include \n\n\t#endif\n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n\tvHighPrecisionZW = gl_Position.zw;\n\n}\n`;\n\nexport const fragment = /* glsl */`\n#if DEPTH_PACKING == 3200\n\n\tuniform float opacity;\n\n#endif\n\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n\nvarying vec2 vHighPrecisionZW;\n\nvoid main() {\n\n\t#include \n\n\tvec4 diffuseColor = vec4( 1.0 );\n\n\t#if DEPTH_PACKING == 3200\n\n\t\tdiffuseColor.a = opacity;\n\n\t#endif\n\n\t#include \n\t#include \n\t#include \n\t#include \n\n\t#include \n\n\t// Higher precision equivalent of gl_FragCoord.z. This assumes depthRange has been left to its default values.\n\tfloat fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5;\n\n\t#if DEPTH_PACKING == 3200\n\n\t\tgl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity );\n\n\t#elif DEPTH_PACKING == 3201\n\n\t\tgl_FragColor = packDepthToRGBA( fragCoordZ );\n\n\t#endif\n\n}\n`;\n","export const vertex = /* glsl */`\n#define DISTANCE\n\nvarying vec3 vWorldPosition;\n\n#include \n#include \n#include \n#include \n#include \n#include \n\nvoid main() {\n\n\t#include \n\n\t#include \n\n\t#ifdef USE_DISPLACEMENTMAP\n\n\t\t#include \n\t\t#include \n\t\t#include \n\n\t#endif\n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n\tvWorldPosition = worldPosition.xyz;\n\n}\n`;\n\nexport const fragment = /* glsl */`\n#define DISTANCE\n\nuniform vec3 referencePosition;\nuniform float nearDistance;\nuniform float farDistance;\nvarying vec3 vWorldPosition;\n\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n\nvoid main () {\n\n\t#include \n\n\tvec4 diffuseColor = vec4( 1.0 );\n\n\t#include \n\t#include \n\t#include \n\t#include \n\n\tfloat dist = length( vWorldPosition - referencePosition );\n\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\n\tdist = saturate( dist ); // clamp to [ 0, 1 ]\n\n\tgl_FragColor = packDepthToRGBA( dist );\n\n}\n`;\n","export const vertex = /* glsl */`\nvarying vec3 vWorldDirection;\n\n#include \n\nvoid main() {\n\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\n\t#include \n\t#include \n\n}\n`;\n\nexport const fragment = /* glsl */`\nuniform sampler2D tEquirect;\n\nvarying vec3 vWorldDirection;\n\n#include \n\nvoid main() {\n\n\tvec3 direction = normalize( vWorldDirection );\n\n\tvec2 sampleUV = equirectUv( direction );\n\n\tgl_FragColor = texture2D( tEquirect, sampleUV );\n\n\t#include \n\t#include \n\n}\n`;\n","export const vertex = /* glsl */`\nuniform float scale;\nattribute float lineDistance;\n\nvarying float vLineDistance;\n\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n\nvoid main() {\n\n\tvLineDistance = scale * lineDistance;\n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n}\n`;\n\nexport const fragment = /* glsl */`\nuniform vec3 diffuse;\nuniform float opacity;\n\nuniform float dashSize;\nuniform float totalSize;\n\nvarying float vLineDistance;\n\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n\nvoid main() {\n\n\t#include \n\n\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\n\n\t\tdiscard;\n\n\t}\n\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\n\t#include \n\t#include \n\t#include \n\n\toutgoingLight = diffuseColor.rgb; // simple shader\n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n}\n`;\n","export const vertex = /* glsl */`\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n\nvoid main() {\n\n\t#include \n\t#include \n\t#include \n\n\t#if defined ( USE_ENVMAP ) || defined ( USE_SKINNING )\n\n\t\t#include \n\t\t#include \n\t\t#include \n\t\t#include \n\t\t#include \n\n\t#endif\n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n\t#include \n\t#include \n\t#include \n\n}\n`;\n\nexport const fragment = /* glsl */`\nuniform vec3 diffuse;\nuniform float opacity;\n\n#ifndef FLAT_SHADED\n\n\tvarying vec3 vNormal;\n\n#endif\n\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n\nvoid main() {\n\n\t#include \n\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\n\t// accumulation (baked indirect lighting only)\n\t#ifdef USE_LIGHTMAP\n\n\t\tvec4 lightMapTexel = texture2D( lightMap, vLightMapUv );\n\t\treflectedLight.indirectDiffuse += lightMapTexel.rgb * lightMapIntensity * RECIPROCAL_PI;\n\n\t#else\n\n\t\treflectedLight.indirectDiffuse += vec3( 1.0 );\n\n\t#endif\n\n\t// modulation\n\t#include \n\n\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\n\n\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\n\n\t#include \n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n}\n`;\n","export const vertex = /* glsl */`\n#define LAMBERT\n\nvarying vec3 vViewPosition;\n\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n\nvoid main() {\n\n\t#include \n\t#include \n\t#include \n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n\tvViewPosition = - mvPosition.xyz;\n\n\t#include \n\t#include \n\t#include \n\t#include \n\n}\n`;\n\nexport const fragment = /* glsl */`\n#define LAMBERT\n\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\n\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n\nvoid main() {\n\n\t#include \n\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n\t// accumulation\n\t#include \n\t#include \n\t#include \n\t#include \n\n\t// modulation\n\t#include \n\n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n}\n`;\n","export const vertex = /* glsl */`\n#define MATCAP\n\nvarying vec3 vViewPosition;\n\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n\n#include \n#include \n\nvoid main() {\n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n\t#include \n\t#include \n\t#include \n\n\tvViewPosition = - mvPosition.xyz;\n\n}\n`;\n\nexport const fragment = /* glsl */`\n#define MATCAP\n\nuniform vec3 diffuse;\nuniform float opacity;\nuniform sampler2D matcap;\n\nvarying vec3 vViewPosition;\n\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n\nvoid main() {\n\n\t#include \n\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n\tvec3 viewDir = normalize( vViewPosition );\n\tvec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) );\n\tvec3 y = cross( viewDir, x );\n\tvec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5; // 0.495 to remove artifacts caused by undersized matcap disks\n\n\t#ifdef USE_MATCAP\n\n\t\tvec4 matcapColor = texture2D( matcap, uv );\n\n\t#else\n\n\t\tvec4 matcapColor = vec4( vec3( mix( 0.2, 0.8, uv.y ) ), 1.0 ); // default if matcap is missing\n\n\t#endif\n\n\tvec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb;\n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n}\n`;\n","export const vertex = /* glsl */`\n#define NORMAL\n\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE )\n\n\tvarying vec3 vViewPosition;\n\n#endif\n\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n\nvoid main() {\n\n\t#include \n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE )\n\n\tvViewPosition = - mvPosition.xyz;\n\n#endif\n\n}\n`;\n\nexport const fragment = /* glsl */`\n#define NORMAL\n\nuniform float opacity;\n\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE )\n\n\tvarying vec3 vViewPosition;\n\n#endif\n\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n\nvoid main() {\n\n\t#include \n\t#include \n\t#include \n\t#include \n\n\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\n\n\t#ifdef OPAQUE\n\n\t\tgl_FragColor.a = 1.0;\n\n\t#endif\n\n}\n`;\n","export const vertex = /* glsl */`\n#define PHONG\n\nvarying vec3 vViewPosition;\n\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n\nvoid main() {\n\n\t#include \n\t#include \n\t#include \n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n\tvViewPosition = - mvPosition.xyz;\n\n\t#include \n\t#include \n\t#include \n\t#include \n\n}\n`;\n\nexport const fragment = /* glsl */`\n#define PHONG\n\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n\nvoid main() {\n\n\t#include \n\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n\t// accumulation\n\t#include \n\t#include \n\t#include \n\t#include \n\n\t// modulation\n\t#include \n\n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n}\n`;\n","export const vertex = /* glsl */`\n#define STANDARD\n\nvarying vec3 vViewPosition;\n\n#ifdef USE_TRANSMISSION\n\n\tvarying vec3 vWorldPosition;\n\n#endif\n\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n\nvoid main() {\n\n\t#include \n\t#include \n\t#include \n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n\tvViewPosition = - mvPosition.xyz;\n\n\t#include \n\t#include \n\t#include \n\n#ifdef USE_TRANSMISSION\n\n\tvWorldPosition = worldPosition.xyz;\n\n#endif\n}\n`;\n\nexport const fragment = /* glsl */`\n#define STANDARD\n\n#ifdef PHYSICAL\n\t#define IOR\n\t#define USE_SPECULAR\n#endif\n\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n\n#ifdef IOR\n\tuniform float ior;\n#endif\n\n#ifdef USE_SPECULAR\n\tuniform float specularIntensity;\n\tuniform vec3 specularColor;\n\n\t#ifdef USE_SPECULAR_COLORMAP\n\t\tuniform sampler2D specularColorMap;\n\t#endif\n\n\t#ifdef USE_SPECULAR_INTENSITYMAP\n\t\tuniform sampler2D specularIntensityMap;\n\t#endif\n#endif\n\n#ifdef USE_CLEARCOAT\n\tuniform float clearcoat;\n\tuniform float clearcoatRoughness;\n#endif\n\n#ifdef USE_IRIDESCENCE\n\tuniform float iridescence;\n\tuniform float iridescenceIOR;\n\tuniform float iridescenceThicknessMinimum;\n\tuniform float iridescenceThicknessMaximum;\n#endif\n\n#ifdef USE_SHEEN\n\tuniform vec3 sheenColor;\n\tuniform float sheenRoughness;\n\n\t#ifdef USE_SHEEN_COLORMAP\n\t\tuniform sampler2D sheenColorMap;\n\t#endif\n\n\t#ifdef USE_SHEEN_ROUGHNESSMAP\n\t\tuniform sampler2D sheenRoughnessMap;\n\t#endif\n#endif\n\n#ifdef USE_ANISOTROPY\n\tuniform vec2 anisotropyVector;\n\n\t#ifdef USE_ANISOTROPYMAP\n\t\tuniform sampler2D anisotropyMap;\n\t#endif\n#endif\n\nvarying vec3 vViewPosition;\n\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n\nvoid main() {\n\n\t#include \n\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n\t// accumulation\n\t#include \n\t#include \n\t#include \n\t#include \n\n\t// modulation\n\t#include \n\n\tvec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse;\n\tvec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular;\n\n\t#include \n\n\tvec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance;\n\n\t#ifdef USE_SHEEN\n\n\t\t// Sheen energy compensation approximation calculation can be found at the end of\n\t\t// https://drive.google.com/file/d/1T0D1VSyR4AllqIJTQAraEIzjlb5h4FKH/view?usp=sharing\n\t\tfloat sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor );\n\n\t\toutgoingLight = outgoingLight * sheenEnergyComp + sheenSpecularDirect + sheenSpecularIndirect;\n\n\t#endif\n\n\t#ifdef USE_CLEARCOAT\n\n\t\tfloat dotNVcc = saturate( dot( geometryClearcoatNormal, geometryViewDir ) );\n\n\t\tvec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc );\n\n\t\toutgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + ( clearcoatSpecularDirect + clearcoatSpecularIndirect ) * material.clearcoat;\n\n\t#endif\n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n}\n`;\n","export const vertex = /* glsl */`\n#define TOON\n\nvarying vec3 vViewPosition;\n\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n\nvoid main() {\n\n\t#include \n\t#include \n\t#include \n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n\tvViewPosition = - mvPosition.xyz;\n\n\t#include \n\t#include \n\t#include \n\n}\n`;\n\nexport const fragment = /* glsl */`\n#define TOON\n\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\n\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n\nvoid main() {\n\n\t#include \n\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n\t// accumulation\n\t#include \n\t#include \n\t#include \n\t#include \n\n\t// modulation\n\t#include \n\n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n}\n`;\n","export const vertex = /* glsl */`\nuniform float size;\nuniform float scale;\n\n#include \n#include \n#include \n#include \n#include \n#include \n\n#ifdef USE_POINTS_UV\n\n\tvarying vec2 vUv;\n\tuniform mat3 uvTransform;\n\n#endif\n\nvoid main() {\n\n\t#ifdef USE_POINTS_UV\n\n\t\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n\n\t#endif\n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n\tgl_PointSize = size;\n\n\t#ifdef USE_SIZEATTENUATION\n\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\n\t\tif ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z );\n\n\t#endif\n\n\t#include \n\t#include \n\t#include \n\t#include \n\n}\n`;\n\nexport const fragment = /* glsl */`\nuniform vec3 diffuse;\nuniform float opacity;\n\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n\nvoid main() {\n\n\t#include \n\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n\toutgoingLight = diffuseColor.rgb;\n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n}\n`;\n","export const vertex = /* glsl */`\n#include \n#include \n#include \n#include \n#include \n#include \n\nvoid main() {\n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n\t#include \n\t#include \n\t#include \n\n}\n`;\n\nexport const fragment = /* glsl */`\nuniform vec3 color;\nuniform float opacity;\n\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n\nvoid main() {\n\n\t#include \n\n\tgl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) );\n\n\t#include \n\t#include \n\t#include \n\n}\n`;\n","export const vertex = /* glsl */`\nuniform float rotation;\nuniform vec2 center;\n\n#include \n#include \n#include \n#include \n#include \n\nvoid main() {\n\n\t#include \n\n\tvec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );\n\n\tvec2 scale;\n\tscale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );\n\tscale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );\n\n\t#ifndef USE_SIZEATTENUATION\n\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\n\t\tif ( isPerspective ) scale *= - mvPosition.z;\n\n\t#endif\n\n\tvec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale;\n\n\tvec2 rotatedPosition;\n\trotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;\n\trotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;\n\n\tmvPosition.xy += rotatedPosition;\n\n\tgl_Position = projectionMatrix * mvPosition;\n\n\t#include \n\t#include \n\t#include \n\n}\n`;\n\nexport const fragment = /* glsl */`\nuniform vec3 diffuse;\nuniform float opacity;\n\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n\nvoid main() {\n\n\t#include \n\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\n\toutgoingLight = diffuseColor.rgb;\n\n\t#include \n\t#include \n\t#include \n\t#include \n\n}\n`;\n","import { Color } from '../../math/Color.js';\nimport { Vector2 } from '../../math/Vector2.js';\nimport { Matrix3 } from '../../math/Matrix3.js';\n\n/**\n * Uniforms library for shared webgl shaders\n */\n\nconst UniformsLib = {\n\n\tcommon: {\n\n\t\tdiffuse: { value: /*@__PURE__*/ new Color( 0xffffff ) },\n\t\topacity: { value: 1.0 },\n\n\t\tmap: { value: null },\n\t\tmapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\n\t\talphaMap: { value: null },\n\t\talphaMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\n\t\talphaTest: { value: 0 }\n\n\t},\n\n\tspecularmap: {\n\n\t\tspecularMap: { value: null },\n\t\tspecularMapTransform: { value: /*@__PURE__*/ new Matrix3() }\n\n\t},\n\n\tenvmap: {\n\n\t\tenvMap: { value: null },\n\t\tflipEnvMap: { value: - 1 },\n\t\treflectivity: { value: 1.0 }, // basic, lambert, phong\n\t\tior: { value: 1.5 }, // physical\n\t\trefractionRatio: { value: 0.98 }, // basic, lambert, phong\n\n\t},\n\n\taomap: {\n\n\t\taoMap: { value: null },\n\t\taoMapIntensity: { value: 1 },\n\t\taoMapTransform: { value: /*@__PURE__*/ new Matrix3() }\n\n\t},\n\n\tlightmap: {\n\n\t\tlightMap: { value: null },\n\t\tlightMapIntensity: { value: 1 },\n\t\tlightMapTransform: { value: /*@__PURE__*/ new Matrix3() }\n\n\t},\n\n\tbumpmap: {\n\n\t\tbumpMap: { value: null },\n\t\tbumpMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\tbumpScale: { value: 1 }\n\n\t},\n\n\tnormalmap: {\n\n\t\tnormalMap: { value: null },\n\t\tnormalMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\tnormalScale: { value: /*@__PURE__*/ new Vector2( 1, 1 ) }\n\n\t},\n\n\tdisplacementmap: {\n\n\t\tdisplacementMap: { value: null },\n\t\tdisplacementMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\tdisplacementScale: { value: 1 },\n\t\tdisplacementBias: { value: 0 }\n\n\t},\n\n\temissivemap: {\n\n\t\temissiveMap: { value: null },\n\t\temissiveMapTransform: { value: /*@__PURE__*/ new Matrix3() }\n\n\t},\n\n\tmetalnessmap: {\n\n\t\tmetalnessMap: { value: null },\n\t\tmetalnessMapTransform: { value: /*@__PURE__*/ new Matrix3() }\n\n\t},\n\n\troughnessmap: {\n\n\t\troughnessMap: { value: null },\n\t\troughnessMapTransform: { value: /*@__PURE__*/ new Matrix3() }\n\n\t},\n\n\tgradientmap: {\n\n\t\tgradientMap: { value: null }\n\n\t},\n\n\tfog: {\n\n\t\tfogDensity: { value: 0.00025 },\n\t\tfogNear: { value: 1 },\n\t\tfogFar: { value: 2000 },\n\t\tfogColor: { value: /*@__PURE__*/ new Color( 0xffffff ) }\n\n\t},\n\n\tlights: {\n\n\t\tambientLightColor: { value: [] },\n\n\t\tlightProbe: { value: [] },\n\n\t\tdirectionalLights: { value: [], properties: {\n\t\t\tdirection: {},\n\t\t\tcolor: {}\n\t\t} },\n\n\t\tdirectionalLightShadows: { value: [], properties: {\n\t\t\tshadowBias: {},\n\t\t\tshadowNormalBias: {},\n\t\t\tshadowRadius: {},\n\t\t\tshadowMapSize: {}\n\t\t} },\n\n\t\tdirectionalShadowMap: { value: [] },\n\t\tdirectionalShadowMatrix: { value: [] },\n\n\t\tspotLights: { value: [], properties: {\n\t\t\tcolor: {},\n\t\t\tposition: {},\n\t\t\tdirection: {},\n\t\t\tdistance: {},\n\t\t\tconeCos: {},\n\t\t\tpenumbraCos: {},\n\t\t\tdecay: {}\n\t\t} },\n\n\t\tspotLightShadows: { value: [], properties: {\n\t\t\tshadowBias: {},\n\t\t\tshadowNormalBias: {},\n\t\t\tshadowRadius: {},\n\t\t\tshadowMapSize: {}\n\t\t} },\n\n\t\tspotLightMap: { value: [] },\n\t\tspotShadowMap: { value: [] },\n\t\tspotLightMatrix: { value: [] },\n\n\t\tpointLights: { value: [], properties: {\n\t\t\tcolor: {},\n\t\t\tposition: {},\n\t\t\tdecay: {},\n\t\t\tdistance: {}\n\t\t} },\n\n\t\tpointLightShadows: { value: [], properties: {\n\t\t\tshadowBias: {},\n\t\t\tshadowNormalBias: {},\n\t\t\tshadowRadius: {},\n\t\t\tshadowMapSize: {},\n\t\t\tshadowCameraNear: {},\n\t\t\tshadowCameraFar: {}\n\t\t} },\n\n\t\tpointShadowMap: { value: [] },\n\t\tpointShadowMatrix: { value: [] },\n\n\t\themisphereLights: { value: [], properties: {\n\t\t\tdirection: {},\n\t\t\tskyColor: {},\n\t\t\tgroundColor: {}\n\t\t} },\n\n\t\t// TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src\n\t\trectAreaLights: { value: [], properties: {\n\t\t\tcolor: {},\n\t\t\tposition: {},\n\t\t\twidth: {},\n\t\t\theight: {}\n\t\t} },\n\n\t\tltc_1: { value: null },\n\t\tltc_2: { value: null }\n\n\t},\n\n\tpoints: {\n\n\t\tdiffuse: { value: /*@__PURE__*/ new Color( 0xffffff ) },\n\t\topacity: { value: 1.0 },\n\t\tsize: { value: 1.0 },\n\t\tscale: { value: 1.0 },\n\t\tmap: { value: null },\n\t\talphaMap: { value: null },\n\t\talphaMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\talphaTest: { value: 0 },\n\t\tuvTransform: { value: /*@__PURE__*/ new Matrix3() }\n\n\t},\n\n\tsprite: {\n\n\t\tdiffuse: { value: /*@__PURE__*/ new Color( 0xffffff ) },\n\t\topacity: { value: 1.0 },\n\t\tcenter: { value: /*@__PURE__*/ new Vector2( 0.5, 0.5 ) },\n\t\trotation: { value: 0.0 },\n\t\tmap: { value: null },\n\t\tmapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\talphaMap: { value: null },\n\t\talphaMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\talphaTest: { value: 0 }\n\n\t}\n\n};\n\nexport { UniformsLib };\n","import { ShaderChunk } from './ShaderChunk.js';\nimport { mergeUniforms } from './UniformsUtils.js';\nimport { Vector2 } from '../../math/Vector2.js';\nimport { Vector3 } from '../../math/Vector3.js';\nimport { UniformsLib } from './UniformsLib.js';\nimport { Color } from '../../math/Color.js';\nimport { Matrix3 } from '../../math/Matrix3.js';\n\nconst ShaderLib = {\n\n\tbasic: {\n\n\t\tuniforms: /*@__PURE__*/ mergeUniforms( [\n\t\t\tUniformsLib.common,\n\t\t\tUniformsLib.specularmap,\n\t\t\tUniformsLib.envmap,\n\t\t\tUniformsLib.aomap,\n\t\t\tUniformsLib.lightmap,\n\t\t\tUniformsLib.fog\n\t\t] ),\n\n\t\tvertexShader: ShaderChunk.meshbasic_vert,\n\t\tfragmentShader: ShaderChunk.meshbasic_frag\n\n\t},\n\n\tlambert: {\n\n\t\tuniforms: /*@__PURE__*/ mergeUniforms( [\n\t\t\tUniformsLib.common,\n\t\t\tUniformsLib.specularmap,\n\t\t\tUniformsLib.envmap,\n\t\t\tUniformsLib.aomap,\n\t\t\tUniformsLib.lightmap,\n\t\t\tUniformsLib.emissivemap,\n\t\t\tUniformsLib.bumpmap,\n\t\t\tUniformsLib.normalmap,\n\t\t\tUniformsLib.displacementmap,\n\t\t\tUniformsLib.fog,\n\t\t\tUniformsLib.lights,\n\t\t\t{\n\t\t\t\temissive: { value: /*@__PURE__*/ new Color( 0x000000 ) }\n\t\t\t}\n\t\t] ),\n\n\t\tvertexShader: ShaderChunk.meshlambert_vert,\n\t\tfragmentShader: ShaderChunk.meshlambert_frag\n\n\t},\n\n\tphong: {\n\n\t\tuniforms: /*@__PURE__*/ mergeUniforms( [\n\t\t\tUniformsLib.common,\n\t\t\tUniformsLib.specularmap,\n\t\t\tUniformsLib.envmap,\n\t\t\tUniformsLib.aomap,\n\t\t\tUniformsLib.lightmap,\n\t\t\tUniformsLib.emissivemap,\n\t\t\tUniformsLib.bumpmap,\n\t\t\tUniformsLib.normalmap,\n\t\t\tUniformsLib.displacementmap,\n\t\t\tUniformsLib.fog,\n\t\t\tUniformsLib.lights,\n\t\t\t{\n\t\t\t\temissive: { value: /*@__PURE__*/ new Color( 0x000000 ) },\n\t\t\t\tspecular: { value: /*@__PURE__*/ new Color( 0x111111 ) },\n\t\t\t\tshininess: { value: 30 }\n\t\t\t}\n\t\t] ),\n\n\t\tvertexShader: ShaderChunk.meshphong_vert,\n\t\tfragmentShader: ShaderChunk.meshphong_frag\n\n\t},\n\n\tstandard: {\n\n\t\tuniforms: /*@__PURE__*/ mergeUniforms( [\n\t\t\tUniformsLib.common,\n\t\t\tUniformsLib.envmap,\n\t\t\tUniformsLib.aomap,\n\t\t\tUniformsLib.lightmap,\n\t\t\tUniformsLib.emissivemap,\n\t\t\tUniformsLib.bumpmap,\n\t\t\tUniformsLib.normalmap,\n\t\t\tUniformsLib.displacementmap,\n\t\t\tUniformsLib.roughnessmap,\n\t\t\tUniformsLib.metalnessmap,\n\t\t\tUniformsLib.fog,\n\t\t\tUniformsLib.lights,\n\t\t\t{\n\t\t\t\temissive: { value: /*@__PURE__*/ new Color( 0x000000 ) },\n\t\t\t\troughness: { value: 1.0 },\n\t\t\t\tmetalness: { value: 0.0 },\n\t\t\t\tenvMapIntensity: { value: 1 } // temporary\n\t\t\t}\n\t\t] ),\n\n\t\tvertexShader: ShaderChunk.meshphysical_vert,\n\t\tfragmentShader: ShaderChunk.meshphysical_frag\n\n\t},\n\n\ttoon: {\n\n\t\tuniforms: /*@__PURE__*/ mergeUniforms( [\n\t\t\tUniformsLib.common,\n\t\t\tUniformsLib.aomap,\n\t\t\tUniformsLib.lightmap,\n\t\t\tUniformsLib.emissivemap,\n\t\t\tUniformsLib.bumpmap,\n\t\t\tUniformsLib.normalmap,\n\t\t\tUniformsLib.displacementmap,\n\t\t\tUniformsLib.gradientmap,\n\t\t\tUniformsLib.fog,\n\t\t\tUniformsLib.lights,\n\t\t\t{\n\t\t\t\temissive: { value: /*@__PURE__*/ new Color( 0x000000 ) }\n\t\t\t}\n\t\t] ),\n\n\t\tvertexShader: ShaderChunk.meshtoon_vert,\n\t\tfragmentShader: ShaderChunk.meshtoon_frag\n\n\t},\n\n\tmatcap: {\n\n\t\tuniforms: /*@__PURE__*/ mergeUniforms( [\n\t\t\tUniformsLib.common,\n\t\t\tUniformsLib.bumpmap,\n\t\t\tUniformsLib.normalmap,\n\t\t\tUniformsLib.displacementmap,\n\t\t\tUniformsLib.fog,\n\t\t\t{\n\t\t\t\tmatcap: { value: null }\n\t\t\t}\n\t\t] ),\n\n\t\tvertexShader: ShaderChunk.meshmatcap_vert,\n\t\tfragmentShader: ShaderChunk.meshmatcap_frag\n\n\t},\n\n\tpoints: {\n\n\t\tuniforms: /*@__PURE__*/ mergeUniforms( [\n\t\t\tUniformsLib.points,\n\t\t\tUniformsLib.fog\n\t\t] ),\n\n\t\tvertexShader: ShaderChunk.points_vert,\n\t\tfragmentShader: ShaderChunk.points_frag\n\n\t},\n\n\tdashed: {\n\n\t\tuniforms: /*@__PURE__*/ mergeUniforms( [\n\t\t\tUniformsLib.common,\n\t\t\tUniformsLib.fog,\n\t\t\t{\n\t\t\t\tscale: { value: 1 },\n\t\t\t\tdashSize: { value: 1 },\n\t\t\t\ttotalSize: { value: 2 }\n\t\t\t}\n\t\t] ),\n\n\t\tvertexShader: ShaderChunk.linedashed_vert,\n\t\tfragmentShader: ShaderChunk.linedashed_frag\n\n\t},\n\n\tdepth: {\n\n\t\tuniforms: /*@__PURE__*/ mergeUniforms( [\n\t\t\tUniformsLib.common,\n\t\t\tUniformsLib.displacementmap\n\t\t] ),\n\n\t\tvertexShader: ShaderChunk.depth_vert,\n\t\tfragmentShader: ShaderChunk.depth_frag\n\n\t},\n\n\tnormal: {\n\n\t\tuniforms: /*@__PURE__*/ mergeUniforms( [\n\t\t\tUniformsLib.common,\n\t\t\tUniformsLib.bumpmap,\n\t\t\tUniformsLib.normalmap,\n\t\t\tUniformsLib.displacementmap,\n\t\t\t{\n\t\t\t\topacity: { value: 1.0 }\n\t\t\t}\n\t\t] ),\n\n\t\tvertexShader: ShaderChunk.meshnormal_vert,\n\t\tfragmentShader: ShaderChunk.meshnormal_frag\n\n\t},\n\n\tsprite: {\n\n\t\tuniforms: /*@__PURE__*/ mergeUniforms( [\n\t\t\tUniformsLib.sprite,\n\t\t\tUniformsLib.fog\n\t\t] ),\n\n\t\tvertexShader: ShaderChunk.sprite_vert,\n\t\tfragmentShader: ShaderChunk.sprite_frag\n\n\t},\n\n\tbackground: {\n\n\t\tuniforms: {\n\t\t\tuvTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\t\tt2D: { value: null },\n\t\t\tbackgroundIntensity: { value: 1 }\n\t\t},\n\n\t\tvertexShader: ShaderChunk.background_vert,\n\t\tfragmentShader: ShaderChunk.background_frag\n\n\t},\n\n\tbackgroundCube: {\n\n\t\tuniforms: {\n\t\t\tenvMap: { value: null },\n\t\t\tflipEnvMap: { value: - 1 },\n\t\t\tbackgroundBlurriness: { value: 0 },\n\t\t\tbackgroundIntensity: { value: 1 }\n\t\t},\n\n\t\tvertexShader: ShaderChunk.backgroundCube_vert,\n\t\tfragmentShader: ShaderChunk.backgroundCube_frag\n\n\t},\n\n\tcube: {\n\n\t\tuniforms: {\n\t\t\ttCube: { value: null },\n\t\t\ttFlip: { value: - 1 },\n\t\t\topacity: { value: 1.0 }\n\t\t},\n\n\t\tvertexShader: ShaderChunk.cube_vert,\n\t\tfragmentShader: ShaderChunk.cube_frag\n\n\t},\n\n\tequirect: {\n\n\t\tuniforms: {\n\t\t\ttEquirect: { value: null },\n\t\t},\n\n\t\tvertexShader: ShaderChunk.equirect_vert,\n\t\tfragmentShader: ShaderChunk.equirect_frag\n\n\t},\n\n\tdistanceRGBA: {\n\n\t\tuniforms: /*@__PURE__*/ mergeUniforms( [\n\t\t\tUniformsLib.common,\n\t\t\tUniformsLib.displacementmap,\n\t\t\t{\n\t\t\t\treferencePosition: { value: /*@__PURE__*/ new Vector3() },\n\t\t\t\tnearDistance: { value: 1 },\n\t\t\t\tfarDistance: { value: 1000 }\n\t\t\t}\n\t\t] ),\n\n\t\tvertexShader: ShaderChunk.distanceRGBA_vert,\n\t\tfragmentShader: ShaderChunk.distanceRGBA_frag\n\n\t},\n\n\tshadow: {\n\n\t\tuniforms: /*@__PURE__*/ mergeUniforms( [\n\t\t\tUniformsLib.lights,\n\t\t\tUniformsLib.fog,\n\t\t\t{\n\t\t\t\tcolor: { value: /*@__PURE__*/ new Color( 0x00000 ) },\n\t\t\t\topacity: { value: 1.0 }\n\t\t\t},\n\t\t] ),\n\n\t\tvertexShader: ShaderChunk.shadow_vert,\n\t\tfragmentShader: ShaderChunk.shadow_frag\n\n\t}\n\n};\n\nShaderLib.physical = {\n\n\tuniforms: /*@__PURE__*/ mergeUniforms( [\n\t\tShaderLib.standard.uniforms,\n\t\t{\n\t\t\tclearcoat: { value: 0 },\n\t\t\tclearcoatMap: { value: null },\n\t\t\tclearcoatMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\t\tclearcoatNormalMap: { value: null },\n\t\t\tclearcoatNormalMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\t\tclearcoatNormalScale: { value: /*@__PURE__*/ new Vector2( 1, 1 ) },\n\t\t\tclearcoatRoughness: { value: 0 },\n\t\t\tclearcoatRoughnessMap: { value: null },\n\t\t\tclearcoatRoughnessMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\t\tiridescence: { value: 0 },\n\t\t\tiridescenceMap: { value: null },\n\t\t\tiridescenceMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\t\tiridescenceIOR: { value: 1.3 },\n\t\t\tiridescenceThicknessMinimum: { value: 100 },\n\t\t\tiridescenceThicknessMaximum: { value: 400 },\n\t\t\tiridescenceThicknessMap: { value: null },\n\t\t\tiridescenceThicknessMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\t\tsheen: { value: 0 },\n\t\t\tsheenColor: { value: /*@__PURE__*/ new Color( 0x000000 ) },\n\t\t\tsheenColorMap: { value: null },\n\t\t\tsheenColorMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\t\tsheenRoughness: { value: 1 },\n\t\t\tsheenRoughnessMap: { value: null },\n\t\t\tsheenRoughnessMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\t\ttransmission: { value: 0 },\n\t\t\ttransmissionMap: { value: null },\n\t\t\ttransmissionMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\t\ttransmissionSamplerSize: { value: /*@__PURE__*/ new Vector2() },\n\t\t\ttransmissionSamplerMap: { value: null },\n\t\t\tthickness: { value: 0 },\n\t\t\tthicknessMap: { value: null },\n\t\t\tthicknessMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\t\tattenuationDistance: { value: 0 },\n\t\t\tattenuationColor: { value: /*@__PURE__*/ new Color( 0x000000 ) },\n\t\t\tspecularColor: { value: /*@__PURE__*/ new Color( 1, 1, 1 ) },\n\t\t\tspecularColorMap: { value: null },\n\t\t\tspecularColorMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\t\tspecularIntensity: { value: 1 },\n\t\t\tspecularIntensityMap: { value: null },\n\t\t\tspecularIntensityMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\t\tanisotropyVector: { value: /*@__PURE__*/ new Vector2() },\n\t\t\tanisotropyMap: { value: null },\n\t\t\tanisotropyMapTransform: { value: /*@__PURE__*/ new Matrix3() },\n\t\t}\n\t] ),\n\n\tvertexShader: ShaderChunk.meshphysical_vert,\n\tfragmentShader: ShaderChunk.meshphysical_frag\n\n};\n\n\nexport { ShaderLib };\n","import { BackSide, FrontSide, CubeUVReflectionMapping, SRGBTransfer } from '../../constants.js';\nimport { BoxGeometry } from '../../geometries/BoxGeometry.js';\nimport { PlaneGeometry } from '../../geometries/PlaneGeometry.js';\nimport { ShaderMaterial } from '../../materials/ShaderMaterial.js';\nimport { Color } from '../../math/Color.js';\nimport { ColorManagement } from '../../math/ColorManagement.js';\nimport { Mesh } from '../../objects/Mesh.js';\nimport { ShaderLib } from '../shaders/ShaderLib.js';\nimport { cloneUniforms, getUnlitUniformColorSpace } from '../shaders/UniformsUtils.js';\n\nconst _rgb = { r: 0, b: 0, g: 0 };\n\nfunction WebGLBackground( renderer, cubemaps, cubeuvmaps, state, objects, alpha, premultipliedAlpha ) {\n\n\tconst clearColor = new Color( 0x000000 );\n\tlet clearAlpha = alpha === true ? 0 : 1;\n\n\tlet planeMesh;\n\tlet boxMesh;\n\n\tlet currentBackground = null;\n\tlet currentBackgroundVersion = 0;\n\tlet currentTonemapping = null;\n\n\tfunction render( renderList, scene ) {\n\n\t\tlet forceClear = false;\n\t\tlet background = scene.isScene === true ? scene.background : null;\n\n\t\tif ( background && background.isTexture ) {\n\n\t\t\tconst usePMREM = scene.backgroundBlurriness > 0; // use PMREM if the user wants to blur the background\n\t\t\tbackground = ( usePMREM ? cubeuvmaps : cubemaps ).get( background );\n\n\t\t}\n\n\t\tif ( background === null ) {\n\n\t\t\tsetClear( clearColor, clearAlpha );\n\n\t\t} else if ( background && background.isColor ) {\n\n\t\t\tsetClear( background, 1 );\n\t\t\tforceClear = true;\n\n\t\t}\n\n\t\tconst environmentBlendMode = renderer.xr.getEnvironmentBlendMode();\n\n\t\tif ( environmentBlendMode === 'additive' ) {\n\n\t\t\tstate.buffers.color.setClear( 0, 0, 0, 1, premultipliedAlpha );\n\n\t\t} else if ( environmentBlendMode === 'alpha-blend' ) {\n\n\t\t\tstate.buffers.color.setClear( 0, 0, 0, 0, premultipliedAlpha );\n\n\t\t}\n\n\t\tif ( renderer.autoClear || forceClear ) {\n\n\t\t\trenderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil );\n\n\t\t}\n\n\t\tif ( background && ( background.isCubeTexture || background.mapping === CubeUVReflectionMapping ) ) {\n\n\t\t\tif ( boxMesh === undefined ) {\n\n\t\t\t\tboxMesh = new Mesh(\n\t\t\t\t\tnew BoxGeometry( 1, 1, 1 ),\n\t\t\t\t\tnew ShaderMaterial( {\n\t\t\t\t\t\tname: 'BackgroundCubeMaterial',\n\t\t\t\t\t\tuniforms: cloneUniforms( ShaderLib.backgroundCube.uniforms ),\n\t\t\t\t\t\tvertexShader: ShaderLib.backgroundCube.vertexShader,\n\t\t\t\t\t\tfragmentShader: ShaderLib.backgroundCube.fragmentShader,\n\t\t\t\t\t\tside: BackSide,\n\t\t\t\t\t\tdepthTest: false,\n\t\t\t\t\t\tdepthWrite: false,\n\t\t\t\t\t\tfog: false\n\t\t\t\t\t} )\n\t\t\t\t);\n\n\t\t\t\tboxMesh.geometry.deleteAttribute( 'normal' );\n\t\t\t\tboxMesh.geometry.deleteAttribute( 'uv' );\n\n\t\t\t\tboxMesh.onBeforeRender = function ( renderer, scene, camera ) {\n\n\t\t\t\t\tthis.matrixWorld.copyPosition( camera.matrixWorld );\n\n\t\t\t\t};\n\n\t\t\t\t// add \"envMap\" material property so the renderer can evaluate it like for built-in materials\n\t\t\t\tObject.defineProperty( boxMesh.material, 'envMap', {\n\n\t\t\t\t\tget: function () {\n\n\t\t\t\t\t\treturn this.uniforms.envMap.value;\n\n\t\t\t\t\t}\n\n\t\t\t\t} );\n\n\t\t\t\tobjects.update( boxMesh );\n\n\t\t\t}\n\n\t\t\tboxMesh.material.uniforms.envMap.value = background;\n\t\t\tboxMesh.material.uniforms.flipEnvMap.value = ( background.isCubeTexture && background.isRenderTargetTexture === false ) ? - 1 : 1;\n\t\t\tboxMesh.material.uniforms.backgroundBlurriness.value = scene.backgroundBlurriness;\n\t\t\tboxMesh.material.uniforms.backgroundIntensity.value = scene.backgroundIntensity;\n\t\t\tboxMesh.material.toneMapped = ColorManagement.getTransfer( background.colorSpace ) !== SRGBTransfer;\n\n\t\t\tif ( currentBackground !== background ||\n\t\t\t\tcurrentBackgroundVersion !== background.version ||\n\t\t\t\tcurrentTonemapping !== renderer.toneMapping ) {\n\n\t\t\t\tboxMesh.material.needsUpdate = true;\n\n\t\t\t\tcurrentBackground = background;\n\t\t\t\tcurrentBackgroundVersion = background.version;\n\t\t\t\tcurrentTonemapping = renderer.toneMapping;\n\n\t\t\t}\n\n\t\t\tboxMesh.layers.enableAll();\n\n\t\t\t// push to the pre-sorted opaque render list\n\t\t\trenderList.unshift( boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null );\n\n\t\t} else if ( background && background.isTexture ) {\n\n\t\t\tif ( planeMesh === undefined ) {\n\n\t\t\t\tplaneMesh = new Mesh(\n\t\t\t\t\tnew PlaneGeometry( 2, 2 ),\n\t\t\t\t\tnew ShaderMaterial( {\n\t\t\t\t\t\tname: 'BackgroundMaterial',\n\t\t\t\t\t\tuniforms: cloneUniforms( ShaderLib.background.uniforms ),\n\t\t\t\t\t\tvertexShader: ShaderLib.background.vertexShader,\n\t\t\t\t\t\tfragmentShader: ShaderLib.background.fragmentShader,\n\t\t\t\t\t\tside: FrontSide,\n\t\t\t\t\t\tdepthTest: false,\n\t\t\t\t\t\tdepthWrite: false,\n\t\t\t\t\t\tfog: false\n\t\t\t\t\t} )\n\t\t\t\t);\n\n\t\t\t\tplaneMesh.geometry.deleteAttribute( 'normal' );\n\n\t\t\t\t// add \"map\" material property so the renderer can evaluate it like for built-in materials\n\t\t\t\tObject.defineProperty( planeMesh.material, 'map', {\n\n\t\t\t\t\tget: function () {\n\n\t\t\t\t\t\treturn this.uniforms.t2D.value;\n\n\t\t\t\t\t}\n\n\t\t\t\t} );\n\n\t\t\t\tobjects.update( planeMesh );\n\n\t\t\t}\n\n\t\t\tplaneMesh.material.uniforms.t2D.value = background;\n\t\t\tplaneMesh.material.uniforms.backgroundIntensity.value = scene.backgroundIntensity;\n\t\t\tplaneMesh.material.toneMapped = ColorManagement.getTransfer( background.colorSpace ) !== SRGBTransfer;\n\n\t\t\tif ( background.matrixAutoUpdate === true ) {\n\n\t\t\t\tbackground.updateMatrix();\n\n\t\t\t}\n\n\t\t\tplaneMesh.material.uniforms.uvTransform.value.copy( background.matrix );\n\n\t\t\tif ( currentBackground !== background ||\n\t\t\t\tcurrentBackgroundVersion !== background.version ||\n\t\t\t\tcurrentTonemapping !== renderer.toneMapping ) {\n\n\t\t\t\tplaneMesh.material.needsUpdate = true;\n\n\t\t\t\tcurrentBackground = background;\n\t\t\t\tcurrentBackgroundVersion = background.version;\n\t\t\t\tcurrentTonemapping = renderer.toneMapping;\n\n\t\t\t}\n\n\t\t\tplaneMesh.layers.enableAll();\n\n\t\t\t// push to the pre-sorted opaque render list\n\t\t\trenderList.unshift( planeMesh, planeMesh.geometry, planeMesh.material, 0, 0, null );\n\n\t\t}\n\n\t}\n\n\tfunction setClear( color, alpha ) {\n\n\t\tcolor.getRGB( _rgb, getUnlitUniformColorSpace( renderer ) );\n\n\t\tstate.buffers.color.setClear( _rgb.r, _rgb.g, _rgb.b, alpha, premultipliedAlpha );\n\n\t}\n\n\treturn {\n\n\t\tgetClearColor: function () {\n\n\t\t\treturn clearColor;\n\n\t\t},\n\t\tsetClearColor: function ( color, alpha = 1 ) {\n\n\t\t\tclearColor.set( color );\n\t\t\tclearAlpha = alpha;\n\t\t\tsetClear( clearColor, clearAlpha );\n\n\t\t},\n\t\tgetClearAlpha: function () {\n\n\t\t\treturn clearAlpha;\n\n\t\t},\n\t\tsetClearAlpha: function ( alpha ) {\n\n\t\t\tclearAlpha = alpha;\n\t\t\tsetClear( clearColor, clearAlpha );\n\n\t\t},\n\t\trender: render\n\n\t};\n\n}\n\n\nexport { WebGLBackground };\n","import { IntType } from '../../constants.js';\n\nfunction WebGLBindingStates( gl, extensions, attributes, capabilities ) {\n\n\tconst maxVertexAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS );\n\n\tconst extension = capabilities.isWebGL2 ? null : extensions.get( 'OES_vertex_array_object' );\n\tconst vaoAvailable = capabilities.isWebGL2 || extension !== null;\n\n\tconst bindingStates = {};\n\n\tconst defaultState = createBindingState( null );\n\tlet currentState = defaultState;\n\tlet forceUpdate = false;\n\n\tfunction setup( object, material, program, geometry, index ) {\n\n\t\tlet updateBuffers = false;\n\n\t\tif ( vaoAvailable ) {\n\n\t\t\tconst state = getBindingState( geometry, program, material );\n\n\t\t\tif ( currentState !== state ) {\n\n\t\t\t\tcurrentState = state;\n\t\t\t\tbindVertexArrayObject( currentState.object );\n\n\t\t\t}\n\n\t\t\tupdateBuffers = needsUpdate( object, geometry, program, index );\n\n\t\t\tif ( updateBuffers ) saveCache( object, geometry, program, index );\n\n\t\t} else {\n\n\t\t\tconst wireframe = ( material.wireframe === true );\n\n\t\t\tif ( currentState.geometry !== geometry.id ||\n\t\t\t\tcurrentState.program !== program.id ||\n\t\t\t\tcurrentState.wireframe !== wireframe ) {\n\n\t\t\t\tcurrentState.geometry = geometry.id;\n\t\t\t\tcurrentState.program = program.id;\n\t\t\t\tcurrentState.wireframe = wireframe;\n\n\t\t\t\tupdateBuffers = true;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( index !== null ) {\n\n\t\t\tattributes.update( index, gl.ELEMENT_ARRAY_BUFFER );\n\n\t\t}\n\n\t\tif ( updateBuffers || forceUpdate ) {\n\n\t\t\tforceUpdate = false;\n\n\t\t\tsetupVertexAttributes( object, material, program, geometry );\n\n\t\t\tif ( index !== null ) {\n\n\t\t\t\tgl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, attributes.get( index ).buffer );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tfunction createVertexArrayObject() {\n\n\t\tif ( capabilities.isWebGL2 ) return gl.createVertexArray();\n\n\t\treturn extension.createVertexArrayOES();\n\n\t}\n\n\tfunction bindVertexArrayObject( vao ) {\n\n\t\tif ( capabilities.isWebGL2 ) return gl.bindVertexArray( vao );\n\n\t\treturn extension.bindVertexArrayOES( vao );\n\n\t}\n\n\tfunction deleteVertexArrayObject( vao ) {\n\n\t\tif ( capabilities.isWebGL2 ) return gl.deleteVertexArray( vao );\n\n\t\treturn extension.deleteVertexArrayOES( vao );\n\n\t}\n\n\tfunction getBindingState( geometry, program, material ) {\n\n\t\tconst wireframe = ( material.wireframe === true );\n\n\t\tlet programMap = bindingStates[ geometry.id ];\n\n\t\tif ( programMap === undefined ) {\n\n\t\t\tprogramMap = {};\n\t\t\tbindingStates[ geometry.id ] = programMap;\n\n\t\t}\n\n\t\tlet stateMap = programMap[ program.id ];\n\n\t\tif ( stateMap === undefined ) {\n\n\t\t\tstateMap = {};\n\t\t\tprogramMap[ program.id ] = stateMap;\n\n\t\t}\n\n\t\tlet state = stateMap[ wireframe ];\n\n\t\tif ( state === undefined ) {\n\n\t\t\tstate = createBindingState( createVertexArrayObject() );\n\t\t\tstateMap[ wireframe ] = state;\n\n\t\t}\n\n\t\treturn state;\n\n\t}\n\n\tfunction createBindingState( vao ) {\n\n\t\tconst newAttributes = [];\n\t\tconst enabledAttributes = [];\n\t\tconst attributeDivisors = [];\n\n\t\tfor ( let i = 0; i < maxVertexAttributes; i ++ ) {\n\n\t\t\tnewAttributes[ i ] = 0;\n\t\t\tenabledAttributes[ i ] = 0;\n\t\t\tattributeDivisors[ i ] = 0;\n\n\t\t}\n\n\t\treturn {\n\n\t\t\t// for backward compatibility on non-VAO support browser\n\t\t\tgeometry: null,\n\t\t\tprogram: null,\n\t\t\twireframe: false,\n\n\t\t\tnewAttributes: newAttributes,\n\t\t\tenabledAttributes: enabledAttributes,\n\t\t\tattributeDivisors: attributeDivisors,\n\t\t\tobject: vao,\n\t\t\tattributes: {},\n\t\t\tindex: null\n\n\t\t};\n\n\t}\n\n\tfunction needsUpdate( object, geometry, program, index ) {\n\n\t\tconst cachedAttributes = currentState.attributes;\n\t\tconst geometryAttributes = geometry.attributes;\n\n\t\tlet attributesNum = 0;\n\n\t\tconst programAttributes = program.getAttributes();\n\n\t\tfor ( const name in programAttributes ) {\n\n\t\t\tconst programAttribute = programAttributes[ name ];\n\n\t\t\tif ( programAttribute.location >= 0 ) {\n\n\t\t\t\tconst cachedAttribute = cachedAttributes[ name ];\n\t\t\t\tlet geometryAttribute = geometryAttributes[ name ];\n\n\t\t\t\tif ( geometryAttribute === undefined ) {\n\n\t\t\t\t\tif ( name === 'instanceMatrix' && object.instanceMatrix ) geometryAttribute = object.instanceMatrix;\n\t\t\t\t\tif ( name === 'instanceColor' && object.instanceColor ) geometryAttribute = object.instanceColor;\n\n\t\t\t\t}\n\n\t\t\t\tif ( cachedAttribute === undefined ) return true;\n\n\t\t\t\tif ( cachedAttribute.attribute !== geometryAttribute ) return true;\n\n\t\t\t\tif ( geometryAttribute && cachedAttribute.data !== geometryAttribute.data ) return true;\n\n\t\t\t\tattributesNum ++;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( currentState.attributesNum !== attributesNum ) return true;\n\n\t\tif ( currentState.index !== index ) return true;\n\n\t\treturn false;\n\n\t}\n\n\tfunction saveCache( object, geometry, program, index ) {\n\n\t\tconst cache = {};\n\t\tconst attributes = geometry.attributes;\n\t\tlet attributesNum = 0;\n\n\t\tconst programAttributes = program.getAttributes();\n\n\t\tfor ( const name in programAttributes ) {\n\n\t\t\tconst programAttribute = programAttributes[ name ];\n\n\t\t\tif ( programAttribute.location >= 0 ) {\n\n\t\t\t\tlet attribute = attributes[ name ];\n\n\t\t\t\tif ( attribute === undefined ) {\n\n\t\t\t\t\tif ( name === 'instanceMatrix' && object.instanceMatrix ) attribute = object.instanceMatrix;\n\t\t\t\t\tif ( name === 'instanceColor' && object.instanceColor ) attribute = object.instanceColor;\n\n\t\t\t\t}\n\n\t\t\t\tconst data = {};\n\t\t\t\tdata.attribute = attribute;\n\n\t\t\t\tif ( attribute && attribute.data ) {\n\n\t\t\t\t\tdata.data = attribute.data;\n\n\t\t\t\t}\n\n\t\t\t\tcache[ name ] = data;\n\n\t\t\t\tattributesNum ++;\n\n\t\t\t}\n\n\t\t}\n\n\t\tcurrentState.attributes = cache;\n\t\tcurrentState.attributesNum = attributesNum;\n\n\t\tcurrentState.index = index;\n\n\t}\n\n\tfunction initAttributes() {\n\n\t\tconst newAttributes = currentState.newAttributes;\n\n\t\tfor ( let i = 0, il = newAttributes.length; i < il; i ++ ) {\n\n\t\t\tnewAttributes[ i ] = 0;\n\n\t\t}\n\n\t}\n\n\tfunction enableAttribute( attribute ) {\n\n\t\tenableAttributeAndDivisor( attribute, 0 );\n\n\t}\n\n\tfunction enableAttributeAndDivisor( attribute, meshPerAttribute ) {\n\n\t\tconst newAttributes = currentState.newAttributes;\n\t\tconst enabledAttributes = currentState.enabledAttributes;\n\t\tconst attributeDivisors = currentState.attributeDivisors;\n\n\t\tnewAttributes[ attribute ] = 1;\n\n\t\tif ( enabledAttributes[ attribute ] === 0 ) {\n\n\t\t\tgl.enableVertexAttribArray( attribute );\n\t\t\tenabledAttributes[ attribute ] = 1;\n\n\t\t}\n\n\t\tif ( attributeDivisors[ attribute ] !== meshPerAttribute ) {\n\n\t\t\tconst extension = capabilities.isWebGL2 ? gl : extensions.get( 'ANGLE_instanced_arrays' );\n\n\t\t\textension[ capabilities.isWebGL2 ? 'vertexAttribDivisor' : 'vertexAttribDivisorANGLE' ]( attribute, meshPerAttribute );\n\t\t\tattributeDivisors[ attribute ] = meshPerAttribute;\n\n\t\t}\n\n\t}\n\n\tfunction disableUnusedAttributes() {\n\n\t\tconst newAttributes = currentState.newAttributes;\n\t\tconst enabledAttributes = currentState.enabledAttributes;\n\n\t\tfor ( let i = 0, il = enabledAttributes.length; i < il; i ++ ) {\n\n\t\t\tif ( enabledAttributes[ i ] !== newAttributes[ i ] ) {\n\n\t\t\t\tgl.disableVertexAttribArray( i );\n\t\t\t\tenabledAttributes[ i ] = 0;\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tfunction vertexAttribPointer( index, size, type, normalized, stride, offset, integer ) {\n\n\t\tif ( integer === true ) {\n\n\t\t\tgl.vertexAttribIPointer( index, size, type, stride, offset );\n\n\t\t} else {\n\n\t\t\tgl.vertexAttribPointer( index, size, type, normalized, stride, offset );\n\n\t\t}\n\n\t}\n\n\tfunction setupVertexAttributes( object, material, program, geometry ) {\n\n\t\tif ( capabilities.isWebGL2 === false && ( object.isInstancedMesh || geometry.isInstancedBufferGeometry ) ) {\n\n\t\t\tif ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) return;\n\n\t\t}\n\n\t\tinitAttributes();\n\n\t\tconst geometryAttributes = geometry.attributes;\n\n\t\tconst programAttributes = program.getAttributes();\n\n\t\tconst materialDefaultAttributeValues = material.defaultAttributeValues;\n\n\t\tfor ( const name in programAttributes ) {\n\n\t\t\tconst programAttribute = programAttributes[ name ];\n\n\t\t\tif ( programAttribute.location >= 0 ) {\n\n\t\t\t\tlet geometryAttribute = geometryAttributes[ name ];\n\n\t\t\t\tif ( geometryAttribute === undefined ) {\n\n\t\t\t\t\tif ( name === 'instanceMatrix' && object.instanceMatrix ) geometryAttribute = object.instanceMatrix;\n\t\t\t\t\tif ( name === 'instanceColor' && object.instanceColor ) geometryAttribute = object.instanceColor;\n\n\t\t\t\t}\n\n\t\t\t\tif ( geometryAttribute !== undefined ) {\n\n\t\t\t\t\tconst normalized = geometryAttribute.normalized;\n\t\t\t\t\tconst size = geometryAttribute.itemSize;\n\n\t\t\t\t\tconst attribute = attributes.get( geometryAttribute );\n\n\t\t\t\t\t// TODO Attribute may not be available on context restore\n\n\t\t\t\t\tif ( attribute === undefined ) continue;\n\n\t\t\t\t\tconst buffer = attribute.buffer;\n\t\t\t\t\tconst type = attribute.type;\n\t\t\t\t\tconst bytesPerElement = attribute.bytesPerElement;\n\n\t\t\t\t\t// check for integer attributes (WebGL 2 only)\n\n\t\t\t\t\tconst integer = ( capabilities.isWebGL2 === true && ( type === gl.INT || type === gl.UNSIGNED_INT || geometryAttribute.gpuType === IntType ) );\n\n\t\t\t\t\tif ( geometryAttribute.isInterleavedBufferAttribute ) {\n\n\t\t\t\t\t\tconst data = geometryAttribute.data;\n\t\t\t\t\t\tconst stride = data.stride;\n\t\t\t\t\t\tconst offset = geometryAttribute.offset;\n\n\t\t\t\t\t\tif ( data.isInstancedInterleavedBuffer ) {\n\n\t\t\t\t\t\t\tfor ( let i = 0; i < programAttribute.locationSize; i ++ ) {\n\n\t\t\t\t\t\t\t\tenableAttributeAndDivisor( programAttribute.location + i, data.meshPerAttribute );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif ( object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined ) {\n\n\t\t\t\t\t\t\t\tgeometry._maxInstanceCount = data.meshPerAttribute * data.count;\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tfor ( let i = 0; i < programAttribute.locationSize; i ++ ) {\n\n\t\t\t\t\t\t\t\tenableAttribute( programAttribute.location + i );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tgl.bindBuffer( gl.ARRAY_BUFFER, buffer );\n\n\t\t\t\t\t\tfor ( let i = 0; i < programAttribute.locationSize; i ++ ) {\n\n\t\t\t\t\t\t\tvertexAttribPointer(\n\t\t\t\t\t\t\t\tprogramAttribute.location + i,\n\t\t\t\t\t\t\t\tsize / programAttribute.locationSize,\n\t\t\t\t\t\t\t\ttype,\n\t\t\t\t\t\t\t\tnormalized,\n\t\t\t\t\t\t\t\tstride * bytesPerElement,\n\t\t\t\t\t\t\t\t( offset + ( size / programAttribute.locationSize ) * i ) * bytesPerElement,\n\t\t\t\t\t\t\t\tinteger\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tif ( geometryAttribute.isInstancedBufferAttribute ) {\n\n\t\t\t\t\t\t\tfor ( let i = 0; i < programAttribute.locationSize; i ++ ) {\n\n\t\t\t\t\t\t\t\tenableAttributeAndDivisor( programAttribute.location + i, geometryAttribute.meshPerAttribute );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif ( object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined ) {\n\n\t\t\t\t\t\t\t\tgeometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count;\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tfor ( let i = 0; i < programAttribute.locationSize; i ++ ) {\n\n\t\t\t\t\t\t\t\tenableAttribute( programAttribute.location + i );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tgl.bindBuffer( gl.ARRAY_BUFFER, buffer );\n\n\t\t\t\t\t\tfor ( let i = 0; i < programAttribute.locationSize; i ++ ) {\n\n\t\t\t\t\t\t\tvertexAttribPointer(\n\t\t\t\t\t\t\t\tprogramAttribute.location + i,\n\t\t\t\t\t\t\t\tsize / programAttribute.locationSize,\n\t\t\t\t\t\t\t\ttype,\n\t\t\t\t\t\t\t\tnormalized,\n\t\t\t\t\t\t\t\tsize * bytesPerElement,\n\t\t\t\t\t\t\t\t( size / programAttribute.locationSize ) * i * bytesPerElement,\n\t\t\t\t\t\t\t\tinteger\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t} else if ( materialDefaultAttributeValues !== undefined ) {\n\n\t\t\t\t\tconst value = materialDefaultAttributeValues[ name ];\n\n\t\t\t\t\tif ( value !== undefined ) {\n\n\t\t\t\t\t\tswitch ( value.length ) {\n\n\t\t\t\t\t\t\tcase 2:\n\t\t\t\t\t\t\t\tgl.vertexAttrib2fv( programAttribute.location, value );\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tcase 3:\n\t\t\t\t\t\t\t\tgl.vertexAttrib3fv( programAttribute.location, value );\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tcase 4:\n\t\t\t\t\t\t\t\tgl.vertexAttrib4fv( programAttribute.location, value );\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\tgl.vertexAttrib1fv( programAttribute.location, value );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tdisableUnusedAttributes();\n\n\t}\n\n\tfunction dispose() {\n\n\t\treset();\n\n\t\tfor ( const geometryId in bindingStates ) {\n\n\t\t\tconst programMap = bindingStates[ geometryId ];\n\n\t\t\tfor ( const programId in programMap ) {\n\n\t\t\t\tconst stateMap = programMap[ programId ];\n\n\t\t\t\tfor ( const wireframe in stateMap ) {\n\n\t\t\t\t\tdeleteVertexArrayObject( stateMap[ wireframe ].object );\n\n\t\t\t\t\tdelete stateMap[ wireframe ];\n\n\t\t\t\t}\n\n\t\t\t\tdelete programMap[ programId ];\n\n\t\t\t}\n\n\t\t\tdelete bindingStates[ geometryId ];\n\n\t\t}\n\n\t}\n\n\tfunction releaseStatesOfGeometry( geometry ) {\n\n\t\tif ( bindingStates[ geometry.id ] === undefined ) return;\n\n\t\tconst programMap = bindingStates[ geometry.id ];\n\n\t\tfor ( const programId in programMap ) {\n\n\t\t\tconst stateMap = programMap[ programId ];\n\n\t\t\tfor ( const wireframe in stateMap ) {\n\n\t\t\t\tdeleteVertexArrayObject( stateMap[ wireframe ].object );\n\n\t\t\t\tdelete stateMap[ wireframe ];\n\n\t\t\t}\n\n\t\t\tdelete programMap[ programId ];\n\n\t\t}\n\n\t\tdelete bindingStates[ geometry.id ];\n\n\t}\n\n\tfunction releaseStatesOfProgram( program ) {\n\n\t\tfor ( const geometryId in bindingStates ) {\n\n\t\t\tconst programMap = bindingStates[ geometryId ];\n\n\t\t\tif ( programMap[ program.id ] === undefined ) continue;\n\n\t\t\tconst stateMap = programMap[ program.id ];\n\n\t\t\tfor ( const wireframe in stateMap ) {\n\n\t\t\t\tdeleteVertexArrayObject( stateMap[ wireframe ].object );\n\n\t\t\t\tdelete stateMap[ wireframe ];\n\n\t\t\t}\n\n\t\t\tdelete programMap[ program.id ];\n\n\t\t}\n\n\t}\n\n\tfunction reset() {\n\n\t\tresetDefaultState();\n\t\tforceUpdate = true;\n\n\t\tif ( currentState === defaultState ) return;\n\n\t\tcurrentState = defaultState;\n\t\tbindVertexArrayObject( currentState.object );\n\n\t}\n\n\t// for backward-compatibility\n\n\tfunction resetDefaultState() {\n\n\t\tdefaultState.geometry = null;\n\t\tdefaultState.program = null;\n\t\tdefaultState.wireframe = false;\n\n\t}\n\n\treturn {\n\n\t\tsetup: setup,\n\t\treset: reset,\n\t\tresetDefaultState: resetDefaultState,\n\t\tdispose: dispose,\n\t\treleaseStatesOfGeometry: releaseStatesOfGeometry,\n\t\treleaseStatesOfProgram: releaseStatesOfProgram,\n\n\t\tinitAttributes: initAttributes,\n\t\tenableAttribute: enableAttribute,\n\t\tdisableUnusedAttributes: disableUnusedAttributes\n\n\t};\n\n}\n\n\nexport { WebGLBindingStates };\n","function WebGLBufferRenderer( gl, extensions, info, capabilities ) {\n\n\tconst isWebGL2 = capabilities.isWebGL2;\n\n\tlet mode;\n\n\tfunction setMode( value ) {\n\n\t\tmode = value;\n\n\t}\n\n\tfunction render( start, count ) {\n\n\t\tgl.drawArrays( mode, start, count );\n\n\t\tinfo.update( count, mode, 1 );\n\n\t}\n\n\tfunction renderInstances( start, count, primcount ) {\n\n\t\tif ( primcount === 0 ) return;\n\n\t\tlet extension, methodName;\n\n\t\tif ( isWebGL2 ) {\n\n\t\t\textension = gl;\n\t\t\tmethodName = 'drawArraysInstanced';\n\n\t\t} else {\n\n\t\t\textension = extensions.get( 'ANGLE_instanced_arrays' );\n\t\t\tmethodName = 'drawArraysInstancedANGLE';\n\n\t\t\tif ( extension === null ) {\n\n\t\t\t\tconsole.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t}\n\n\t\textension[ methodName ]( mode, start, count, primcount );\n\n\t\tinfo.update( count, mode, primcount );\n\n\t}\n\n\t//\n\n\tthis.setMode = setMode;\n\tthis.render = render;\n\tthis.renderInstances = renderInstances;\n\n}\n\n\nexport { WebGLBufferRenderer };\n","function WebGLCapabilities( gl, extensions, parameters ) {\n\n\tlet maxAnisotropy;\n\n\tfunction getMaxAnisotropy() {\n\n\t\tif ( maxAnisotropy !== undefined ) return maxAnisotropy;\n\n\t\tif ( extensions.has( 'EXT_texture_filter_anisotropic' ) === true ) {\n\n\t\t\tconst extension = extensions.get( 'EXT_texture_filter_anisotropic' );\n\n\t\t\tmaxAnisotropy = gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT );\n\n\t\t} else {\n\n\t\t\tmaxAnisotropy = 0;\n\n\t\t}\n\n\t\treturn maxAnisotropy;\n\n\t}\n\n\tfunction getMaxPrecision( precision ) {\n\n\t\tif ( precision === 'highp' ) {\n\n\t\t\tif ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision > 0 &&\n\t\t\t\tgl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision > 0 ) {\n\n\t\t\t\treturn 'highp';\n\n\t\t\t}\n\n\t\t\tprecision = 'mediump';\n\n\t\t}\n\n\t\tif ( precision === 'mediump' ) {\n\n\t\t\tif ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision > 0 &&\n\t\t\t\tgl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision > 0 ) {\n\n\t\t\t\treturn 'mediump';\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn 'lowp';\n\n\t}\n\n\tconst isWebGL2 = typeof WebGL2RenderingContext !== 'undefined' && gl.constructor.name === 'WebGL2RenderingContext';\n\n\tlet precision = parameters.precision !== undefined ? parameters.precision : 'highp';\n\tconst maxPrecision = getMaxPrecision( precision );\n\n\tif ( maxPrecision !== precision ) {\n\n\t\tconsole.warn( 'THREE.WebGLRenderer:', precision, 'not supported, using', maxPrecision, 'instead.' );\n\t\tprecision = maxPrecision;\n\n\t}\n\n\tconst drawBuffers = isWebGL2 || extensions.has( 'WEBGL_draw_buffers' );\n\n\tconst logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true;\n\n\tconst maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS );\n\tconst maxVertexTextures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS );\n\tconst maxTextureSize = gl.getParameter( gl.MAX_TEXTURE_SIZE );\n\tconst maxCubemapSize = gl.getParameter( gl.MAX_CUBE_MAP_TEXTURE_SIZE );\n\n\tconst maxAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS );\n\tconst maxVertexUniforms = gl.getParameter( gl.MAX_VERTEX_UNIFORM_VECTORS );\n\tconst maxVaryings = gl.getParameter( gl.MAX_VARYING_VECTORS );\n\tconst maxFragmentUniforms = gl.getParameter( gl.MAX_FRAGMENT_UNIFORM_VECTORS );\n\n\tconst vertexTextures = maxVertexTextures > 0;\n\tconst floatFragmentTextures = isWebGL2 || extensions.has( 'OES_texture_float' );\n\tconst floatVertexTextures = vertexTextures && floatFragmentTextures;\n\n\tconst maxSamples = isWebGL2 ? gl.getParameter( gl.MAX_SAMPLES ) : 0;\n\n\treturn {\n\n\t\tisWebGL2: isWebGL2,\n\n\t\tdrawBuffers: drawBuffers,\n\n\t\tgetMaxAnisotropy: getMaxAnisotropy,\n\t\tgetMaxPrecision: getMaxPrecision,\n\n\t\tprecision: precision,\n\t\tlogarithmicDepthBuffer: logarithmicDepthBuffer,\n\n\t\tmaxTextures: maxTextures,\n\t\tmaxVertexTextures: maxVertexTextures,\n\t\tmaxTextureSize: maxTextureSize,\n\t\tmaxCubemapSize: maxCubemapSize,\n\n\t\tmaxAttributes: maxAttributes,\n\t\tmaxVertexUniforms: maxVertexUniforms,\n\t\tmaxVaryings: maxVaryings,\n\t\tmaxFragmentUniforms: maxFragmentUniforms,\n\n\t\tvertexTextures: vertexTextures,\n\t\tfloatFragmentTextures: floatFragmentTextures,\n\t\tfloatVertexTextures: floatVertexTextures,\n\n\t\tmaxSamples: maxSamples\n\n\t};\n\n}\n\n\nexport { WebGLCapabilities };\n","import { Matrix3 } from '../../math/Matrix3.js';\nimport { Plane } from '../../math/Plane.js';\n\nfunction WebGLClipping( properties ) {\n\n\tconst scope = this;\n\n\tlet globalState = null,\n\t\tnumGlobalPlanes = 0,\n\t\tlocalClippingEnabled = false,\n\t\trenderingShadows = false;\n\n\tconst plane = new Plane(),\n\t\tviewNormalMatrix = new Matrix3(),\n\n\t\tuniform = { value: null, needsUpdate: false };\n\n\tthis.uniform = uniform;\n\tthis.numPlanes = 0;\n\tthis.numIntersection = 0;\n\n\tthis.init = function ( planes, enableLocalClipping ) {\n\n\t\tconst enabled =\n\t\t\tplanes.length !== 0 ||\n\t\t\tenableLocalClipping ||\n\t\t\t// enable state of previous frame - the clipping code has to\n\t\t\t// run another frame in order to reset the state:\n\t\t\tnumGlobalPlanes !== 0 ||\n\t\t\tlocalClippingEnabled;\n\n\t\tlocalClippingEnabled = enableLocalClipping;\n\n\t\tnumGlobalPlanes = planes.length;\n\n\t\treturn enabled;\n\n\t};\n\n\tthis.beginShadows = function () {\n\n\t\trenderingShadows = true;\n\t\tprojectPlanes( null );\n\n\t};\n\n\tthis.endShadows = function () {\n\n\t\trenderingShadows = false;\n\n\t};\n\n\tthis.setGlobalState = function ( planes, camera ) {\n\n\t\tglobalState = projectPlanes( planes, camera, 0 );\n\n\t};\n\n\tthis.setState = function ( material, camera, useCache ) {\n\n\t\tconst planes = material.clippingPlanes,\n\t\t\tclipIntersection = material.clipIntersection,\n\t\t\tclipShadows = material.clipShadows;\n\n\t\tconst materialProperties = properties.get( material );\n\n\t\tif ( ! localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && ! clipShadows ) {\n\n\t\t\t// there's no local clipping\n\n\t\t\tif ( renderingShadows ) {\n\n\t\t\t\t// there's no global clipping\n\n\t\t\t\tprojectPlanes( null );\n\n\t\t\t} else {\n\n\t\t\t\tresetGlobalState();\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\tconst nGlobal = renderingShadows ? 0 : numGlobalPlanes,\n\t\t\t\tlGlobal = nGlobal * 4;\n\n\t\t\tlet dstArray = materialProperties.clippingState || null;\n\n\t\t\tuniform.value = dstArray; // ensure unique state\n\n\t\t\tdstArray = projectPlanes( planes, camera, lGlobal, useCache );\n\n\t\t\tfor ( let i = 0; i !== lGlobal; ++ i ) {\n\n\t\t\t\tdstArray[ i ] = globalState[ i ];\n\n\t\t\t}\n\n\t\t\tmaterialProperties.clippingState = dstArray;\n\t\t\tthis.numIntersection = clipIntersection ? this.numPlanes : 0;\n\t\t\tthis.numPlanes += nGlobal;\n\n\t\t}\n\n\n\t};\n\n\tfunction resetGlobalState() {\n\n\t\tif ( uniform.value !== globalState ) {\n\n\t\t\tuniform.value = globalState;\n\t\t\tuniform.needsUpdate = numGlobalPlanes > 0;\n\n\t\t}\n\n\t\tscope.numPlanes = numGlobalPlanes;\n\t\tscope.numIntersection = 0;\n\n\t}\n\n\tfunction projectPlanes( planes, camera, dstOffset, skipTransform ) {\n\n\t\tconst nPlanes = planes !== null ? planes.length : 0;\n\t\tlet dstArray = null;\n\n\t\tif ( nPlanes !== 0 ) {\n\n\t\t\tdstArray = uniform.value;\n\n\t\t\tif ( skipTransform !== true || dstArray === null ) {\n\n\t\t\t\tconst flatSize = dstOffset + nPlanes * 4,\n\t\t\t\t\tviewMatrix = camera.matrixWorldInverse;\n\n\t\t\t\tviewNormalMatrix.getNormalMatrix( viewMatrix );\n\n\t\t\t\tif ( dstArray === null || dstArray.length < flatSize ) {\n\n\t\t\t\t\tdstArray = new Float32Array( flatSize );\n\n\t\t\t\t}\n\n\t\t\t\tfor ( let i = 0, i4 = dstOffset; i !== nPlanes; ++ i, i4 += 4 ) {\n\n\t\t\t\t\tplane.copy( planes[ i ] ).applyMatrix4( viewMatrix, viewNormalMatrix );\n\n\t\t\t\t\tplane.normal.toArray( dstArray, i4 );\n\t\t\t\t\tdstArray[ i4 + 3 ] = plane.constant;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tuniform.value = dstArray;\n\t\t\tuniform.needsUpdate = true;\n\n\t\t}\n\n\t\tscope.numPlanes = nPlanes;\n\t\tscope.numIntersection = 0;\n\n\t\treturn dstArray;\n\n\t}\n\n}\n\n\nexport { WebGLClipping };\n","import { CubeReflectionMapping, CubeRefractionMapping, EquirectangularReflectionMapping, EquirectangularRefractionMapping } from '../../constants.js';\nimport { WebGLCubeRenderTarget } from '../WebGLCubeRenderTarget.js';\n\nfunction WebGLCubeMaps( renderer ) {\n\n\tlet cubemaps = new WeakMap();\n\n\tfunction mapTextureMapping( texture, mapping ) {\n\n\t\tif ( mapping === EquirectangularReflectionMapping ) {\n\n\t\t\ttexture.mapping = CubeReflectionMapping;\n\n\t\t} else if ( mapping === EquirectangularRefractionMapping ) {\n\n\t\t\ttexture.mapping = CubeRefractionMapping;\n\n\t\t}\n\n\t\treturn texture;\n\n\t}\n\n\tfunction get( texture ) {\n\n\t\tif ( texture && texture.isTexture && texture.isRenderTargetTexture === false ) {\n\n\t\t\tconst mapping = texture.mapping;\n\n\t\t\tif ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ) {\n\n\t\t\t\tif ( cubemaps.has( texture ) ) {\n\n\t\t\t\t\tconst cubemap = cubemaps.get( texture ).texture;\n\t\t\t\t\treturn mapTextureMapping( cubemap, texture.mapping );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tconst image = texture.image;\n\n\t\t\t\t\tif ( image && image.height > 0 ) {\n\n\t\t\t\t\t\tconst renderTarget = new WebGLCubeRenderTarget( image.height / 2 );\n\t\t\t\t\t\trenderTarget.fromEquirectangularTexture( renderer, texture );\n\t\t\t\t\t\tcubemaps.set( texture, renderTarget );\n\n\t\t\t\t\t\ttexture.addEventListener( 'dispose', onTextureDispose );\n\n\t\t\t\t\t\treturn mapTextureMapping( renderTarget.texture, texture.mapping );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t// image not yet ready. try the conversion next frame\n\n\t\t\t\t\t\treturn null;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn texture;\n\n\t}\n\n\tfunction onTextureDispose( event ) {\n\n\t\tconst texture = event.target;\n\n\t\ttexture.removeEventListener( 'dispose', onTextureDispose );\n\n\t\tconst cubemap = cubemaps.get( texture );\n\n\t\tif ( cubemap !== undefined ) {\n\n\t\t\tcubemaps.delete( texture );\n\t\t\tcubemap.dispose();\n\n\t\t}\n\n\t}\n\n\tfunction dispose() {\n\n\t\tcubemaps = new WeakMap();\n\n\t}\n\n\treturn {\n\t\tget: get,\n\t\tdispose: dispose\n\t};\n\n}\n\nexport { WebGLCubeMaps };\n","import { Camera } from './Camera.js';\n\nclass OrthographicCamera extends Camera {\n\n\tconstructor( left = - 1, right = 1, top = 1, bottom = - 1, near = 0.1, far = 2000 ) {\n\n\t\tsuper();\n\n\t\tthis.isOrthographicCamera = true;\n\n\t\tthis.type = 'OrthographicCamera';\n\n\t\tthis.zoom = 1;\n\t\tthis.view = null;\n\n\t\tthis.left = left;\n\t\tthis.right = right;\n\t\tthis.top = top;\n\t\tthis.bottom = bottom;\n\n\t\tthis.near = near;\n\t\tthis.far = far;\n\n\t\tthis.updateProjectionMatrix();\n\n\t}\n\n\tcopy( source, recursive ) {\n\n\t\tsuper.copy( source, recursive );\n\n\t\tthis.left = source.left;\n\t\tthis.right = source.right;\n\t\tthis.top = source.top;\n\t\tthis.bottom = source.bottom;\n\t\tthis.near = source.near;\n\t\tthis.far = source.far;\n\n\t\tthis.zoom = source.zoom;\n\t\tthis.view = source.view === null ? null : Object.assign( {}, source.view );\n\n\t\treturn this;\n\n\t}\n\n\tsetViewOffset( fullWidth, fullHeight, x, y, width, height ) {\n\n\t\tif ( this.view === null ) {\n\n\t\t\tthis.view = {\n\t\t\t\tenabled: true,\n\t\t\t\tfullWidth: 1,\n\t\t\t\tfullHeight: 1,\n\t\t\t\toffsetX: 0,\n\t\t\t\toffsetY: 0,\n\t\t\t\twidth: 1,\n\t\t\t\theight: 1\n\t\t\t};\n\n\t\t}\n\n\t\tthis.view.enabled = true;\n\t\tthis.view.fullWidth = fullWidth;\n\t\tthis.view.fullHeight = fullHeight;\n\t\tthis.view.offsetX = x;\n\t\tthis.view.offsetY = y;\n\t\tthis.view.width = width;\n\t\tthis.view.height = height;\n\n\t\tthis.updateProjectionMatrix();\n\n\t}\n\n\tclearViewOffset() {\n\n\t\tif ( this.view !== null ) {\n\n\t\t\tthis.view.enabled = false;\n\n\t\t}\n\n\t\tthis.updateProjectionMatrix();\n\n\t}\n\n\tupdateProjectionMatrix() {\n\n\t\tconst dx = ( this.right - this.left ) / ( 2 * this.zoom );\n\t\tconst dy = ( this.top - this.bottom ) / ( 2 * this.zoom );\n\t\tconst cx = ( this.right + this.left ) / 2;\n\t\tconst cy = ( this.top + this.bottom ) / 2;\n\n\t\tlet left = cx - dx;\n\t\tlet right = cx + dx;\n\t\tlet top = cy + dy;\n\t\tlet bottom = cy - dy;\n\n\t\tif ( this.view !== null && this.view.enabled ) {\n\n\t\t\tconst scaleW = ( this.right - this.left ) / this.view.fullWidth / this.zoom;\n\t\t\tconst scaleH = ( this.top - this.bottom ) / this.view.fullHeight / this.zoom;\n\n\t\t\tleft += scaleW * this.view.offsetX;\n\t\t\tright = left + scaleW * this.view.width;\n\t\t\ttop -= scaleH * this.view.offsetY;\n\t\t\tbottom = top - scaleH * this.view.height;\n\n\t\t}\n\n\t\tthis.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far, this.coordinateSystem );\n\n\t\tthis.projectionMatrixInverse.copy( this.projectionMatrix ).invert();\n\n\t}\n\n\ttoJSON( meta ) {\n\n\t\tconst data = super.toJSON( meta );\n\n\t\tdata.object.zoom = this.zoom;\n\t\tdata.object.left = this.left;\n\t\tdata.object.right = this.right;\n\t\tdata.object.top = this.top;\n\t\tdata.object.bottom = this.bottom;\n\t\tdata.object.near = this.near;\n\t\tdata.object.far = this.far;\n\n\t\tif ( this.view !== null ) data.object.view = Object.assign( {}, this.view );\n\n\t\treturn data;\n\n\t}\n\n}\n\nexport { OrthographicCamera };\n","import {\n\tCubeReflectionMapping,\n\tCubeRefractionMapping,\n\tCubeUVReflectionMapping,\n\tLinearFilter,\n\tNoToneMapping,\n\tNoBlending,\n\tRGBAFormat,\n\tHalfFloatType,\n\tBackSide,\n\tLinearSRGBColorSpace\n} from '../constants.js';\n\nimport { BufferAttribute } from '../core/BufferAttribute.js';\nimport { BufferGeometry } from '../core/BufferGeometry.js';\nimport { Mesh } from '../objects/Mesh.js';\nimport { OrthographicCamera } from '../cameras/OrthographicCamera.js';\nimport { PerspectiveCamera } from '../cameras/PerspectiveCamera.js';\nimport { ShaderMaterial } from '../materials/ShaderMaterial.js';\nimport { Vector3 } from '../math/Vector3.js';\nimport { Color } from '../math/Color.js';\nimport { WebGLRenderTarget } from '../renderers/WebGLRenderTarget.js';\nimport { MeshBasicMaterial } from '../materials/MeshBasicMaterial.js';\nimport { BoxGeometry } from '../geometries/BoxGeometry.js';\n\nconst LOD_MIN = 4;\n\n// The standard deviations (radians) associated with the extra mips. These are\n// chosen to approximate a Trowbridge-Reitz distribution function times the\n// geometric shadowing function. These sigma values squared must match the\n// variance #defines in cube_uv_reflection_fragment.glsl.js.\nconst EXTRA_LOD_SIGMA = [ 0.125, 0.215, 0.35, 0.446, 0.526, 0.582 ];\n\n// The maximum length of the blur for loop. Smaller sigmas will use fewer\n// samples and exit early, but not recompile the shader.\nconst MAX_SAMPLES = 20;\n\nconst _flatCamera = /*@__PURE__*/ new OrthographicCamera();\nconst _clearColor = /*@__PURE__*/ new Color();\nlet _oldTarget = null;\nlet _oldActiveCubeFace = 0;\nlet _oldActiveMipmapLevel = 0;\n\n// Golden Ratio\nconst PHI = ( 1 + Math.sqrt( 5 ) ) / 2;\nconst INV_PHI = 1 / PHI;\n\n// Vertices of a dodecahedron (except the opposites, which represent the\n// same axis), used as axis directions evenly spread on a sphere.\nconst _axisDirections = [\n\t/*@__PURE__*/ new Vector3( 1, 1, 1 ),\n\t/*@__PURE__*/ new Vector3( - 1, 1, 1 ),\n\t/*@__PURE__*/ new Vector3( 1, 1, - 1 ),\n\t/*@__PURE__*/ new Vector3( - 1, 1, - 1 ),\n\t/*@__PURE__*/ new Vector3( 0, PHI, INV_PHI ),\n\t/*@__PURE__*/ new Vector3( 0, PHI, - INV_PHI ),\n\t/*@__PURE__*/ new Vector3( INV_PHI, 0, PHI ),\n\t/*@__PURE__*/ new Vector3( - INV_PHI, 0, PHI ),\n\t/*@__PURE__*/ new Vector3( PHI, INV_PHI, 0 ),\n\t/*@__PURE__*/ new Vector3( - PHI, INV_PHI, 0 ) ];\n\n/**\n * This class generates a Prefiltered, Mipmapped Radiance Environment Map\n * (PMREM) from a cubeMap environment texture. This allows different levels of\n * blur to be quickly accessed based on material roughness. It is packed into a\n * special CubeUV format that allows us to perform custom interpolation so that\n * we can support nonlinear formats such as RGBE. Unlike a traditional mipmap\n * chain, it only goes down to the LOD_MIN level (above), and then creates extra\n * even more filtered 'mips' at the same LOD_MIN resolution, associated with\n * higher roughness levels. In this way we maintain resolution to smoothly\n * interpolate diffuse lighting while limiting sampling computation.\n *\n * Paper: Fast, Accurate Image-Based Lighting\n * https://drive.google.com/file/d/15y8r_UpKlU9SvV4ILb0C3qCPecS8pvLz/view\n*/\n\nclass PMREMGenerator {\n\n\tconstructor( renderer ) {\n\n\t\tthis._renderer = renderer;\n\t\tthis._pingPongRenderTarget = null;\n\n\t\tthis._lodMax = 0;\n\t\tthis._cubeSize = 0;\n\t\tthis._lodPlanes = [];\n\t\tthis._sizeLods = [];\n\t\tthis._sigmas = [];\n\n\t\tthis._blurMaterial = null;\n\t\tthis._cubemapMaterial = null;\n\t\tthis._equirectMaterial = null;\n\n\t\tthis._compileMaterial( this._blurMaterial );\n\n\t}\n\n\t/**\n\t * Generates a PMREM from a supplied Scene, which can be faster than using an\n\t * image if networking bandwidth is low. Optional sigma specifies a blur radius\n\t * in radians to be applied to the scene before PMREM generation. Optional near\n\t * and far planes ensure the scene is rendered in its entirety (the cubeCamera\n\t * is placed at the origin).\n\t */\n\tfromScene( scene, sigma = 0, near = 0.1, far = 100 ) {\n\n\t\t_oldTarget = this._renderer.getRenderTarget();\n\t\t_oldActiveCubeFace = this._renderer.getActiveCubeFace();\n\t\t_oldActiveMipmapLevel = this._renderer.getActiveMipmapLevel();\n\n\t\tthis._setSize( 256 );\n\n\t\tconst cubeUVRenderTarget = this._allocateTargets();\n\t\tcubeUVRenderTarget.depthBuffer = true;\n\n\t\tthis._sceneToCubeUV( scene, near, far, cubeUVRenderTarget );\n\n\t\tif ( sigma > 0 ) {\n\n\t\t\tthis._blur( cubeUVRenderTarget, 0, 0, sigma );\n\n\t\t}\n\n\t\tthis._applyPMREM( cubeUVRenderTarget );\n\t\tthis._cleanup( cubeUVRenderTarget );\n\n\t\treturn cubeUVRenderTarget;\n\n\t}\n\n\t/**\n\t * Generates a PMREM from an equirectangular texture, which can be either LDR\n\t * or HDR. The ideal input image size is 1k (1024 x 512),\n\t * as this matches best with the 256 x 256 cubemap output.\n\t */\n\tfromEquirectangular( equirectangular, renderTarget = null ) {\n\n\t\treturn this._fromTexture( equirectangular, renderTarget );\n\n\t}\n\n\t/**\n\t * Generates a PMREM from an cubemap texture, which can be either LDR\n\t * or HDR. The ideal input cube size is 256 x 256,\n\t * as this matches best with the 256 x 256 cubemap output.\n\t */\n\tfromCubemap( cubemap, renderTarget = null ) {\n\n\t\treturn this._fromTexture( cubemap, renderTarget );\n\n\t}\n\n\t/**\n\t * Pre-compiles the cubemap shader. You can get faster start-up by invoking this method during\n\t * your texture's network fetch for increased concurrency.\n\t */\n\tcompileCubemapShader() {\n\n\t\tif ( this._cubemapMaterial === null ) {\n\n\t\t\tthis._cubemapMaterial = _getCubemapMaterial();\n\t\t\tthis._compileMaterial( this._cubemapMaterial );\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Pre-compiles the equirectangular shader. You can get faster start-up by invoking this method during\n\t * your texture's network fetch for increased concurrency.\n\t */\n\tcompileEquirectangularShader() {\n\n\t\tif ( this._equirectMaterial === null ) {\n\n\t\t\tthis._equirectMaterial = _getEquirectMaterial();\n\t\t\tthis._compileMaterial( this._equirectMaterial );\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Disposes of the PMREMGenerator's internal memory. Note that PMREMGenerator is a static class,\n\t * so you should not need more than one PMREMGenerator object. If you do, calling dispose() on\n\t * one of them will cause any others to also become unusable.\n\t */\n\tdispose() {\n\n\t\tthis._dispose();\n\n\t\tif ( this._cubemapMaterial !== null ) this._cubemapMaterial.dispose();\n\t\tif ( this._equirectMaterial !== null ) this._equirectMaterial.dispose();\n\n\t}\n\n\t// private interface\n\n\t_setSize( cubeSize ) {\n\n\t\tthis._lodMax = Math.floor( Math.log2( cubeSize ) );\n\t\tthis._cubeSize = Math.pow( 2, this._lodMax );\n\n\t}\n\n\t_dispose() {\n\n\t\tif ( this._blurMaterial !== null ) this._blurMaterial.dispose();\n\n\t\tif ( this._pingPongRenderTarget !== null ) this._pingPongRenderTarget.dispose();\n\n\t\tfor ( let i = 0; i < this._lodPlanes.length; i ++ ) {\n\n\t\t\tthis._lodPlanes[ i ].dispose();\n\n\t\t}\n\n\t}\n\n\t_cleanup( outputTarget ) {\n\n\t\tthis._renderer.setRenderTarget( _oldTarget, _oldActiveCubeFace, _oldActiveMipmapLevel );\n\t\toutputTarget.scissorTest = false;\n\t\t_setViewport( outputTarget, 0, 0, outputTarget.width, outputTarget.height );\n\n\t}\n\n\t_fromTexture( texture, renderTarget ) {\n\n\t\tif ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping ) {\n\n\t\t\tthis._setSize( texture.image.length === 0 ? 16 : ( texture.image[ 0 ].width || texture.image[ 0 ].image.width ) );\n\n\t\t} else { // Equirectangular\n\n\t\t\tthis._setSize( texture.image.width / 4 );\n\n\t\t}\n\n\t\t_oldTarget = this._renderer.getRenderTarget();\n\t\t_oldActiveCubeFace = this._renderer.getActiveCubeFace();\n\t\t_oldActiveMipmapLevel = this._renderer.getActiveMipmapLevel();\n\n\t\tconst cubeUVRenderTarget = renderTarget || this._allocateTargets();\n\t\tthis._textureToCubeUV( texture, cubeUVRenderTarget );\n\t\tthis._applyPMREM( cubeUVRenderTarget );\n\t\tthis._cleanup( cubeUVRenderTarget );\n\n\t\treturn cubeUVRenderTarget;\n\n\t}\n\n\t_allocateTargets() {\n\n\t\tconst width = 3 * Math.max( this._cubeSize, 16 * 7 );\n\t\tconst height = 4 * this._cubeSize;\n\n\t\tconst params = {\n\t\t\tmagFilter: LinearFilter,\n\t\t\tminFilter: LinearFilter,\n\t\t\tgenerateMipmaps: false,\n\t\t\ttype: HalfFloatType,\n\t\t\tformat: RGBAFormat,\n\t\t\tcolorSpace: LinearSRGBColorSpace,\n\t\t\tdepthBuffer: false\n\t\t};\n\n\t\tconst cubeUVRenderTarget = _createRenderTarget( width, height, params );\n\n\t\tif ( this._pingPongRenderTarget === null || this._pingPongRenderTarget.width !== width || this._pingPongRenderTarget.height !== height ) {\n\n\t\t\tif ( this._pingPongRenderTarget !== null ) {\n\n\t\t\t\tthis._dispose();\n\n\t\t\t}\n\n\t\t\tthis._pingPongRenderTarget = _createRenderTarget( width, height, params );\n\n\t\t\tconst { _lodMax } = this;\n\t\t\t( { sizeLods: this._sizeLods, lodPlanes: this._lodPlanes, sigmas: this._sigmas } = _createPlanes( _lodMax ) );\n\n\t\t\tthis._blurMaterial = _getBlurShader( _lodMax, width, height );\n\n\t\t}\n\n\t\treturn cubeUVRenderTarget;\n\n\t}\n\n\t_compileMaterial( material ) {\n\n\t\tconst tmpMesh = new Mesh( this._lodPlanes[ 0 ], material );\n\t\tthis._renderer.compile( tmpMesh, _flatCamera );\n\n\t}\n\n\t_sceneToCubeUV( scene, near, far, cubeUVRenderTarget ) {\n\n\t\tconst fov = 90;\n\t\tconst aspect = 1;\n\t\tconst cubeCamera = new PerspectiveCamera( fov, aspect, near, far );\n\t\tconst upSign = [ 1, - 1, 1, 1, 1, 1 ];\n\t\tconst forwardSign = [ 1, 1, 1, - 1, - 1, - 1 ];\n\t\tconst renderer = this._renderer;\n\n\t\tconst originalAutoClear = renderer.autoClear;\n\t\tconst toneMapping = renderer.toneMapping;\n\t\trenderer.getClearColor( _clearColor );\n\n\t\trenderer.toneMapping = NoToneMapping;\n\t\trenderer.autoClear = false;\n\n\t\tconst backgroundMaterial = new MeshBasicMaterial( {\n\t\t\tname: 'PMREM.Background',\n\t\t\tside: BackSide,\n\t\t\tdepthWrite: false,\n\t\t\tdepthTest: false,\n\t\t} );\n\n\t\tconst backgroundBox = new Mesh( new BoxGeometry(), backgroundMaterial );\n\n\t\tlet useSolidColor = false;\n\t\tconst background = scene.background;\n\n\t\tif ( background ) {\n\n\t\t\tif ( background.isColor ) {\n\n\t\t\t\tbackgroundMaterial.color.copy( background );\n\t\t\t\tscene.background = null;\n\t\t\t\tuseSolidColor = true;\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\tbackgroundMaterial.color.copy( _clearColor );\n\t\t\tuseSolidColor = true;\n\n\t\t}\n\n\t\tfor ( let i = 0; i < 6; i ++ ) {\n\n\t\t\tconst col = i % 3;\n\n\t\t\tif ( col === 0 ) {\n\n\t\t\t\tcubeCamera.up.set( 0, upSign[ i ], 0 );\n\t\t\t\tcubeCamera.lookAt( forwardSign[ i ], 0, 0 );\n\n\t\t\t} else if ( col === 1 ) {\n\n\t\t\t\tcubeCamera.up.set( 0, 0, upSign[ i ] );\n\t\t\t\tcubeCamera.lookAt( 0, forwardSign[ i ], 0 );\n\n\t\t\t} else {\n\n\t\t\t\tcubeCamera.up.set( 0, upSign[ i ], 0 );\n\t\t\t\tcubeCamera.lookAt( 0, 0, forwardSign[ i ] );\n\n\t\t\t}\n\n\t\t\tconst size = this._cubeSize;\n\n\t\t\t_setViewport( cubeUVRenderTarget, col * size, i > 2 ? size : 0, size, size );\n\n\t\t\trenderer.setRenderTarget( cubeUVRenderTarget );\n\n\t\t\tif ( useSolidColor ) {\n\n\t\t\t\trenderer.render( backgroundBox, cubeCamera );\n\n\t\t\t}\n\n\t\t\trenderer.render( scene, cubeCamera );\n\n\t\t}\n\n\t\tbackgroundBox.geometry.dispose();\n\t\tbackgroundBox.material.dispose();\n\n\t\trenderer.toneMapping = toneMapping;\n\t\trenderer.autoClear = originalAutoClear;\n\t\tscene.background = background;\n\n\t}\n\n\t_textureToCubeUV( texture, cubeUVRenderTarget ) {\n\n\t\tconst renderer = this._renderer;\n\n\t\tconst isCubeTexture = ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping );\n\n\t\tif ( isCubeTexture ) {\n\n\t\t\tif ( this._cubemapMaterial === null ) {\n\n\t\t\t\tthis._cubemapMaterial = _getCubemapMaterial();\n\n\t\t\t}\n\n\t\t\tthis._cubemapMaterial.uniforms.flipEnvMap.value = ( texture.isRenderTargetTexture === false ) ? - 1 : 1;\n\n\t\t} else {\n\n\t\t\tif ( this._equirectMaterial === null ) {\n\n\t\t\t\tthis._equirectMaterial = _getEquirectMaterial();\n\n\t\t\t}\n\n\t\t}\n\n\t\tconst material = isCubeTexture ? this._cubemapMaterial : this._equirectMaterial;\n\t\tconst mesh = new Mesh( this._lodPlanes[ 0 ], material );\n\n\t\tconst uniforms = material.uniforms;\n\n\t\tuniforms[ 'envMap' ].value = texture;\n\n\t\tconst size = this._cubeSize;\n\n\t\t_setViewport( cubeUVRenderTarget, 0, 0, 3 * size, 2 * size );\n\n\t\trenderer.setRenderTarget( cubeUVRenderTarget );\n\t\trenderer.render( mesh, _flatCamera );\n\n\t}\n\n\t_applyPMREM( cubeUVRenderTarget ) {\n\n\t\tconst renderer = this._renderer;\n\t\tconst autoClear = renderer.autoClear;\n\t\trenderer.autoClear = false;\n\n\t\tfor ( let i = 1; i < this._lodPlanes.length; i ++ ) {\n\n\t\t\tconst sigma = Math.sqrt( this._sigmas[ i ] * this._sigmas[ i ] - this._sigmas[ i - 1 ] * this._sigmas[ i - 1 ] );\n\n\t\t\tconst poleAxis = _axisDirections[ ( i - 1 ) % _axisDirections.length ];\n\n\t\t\tthis._blur( cubeUVRenderTarget, i - 1, i, sigma, poleAxis );\n\n\t\t}\n\n\t\trenderer.autoClear = autoClear;\n\n\t}\n\n\t/**\n\t * This is a two-pass Gaussian blur for a cubemap. Normally this is done\n\t * vertically and horizontally, but this breaks down on a cube. Here we apply\n\t * the blur latitudinally (around the poles), and then longitudinally (towards\n\t * the poles) to approximate the orthogonally-separable blur. It is least\n\t * accurate at the poles, but still does a decent job.\n\t */\n\t_blur( cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis ) {\n\n\t\tconst pingPongRenderTarget = this._pingPongRenderTarget;\n\n\t\tthis._halfBlur(\n\t\t\tcubeUVRenderTarget,\n\t\t\tpingPongRenderTarget,\n\t\t\tlodIn,\n\t\t\tlodOut,\n\t\t\tsigma,\n\t\t\t'latitudinal',\n\t\t\tpoleAxis );\n\n\t\tthis._halfBlur(\n\t\t\tpingPongRenderTarget,\n\t\t\tcubeUVRenderTarget,\n\t\t\tlodOut,\n\t\t\tlodOut,\n\t\t\tsigma,\n\t\t\t'longitudinal',\n\t\t\tpoleAxis );\n\n\t}\n\n\t_halfBlur( targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis ) {\n\n\t\tconst renderer = this._renderer;\n\t\tconst blurMaterial = this._blurMaterial;\n\n\t\tif ( direction !== 'latitudinal' && direction !== 'longitudinal' ) {\n\n\t\t\tconsole.error(\n\t\t\t\t'blur direction must be either latitudinal or longitudinal!' );\n\n\t\t}\n\n\t\t// Number of standard deviations at which to cut off the discrete approximation.\n\t\tconst STANDARD_DEVIATIONS = 3;\n\n\t\tconst blurMesh = new Mesh( this._lodPlanes[ lodOut ], blurMaterial );\n\t\tconst blurUniforms = blurMaterial.uniforms;\n\n\t\tconst pixels = this._sizeLods[ lodIn ] - 1;\n\t\tconst radiansPerPixel = isFinite( sigmaRadians ) ? Math.PI / ( 2 * pixels ) : 2 * Math.PI / ( 2 * MAX_SAMPLES - 1 );\n\t\tconst sigmaPixels = sigmaRadians / radiansPerPixel;\n\t\tconst samples = isFinite( sigmaRadians ) ? 1 + Math.floor( STANDARD_DEVIATIONS * sigmaPixels ) : MAX_SAMPLES;\n\n\t\tif ( samples > MAX_SAMPLES ) {\n\n\t\t\tconsole.warn( `sigmaRadians, ${\n\t\t\t\tsigmaRadians}, is too large and will clip, as it requested ${\n\t\t\t\tsamples} samples when the maximum is set to ${MAX_SAMPLES}` );\n\n\t\t}\n\n\t\tconst weights = [];\n\t\tlet sum = 0;\n\n\t\tfor ( let i = 0; i < MAX_SAMPLES; ++ i ) {\n\n\t\t\tconst x = i / sigmaPixels;\n\t\t\tconst weight = Math.exp( - x * x / 2 );\n\t\t\tweights.push( weight );\n\n\t\t\tif ( i === 0 ) {\n\n\t\t\t\tsum += weight;\n\n\t\t\t} else if ( i < samples ) {\n\n\t\t\t\tsum += 2 * weight;\n\n\t\t\t}\n\n\t\t}\n\n\t\tfor ( let i = 0; i < weights.length; i ++ ) {\n\n\t\t\tweights[ i ] = weights[ i ] / sum;\n\n\t\t}\n\n\t\tblurUniforms[ 'envMap' ].value = targetIn.texture;\n\t\tblurUniforms[ 'samples' ].value = samples;\n\t\tblurUniforms[ 'weights' ].value = weights;\n\t\tblurUniforms[ 'latitudinal' ].value = direction === 'latitudinal';\n\n\t\tif ( poleAxis ) {\n\n\t\t\tblurUniforms[ 'poleAxis' ].value = poleAxis;\n\n\t\t}\n\n\t\tconst { _lodMax } = this;\n\t\tblurUniforms[ 'dTheta' ].value = radiansPerPixel;\n\t\tblurUniforms[ 'mipInt' ].value = _lodMax - lodIn;\n\n\t\tconst outputSize = this._sizeLods[ lodOut ];\n\t\tconst x = 3 * outputSize * ( lodOut > _lodMax - LOD_MIN ? lodOut - _lodMax + LOD_MIN : 0 );\n\t\tconst y = 4 * ( this._cubeSize - outputSize );\n\n\t\t_setViewport( targetOut, x, y, 3 * outputSize, 2 * outputSize );\n\t\trenderer.setRenderTarget( targetOut );\n\t\trenderer.render( blurMesh, _flatCamera );\n\n\t}\n\n}\n\n\n\nfunction _createPlanes( lodMax ) {\n\n\tconst lodPlanes = [];\n\tconst sizeLods = [];\n\tconst sigmas = [];\n\n\tlet lod = lodMax;\n\n\tconst totalLods = lodMax - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length;\n\n\tfor ( let i = 0; i < totalLods; i ++ ) {\n\n\t\tconst sizeLod = Math.pow( 2, lod );\n\t\tsizeLods.push( sizeLod );\n\t\tlet sigma = 1.0 / sizeLod;\n\n\t\tif ( i > lodMax - LOD_MIN ) {\n\n\t\t\tsigma = EXTRA_LOD_SIGMA[ i - lodMax + LOD_MIN - 1 ];\n\n\t\t} else if ( i === 0 ) {\n\n\t\t\tsigma = 0;\n\n\t\t}\n\n\t\tsigmas.push( sigma );\n\n\t\tconst texelSize = 1.0 / ( sizeLod - 2 );\n\t\tconst min = - texelSize;\n\t\tconst max = 1 + texelSize;\n\t\tconst uv1 = [ min, min, max, min, max, max, min, min, max, max, min, max ];\n\n\t\tconst cubeFaces = 6;\n\t\tconst vertices = 6;\n\t\tconst positionSize = 3;\n\t\tconst uvSize = 2;\n\t\tconst faceIndexSize = 1;\n\n\t\tconst position = new Float32Array( positionSize * vertices * cubeFaces );\n\t\tconst uv = new Float32Array( uvSize * vertices * cubeFaces );\n\t\tconst faceIndex = new Float32Array( faceIndexSize * vertices * cubeFaces );\n\n\t\tfor ( let face = 0; face < cubeFaces; face ++ ) {\n\n\t\t\tconst x = ( face % 3 ) * 2 / 3 - 1;\n\t\t\tconst y = face > 2 ? 0 : - 1;\n\t\t\tconst coordinates = [\n\t\t\t\tx, y, 0,\n\t\t\t\tx + 2 / 3, y, 0,\n\t\t\t\tx + 2 / 3, y + 1, 0,\n\t\t\t\tx, y, 0,\n\t\t\t\tx + 2 / 3, y + 1, 0,\n\t\t\t\tx, y + 1, 0\n\t\t\t];\n\t\t\tposition.set( coordinates, positionSize * vertices * face );\n\t\t\tuv.set( uv1, uvSize * vertices * face );\n\t\t\tconst fill = [ face, face, face, face, face, face ];\n\t\t\tfaceIndex.set( fill, faceIndexSize * vertices * face );\n\n\t\t}\n\n\t\tconst planes = new BufferGeometry();\n\t\tplanes.setAttribute( 'position', new BufferAttribute( position, positionSize ) );\n\t\tplanes.setAttribute( 'uv', new BufferAttribute( uv, uvSize ) );\n\t\tplanes.setAttribute( 'faceIndex', new BufferAttribute( faceIndex, faceIndexSize ) );\n\t\tlodPlanes.push( planes );\n\n\t\tif ( lod > LOD_MIN ) {\n\n\t\t\tlod --;\n\n\t\t}\n\n\t}\n\n\treturn { lodPlanes, sizeLods, sigmas };\n\n}\n\nfunction _createRenderTarget( width, height, params ) {\n\n\tconst cubeUVRenderTarget = new WebGLRenderTarget( width, height, params );\n\tcubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping;\n\tcubeUVRenderTarget.texture.name = 'PMREM.cubeUv';\n\tcubeUVRenderTarget.scissorTest = true;\n\treturn cubeUVRenderTarget;\n\n}\n\nfunction _setViewport( target, x, y, width, height ) {\n\n\ttarget.viewport.set( x, y, width, height );\n\ttarget.scissor.set( x, y, width, height );\n\n}\n\nfunction _getBlurShader( lodMax, width, height ) {\n\n\tconst weights = new Float32Array( MAX_SAMPLES );\n\tconst poleAxis = new Vector3( 0, 1, 0 );\n\tconst shaderMaterial = new ShaderMaterial( {\n\n\t\tname: 'SphericalGaussianBlur',\n\n\t\tdefines: {\n\t\t\t'n': MAX_SAMPLES,\n\t\t\t'CUBEUV_TEXEL_WIDTH': 1.0 / width,\n\t\t\t'CUBEUV_TEXEL_HEIGHT': 1.0 / height,\n\t\t\t'CUBEUV_MAX_MIP': `${lodMax}.0`,\n\t\t},\n\n\t\tuniforms: {\n\t\t\t'envMap': { value: null },\n\t\t\t'samples': { value: 1 },\n\t\t\t'weights': { value: weights },\n\t\t\t'latitudinal': { value: false },\n\t\t\t'dTheta': { value: 0 },\n\t\t\t'mipInt': { value: 0 },\n\t\t\t'poleAxis': { value: poleAxis }\n\t\t},\n\n\t\tvertexShader: _getCommonVertexShader(),\n\n\t\tfragmentShader: /* glsl */`\n\n\t\t\tprecision mediump float;\n\t\t\tprecision mediump int;\n\n\t\t\tvarying vec3 vOutputDirection;\n\n\t\t\tuniform sampler2D envMap;\n\t\t\tuniform int samples;\n\t\t\tuniform float weights[ n ];\n\t\t\tuniform bool latitudinal;\n\t\t\tuniform float dTheta;\n\t\t\tuniform float mipInt;\n\t\t\tuniform vec3 poleAxis;\n\n\t\t\t#define ENVMAP_TYPE_CUBE_UV\n\t\t\t#include \n\n\t\t\tvec3 getSample( float theta, vec3 axis ) {\n\n\t\t\t\tfloat cosTheta = cos( theta );\n\t\t\t\t// Rodrigues' axis-angle rotation\n\t\t\t\tvec3 sampleDirection = vOutputDirection * cosTheta\n\t\t\t\t\t+ cross( axis, vOutputDirection ) * sin( theta )\n\t\t\t\t\t+ axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta );\n\n\t\t\t\treturn bilinearCubeUV( envMap, sampleDirection, mipInt );\n\n\t\t\t}\n\n\t\t\tvoid main() {\n\n\t\t\t\tvec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection );\n\n\t\t\t\tif ( all( equal( axis, vec3( 0.0 ) ) ) ) {\n\n\t\t\t\t\taxis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x );\n\n\t\t\t\t}\n\n\t\t\t\taxis = normalize( axis );\n\n\t\t\t\tgl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 );\n\t\t\t\tgl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis );\n\n\t\t\t\tfor ( int i = 1; i < n; i++ ) {\n\n\t\t\t\t\tif ( i >= samples ) {\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tfloat theta = dTheta * float( i );\n\t\t\t\t\tgl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis );\n\t\t\t\t\tgl_FragColor.rgb += weights[ i ] * getSample( theta, axis );\n\n\t\t\t\t}\n\n\t\t\t}\n\t\t`,\n\n\t\tblending: NoBlending,\n\t\tdepthTest: false,\n\t\tdepthWrite: false\n\n\t} );\n\n\treturn shaderMaterial;\n\n}\n\nfunction _getEquirectMaterial() {\n\n\treturn new ShaderMaterial( {\n\n\t\tname: 'EquirectangularToCubeUV',\n\n\t\tuniforms: {\n\t\t\t'envMap': { value: null }\n\t\t},\n\n\t\tvertexShader: _getCommonVertexShader(),\n\n\t\tfragmentShader: /* glsl */`\n\n\t\t\tprecision mediump float;\n\t\t\tprecision mediump int;\n\n\t\t\tvarying vec3 vOutputDirection;\n\n\t\t\tuniform sampler2D envMap;\n\n\t\t\t#include \n\n\t\t\tvoid main() {\n\n\t\t\t\tvec3 outputDirection = normalize( vOutputDirection );\n\t\t\t\tvec2 uv = equirectUv( outputDirection );\n\n\t\t\t\tgl_FragColor = vec4( texture2D ( envMap, uv ).rgb, 1.0 );\n\n\t\t\t}\n\t\t`,\n\n\t\tblending: NoBlending,\n\t\tdepthTest: false,\n\t\tdepthWrite: false\n\n\t} );\n\n}\n\nfunction _getCubemapMaterial() {\n\n\treturn new ShaderMaterial( {\n\n\t\tname: 'CubemapToCubeUV',\n\n\t\tuniforms: {\n\t\t\t'envMap': { value: null },\n\t\t\t'flipEnvMap': { value: - 1 }\n\t\t},\n\n\t\tvertexShader: _getCommonVertexShader(),\n\n\t\tfragmentShader: /* glsl */`\n\n\t\t\tprecision mediump float;\n\t\t\tprecision mediump int;\n\n\t\t\tuniform float flipEnvMap;\n\n\t\t\tvarying vec3 vOutputDirection;\n\n\t\t\tuniform samplerCube envMap;\n\n\t\t\tvoid main() {\n\n\t\t\t\tgl_FragColor = textureCube( envMap, vec3( flipEnvMap * vOutputDirection.x, vOutputDirection.yz ) );\n\n\t\t\t}\n\t\t`,\n\n\t\tblending: NoBlending,\n\t\tdepthTest: false,\n\t\tdepthWrite: false\n\n\t} );\n\n}\n\nfunction _getCommonVertexShader() {\n\n\treturn /* glsl */`\n\n\t\tprecision mediump float;\n\t\tprecision mediump int;\n\n\t\tattribute float faceIndex;\n\n\t\tvarying vec3 vOutputDirection;\n\n\t\t// RH coordinate system; PMREM face-indexing convention\n\t\tvec3 getDirection( vec2 uv, float face ) {\n\n\t\t\tuv = 2.0 * uv - 1.0;\n\n\t\t\tvec3 direction = vec3( uv, 1.0 );\n\n\t\t\tif ( face == 0.0 ) {\n\n\t\t\t\tdirection = direction.zyx; // ( 1, v, u ) pos x\n\n\t\t\t} else if ( face == 1.0 ) {\n\n\t\t\t\tdirection = direction.xzy;\n\t\t\t\tdirection.xz *= -1.0; // ( -u, 1, -v ) pos y\n\n\t\t\t} else if ( face == 2.0 ) {\n\n\t\t\t\tdirection.x *= -1.0; // ( -u, v, 1 ) pos z\n\n\t\t\t} else if ( face == 3.0 ) {\n\n\t\t\t\tdirection = direction.zyx;\n\t\t\t\tdirection.xz *= -1.0; // ( -1, v, -u ) neg x\n\n\t\t\t} else if ( face == 4.0 ) {\n\n\t\t\t\tdirection = direction.xzy;\n\t\t\t\tdirection.xy *= -1.0; // ( -u, -1, v ) neg y\n\n\t\t\t} else if ( face == 5.0 ) {\n\n\t\t\t\tdirection.z *= -1.0; // ( u, v, -1 ) neg z\n\n\t\t\t}\n\n\t\t\treturn direction;\n\n\t\t}\n\n\t\tvoid main() {\n\n\t\t\tvOutputDirection = getDirection( uv, faceIndex );\n\t\t\tgl_Position = vec4( position, 1.0 );\n\n\t\t}\n\t`;\n\n}\n\nexport { PMREMGenerator };\n","import { CubeReflectionMapping, CubeRefractionMapping, EquirectangularReflectionMapping, EquirectangularRefractionMapping } from '../../constants.js';\nimport { PMREMGenerator } from '../../extras/PMREMGenerator.js';\n\nfunction WebGLCubeUVMaps( renderer ) {\n\n\tlet cubeUVmaps = new WeakMap();\n\n\tlet pmremGenerator = null;\n\n\tfunction get( texture ) {\n\n\t\tif ( texture && texture.isTexture ) {\n\n\t\t\tconst mapping = texture.mapping;\n\n\t\t\tconst isEquirectMap = ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping );\n\t\t\tconst isCubeMap = ( mapping === CubeReflectionMapping || mapping === CubeRefractionMapping );\n\n\t\t\t// equirect/cube map to cubeUV conversion\n\n\t\t\tif ( isEquirectMap || isCubeMap ) {\n\n\t\t\t\tif ( texture.isRenderTargetTexture && texture.needsPMREMUpdate === true ) {\n\n\t\t\t\t\ttexture.needsPMREMUpdate = false;\n\n\t\t\t\t\tlet renderTarget = cubeUVmaps.get( texture );\n\n\t\t\t\t\tif ( pmremGenerator === null ) pmremGenerator = new PMREMGenerator( renderer );\n\n\t\t\t\t\trenderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular( texture, renderTarget ) : pmremGenerator.fromCubemap( texture, renderTarget );\n\t\t\t\t\tcubeUVmaps.set( texture, renderTarget );\n\n\t\t\t\t\treturn renderTarget.texture;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tif ( cubeUVmaps.has( texture ) ) {\n\n\t\t\t\t\t\treturn cubeUVmaps.get( texture ).texture;\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tconst image = texture.image;\n\n\t\t\t\t\t\tif ( ( isEquirectMap && image && image.height > 0 ) || ( isCubeMap && image && isCubeTextureComplete( image ) ) ) {\n\n\t\t\t\t\t\t\tif ( pmremGenerator === null ) pmremGenerator = new PMREMGenerator( renderer );\n\n\t\t\t\t\t\t\tconst renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular( texture ) : pmremGenerator.fromCubemap( texture );\n\t\t\t\t\t\t\tcubeUVmaps.set( texture, renderTarget );\n\n\t\t\t\t\t\t\ttexture.addEventListener( 'dispose', onTextureDispose );\n\n\t\t\t\t\t\t\treturn renderTarget.texture;\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t// image not yet ready. try the conversion next frame\n\n\t\t\t\t\t\t\treturn null;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn texture;\n\n\t}\n\n\tfunction isCubeTextureComplete( image ) {\n\n\t\tlet count = 0;\n\t\tconst length = 6;\n\n\t\tfor ( let i = 0; i < length; i ++ ) {\n\n\t\t\tif ( image[ i ] !== undefined ) count ++;\n\n\t\t}\n\n\t\treturn count === length;\n\n\n\t}\n\n\tfunction onTextureDispose( event ) {\n\n\t\tconst texture = event.target;\n\n\t\ttexture.removeEventListener( 'dispose', onTextureDispose );\n\n\t\tconst cubemapUV = cubeUVmaps.get( texture );\n\n\t\tif ( cubemapUV !== undefined ) {\n\n\t\t\tcubeUVmaps.delete( texture );\n\t\t\tcubemapUV.dispose();\n\n\t\t}\n\n\t}\n\n\tfunction dispose() {\n\n\t\tcubeUVmaps = new WeakMap();\n\n\t\tif ( pmremGenerator !== null ) {\n\n\t\t\tpmremGenerator.dispose();\n\t\t\tpmremGenerator = null;\n\n\t\t}\n\n\t}\n\n\treturn {\n\t\tget: get,\n\t\tdispose: dispose\n\t};\n\n}\n\nexport { WebGLCubeUVMaps };\n","function WebGLExtensions( gl ) {\n\n\tconst extensions = {};\n\n\tfunction getExtension( name ) {\n\n\t\tif ( extensions[ name ] !== undefined ) {\n\n\t\t\treturn extensions[ name ];\n\n\t\t}\n\n\t\tlet extension;\n\n\t\tswitch ( name ) {\n\n\t\t\tcase 'WEBGL_depth_texture':\n\t\t\t\textension = gl.getExtension( 'WEBGL_depth_texture' ) || gl.getExtension( 'MOZ_WEBGL_depth_texture' ) || gl.getExtension( 'WEBKIT_WEBGL_depth_texture' );\n\t\t\t\tbreak;\n\n\t\t\tcase 'EXT_texture_filter_anisotropic':\n\t\t\t\textension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' );\n\t\t\t\tbreak;\n\n\t\t\tcase 'WEBGL_compressed_texture_s3tc':\n\t\t\t\textension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' );\n\t\t\t\tbreak;\n\n\t\t\tcase 'WEBGL_compressed_texture_pvrtc':\n\t\t\t\textension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' );\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\textension = gl.getExtension( name );\n\n\t\t}\n\n\t\textensions[ name ] = extension;\n\n\t\treturn extension;\n\n\t}\n\n\treturn {\n\n\t\thas: function ( name ) {\n\n\t\t\treturn getExtension( name ) !== null;\n\n\t\t},\n\n\t\tinit: function ( capabilities ) {\n\n\t\t\tif ( capabilities.isWebGL2 ) {\n\n\t\t\t\tgetExtension( 'EXT_color_buffer_float' );\n\n\t\t\t} else {\n\n\t\t\t\tgetExtension( 'WEBGL_depth_texture' );\n\t\t\t\tgetExtension( 'OES_texture_float' );\n\t\t\t\tgetExtension( 'OES_texture_half_float' );\n\t\t\t\tgetExtension( 'OES_texture_half_float_linear' );\n\t\t\t\tgetExtension( 'OES_standard_derivatives' );\n\t\t\t\tgetExtension( 'OES_element_index_uint' );\n\t\t\t\tgetExtension( 'OES_vertex_array_object' );\n\t\t\t\tgetExtension( 'ANGLE_instanced_arrays' );\n\n\t\t\t}\n\n\t\t\tgetExtension( 'OES_texture_float_linear' );\n\t\t\tgetExtension( 'EXT_color_buffer_half_float' );\n\t\t\tgetExtension( 'WEBGL_multisampled_render_to_texture' );\n\n\t\t},\n\n\t\tget: function ( name ) {\n\n\t\t\tconst extension = getExtension( name );\n\n\t\t\tif ( extension === null ) {\n\n\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' );\n\n\t\t\t}\n\n\t\t\treturn extension;\n\n\t\t}\n\n\t};\n\n}\n\n\nexport { WebGLExtensions };\n","import { Uint16BufferAttribute, Uint32BufferAttribute } from '../../core/BufferAttribute.js';\nimport { arrayNeedsUint32 } from '../../utils.js';\n\nfunction WebGLGeometries( gl, attributes, info, bindingStates ) {\n\n\tconst geometries = {};\n\tconst wireframeAttributes = new WeakMap();\n\n\tfunction onGeometryDispose( event ) {\n\n\t\tconst geometry = event.target;\n\n\t\tif ( geometry.index !== null ) {\n\n\t\t\tattributes.remove( geometry.index );\n\n\t\t}\n\n\t\tfor ( const name in geometry.attributes ) {\n\n\t\t\tattributes.remove( geometry.attributes[ name ] );\n\n\t\t}\n\n\t\tfor ( const name in geometry.morphAttributes ) {\n\n\t\t\tconst array = geometry.morphAttributes[ name ];\n\n\t\t\tfor ( let i = 0, l = array.length; i < l; i ++ ) {\n\n\t\t\t\tattributes.remove( array[ i ] );\n\n\t\t\t}\n\n\t\t}\n\n\t\tgeometry.removeEventListener( 'dispose', onGeometryDispose );\n\n\t\tdelete geometries[ geometry.id ];\n\n\t\tconst attribute = wireframeAttributes.get( geometry );\n\n\t\tif ( attribute ) {\n\n\t\t\tattributes.remove( attribute );\n\t\t\twireframeAttributes.delete( geometry );\n\n\t\t}\n\n\t\tbindingStates.releaseStatesOfGeometry( geometry );\n\n\t\tif ( geometry.isInstancedBufferGeometry === true ) {\n\n\t\t\tdelete geometry._maxInstanceCount;\n\n\t\t}\n\n\t\t//\n\n\t\tinfo.memory.geometries --;\n\n\t}\n\n\tfunction get( object, geometry ) {\n\n\t\tif ( geometries[ geometry.id ] === true ) return geometry;\n\n\t\tgeometry.addEventListener( 'dispose', onGeometryDispose );\n\n\t\tgeometries[ geometry.id ] = true;\n\n\t\tinfo.memory.geometries ++;\n\n\t\treturn geometry;\n\n\t}\n\n\tfunction update( geometry ) {\n\n\t\tconst geometryAttributes = geometry.attributes;\n\n\t\t// Updating index buffer in VAO now. See WebGLBindingStates.\n\n\t\tfor ( const name in geometryAttributes ) {\n\n\t\t\tattributes.update( geometryAttributes[ name ], gl.ARRAY_BUFFER );\n\n\t\t}\n\n\t\t// morph targets\n\n\t\tconst morphAttributes = geometry.morphAttributes;\n\n\t\tfor ( const name in morphAttributes ) {\n\n\t\t\tconst array = morphAttributes[ name ];\n\n\t\t\tfor ( let i = 0, l = array.length; i < l; i ++ ) {\n\n\t\t\t\tattributes.update( array[ i ], gl.ARRAY_BUFFER );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tfunction updateWireframeAttribute( geometry ) {\n\n\t\tconst indices = [];\n\n\t\tconst geometryIndex = geometry.index;\n\t\tconst geometryPosition = geometry.attributes.position;\n\t\tlet version = 0;\n\n\t\tif ( geometryIndex !== null ) {\n\n\t\t\tconst array = geometryIndex.array;\n\t\t\tversion = geometryIndex.version;\n\n\t\t\tfor ( let i = 0, l = array.length; i < l; i += 3 ) {\n\n\t\t\t\tconst a = array[ i + 0 ];\n\t\t\t\tconst b = array[ i + 1 ];\n\t\t\t\tconst c = array[ i + 2 ];\n\n\t\t\t\tindices.push( a, b, b, c, c, a );\n\n\t\t\t}\n\n\t\t} else if ( geometryPosition !== undefined ) {\n\n\t\t\tconst array = geometryPosition.array;\n\t\t\tversion = geometryPosition.version;\n\n\t\t\tfor ( let i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) {\n\n\t\t\t\tconst a = i + 0;\n\t\t\t\tconst b = i + 1;\n\t\t\t\tconst c = i + 2;\n\n\t\t\t\tindices.push( a, b, b, c, c, a );\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\treturn;\n\n\t\t}\n\n\t\tconst attribute = new ( arrayNeedsUint32( indices ) ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 );\n\t\tattribute.version = version;\n\n\t\t// Updating index buffer in VAO now. See WebGLBindingStates\n\n\t\t//\n\n\t\tconst previousAttribute = wireframeAttributes.get( geometry );\n\n\t\tif ( previousAttribute ) attributes.remove( previousAttribute );\n\n\t\t//\n\n\t\twireframeAttributes.set( geometry, attribute );\n\n\t}\n\n\tfunction getWireframeAttribute( geometry ) {\n\n\t\tconst currentAttribute = wireframeAttributes.get( geometry );\n\n\t\tif ( currentAttribute ) {\n\n\t\t\tconst geometryIndex = geometry.index;\n\n\t\t\tif ( geometryIndex !== null ) {\n\n\t\t\t\t// if the attribute is obsolete, create a new one\n\n\t\t\t\tif ( currentAttribute.version < geometryIndex.version ) {\n\n\t\t\t\t\tupdateWireframeAttribute( geometry );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\tupdateWireframeAttribute( geometry );\n\n\t\t}\n\n\t\treturn wireframeAttributes.get( geometry );\n\n\t}\n\n\treturn {\n\n\t\tget: get,\n\t\tupdate: update,\n\n\t\tgetWireframeAttribute: getWireframeAttribute\n\n\t};\n\n}\n\n\nexport { WebGLGeometries };\n","function WebGLIndexedBufferRenderer( gl, extensions, info, capabilities ) {\n\n\tconst isWebGL2 = capabilities.isWebGL2;\n\n\tlet mode;\n\n\tfunction setMode( value ) {\n\n\t\tmode = value;\n\n\t}\n\n\tlet type, bytesPerElement;\n\n\tfunction setIndex( value ) {\n\n\t\ttype = value.type;\n\t\tbytesPerElement = value.bytesPerElement;\n\n\t}\n\n\tfunction render( start, count ) {\n\n\t\tgl.drawElements( mode, count, type, start * bytesPerElement );\n\n\t\tinfo.update( count, mode, 1 );\n\n\t}\n\n\tfunction renderInstances( start, count, primcount ) {\n\n\t\tif ( primcount === 0 ) return;\n\n\t\tlet extension, methodName;\n\n\t\tif ( isWebGL2 ) {\n\n\t\t\textension = gl;\n\t\t\tmethodName = 'drawElementsInstanced';\n\n\t\t} else {\n\n\t\t\textension = extensions.get( 'ANGLE_instanced_arrays' );\n\t\t\tmethodName = 'drawElementsInstancedANGLE';\n\n\t\t\tif ( extension === null ) {\n\n\t\t\t\tconsole.error( 'THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t}\n\n\t\textension[ methodName ]( mode, count, type, start * bytesPerElement, primcount );\n\n\t\tinfo.update( count, mode, primcount );\n\n\t}\n\n\t//\n\n\tthis.setMode = setMode;\n\tthis.setIndex = setIndex;\n\tthis.render = render;\n\tthis.renderInstances = renderInstances;\n\n}\n\n\nexport { WebGLIndexedBufferRenderer };\n","function WebGLInfo( gl ) {\n\n\tconst memory = {\n\t\tgeometries: 0,\n\t\ttextures: 0\n\t};\n\n\tconst render = {\n\t\tframe: 0,\n\t\tcalls: 0,\n\t\ttriangles: 0,\n\t\tpoints: 0,\n\t\tlines: 0\n\t};\n\n\tfunction update( count, mode, instanceCount ) {\n\n\t\trender.calls ++;\n\n\t\tswitch ( mode ) {\n\n\t\t\tcase gl.TRIANGLES:\n\t\t\t\trender.triangles += instanceCount * ( count / 3 );\n\t\t\t\tbreak;\n\n\t\t\tcase gl.LINES:\n\t\t\t\trender.lines += instanceCount * ( count / 2 );\n\t\t\t\tbreak;\n\n\t\t\tcase gl.LINE_STRIP:\n\t\t\t\trender.lines += instanceCount * ( count - 1 );\n\t\t\t\tbreak;\n\n\t\t\tcase gl.LINE_LOOP:\n\t\t\t\trender.lines += instanceCount * count;\n\t\t\t\tbreak;\n\n\t\t\tcase gl.POINTS:\n\t\t\t\trender.points += instanceCount * count;\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tconsole.error( 'THREE.WebGLInfo: Unknown draw mode:', mode );\n\t\t\t\tbreak;\n\n\t\t}\n\n\t}\n\n\tfunction reset() {\n\n\t\trender.calls = 0;\n\t\trender.triangles = 0;\n\t\trender.points = 0;\n\t\trender.lines = 0;\n\n\t}\n\n\treturn {\n\t\tmemory: memory,\n\t\trender: render,\n\t\tprograms: null,\n\t\tautoReset: true,\n\t\treset: reset,\n\t\tupdate: update\n\t};\n\n}\n\n\nexport { WebGLInfo };\n","import { FloatType } from '../../constants.js';\nimport { DataArrayTexture } from '../../textures/DataArrayTexture.js';\nimport { Vector4 } from '../../math/Vector4.js';\nimport { Vector2 } from '../../math/Vector2.js';\n\nfunction numericalSort( a, b ) {\n\n\treturn a[ 0 ] - b[ 0 ];\n\n}\n\nfunction absNumericalSort( a, b ) {\n\n\treturn Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] );\n\n}\n\nfunction WebGLMorphtargets( gl, capabilities, textures ) {\n\n\tconst influencesList = {};\n\tconst morphInfluences = new Float32Array( 8 );\n\tconst morphTextures = new WeakMap();\n\tconst morph = new Vector4();\n\n\tconst workInfluences = [];\n\n\tfor ( let i = 0; i < 8; i ++ ) {\n\n\t\tworkInfluences[ i ] = [ i, 0 ];\n\n\t}\n\n\tfunction update( object, geometry, program ) {\n\n\t\tconst objectInfluences = object.morphTargetInfluences;\n\n\t\tif ( capabilities.isWebGL2 === true ) {\n\n\t\t\t// instead of using attributes, the WebGL 2 code path encodes morph targets\n\t\t\t// into an array of data textures. Each layer represents a single morph target.\n\n\t\t\tconst morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color;\n\t\t\tconst morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0;\n\n\t\t\tlet entry = morphTextures.get( geometry );\n\n\t\t\tif ( entry === undefined || entry.count !== morphTargetsCount ) {\n\n\t\t\t\tif ( entry !== undefined ) entry.texture.dispose();\n\n\t\t\t\tconst hasMorphPosition = geometry.morphAttributes.position !== undefined;\n\t\t\t\tconst hasMorphNormals = geometry.morphAttributes.normal !== undefined;\n\t\t\t\tconst hasMorphColors = geometry.morphAttributes.color !== undefined;\n\n\t\t\t\tconst morphTargets = geometry.morphAttributes.position || [];\n\t\t\t\tconst morphNormals = geometry.morphAttributes.normal || [];\n\t\t\t\tconst morphColors = geometry.morphAttributes.color || [];\n\n\t\t\t\tlet vertexDataCount = 0;\n\n\t\t\t\tif ( hasMorphPosition === true ) vertexDataCount = 1;\n\t\t\t\tif ( hasMorphNormals === true ) vertexDataCount = 2;\n\t\t\t\tif ( hasMorphColors === true ) vertexDataCount = 3;\n\n\t\t\t\tlet width = geometry.attributes.position.count * vertexDataCount;\n\t\t\t\tlet height = 1;\n\n\t\t\t\tif ( width > capabilities.maxTextureSize ) {\n\n\t\t\t\t\theight = Math.ceil( width / capabilities.maxTextureSize );\n\t\t\t\t\twidth = capabilities.maxTextureSize;\n\n\t\t\t\t}\n\n\t\t\t\tconst buffer = new Float32Array( width * height * 4 * morphTargetsCount );\n\n\t\t\t\tconst texture = new DataArrayTexture( buffer, width, height, morphTargetsCount );\n\t\t\t\ttexture.type = FloatType;\n\t\t\t\ttexture.needsUpdate = true;\n\n\t\t\t\t// fill buffer\n\n\t\t\t\tconst vertexDataStride = vertexDataCount * 4;\n\n\t\t\t\tfor ( let i = 0; i < morphTargetsCount; i ++ ) {\n\n\t\t\t\t\tconst morphTarget = morphTargets[ i ];\n\t\t\t\t\tconst morphNormal = morphNormals[ i ];\n\t\t\t\t\tconst morphColor = morphColors[ i ];\n\n\t\t\t\t\tconst offset = width * height * 4 * i;\n\n\t\t\t\t\tfor ( let j = 0; j < morphTarget.count; j ++ ) {\n\n\t\t\t\t\t\tconst stride = j * vertexDataStride;\n\n\t\t\t\t\t\tif ( hasMorphPosition === true ) {\n\n\t\t\t\t\t\t\tmorph.fromBufferAttribute( morphTarget, j );\n\n\t\t\t\t\t\t\tbuffer[ offset + stride + 0 ] = morph.x;\n\t\t\t\t\t\t\tbuffer[ offset + stride + 1 ] = morph.y;\n\t\t\t\t\t\t\tbuffer[ offset + stride + 2 ] = morph.z;\n\t\t\t\t\t\t\tbuffer[ offset + stride + 3 ] = 0;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ( hasMorphNormals === true ) {\n\n\t\t\t\t\t\t\tmorph.fromBufferAttribute( morphNormal, j );\n\n\t\t\t\t\t\t\tbuffer[ offset + stride + 4 ] = morph.x;\n\t\t\t\t\t\t\tbuffer[ offset + stride + 5 ] = morph.y;\n\t\t\t\t\t\t\tbuffer[ offset + stride + 6 ] = morph.z;\n\t\t\t\t\t\t\tbuffer[ offset + stride + 7 ] = 0;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ( hasMorphColors === true ) {\n\n\t\t\t\t\t\t\tmorph.fromBufferAttribute( morphColor, j );\n\n\t\t\t\t\t\t\tbuffer[ offset + stride + 8 ] = morph.x;\n\t\t\t\t\t\t\tbuffer[ offset + stride + 9 ] = morph.y;\n\t\t\t\t\t\t\tbuffer[ offset + stride + 10 ] = morph.z;\n\t\t\t\t\t\t\tbuffer[ offset + stride + 11 ] = ( morphColor.itemSize === 4 ) ? morph.w : 1;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tentry = {\n\t\t\t\t\tcount: morphTargetsCount,\n\t\t\t\t\ttexture: texture,\n\t\t\t\t\tsize: new Vector2( width, height )\n\t\t\t\t};\n\n\t\t\t\tmorphTextures.set( geometry, entry );\n\n\t\t\t\tfunction disposeTexture() {\n\n\t\t\t\t\ttexture.dispose();\n\n\t\t\t\t\tmorphTextures.delete( geometry );\n\n\t\t\t\t\tgeometry.removeEventListener( 'dispose', disposeTexture );\n\n\t\t\t\t}\n\n\t\t\t\tgeometry.addEventListener( 'dispose', disposeTexture );\n\n\t\t\t}\n\n\t\t\t//\n\n\t\t\tlet morphInfluencesSum = 0;\n\n\t\t\tfor ( let i = 0; i < objectInfluences.length; i ++ ) {\n\n\t\t\t\tmorphInfluencesSum += objectInfluences[ i ];\n\n\t\t\t}\n\n\t\t\tconst morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum;\n\n\t\t\tprogram.getUniforms().setValue( gl, 'morphTargetBaseInfluence', morphBaseInfluence );\n\t\t\tprogram.getUniforms().setValue( gl, 'morphTargetInfluences', objectInfluences );\n\n\t\t\tprogram.getUniforms().setValue( gl, 'morphTargetsTexture', entry.texture, textures );\n\t\t\tprogram.getUniforms().setValue( gl, 'morphTargetsTextureSize', entry.size );\n\n\n\t\t} else {\n\n\t\t\t// When object doesn't have morph target influences defined, we treat it as a 0-length array\n\t\t\t// This is important to make sure we set up morphTargetBaseInfluence / morphTargetInfluences\n\n\t\t\tconst length = objectInfluences === undefined ? 0 : objectInfluences.length;\n\n\t\t\tlet influences = influencesList[ geometry.id ];\n\n\t\t\tif ( influences === undefined || influences.length !== length ) {\n\n\t\t\t\t// initialise list\n\n\t\t\t\tinfluences = [];\n\n\t\t\t\tfor ( let i = 0; i < length; i ++ ) {\n\n\t\t\t\t\tinfluences[ i ] = [ i, 0 ];\n\n\t\t\t\t}\n\n\t\t\t\tinfluencesList[ geometry.id ] = influences;\n\n\t\t\t}\n\n\t\t\t// Collect influences\n\n\t\t\tfor ( let i = 0; i < length; i ++ ) {\n\n\t\t\t\tconst influence = influences[ i ];\n\n\t\t\t\tinfluence[ 0 ] = i;\n\t\t\t\tinfluence[ 1 ] = objectInfluences[ i ];\n\n\t\t\t}\n\n\t\t\tinfluences.sort( absNumericalSort );\n\n\t\t\tfor ( let i = 0; i < 8; i ++ ) {\n\n\t\t\t\tif ( i < length && influences[ i ][ 1 ] ) {\n\n\t\t\t\t\tworkInfluences[ i ][ 0 ] = influences[ i ][ 0 ];\n\t\t\t\t\tworkInfluences[ i ][ 1 ] = influences[ i ][ 1 ];\n\n\t\t\t\t} else {\n\n\t\t\t\t\tworkInfluences[ i ][ 0 ] = Number.MAX_SAFE_INTEGER;\n\t\t\t\t\tworkInfluences[ i ][ 1 ] = 0;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tworkInfluences.sort( numericalSort );\n\n\t\t\tconst morphTargets = geometry.morphAttributes.position;\n\t\t\tconst morphNormals = geometry.morphAttributes.normal;\n\n\t\t\tlet morphInfluencesSum = 0;\n\n\t\t\tfor ( let i = 0; i < 8; i ++ ) {\n\n\t\t\t\tconst influence = workInfluences[ i ];\n\t\t\t\tconst index = influence[ 0 ];\n\t\t\t\tconst value = influence[ 1 ];\n\n\t\t\t\tif ( index !== Number.MAX_SAFE_INTEGER && value ) {\n\n\t\t\t\t\tif ( morphTargets && geometry.getAttribute( 'morphTarget' + i ) !== morphTargets[ index ] ) {\n\n\t\t\t\t\t\tgeometry.setAttribute( 'morphTarget' + i, morphTargets[ index ] );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( morphNormals && geometry.getAttribute( 'morphNormal' + i ) !== morphNormals[ index ] ) {\n\n\t\t\t\t\t\tgeometry.setAttribute( 'morphNormal' + i, morphNormals[ index ] );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tmorphInfluences[ i ] = value;\n\t\t\t\t\tmorphInfluencesSum += value;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tif ( morphTargets && geometry.hasAttribute( 'morphTarget' + i ) === true ) {\n\n\t\t\t\t\t\tgeometry.deleteAttribute( 'morphTarget' + i );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( morphNormals && geometry.hasAttribute( 'morphNormal' + i ) === true ) {\n\n\t\t\t\t\t\tgeometry.deleteAttribute( 'morphNormal' + i );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tmorphInfluences[ i ] = 0;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// GLSL shader uses formula baseinfluence * base + sum(target * influence)\n\t\t\t// This allows us to switch between absolute morphs and relative morphs without changing shader code\n\t\t\t// When baseinfluence = 1 - sum(influence), the above is equivalent to sum((target - base) * influence)\n\t\t\tconst morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum;\n\n\t\t\tprogram.getUniforms().setValue( gl, 'morphTargetBaseInfluence', morphBaseInfluence );\n\t\t\tprogram.getUniforms().setValue( gl, 'morphTargetInfluences', morphInfluences );\n\n\t\t}\n\n\t}\n\n\treturn {\n\n\t\tupdate: update\n\n\t};\n\n}\n\n\nexport { WebGLMorphtargets };\n","function WebGLObjects( gl, geometries, attributes, info ) {\n\n\tlet updateMap = new WeakMap();\n\n\tfunction update( object ) {\n\n\t\tconst frame = info.render.frame;\n\n\t\tconst geometry = object.geometry;\n\t\tconst buffergeometry = geometries.get( object, geometry );\n\n\t\t// Update once per frame\n\n\t\tif ( updateMap.get( buffergeometry ) !== frame ) {\n\n\t\t\tgeometries.update( buffergeometry );\n\n\t\t\tupdateMap.set( buffergeometry, frame );\n\n\t\t}\n\n\t\tif ( object.isInstancedMesh ) {\n\n\t\t\tif ( object.hasEventListener( 'dispose', onInstancedMeshDispose ) === false ) {\n\n\t\t\t\tobject.addEventListener( 'dispose', onInstancedMeshDispose );\n\n\t\t\t}\n\n\t\t\tif ( updateMap.get( object ) !== frame ) {\n\n\t\t\t\tattributes.update( object.instanceMatrix, gl.ARRAY_BUFFER );\n\n\t\t\t\tif ( object.instanceColor !== null ) {\n\n\t\t\t\t\tattributes.update( object.instanceColor, gl.ARRAY_BUFFER );\n\n\t\t\t\t}\n\n\t\t\t\tupdateMap.set( object, frame );\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( object.isSkinnedMesh ) {\n\n\t\t\tconst skeleton = object.skeleton;\n\n\t\t\tif ( updateMap.get( skeleton ) !== frame ) {\n\n\t\t\t\tskeleton.update();\n\n\t\t\t\tupdateMap.set( skeleton, frame );\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn buffergeometry;\n\n\t}\n\n\tfunction dispose() {\n\n\t\tupdateMap = new WeakMap();\n\n\t}\n\n\tfunction onInstancedMeshDispose( event ) {\n\n\t\tconst instancedMesh = event.target;\n\n\t\tinstancedMesh.removeEventListener( 'dispose', onInstancedMeshDispose );\n\n\t\tattributes.remove( instancedMesh.instanceMatrix );\n\n\t\tif ( instancedMesh.instanceColor !== null ) attributes.remove( instancedMesh.instanceColor );\n\n\t}\n\n\treturn {\n\n\t\tupdate: update,\n\t\tdispose: dispose\n\n\t};\n\n}\n\n\nexport { WebGLObjects };\n","/**\n * Uniforms of a program.\n * Those form a tree structure with a special top-level container for the root,\n * which you get by calling 'new WebGLUniforms( gl, program )'.\n *\n *\n * Properties of inner nodes including the top-level container:\n *\n * .seq - array of nested uniforms\n * .map - nested uniforms by name\n *\n *\n * Methods of all nodes except the top-level container:\n *\n * .setValue( gl, value, [textures] )\n *\n * \t\tuploads a uniform value(s)\n * \tthe 'textures' parameter is needed for sampler uniforms\n *\n *\n * Static methods of the top-level container (textures factorizations):\n *\n * .upload( gl, seq, values, textures )\n *\n * \t\tsets uniforms in 'seq' to 'values[id].value'\n *\n * .seqWithValue( seq, values ) : filteredSeq\n *\n * \t\tfilters 'seq' entries with corresponding entry in values\n *\n *\n * Methods of the top-level container (textures factorizations):\n *\n * .setValue( gl, name, value, textures )\n *\n * \t\tsets uniform with name 'name' to 'value'\n *\n * .setOptional( gl, obj, prop )\n *\n * \t\tlike .set for an optional property of the object\n *\n */\n\nimport { CubeTexture } from '../../textures/CubeTexture.js';\nimport { Texture } from '../../textures/Texture.js';\nimport { DataArrayTexture } from '../../textures/DataArrayTexture.js';\nimport { Data3DTexture } from '../../textures/Data3DTexture.js';\n\nconst emptyTexture = /*@__PURE__*/ new Texture();\nconst emptyArrayTexture = /*@__PURE__*/ new DataArrayTexture();\nconst empty3dTexture = /*@__PURE__*/ new Data3DTexture();\nconst emptyCubeTexture = /*@__PURE__*/ new CubeTexture();\n\n// --- Utilities ---\n\n// Array Caches (provide typed arrays for temporary by size)\n\nconst arrayCacheF32 = [];\nconst arrayCacheI32 = [];\n\n// Float32Array caches used for uploading Matrix uniforms\n\nconst mat4array = new Float32Array( 16 );\nconst mat3array = new Float32Array( 9 );\nconst mat2array = new Float32Array( 4 );\n\n// Flattening for arrays of vectors and matrices\n\nfunction flatten( array, nBlocks, blockSize ) {\n\n\tconst firstElem = array[ 0 ];\n\n\tif ( firstElem <= 0 || firstElem > 0 ) return array;\n\t// unoptimized: ! isNaN( firstElem )\n\t// see http://jacksondunstan.com/articles/983\n\n\tconst n = nBlocks * blockSize;\n\tlet r = arrayCacheF32[ n ];\n\n\tif ( r === undefined ) {\n\n\t\tr = new Float32Array( n );\n\t\tarrayCacheF32[ n ] = r;\n\n\t}\n\n\tif ( nBlocks !== 0 ) {\n\n\t\tfirstElem.toArray( r, 0 );\n\n\t\tfor ( let i = 1, offset = 0; i !== nBlocks; ++ i ) {\n\n\t\t\toffset += blockSize;\n\t\t\tarray[ i ].toArray( r, offset );\n\n\t\t}\n\n\t}\n\n\treturn r;\n\n}\n\nfunction arraysEqual( a, b ) {\n\n\tif ( a.length !== b.length ) return false;\n\n\tfor ( let i = 0, l = a.length; i < l; i ++ ) {\n\n\t\tif ( a[ i ] !== b[ i ] ) return false;\n\n\t}\n\n\treturn true;\n\n}\n\nfunction copyArray( a, b ) {\n\n\tfor ( let i = 0, l = b.length; i < l; i ++ ) {\n\n\t\ta[ i ] = b[ i ];\n\n\t}\n\n}\n\n// Texture unit allocation\n\nfunction allocTexUnits( textures, n ) {\n\n\tlet r = arrayCacheI32[ n ];\n\n\tif ( r === undefined ) {\n\n\t\tr = new Int32Array( n );\n\t\tarrayCacheI32[ n ] = r;\n\n\t}\n\n\tfor ( let i = 0; i !== n; ++ i ) {\n\n\t\tr[ i ] = textures.allocateTextureUnit();\n\n\t}\n\n\treturn r;\n\n}\n\n// --- Setters ---\n\n// Note: Defining these methods externally, because they come in a bunch\n// and this way their names minify.\n\n// Single scalar\n\nfunction setValueV1f( gl, v ) {\n\n\tconst cache = this.cache;\n\n\tif ( cache[ 0 ] === v ) return;\n\n\tgl.uniform1f( this.addr, v );\n\n\tcache[ 0 ] = v;\n\n}\n\n// Single float vector (from flat array or THREE.VectorN)\n\nfunction setValueV2f( gl, v ) {\n\n\tconst cache = this.cache;\n\n\tif ( v.x !== undefined ) {\n\n\t\tif ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) {\n\n\t\t\tgl.uniform2f( this.addr, v.x, v.y );\n\n\t\t\tcache[ 0 ] = v.x;\n\t\t\tcache[ 1 ] = v.y;\n\n\t\t}\n\n\t} else {\n\n\t\tif ( arraysEqual( cache, v ) ) return;\n\n\t\tgl.uniform2fv( this.addr, v );\n\n\t\tcopyArray( cache, v );\n\n\t}\n\n}\n\nfunction setValueV3f( gl, v ) {\n\n\tconst cache = this.cache;\n\n\tif ( v.x !== undefined ) {\n\n\t\tif ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) {\n\n\t\t\tgl.uniform3f( this.addr, v.x, v.y, v.z );\n\n\t\t\tcache[ 0 ] = v.x;\n\t\t\tcache[ 1 ] = v.y;\n\t\t\tcache[ 2 ] = v.z;\n\n\t\t}\n\n\t} else if ( v.r !== undefined ) {\n\n\t\tif ( cache[ 0 ] !== v.r || cache[ 1 ] !== v.g || cache[ 2 ] !== v.b ) {\n\n\t\t\tgl.uniform3f( this.addr, v.r, v.g, v.b );\n\n\t\t\tcache[ 0 ] = v.r;\n\t\t\tcache[ 1 ] = v.g;\n\t\t\tcache[ 2 ] = v.b;\n\n\t\t}\n\n\t} else {\n\n\t\tif ( arraysEqual( cache, v ) ) return;\n\n\t\tgl.uniform3fv( this.addr, v );\n\n\t\tcopyArray( cache, v );\n\n\t}\n\n}\n\nfunction setValueV4f( gl, v ) {\n\n\tconst cache = this.cache;\n\n\tif ( v.x !== undefined ) {\n\n\t\tif ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) {\n\n\t\t\tgl.uniform4f( this.addr, v.x, v.y, v.z, v.w );\n\n\t\t\tcache[ 0 ] = v.x;\n\t\t\tcache[ 1 ] = v.y;\n\t\t\tcache[ 2 ] = v.z;\n\t\t\tcache[ 3 ] = v.w;\n\n\t\t}\n\n\t} else {\n\n\t\tif ( arraysEqual( cache, v ) ) return;\n\n\t\tgl.uniform4fv( this.addr, v );\n\n\t\tcopyArray( cache, v );\n\n\t}\n\n}\n\n// Single matrix (from flat array or THREE.MatrixN)\n\nfunction setValueM2( gl, v ) {\n\n\tconst cache = this.cache;\n\tconst elements = v.elements;\n\n\tif ( elements === undefined ) {\n\n\t\tif ( arraysEqual( cache, v ) ) return;\n\n\t\tgl.uniformMatrix2fv( this.addr, false, v );\n\n\t\tcopyArray( cache, v );\n\n\t} else {\n\n\t\tif ( arraysEqual( cache, elements ) ) return;\n\n\t\tmat2array.set( elements );\n\n\t\tgl.uniformMatrix2fv( this.addr, false, mat2array );\n\n\t\tcopyArray( cache, elements );\n\n\t}\n\n}\n\nfunction setValueM3( gl, v ) {\n\n\tconst cache = this.cache;\n\tconst elements = v.elements;\n\n\tif ( elements === undefined ) {\n\n\t\tif ( arraysEqual( cache, v ) ) return;\n\n\t\tgl.uniformMatrix3fv( this.addr, false, v );\n\n\t\tcopyArray( cache, v );\n\n\t} else {\n\n\t\tif ( arraysEqual( cache, elements ) ) return;\n\n\t\tmat3array.set( elements );\n\n\t\tgl.uniformMatrix3fv( this.addr, false, mat3array );\n\n\t\tcopyArray( cache, elements );\n\n\t}\n\n}\n\nfunction setValueM4( gl, v ) {\n\n\tconst cache = this.cache;\n\tconst elements = v.elements;\n\n\tif ( elements === undefined ) {\n\n\t\tif ( arraysEqual( cache, v ) ) return;\n\n\t\tgl.uniformMatrix4fv( this.addr, false, v );\n\n\t\tcopyArray( cache, v );\n\n\t} else {\n\n\t\tif ( arraysEqual( cache, elements ) ) return;\n\n\t\tmat4array.set( elements );\n\n\t\tgl.uniformMatrix4fv( this.addr, false, mat4array );\n\n\t\tcopyArray( cache, elements );\n\n\t}\n\n}\n\n// Single integer / boolean\n\nfunction setValueV1i( gl, v ) {\n\n\tconst cache = this.cache;\n\n\tif ( cache[ 0 ] === v ) return;\n\n\tgl.uniform1i( this.addr, v );\n\n\tcache[ 0 ] = v;\n\n}\n\n// Single integer / boolean vector (from flat array or THREE.VectorN)\n\nfunction setValueV2i( gl, v ) {\n\n\tconst cache = this.cache;\n\n\tif ( v.x !== undefined ) {\n\n\t\tif ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) {\n\n\t\t\tgl.uniform2i( this.addr, v.x, v.y );\n\n\t\t\tcache[ 0 ] = v.x;\n\t\t\tcache[ 1 ] = v.y;\n\n\t\t}\n\n\t} else {\n\n\t\tif ( arraysEqual( cache, v ) ) return;\n\n\t\tgl.uniform2iv( this.addr, v );\n\n\t\tcopyArray( cache, v );\n\n\t}\n\n}\n\nfunction setValueV3i( gl, v ) {\n\n\tconst cache = this.cache;\n\n\tif ( v.x !== undefined ) {\n\n\t\tif ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) {\n\n\t\t\tgl.uniform3i( this.addr, v.x, v.y, v.z );\n\n\t\t\tcache[ 0 ] = v.x;\n\t\t\tcache[ 1 ] = v.y;\n\t\t\tcache[ 2 ] = v.z;\n\n\t\t}\n\n\t} else {\n\n\t\tif ( arraysEqual( cache, v ) ) return;\n\n\t\tgl.uniform3iv( this.addr, v );\n\n\t\tcopyArray( cache, v );\n\n\t}\n\n}\n\nfunction setValueV4i( gl, v ) {\n\n\tconst cache = this.cache;\n\n\tif ( v.x !== undefined ) {\n\n\t\tif ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) {\n\n\t\t\tgl.uniform4i( this.addr, v.x, v.y, v.z, v.w );\n\n\t\t\tcache[ 0 ] = v.x;\n\t\t\tcache[ 1 ] = v.y;\n\t\t\tcache[ 2 ] = v.z;\n\t\t\tcache[ 3 ] = v.w;\n\n\t\t}\n\n\t} else {\n\n\t\tif ( arraysEqual( cache, v ) ) return;\n\n\t\tgl.uniform4iv( this.addr, v );\n\n\t\tcopyArray( cache, v );\n\n\t}\n\n}\n\n// Single unsigned integer\n\nfunction setValueV1ui( gl, v ) {\n\n\tconst cache = this.cache;\n\n\tif ( cache[ 0 ] === v ) return;\n\n\tgl.uniform1ui( this.addr, v );\n\n\tcache[ 0 ] = v;\n\n}\n\n// Single unsigned integer vector (from flat array or THREE.VectorN)\n\nfunction setValueV2ui( gl, v ) {\n\n\tconst cache = this.cache;\n\n\tif ( v.x !== undefined ) {\n\n\t\tif ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) {\n\n\t\t\tgl.uniform2ui( this.addr, v.x, v.y );\n\n\t\t\tcache[ 0 ] = v.x;\n\t\t\tcache[ 1 ] = v.y;\n\n\t\t}\n\n\t} else {\n\n\t\tif ( arraysEqual( cache, v ) ) return;\n\n\t\tgl.uniform2uiv( this.addr, v );\n\n\t\tcopyArray( cache, v );\n\n\t}\n\n}\n\nfunction setValueV3ui( gl, v ) {\n\n\tconst cache = this.cache;\n\n\tif ( v.x !== undefined ) {\n\n\t\tif ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) {\n\n\t\t\tgl.uniform3ui( this.addr, v.x, v.y, v.z );\n\n\t\t\tcache[ 0 ] = v.x;\n\t\t\tcache[ 1 ] = v.y;\n\t\t\tcache[ 2 ] = v.z;\n\n\t\t}\n\n\t} else {\n\n\t\tif ( arraysEqual( cache, v ) ) return;\n\n\t\tgl.uniform3uiv( this.addr, v );\n\n\t\tcopyArray( cache, v );\n\n\t}\n\n}\n\nfunction setValueV4ui( gl, v ) {\n\n\tconst cache = this.cache;\n\n\tif ( v.x !== undefined ) {\n\n\t\tif ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) {\n\n\t\t\tgl.uniform4ui( this.addr, v.x, v.y, v.z, v.w );\n\n\t\t\tcache[ 0 ] = v.x;\n\t\t\tcache[ 1 ] = v.y;\n\t\t\tcache[ 2 ] = v.z;\n\t\t\tcache[ 3 ] = v.w;\n\n\t\t}\n\n\t} else {\n\n\t\tif ( arraysEqual( cache, v ) ) return;\n\n\t\tgl.uniform4uiv( this.addr, v );\n\n\t\tcopyArray( cache, v );\n\n\t}\n\n}\n\n\n// Single texture (2D / Cube)\n\nfunction setValueT1( gl, v, textures ) {\n\n\tconst cache = this.cache;\n\tconst unit = textures.allocateTextureUnit();\n\n\tif ( cache[ 0 ] !== unit ) {\n\n\t\tgl.uniform1i( this.addr, unit );\n\t\tcache[ 0 ] = unit;\n\n\t}\n\n\ttextures.setTexture2D( v || emptyTexture, unit );\n\n}\n\nfunction setValueT3D1( gl, v, textures ) {\n\n\tconst cache = this.cache;\n\tconst unit = textures.allocateTextureUnit();\n\n\tif ( cache[ 0 ] !== unit ) {\n\n\t\tgl.uniform1i( this.addr, unit );\n\t\tcache[ 0 ] = unit;\n\n\t}\n\n\ttextures.setTexture3D( v || empty3dTexture, unit );\n\n}\n\nfunction setValueT6( gl, v, textures ) {\n\n\tconst cache = this.cache;\n\tconst unit = textures.allocateTextureUnit();\n\n\tif ( cache[ 0 ] !== unit ) {\n\n\t\tgl.uniform1i( this.addr, unit );\n\t\tcache[ 0 ] = unit;\n\n\t}\n\n\ttextures.setTextureCube( v || emptyCubeTexture, unit );\n\n}\n\nfunction setValueT2DArray1( gl, v, textures ) {\n\n\tconst cache = this.cache;\n\tconst unit = textures.allocateTextureUnit();\n\n\tif ( cache[ 0 ] !== unit ) {\n\n\t\tgl.uniform1i( this.addr, unit );\n\t\tcache[ 0 ] = unit;\n\n\t}\n\n\ttextures.setTexture2DArray( v || emptyArrayTexture, unit );\n\n}\n\n// Helper to pick the right setter for the singular case\n\nfunction getSingularSetter( type ) {\n\n\tswitch ( type ) {\n\n\t\tcase 0x1406: return setValueV1f; // FLOAT\n\t\tcase 0x8b50: return setValueV2f; // _VEC2\n\t\tcase 0x8b51: return setValueV3f; // _VEC3\n\t\tcase 0x8b52: return setValueV4f; // _VEC4\n\n\t\tcase 0x8b5a: return setValueM2; // _MAT2\n\t\tcase 0x8b5b: return setValueM3; // _MAT3\n\t\tcase 0x8b5c: return setValueM4; // _MAT4\n\n\t\tcase 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL\n\t\tcase 0x8b53: case 0x8b57: return setValueV2i; // _VEC2\n\t\tcase 0x8b54: case 0x8b58: return setValueV3i; // _VEC3\n\t\tcase 0x8b55: case 0x8b59: return setValueV4i; // _VEC4\n\n\t\tcase 0x1405: return setValueV1ui; // UINT\n\t\tcase 0x8dc6: return setValueV2ui; // _VEC2\n\t\tcase 0x8dc7: return setValueV3ui; // _VEC3\n\t\tcase 0x8dc8: return setValueV4ui; // _VEC4\n\n\t\tcase 0x8b5e: // SAMPLER_2D\n\t\tcase 0x8d66: // SAMPLER_EXTERNAL_OES\n\t\tcase 0x8dca: // INT_SAMPLER_2D\n\t\tcase 0x8dd2: // UNSIGNED_INT_SAMPLER_2D\n\t\tcase 0x8b62: // SAMPLER_2D_SHADOW\n\t\t\treturn setValueT1;\n\n\t\tcase 0x8b5f: // SAMPLER_3D\n\t\tcase 0x8dcb: // INT_SAMPLER_3D\n\t\tcase 0x8dd3: // UNSIGNED_INT_SAMPLER_3D\n\t\t\treturn setValueT3D1;\n\n\t\tcase 0x8b60: // SAMPLER_CUBE\n\t\tcase 0x8dcc: // INT_SAMPLER_CUBE\n\t\tcase 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE\n\t\tcase 0x8dc5: // SAMPLER_CUBE_SHADOW\n\t\t\treturn setValueT6;\n\n\t\tcase 0x8dc1: // SAMPLER_2D_ARRAY\n\t\tcase 0x8dcf: // INT_SAMPLER_2D_ARRAY\n\t\tcase 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY\n\t\tcase 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW\n\t\t\treturn setValueT2DArray1;\n\n\t}\n\n}\n\n\n// Array of scalars\n\nfunction setValueV1fArray( gl, v ) {\n\n\tgl.uniform1fv( this.addr, v );\n\n}\n\n// Array of vectors (from flat array or array of THREE.VectorN)\n\nfunction setValueV2fArray( gl, v ) {\n\n\tconst data = flatten( v, this.size, 2 );\n\n\tgl.uniform2fv( this.addr, data );\n\n}\n\nfunction setValueV3fArray( gl, v ) {\n\n\tconst data = flatten( v, this.size, 3 );\n\n\tgl.uniform3fv( this.addr, data );\n\n}\n\nfunction setValueV4fArray( gl, v ) {\n\n\tconst data = flatten( v, this.size, 4 );\n\n\tgl.uniform4fv( this.addr, data );\n\n}\n\n// Array of matrices (from flat array or array of THREE.MatrixN)\n\nfunction setValueM2Array( gl, v ) {\n\n\tconst data = flatten( v, this.size, 4 );\n\n\tgl.uniformMatrix2fv( this.addr, false, data );\n\n}\n\nfunction setValueM3Array( gl, v ) {\n\n\tconst data = flatten( v, this.size, 9 );\n\n\tgl.uniformMatrix3fv( this.addr, false, data );\n\n}\n\nfunction setValueM4Array( gl, v ) {\n\n\tconst data = flatten( v, this.size, 16 );\n\n\tgl.uniformMatrix4fv( this.addr, false, data );\n\n}\n\n// Array of integer / boolean\n\nfunction setValueV1iArray( gl, v ) {\n\n\tgl.uniform1iv( this.addr, v );\n\n}\n\n// Array of integer / boolean vectors (from flat array)\n\nfunction setValueV2iArray( gl, v ) {\n\n\tgl.uniform2iv( this.addr, v );\n\n}\n\nfunction setValueV3iArray( gl, v ) {\n\n\tgl.uniform3iv( this.addr, v );\n\n}\n\nfunction setValueV4iArray( gl, v ) {\n\n\tgl.uniform4iv( this.addr, v );\n\n}\n\n// Array of unsigned integer\n\nfunction setValueV1uiArray( gl, v ) {\n\n\tgl.uniform1uiv( this.addr, v );\n\n}\n\n// Array of unsigned integer vectors (from flat array)\n\nfunction setValueV2uiArray( gl, v ) {\n\n\tgl.uniform2uiv( this.addr, v );\n\n}\n\nfunction setValueV3uiArray( gl, v ) {\n\n\tgl.uniform3uiv( this.addr, v );\n\n}\n\nfunction setValueV4uiArray( gl, v ) {\n\n\tgl.uniform4uiv( this.addr, v );\n\n}\n\n\n// Array of textures (2D / 3D / Cube / 2DArray)\n\nfunction setValueT1Array( gl, v, textures ) {\n\n\tconst cache = this.cache;\n\n\tconst n = v.length;\n\n\tconst units = allocTexUnits( textures, n );\n\n\tif ( ! arraysEqual( cache, units ) ) {\n\n\t\tgl.uniform1iv( this.addr, units );\n\n\t\tcopyArray( cache, units );\n\n\t}\n\n\tfor ( let i = 0; i !== n; ++ i ) {\n\n\t\ttextures.setTexture2D( v[ i ] || emptyTexture, units[ i ] );\n\n\t}\n\n}\n\nfunction setValueT3DArray( gl, v, textures ) {\n\n\tconst cache = this.cache;\n\n\tconst n = v.length;\n\n\tconst units = allocTexUnits( textures, n );\n\n\tif ( ! arraysEqual( cache, units ) ) {\n\n\t\tgl.uniform1iv( this.addr, units );\n\n\t\tcopyArray( cache, units );\n\n\t}\n\n\tfor ( let i = 0; i !== n; ++ i ) {\n\n\t\ttextures.setTexture3D( v[ i ] || empty3dTexture, units[ i ] );\n\n\t}\n\n}\n\nfunction setValueT6Array( gl, v, textures ) {\n\n\tconst cache = this.cache;\n\n\tconst n = v.length;\n\n\tconst units = allocTexUnits( textures, n );\n\n\tif ( ! arraysEqual( cache, units ) ) {\n\n\t\tgl.uniform1iv( this.addr, units );\n\n\t\tcopyArray( cache, units );\n\n\t}\n\n\tfor ( let i = 0; i !== n; ++ i ) {\n\n\t\ttextures.setTextureCube( v[ i ] || emptyCubeTexture, units[ i ] );\n\n\t}\n\n}\n\nfunction setValueT2DArrayArray( gl, v, textures ) {\n\n\tconst cache = this.cache;\n\n\tconst n = v.length;\n\n\tconst units = allocTexUnits( textures, n );\n\n\tif ( ! arraysEqual( cache, units ) ) {\n\n\t\tgl.uniform1iv( this.addr, units );\n\n\t\tcopyArray( cache, units );\n\n\t}\n\n\tfor ( let i = 0; i !== n; ++ i ) {\n\n\t\ttextures.setTexture2DArray( v[ i ] || emptyArrayTexture, units[ i ] );\n\n\t}\n\n}\n\n\n// Helper to pick the right setter for a pure (bottom-level) array\n\nfunction getPureArraySetter( type ) {\n\n\tswitch ( type ) {\n\n\t\tcase 0x1406: return setValueV1fArray; // FLOAT\n\t\tcase 0x8b50: return setValueV2fArray; // _VEC2\n\t\tcase 0x8b51: return setValueV3fArray; // _VEC3\n\t\tcase 0x8b52: return setValueV4fArray; // _VEC4\n\n\t\tcase 0x8b5a: return setValueM2Array; // _MAT2\n\t\tcase 0x8b5b: return setValueM3Array; // _MAT3\n\t\tcase 0x8b5c: return setValueM4Array; // _MAT4\n\n\t\tcase 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL\n\t\tcase 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2\n\t\tcase 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3\n\t\tcase 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4\n\n\t\tcase 0x1405: return setValueV1uiArray; // UINT\n\t\tcase 0x8dc6: return setValueV2uiArray; // _VEC2\n\t\tcase 0x8dc7: return setValueV3uiArray; // _VEC3\n\t\tcase 0x8dc8: return setValueV4uiArray; // _VEC4\n\n\t\tcase 0x8b5e: // SAMPLER_2D\n\t\tcase 0x8d66: // SAMPLER_EXTERNAL_OES\n\t\tcase 0x8dca: // INT_SAMPLER_2D\n\t\tcase 0x8dd2: // UNSIGNED_INT_SAMPLER_2D\n\t\tcase 0x8b62: // SAMPLER_2D_SHADOW\n\t\t\treturn setValueT1Array;\n\n\t\tcase 0x8b5f: // SAMPLER_3D\n\t\tcase 0x8dcb: // INT_SAMPLER_3D\n\t\tcase 0x8dd3: // UNSIGNED_INT_SAMPLER_3D\n\t\t\treturn setValueT3DArray;\n\n\t\tcase 0x8b60: // SAMPLER_CUBE\n\t\tcase 0x8dcc: // INT_SAMPLER_CUBE\n\t\tcase 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE\n\t\tcase 0x8dc5: // SAMPLER_CUBE_SHADOW\n\t\t\treturn setValueT6Array;\n\n\t\tcase 0x8dc1: // SAMPLER_2D_ARRAY\n\t\tcase 0x8dcf: // INT_SAMPLER_2D_ARRAY\n\t\tcase 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY\n\t\tcase 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW\n\t\t\treturn setValueT2DArrayArray;\n\n\t}\n\n}\n\n// --- Uniform Classes ---\n\nclass SingleUniform {\n\n\tconstructor( id, activeInfo, addr ) {\n\n\t\tthis.id = id;\n\t\tthis.addr = addr;\n\t\tthis.cache = [];\n\t\tthis.setValue = getSingularSetter( activeInfo.type );\n\n\t\t// this.path = activeInfo.name; // DEBUG\n\n\t}\n\n}\n\nclass PureArrayUniform {\n\n\tconstructor( id, activeInfo, addr ) {\n\n\t\tthis.id = id;\n\t\tthis.addr = addr;\n\t\tthis.cache = [];\n\t\tthis.size = activeInfo.size;\n\t\tthis.setValue = getPureArraySetter( activeInfo.type );\n\n\t\t// this.path = activeInfo.name; // DEBUG\n\n\t}\n\n}\n\nclass StructuredUniform {\n\n\tconstructor( id ) {\n\n\t\tthis.id = id;\n\n\t\tthis.seq = [];\n\t\tthis.map = {};\n\n\t}\n\n\tsetValue( gl, value, textures ) {\n\n\t\tconst seq = this.seq;\n\n\t\tfor ( let i = 0, n = seq.length; i !== n; ++ i ) {\n\n\t\t\tconst u = seq[ i ];\n\t\t\tu.setValue( gl, value[ u.id ], textures );\n\n\t\t}\n\n\t}\n\n}\n\n// --- Top-level ---\n\n// Parser - builds up the property tree from the path strings\n\nconst RePathPart = /(\\w+)(\\])?(\\[|\\.)?/g;\n\n// extracts\n// \t- the identifier (member name or array index)\n// - followed by an optional right bracket (found when array index)\n// - followed by an optional left bracket or dot (type of subscript)\n//\n// Note: These portions can be read in a non-overlapping fashion and\n// allow straightforward parsing of the hierarchy that WebGL encodes\n// in the uniform names.\n\nfunction addUniform( container, uniformObject ) {\n\n\tcontainer.seq.push( uniformObject );\n\tcontainer.map[ uniformObject.id ] = uniformObject;\n\n}\n\nfunction parseUniform( activeInfo, addr, container ) {\n\n\tconst path = activeInfo.name,\n\t\tpathLength = path.length;\n\n\t// reset RegExp object, because of the early exit of a previous run\n\tRePathPart.lastIndex = 0;\n\n\twhile ( true ) {\n\n\t\tconst match = RePathPart.exec( path ),\n\t\t\tmatchEnd = RePathPart.lastIndex;\n\n\t\tlet id = match[ 1 ];\n\t\tconst idIsIndex = match[ 2 ] === ']',\n\t\t\tsubscript = match[ 3 ];\n\n\t\tif ( idIsIndex ) id = id | 0; // convert to integer\n\n\t\tif ( subscript === undefined || subscript === '[' && matchEnd + 2 === pathLength ) {\n\n\t\t\t// bare name or \"pure\" bottom-level array \"[0]\" suffix\n\n\t\t\taddUniform( container, subscript === undefined ?\n\t\t\t\tnew SingleUniform( id, activeInfo, addr ) :\n\t\t\t\tnew PureArrayUniform( id, activeInfo, addr ) );\n\n\t\t\tbreak;\n\n\t\t} else {\n\n\t\t\t// step into inner node / create it in case it doesn't exist\n\n\t\t\tconst map = container.map;\n\t\t\tlet next = map[ id ];\n\n\t\t\tif ( next === undefined ) {\n\n\t\t\t\tnext = new StructuredUniform( id );\n\t\t\t\taddUniform( container, next );\n\n\t\t\t}\n\n\t\t\tcontainer = next;\n\n\t\t}\n\n\t}\n\n}\n\n// Root Container\n\nclass WebGLUniforms {\n\n\tconstructor( gl, program ) {\n\n\t\tthis.seq = [];\n\t\tthis.map = {};\n\n\t\tconst n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS );\n\n\t\tfor ( let i = 0; i < n; ++ i ) {\n\n\t\t\tconst info = gl.getActiveUniform( program, i ),\n\t\t\t\taddr = gl.getUniformLocation( program, info.name );\n\n\t\t\tparseUniform( info, addr, this );\n\n\t\t}\n\n\t}\n\n\tsetValue( gl, name, value, textures ) {\n\n\t\tconst u = this.map[ name ];\n\n\t\tif ( u !== undefined ) u.setValue( gl, value, textures );\n\n\t}\n\n\tsetOptional( gl, object, name ) {\n\n\t\tconst v = object[ name ];\n\n\t\tif ( v !== undefined ) this.setValue( gl, name, v );\n\n\t}\n\n\tstatic upload( gl, seq, values, textures ) {\n\n\t\tfor ( let i = 0, n = seq.length; i !== n; ++ i ) {\n\n\t\t\tconst u = seq[ i ],\n\t\t\t\tv = values[ u.id ];\n\n\t\t\tif ( v.needsUpdate !== false ) {\n\n\t\t\t\t// note: always updating when .needsUpdate is undefined\n\t\t\t\tu.setValue( gl, v.value, textures );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tstatic seqWithValue( seq, values ) {\n\n\t\tconst r = [];\n\n\t\tfor ( let i = 0, n = seq.length; i !== n; ++ i ) {\n\n\t\t\tconst u = seq[ i ];\n\t\t\tif ( u.id in values ) r.push( u );\n\n\t\t}\n\n\t\treturn r;\n\n\t}\n\n}\n\nexport { WebGLUniforms };\n","function WebGLShader( gl, type, string ) {\n\n\tconst shader = gl.createShader( type );\n\n\tgl.shaderSource( shader, string );\n\tgl.compileShader( shader );\n\n\treturn shader;\n\n}\n\nexport { WebGLShader };\n","import { WebGLUniforms } from './WebGLUniforms.js';\nimport { WebGLShader } from './WebGLShader.js';\nimport { ShaderChunk } from '../shaders/ShaderChunk.js';\nimport { NoToneMapping, AddOperation, MixOperation, MultiplyOperation, CubeRefractionMapping, CubeUVReflectionMapping, CubeReflectionMapping, PCFSoftShadowMap, PCFShadowMap, VSMShadowMap, ACESFilmicToneMapping, CineonToneMapping, CustomToneMapping, ReinhardToneMapping, LinearToneMapping, GLSL3, LinearSRGBColorSpace, SRGBColorSpace, LinearDisplayP3ColorSpace, DisplayP3ColorSpace, P3Primaries, Rec709Primaries } from '../../constants.js';\nimport { ColorManagement } from '../../math/ColorManagement.js';\n\n// From https://www.khronos.org/registry/webgl/extensions/KHR_parallel_shader_compile/\nconst COMPLETION_STATUS_KHR = 0x91B1;\n\nlet programIdCount = 0;\n\nfunction handleSource( string, errorLine ) {\n\n\tconst lines = string.split( '\\n' );\n\tconst lines2 = [];\n\n\tconst from = Math.max( errorLine - 6, 0 );\n\tconst to = Math.min( errorLine + 6, lines.length );\n\n\tfor ( let i = from; i < to; i ++ ) {\n\n\t\tconst line = i + 1;\n\t\tlines2.push( `${line === errorLine ? '>' : ' '} ${line}: ${lines[ i ]}` );\n\n\t}\n\n\treturn lines2.join( '\\n' );\n\n}\n\nfunction getEncodingComponents( colorSpace ) {\n\n\tconst workingPrimaries = ColorManagement.getPrimaries( ColorManagement.workingColorSpace );\n\tconst encodingPrimaries = ColorManagement.getPrimaries( colorSpace );\n\n\tlet gamutMapping;\n\n\tif ( workingPrimaries === encodingPrimaries ) {\n\n\t\tgamutMapping = '';\n\n\t} else if ( workingPrimaries === P3Primaries && encodingPrimaries === Rec709Primaries ) {\n\n\t\tgamutMapping = 'LinearDisplayP3ToLinearSRGB';\n\n\t} else if ( workingPrimaries === Rec709Primaries && encodingPrimaries === P3Primaries ) {\n\n\t\tgamutMapping = 'LinearSRGBToLinearDisplayP3';\n\n\t}\n\n\tswitch ( colorSpace ) {\n\n\t\tcase LinearSRGBColorSpace:\n\t\tcase LinearDisplayP3ColorSpace:\n\t\t\treturn [ gamutMapping, 'LinearTransferOETF' ];\n\n\t\tcase SRGBColorSpace:\n\t\tcase DisplayP3ColorSpace:\n\t\t\treturn [ gamutMapping, 'sRGBTransferOETF' ];\n\n\t\tdefault:\n\t\t\tconsole.warn( 'THREE.WebGLProgram: Unsupported color space:', colorSpace );\n\t\t\treturn [ gamutMapping, 'LinearTransferOETF' ];\n\n\t}\n\n}\n\nfunction getShaderErrors( gl, shader, type ) {\n\n\tconst status = gl.getShaderParameter( shader, gl.COMPILE_STATUS );\n\tconst errors = gl.getShaderInfoLog( shader ).trim();\n\n\tif ( status && errors === '' ) return '';\n\n\tconst errorMatches = /ERROR: 0:(\\d+)/.exec( errors );\n\tif ( errorMatches ) {\n\n\t\t// --enable-privileged-webgl-extension\n\t\t// console.log( '**' + type + '**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) );\n\n\t\tconst errorLine = parseInt( errorMatches[ 1 ] );\n\t\treturn type.toUpperCase() + '\\n\\n' + errors + '\\n\\n' + handleSource( gl.getShaderSource( shader ), errorLine );\n\n\t} else {\n\n\t\treturn errors;\n\n\t}\n\n}\n\nfunction getTexelEncodingFunction( functionName, colorSpace ) {\n\n\tconst components = getEncodingComponents( colorSpace );\n\treturn `vec4 ${functionName}( vec4 value ) { return ${components[ 0 ]}( ${components[ 1 ]}( value ) ); }`;\n\n}\n\nfunction getToneMappingFunction( functionName, toneMapping ) {\n\n\tlet toneMappingName;\n\n\tswitch ( toneMapping ) {\n\n\t\tcase LinearToneMapping:\n\t\t\ttoneMappingName = 'Linear';\n\t\t\tbreak;\n\n\t\tcase ReinhardToneMapping:\n\t\t\ttoneMappingName = 'Reinhard';\n\t\t\tbreak;\n\n\t\tcase CineonToneMapping:\n\t\t\ttoneMappingName = 'OptimizedCineon';\n\t\t\tbreak;\n\n\t\tcase ACESFilmicToneMapping:\n\t\t\ttoneMappingName = 'ACESFilmic';\n\t\t\tbreak;\n\n\t\tcase CustomToneMapping:\n\t\t\ttoneMappingName = 'Custom';\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tconsole.warn( 'THREE.WebGLProgram: Unsupported toneMapping:', toneMapping );\n\t\t\ttoneMappingName = 'Linear';\n\n\t}\n\n\treturn 'vec3 ' + functionName + '( vec3 color ) { return ' + toneMappingName + 'ToneMapping( color ); }';\n\n}\n\nfunction generateExtensions( parameters ) {\n\n\tconst chunks = [\n\t\t( parameters.extensionDerivatives || !! parameters.envMapCubeUVHeight || parameters.bumpMap || parameters.normalMapTangentSpace || parameters.clearcoatNormalMap || parameters.flatShading || parameters.shaderID === 'physical' ) ? '#extension GL_OES_standard_derivatives : enable' : '',\n\t\t( parameters.extensionFragDepth || parameters.logarithmicDepthBuffer ) && parameters.rendererExtensionFragDepth ? '#extension GL_EXT_frag_depth : enable' : '',\n\t\t( parameters.extensionDrawBuffers && parameters.rendererExtensionDrawBuffers ) ? '#extension GL_EXT_draw_buffers : require' : '',\n\t\t( parameters.extensionShaderTextureLOD || parameters.envMap || parameters.transmission ) && parameters.rendererExtensionShaderTextureLod ? '#extension GL_EXT_shader_texture_lod : enable' : ''\n\t];\n\n\treturn chunks.filter( filterEmptyLine ).join( '\\n' );\n\n}\n\nfunction generateDefines( defines ) {\n\n\tconst chunks = [];\n\n\tfor ( const name in defines ) {\n\n\t\tconst value = defines[ name ];\n\n\t\tif ( value === false ) continue;\n\n\t\tchunks.push( '#define ' + name + ' ' + value );\n\n\t}\n\n\treturn chunks.join( '\\n' );\n\n}\n\nfunction fetchAttributeLocations( gl, program ) {\n\n\tconst attributes = {};\n\n\tconst n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES );\n\n\tfor ( let i = 0; i < n; i ++ ) {\n\n\t\tconst info = gl.getActiveAttrib( program, i );\n\t\tconst name = info.name;\n\n\t\tlet locationSize = 1;\n\t\tif ( info.type === gl.FLOAT_MAT2 ) locationSize = 2;\n\t\tif ( info.type === gl.FLOAT_MAT3 ) locationSize = 3;\n\t\tif ( info.type === gl.FLOAT_MAT4 ) locationSize = 4;\n\n\t\t// console.log( 'THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:', name, i );\n\n\t\tattributes[ name ] = {\n\t\t\ttype: info.type,\n\t\t\tlocation: gl.getAttribLocation( program, name ),\n\t\t\tlocationSize: locationSize\n\t\t};\n\n\t}\n\n\treturn attributes;\n\n}\n\nfunction filterEmptyLine( string ) {\n\n\treturn string !== '';\n\n}\n\nfunction replaceLightNums( string, parameters ) {\n\n\tconst numSpotLightCoords = parameters.numSpotLightShadows + parameters.numSpotLightMaps - parameters.numSpotLightShadowsWithMaps;\n\n\treturn string\n\t\t.replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights )\n\t\t.replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights )\n\t\t.replace( /NUM_SPOT_LIGHT_MAPS/g, parameters.numSpotLightMaps )\n\t\t.replace( /NUM_SPOT_LIGHT_COORDS/g, numSpotLightCoords )\n\t\t.replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights )\n\t\t.replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights )\n\t\t.replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights )\n\t\t.replace( /NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows )\n\t\t.replace( /NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS/g, parameters.numSpotLightShadowsWithMaps )\n\t\t.replace( /NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows )\n\t\t.replace( /NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows );\n\n}\n\nfunction replaceClippingPlaneNums( string, parameters ) {\n\n\treturn string\n\t\t.replace( /NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes )\n\t\t.replace( /UNION_CLIPPING_PLANES/g, ( parameters.numClippingPlanes - parameters.numClipIntersection ) );\n\n}\n\n// Resolve Includes\n\nconst includePattern = /^[ \\t]*#include +<([\\w\\d./]+)>/gm;\n\nfunction resolveIncludes( string ) {\n\n\treturn string.replace( includePattern, includeReplacer );\n\n}\n\nconst shaderChunkMap = new Map( [\n\t[ 'encodings_fragment', 'colorspace_fragment' ], // @deprecated, r154\n\t[ 'encodings_pars_fragment', 'colorspace_pars_fragment' ], // @deprecated, r154\n\t[ 'output_fragment', 'opaque_fragment' ], // @deprecated, r154\n] );\n\nfunction includeReplacer( match, include ) {\n\n\tlet string = ShaderChunk[ include ];\n\n\tif ( string === undefined ) {\n\n\t\tconst newInclude = shaderChunkMap.get( include );\n\n\t\tif ( newInclude !== undefined ) {\n\n\t\t\tstring = ShaderChunk[ newInclude ];\n\t\t\tconsole.warn( 'THREE.WebGLRenderer: Shader chunk \"%s\" has been deprecated. Use \"%s\" instead.', include, newInclude );\n\n\t\t} else {\n\n\t\t\tthrow new Error( 'Can not resolve #include <' + include + '>' );\n\n\t\t}\n\n\t}\n\n\treturn resolveIncludes( string );\n\n}\n\n// Unroll Loops\n\nconst unrollLoopPattern = /#pragma unroll_loop_start\\s+for\\s*\\(\\s*int\\s+i\\s*=\\s*(\\d+)\\s*;\\s*i\\s*<\\s*(\\d+)\\s*;\\s*i\\s*\\+\\+\\s*\\)\\s*{([\\s\\S]+?)}\\s+#pragma unroll_loop_end/g;\n\nfunction unrollLoops( string ) {\n\n\treturn string.replace( unrollLoopPattern, loopReplacer );\n\n}\n\nfunction loopReplacer( match, start, end, snippet ) {\n\n\tlet string = '';\n\n\tfor ( let i = parseInt( start ); i < parseInt( end ); i ++ ) {\n\n\t\tstring += snippet\n\t\t\t.replace( /\\[\\s*i\\s*\\]/g, '[ ' + i + ' ]' )\n\t\t\t.replace( /UNROLLED_LOOP_INDEX/g, i );\n\n\t}\n\n\treturn string;\n\n}\n\n//\n\nfunction generatePrecision( parameters ) {\n\n\tlet precisionstring = 'precision ' + parameters.precision + ' float;\\nprecision ' + parameters.precision + ' int;';\n\n\tif ( parameters.precision === 'highp' ) {\n\n\t\tprecisionstring += '\\n#define HIGH_PRECISION';\n\n\t} else if ( parameters.precision === 'mediump' ) {\n\n\t\tprecisionstring += '\\n#define MEDIUM_PRECISION';\n\n\t} else if ( parameters.precision === 'lowp' ) {\n\n\t\tprecisionstring += '\\n#define LOW_PRECISION';\n\n\t}\n\n\treturn precisionstring;\n\n}\n\nfunction generateShadowMapTypeDefine( parameters ) {\n\n\tlet shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC';\n\n\tif ( parameters.shadowMapType === PCFShadowMap ) {\n\n\t\tshadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF';\n\n\t} else if ( parameters.shadowMapType === PCFSoftShadowMap ) {\n\n\t\tshadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT';\n\n\t} else if ( parameters.shadowMapType === VSMShadowMap ) {\n\n\t\tshadowMapTypeDefine = 'SHADOWMAP_TYPE_VSM';\n\n\t}\n\n\treturn shadowMapTypeDefine;\n\n}\n\nfunction generateEnvMapTypeDefine( parameters ) {\n\n\tlet envMapTypeDefine = 'ENVMAP_TYPE_CUBE';\n\n\tif ( parameters.envMap ) {\n\n\t\tswitch ( parameters.envMapMode ) {\n\n\t\t\tcase CubeReflectionMapping:\n\t\t\tcase CubeRefractionMapping:\n\t\t\t\tenvMapTypeDefine = 'ENVMAP_TYPE_CUBE';\n\t\t\t\tbreak;\n\n\t\t\tcase CubeUVReflectionMapping:\n\t\t\t\tenvMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV';\n\t\t\t\tbreak;\n\n\t\t}\n\n\t}\n\n\treturn envMapTypeDefine;\n\n}\n\nfunction generateEnvMapModeDefine( parameters ) {\n\n\tlet envMapModeDefine = 'ENVMAP_MODE_REFLECTION';\n\n\tif ( parameters.envMap ) {\n\n\t\tswitch ( parameters.envMapMode ) {\n\n\t\t\tcase CubeRefractionMapping:\n\n\t\t\t\tenvMapModeDefine = 'ENVMAP_MODE_REFRACTION';\n\t\t\t\tbreak;\n\n\t\t}\n\n\t}\n\n\treturn envMapModeDefine;\n\n}\n\nfunction generateEnvMapBlendingDefine( parameters ) {\n\n\tlet envMapBlendingDefine = 'ENVMAP_BLENDING_NONE';\n\n\tif ( parameters.envMap ) {\n\n\t\tswitch ( parameters.combine ) {\n\n\t\t\tcase MultiplyOperation:\n\t\t\t\tenvMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY';\n\t\t\t\tbreak;\n\n\t\t\tcase MixOperation:\n\t\t\t\tenvMapBlendingDefine = 'ENVMAP_BLENDING_MIX';\n\t\t\t\tbreak;\n\n\t\t\tcase AddOperation:\n\t\t\t\tenvMapBlendingDefine = 'ENVMAP_BLENDING_ADD';\n\t\t\t\tbreak;\n\n\t\t}\n\n\t}\n\n\treturn envMapBlendingDefine;\n\n}\n\nfunction generateCubeUVSize( parameters ) {\n\n\tconst imageHeight = parameters.envMapCubeUVHeight;\n\n\tif ( imageHeight === null ) return null;\n\n\tconst maxMip = Math.log2( imageHeight ) - 2;\n\n\tconst texelHeight = 1.0 / imageHeight;\n\n\tconst texelWidth = 1.0 / ( 3 * Math.max( Math.pow( 2, maxMip ), 7 * 16 ) );\n\n\treturn { texelWidth, texelHeight, maxMip };\n\n}\n\nfunction WebGLProgram( renderer, cacheKey, parameters, bindingStates ) {\n\n\t// TODO Send this event to Three.js DevTools\n\t// console.log( 'WebGLProgram', cacheKey );\n\n\tconst gl = renderer.getContext();\n\n\tconst defines = parameters.defines;\n\n\tlet vertexShader = parameters.vertexShader;\n\tlet fragmentShader = parameters.fragmentShader;\n\n\tconst shadowMapTypeDefine = generateShadowMapTypeDefine( parameters );\n\tconst envMapTypeDefine = generateEnvMapTypeDefine( parameters );\n\tconst envMapModeDefine = generateEnvMapModeDefine( parameters );\n\tconst envMapBlendingDefine = generateEnvMapBlendingDefine( parameters );\n\tconst envMapCubeUVSize = generateCubeUVSize( parameters );\n\n\tconst customExtensions = parameters.isWebGL2 ? '' : generateExtensions( parameters );\n\n\tconst customDefines = generateDefines( defines );\n\n\tconst program = gl.createProgram();\n\n\tlet prefixVertex, prefixFragment;\n\tlet versionString = parameters.glslVersion ? '#version ' + parameters.glslVersion + '\\n' : '';\n\n\tif ( parameters.isRawShaderMaterial ) {\n\n\t\tprefixVertex = [\n\n\t\t\t'#define SHADER_TYPE ' + parameters.shaderType,\n\t\t\t'#define SHADER_NAME ' + parameters.shaderName,\n\n\t\t\tcustomDefines\n\n\t\t].filter( filterEmptyLine ).join( '\\n' );\n\n\t\tif ( prefixVertex.length > 0 ) {\n\n\t\t\tprefixVertex += '\\n';\n\n\t\t}\n\n\t\tprefixFragment = [\n\n\t\t\tcustomExtensions,\n\n\t\t\t'#define SHADER_TYPE ' + parameters.shaderType,\n\t\t\t'#define SHADER_NAME ' + parameters.shaderName,\n\n\t\t\tcustomDefines\n\n\t\t].filter( filterEmptyLine ).join( '\\n' );\n\n\t\tif ( prefixFragment.length > 0 ) {\n\n\t\t\tprefixFragment += '\\n';\n\n\t\t}\n\n\t} else {\n\n\t\tprefixVertex = [\n\n\t\t\tgeneratePrecision( parameters ),\n\n\t\t\t'#define SHADER_TYPE ' + parameters.shaderType,\n\t\t\t'#define SHADER_NAME ' + parameters.shaderName,\n\n\t\t\tcustomDefines,\n\n\t\t\tparameters.instancing ? '#define USE_INSTANCING' : '',\n\t\t\tparameters.instancingColor ? '#define USE_INSTANCING_COLOR' : '',\n\n\t\t\tparameters.useFog && parameters.fog ? '#define USE_FOG' : '',\n\t\t\tparameters.useFog && parameters.fogExp2 ? '#define FOG_EXP2' : '',\n\n\t\t\tparameters.map ? '#define USE_MAP' : '',\n\t\t\tparameters.envMap ? '#define USE_ENVMAP' : '',\n\t\t\tparameters.envMap ? '#define ' + envMapModeDefine : '',\n\t\t\tparameters.lightMap ? '#define USE_LIGHTMAP' : '',\n\t\t\tparameters.aoMap ? '#define USE_AOMAP' : '',\n\t\t\tparameters.bumpMap ? '#define USE_BUMPMAP' : '',\n\t\t\tparameters.normalMap ? '#define USE_NORMALMAP' : '',\n\t\t\tparameters.normalMapObjectSpace ? '#define USE_NORMALMAP_OBJECTSPACE' : '',\n\t\t\tparameters.normalMapTangentSpace ? '#define USE_NORMALMAP_TANGENTSPACE' : '',\n\t\t\tparameters.displacementMap ? '#define USE_DISPLACEMENTMAP' : '',\n\t\t\tparameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',\n\n\t\t\tparameters.anisotropy ? '#define USE_ANISOTROPY' : '',\n\t\t\tparameters.anisotropyMap ? '#define USE_ANISOTROPYMAP' : '',\n\n\t\t\tparameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '',\n\t\t\tparameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '',\n\t\t\tparameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '',\n\n\t\t\tparameters.iridescenceMap ? '#define USE_IRIDESCENCEMAP' : '',\n\t\t\tparameters.iridescenceThicknessMap ? '#define USE_IRIDESCENCE_THICKNESSMAP' : '',\n\n\t\t\tparameters.specularMap ? '#define USE_SPECULARMAP' : '',\n\t\t\tparameters.specularColorMap ? '#define USE_SPECULAR_COLORMAP' : '',\n\t\t\tparameters.specularIntensityMap ? '#define USE_SPECULAR_INTENSITYMAP' : '',\n\n\t\t\tparameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',\n\t\t\tparameters.metalnessMap ? '#define USE_METALNESSMAP' : '',\n\t\t\tparameters.alphaMap ? '#define USE_ALPHAMAP' : '',\n\t\t\tparameters.alphaHash ? '#define USE_ALPHAHASH' : '',\n\n\t\t\tparameters.transmission ? '#define USE_TRANSMISSION' : '',\n\t\t\tparameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '',\n\t\t\tparameters.thicknessMap ? '#define USE_THICKNESSMAP' : '',\n\n\t\t\tparameters.sheenColorMap ? '#define USE_SHEEN_COLORMAP' : '',\n\t\t\tparameters.sheenRoughnessMap ? '#define USE_SHEEN_ROUGHNESSMAP' : '',\n\n\t\t\t//\n\n\t\t\tparameters.mapUv ? '#define MAP_UV ' + parameters.mapUv : '',\n\t\t\tparameters.alphaMapUv ? '#define ALPHAMAP_UV ' + parameters.alphaMapUv : '',\n\t\t\tparameters.lightMapUv ? '#define LIGHTMAP_UV ' + parameters.lightMapUv : '',\n\t\t\tparameters.aoMapUv ? '#define AOMAP_UV ' + parameters.aoMapUv : '',\n\t\t\tparameters.emissiveMapUv ? '#define EMISSIVEMAP_UV ' + parameters.emissiveMapUv : '',\n\t\t\tparameters.bumpMapUv ? '#define BUMPMAP_UV ' + parameters.bumpMapUv : '',\n\t\t\tparameters.normalMapUv ? '#define NORMALMAP_UV ' + parameters.normalMapUv : '',\n\t\t\tparameters.displacementMapUv ? '#define DISPLACEMENTMAP_UV ' + parameters.displacementMapUv : '',\n\n\t\t\tparameters.metalnessMapUv ? '#define METALNESSMAP_UV ' + parameters.metalnessMapUv : '',\n\t\t\tparameters.roughnessMapUv ? '#define ROUGHNESSMAP_UV ' + parameters.roughnessMapUv : '',\n\n\t\t\tparameters.anisotropyMapUv ? '#define ANISOTROPYMAP_UV ' + parameters.anisotropyMapUv : '',\n\n\t\t\tparameters.clearcoatMapUv ? '#define CLEARCOATMAP_UV ' + parameters.clearcoatMapUv : '',\n\t\t\tparameters.clearcoatNormalMapUv ? '#define CLEARCOAT_NORMALMAP_UV ' + parameters.clearcoatNormalMapUv : '',\n\t\t\tparameters.clearcoatRoughnessMapUv ? '#define CLEARCOAT_ROUGHNESSMAP_UV ' + parameters.clearcoatRoughnessMapUv : '',\n\n\t\t\tparameters.iridescenceMapUv ? '#define IRIDESCENCEMAP_UV ' + parameters.iridescenceMapUv : '',\n\t\t\tparameters.iridescenceThicknessMapUv ? '#define IRIDESCENCE_THICKNESSMAP_UV ' + parameters.iridescenceThicknessMapUv : '',\n\n\t\t\tparameters.sheenColorMapUv ? '#define SHEEN_COLORMAP_UV ' + parameters.sheenColorMapUv : '',\n\t\t\tparameters.sheenRoughnessMapUv ? '#define SHEEN_ROUGHNESSMAP_UV ' + parameters.sheenRoughnessMapUv : '',\n\n\t\t\tparameters.specularMapUv ? '#define SPECULARMAP_UV ' + parameters.specularMapUv : '',\n\t\t\tparameters.specularColorMapUv ? '#define SPECULAR_COLORMAP_UV ' + parameters.specularColorMapUv : '',\n\t\t\tparameters.specularIntensityMapUv ? '#define SPECULAR_INTENSITYMAP_UV ' + parameters.specularIntensityMapUv : '',\n\n\t\t\tparameters.transmissionMapUv ? '#define TRANSMISSIONMAP_UV ' + parameters.transmissionMapUv : '',\n\t\t\tparameters.thicknessMapUv ? '#define THICKNESSMAP_UV ' + parameters.thicknessMapUv : '',\n\n\t\t\t//\n\n\t\t\tparameters.vertexTangents && parameters.flatShading === false ? '#define USE_TANGENT' : '',\n\t\t\tparameters.vertexColors ? '#define USE_COLOR' : '',\n\t\t\tparameters.vertexAlphas ? '#define USE_COLOR_ALPHA' : '',\n\t\t\tparameters.vertexUv1s ? '#define USE_UV1' : '',\n\t\t\tparameters.vertexUv2s ? '#define USE_UV2' : '',\n\t\t\tparameters.vertexUv3s ? '#define USE_UV3' : '',\n\n\t\t\tparameters.pointsUvs ? '#define USE_POINTS_UV' : '',\n\n\t\t\tparameters.flatShading ? '#define FLAT_SHADED' : '',\n\n\t\t\tparameters.skinning ? '#define USE_SKINNING' : '',\n\n\t\t\tparameters.morphTargets ? '#define USE_MORPHTARGETS' : '',\n\t\t\tparameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '',\n\t\t\t( parameters.morphColors && parameters.isWebGL2 ) ? '#define USE_MORPHCOLORS' : '',\n\t\t\t( parameters.morphTargetsCount > 0 && parameters.isWebGL2 ) ? '#define MORPHTARGETS_TEXTURE' : '',\n\t\t\t( parameters.morphTargetsCount > 0 && parameters.isWebGL2 ) ? '#define MORPHTARGETS_TEXTURE_STRIDE ' + parameters.morphTextureStride : '',\n\t\t\t( parameters.morphTargetsCount > 0 && parameters.isWebGL2 ) ? '#define MORPHTARGETS_COUNT ' + parameters.morphTargetsCount : '',\n\t\t\tparameters.doubleSided ? '#define DOUBLE_SIDED' : '',\n\t\t\tparameters.flipSided ? '#define FLIP_SIDED' : '',\n\n\t\t\tparameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',\n\t\t\tparameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',\n\n\t\t\tparameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '',\n\n\t\t\tparameters.numLightProbes > 0 ? '#define USE_LIGHT_PROBES' : '',\n\n\t\t\tparameters.useLegacyLights ? '#define LEGACY_LIGHTS' : '',\n\n\t\t\tparameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',\n\t\t\t( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? '#define USE_LOGDEPTHBUF_EXT' : '',\n\n\t\t\t'uniform mat4 modelMatrix;',\n\t\t\t'uniform mat4 modelViewMatrix;',\n\t\t\t'uniform mat4 projectionMatrix;',\n\t\t\t'uniform mat4 viewMatrix;',\n\t\t\t'uniform mat3 normalMatrix;',\n\t\t\t'uniform vec3 cameraPosition;',\n\t\t\t'uniform bool isOrthographic;',\n\n\t\t\t'#ifdef USE_INSTANCING',\n\n\t\t\t'\tattribute mat4 instanceMatrix;',\n\n\t\t\t'#endif',\n\n\t\t\t'#ifdef USE_INSTANCING_COLOR',\n\n\t\t\t'\tattribute vec3 instanceColor;',\n\n\t\t\t'#endif',\n\n\t\t\t'attribute vec3 position;',\n\t\t\t'attribute vec3 normal;',\n\t\t\t'attribute vec2 uv;',\n\n\t\t\t'#ifdef USE_UV1',\n\n\t\t\t'\tattribute vec2 uv1;',\n\n\t\t\t'#endif',\n\n\t\t\t'#ifdef USE_UV2',\n\n\t\t\t'\tattribute vec2 uv2;',\n\n\t\t\t'#endif',\n\n\t\t\t'#ifdef USE_UV3',\n\n\t\t\t'\tattribute vec2 uv3;',\n\n\t\t\t'#endif',\n\n\t\t\t'#ifdef USE_TANGENT',\n\n\t\t\t'\tattribute vec4 tangent;',\n\n\t\t\t'#endif',\n\n\t\t\t'#if defined( USE_COLOR_ALPHA )',\n\n\t\t\t'\tattribute vec4 color;',\n\n\t\t\t'#elif defined( USE_COLOR )',\n\n\t\t\t'\tattribute vec3 color;',\n\n\t\t\t'#endif',\n\n\t\t\t'#if ( defined( USE_MORPHTARGETS ) && ! defined( MORPHTARGETS_TEXTURE ) )',\n\n\t\t\t'\tattribute vec3 morphTarget0;',\n\t\t\t'\tattribute vec3 morphTarget1;',\n\t\t\t'\tattribute vec3 morphTarget2;',\n\t\t\t'\tattribute vec3 morphTarget3;',\n\n\t\t\t'\t#ifdef USE_MORPHNORMALS',\n\n\t\t\t'\t\tattribute vec3 morphNormal0;',\n\t\t\t'\t\tattribute vec3 morphNormal1;',\n\t\t\t'\t\tattribute vec3 morphNormal2;',\n\t\t\t'\t\tattribute vec3 morphNormal3;',\n\n\t\t\t'\t#else',\n\n\t\t\t'\t\tattribute vec3 morphTarget4;',\n\t\t\t'\t\tattribute vec3 morphTarget5;',\n\t\t\t'\t\tattribute vec3 morphTarget6;',\n\t\t\t'\t\tattribute vec3 morphTarget7;',\n\n\t\t\t'\t#endif',\n\n\t\t\t'#endif',\n\n\t\t\t'#ifdef USE_SKINNING',\n\n\t\t\t'\tattribute vec4 skinIndex;',\n\t\t\t'\tattribute vec4 skinWeight;',\n\n\t\t\t'#endif',\n\n\t\t\t'\\n'\n\n\t\t].filter( filterEmptyLine ).join( '\\n' );\n\n\t\tprefixFragment = [\n\n\t\t\tcustomExtensions,\n\n\t\t\tgeneratePrecision( parameters ),\n\n\t\t\t'#define SHADER_TYPE ' + parameters.shaderType,\n\t\t\t'#define SHADER_NAME ' + parameters.shaderName,\n\n\t\t\tcustomDefines,\n\n\t\t\tparameters.useFog && parameters.fog ? '#define USE_FOG' : '',\n\t\t\tparameters.useFog && parameters.fogExp2 ? '#define FOG_EXP2' : '',\n\n\t\t\tparameters.map ? '#define USE_MAP' : '',\n\t\t\tparameters.matcap ? '#define USE_MATCAP' : '',\n\t\t\tparameters.envMap ? '#define USE_ENVMAP' : '',\n\t\t\tparameters.envMap ? '#define ' + envMapTypeDefine : '',\n\t\t\tparameters.envMap ? '#define ' + envMapModeDefine : '',\n\t\t\tparameters.envMap ? '#define ' + envMapBlendingDefine : '',\n\t\t\tenvMapCubeUVSize ? '#define CUBEUV_TEXEL_WIDTH ' + envMapCubeUVSize.texelWidth : '',\n\t\t\tenvMapCubeUVSize ? '#define CUBEUV_TEXEL_HEIGHT ' + envMapCubeUVSize.texelHeight : '',\n\t\t\tenvMapCubeUVSize ? '#define CUBEUV_MAX_MIP ' + envMapCubeUVSize.maxMip + '.0' : '',\n\t\t\tparameters.lightMap ? '#define USE_LIGHTMAP' : '',\n\t\t\tparameters.aoMap ? '#define USE_AOMAP' : '',\n\t\t\tparameters.bumpMap ? '#define USE_BUMPMAP' : '',\n\t\t\tparameters.normalMap ? '#define USE_NORMALMAP' : '',\n\t\t\tparameters.normalMapObjectSpace ? '#define USE_NORMALMAP_OBJECTSPACE' : '',\n\t\t\tparameters.normalMapTangentSpace ? '#define USE_NORMALMAP_TANGENTSPACE' : '',\n\t\t\tparameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',\n\n\t\t\tparameters.anisotropy ? '#define USE_ANISOTROPY' : '',\n\t\t\tparameters.anisotropyMap ? '#define USE_ANISOTROPYMAP' : '',\n\n\t\t\tparameters.clearcoat ? '#define USE_CLEARCOAT' : '',\n\t\t\tparameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '',\n\t\t\tparameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '',\n\t\t\tparameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '',\n\n\t\t\tparameters.iridescence ? '#define USE_IRIDESCENCE' : '',\n\t\t\tparameters.iridescenceMap ? '#define USE_IRIDESCENCEMAP' : '',\n\t\t\tparameters.iridescenceThicknessMap ? '#define USE_IRIDESCENCE_THICKNESSMAP' : '',\n\n\t\t\tparameters.specularMap ? '#define USE_SPECULARMAP' : '',\n\t\t\tparameters.specularColorMap ? '#define USE_SPECULAR_COLORMAP' : '',\n\t\t\tparameters.specularIntensityMap ? '#define USE_SPECULAR_INTENSITYMAP' : '',\n\n\t\t\tparameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',\n\t\t\tparameters.metalnessMap ? '#define USE_METALNESSMAP' : '',\n\n\t\t\tparameters.alphaMap ? '#define USE_ALPHAMAP' : '',\n\t\t\tparameters.alphaTest ? '#define USE_ALPHATEST' : '',\n\t\t\tparameters.alphaHash ? '#define USE_ALPHAHASH' : '',\n\n\t\t\tparameters.sheen ? '#define USE_SHEEN' : '',\n\t\t\tparameters.sheenColorMap ? '#define USE_SHEEN_COLORMAP' : '',\n\t\t\tparameters.sheenRoughnessMap ? '#define USE_SHEEN_ROUGHNESSMAP' : '',\n\n\t\t\tparameters.transmission ? '#define USE_TRANSMISSION' : '',\n\t\t\tparameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '',\n\t\t\tparameters.thicknessMap ? '#define USE_THICKNESSMAP' : '',\n\n\t\t\tparameters.vertexTangents && parameters.flatShading === false ? '#define USE_TANGENT' : '',\n\t\t\tparameters.vertexColors || parameters.instancingColor ? '#define USE_COLOR' : '',\n\t\t\tparameters.vertexAlphas ? '#define USE_COLOR_ALPHA' : '',\n\t\t\tparameters.vertexUv1s ? '#define USE_UV1' : '',\n\t\t\tparameters.vertexUv2s ? '#define USE_UV2' : '',\n\t\t\tparameters.vertexUv3s ? '#define USE_UV3' : '',\n\n\t\t\tparameters.pointsUvs ? '#define USE_POINTS_UV' : '',\n\n\t\t\tparameters.gradientMap ? '#define USE_GRADIENTMAP' : '',\n\n\t\t\tparameters.flatShading ? '#define FLAT_SHADED' : '',\n\n\t\t\tparameters.doubleSided ? '#define DOUBLE_SIDED' : '',\n\t\t\tparameters.flipSided ? '#define FLIP_SIDED' : '',\n\n\t\t\tparameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',\n\t\t\tparameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',\n\n\t\t\tparameters.premultipliedAlpha ? '#define PREMULTIPLIED_ALPHA' : '',\n\n\t\t\tparameters.numLightProbes > 0 ? '#define USE_LIGHT_PROBES' : '',\n\n\t\t\tparameters.useLegacyLights ? '#define LEGACY_LIGHTS' : '',\n\n\t\t\tparameters.decodeVideoTexture ? '#define DECODE_VIDEO_TEXTURE' : '',\n\n\t\t\tparameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',\n\t\t\t( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? '#define USE_LOGDEPTHBUF_EXT' : '',\n\n\t\t\t'uniform mat4 viewMatrix;',\n\t\t\t'uniform vec3 cameraPosition;',\n\t\t\t'uniform bool isOrthographic;',\n\n\t\t\t( parameters.toneMapping !== NoToneMapping ) ? '#define TONE_MAPPING' : '',\n\t\t\t( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ 'tonemapping_pars_fragment' ] : '', // this code is required here because it is used by the toneMapping() function defined below\n\t\t\t( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( 'toneMapping', parameters.toneMapping ) : '',\n\n\t\t\tparameters.dithering ? '#define DITHERING' : '',\n\t\t\tparameters.opaque ? '#define OPAQUE' : '',\n\n\t\t\tShaderChunk[ 'colorspace_pars_fragment' ], // this code is required here because it is used by the various encoding/decoding function defined below\n\t\t\tgetTexelEncodingFunction( 'linearToOutputTexel', parameters.outputColorSpace ),\n\n\t\t\tparameters.useDepthPacking ? '#define DEPTH_PACKING ' + parameters.depthPacking : '',\n\n\t\t\t'\\n'\n\n\t\t].filter( filterEmptyLine ).join( '\\n' );\n\n\t}\n\n\tvertexShader = resolveIncludes( vertexShader );\n\tvertexShader = replaceLightNums( vertexShader, parameters );\n\tvertexShader = replaceClippingPlaneNums( vertexShader, parameters );\n\n\tfragmentShader = resolveIncludes( fragmentShader );\n\tfragmentShader = replaceLightNums( fragmentShader, parameters );\n\tfragmentShader = replaceClippingPlaneNums( fragmentShader, parameters );\n\n\tvertexShader = unrollLoops( vertexShader );\n\tfragmentShader = unrollLoops( fragmentShader );\n\n\tif ( parameters.isWebGL2 && parameters.isRawShaderMaterial !== true ) {\n\n\t\t// GLSL 3.0 conversion for built-in materials and ShaderMaterial\n\n\t\tversionString = '#version 300 es\\n';\n\n\t\tprefixVertex = [\n\t\t\t'precision mediump sampler2DArray;',\n\t\t\t'#define attribute in',\n\t\t\t'#define varying out',\n\t\t\t'#define texture2D texture'\n\t\t].join( '\\n' ) + '\\n' + prefixVertex;\n\n\t\tprefixFragment = [\n\t\t\t'precision mediump sampler2DArray;',\n\t\t\t'#define varying in',\n\t\t\t( parameters.glslVersion === GLSL3 ) ? '' : 'layout(location = 0) out highp vec4 pc_fragColor;',\n\t\t\t( parameters.glslVersion === GLSL3 ) ? '' : '#define gl_FragColor pc_fragColor',\n\t\t\t'#define gl_FragDepthEXT gl_FragDepth',\n\t\t\t'#define texture2D texture',\n\t\t\t'#define textureCube texture',\n\t\t\t'#define texture2DProj textureProj',\n\t\t\t'#define texture2DLodEXT textureLod',\n\t\t\t'#define texture2DProjLodEXT textureProjLod',\n\t\t\t'#define textureCubeLodEXT textureLod',\n\t\t\t'#define texture2DGradEXT textureGrad',\n\t\t\t'#define texture2DProjGradEXT textureProjGrad',\n\t\t\t'#define textureCubeGradEXT textureGrad'\n\t\t].join( '\\n' ) + '\\n' + prefixFragment;\n\n\t}\n\n\tconst vertexGlsl = versionString + prefixVertex + vertexShader;\n\tconst fragmentGlsl = versionString + prefixFragment + fragmentShader;\n\n\t// console.log( '*VERTEX*', vertexGlsl );\n\t// console.log( '*FRAGMENT*', fragmentGlsl );\n\n\tconst glVertexShader = WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl );\n\tconst glFragmentShader = WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl );\n\n\tgl.attachShader( program, glVertexShader );\n\tgl.attachShader( program, glFragmentShader );\n\n\t// Force a particular attribute to index 0.\n\n\tif ( parameters.index0AttributeName !== undefined ) {\n\n\t\tgl.bindAttribLocation( program, 0, parameters.index0AttributeName );\n\n\t} else if ( parameters.morphTargets === true ) {\n\n\t\t// programs with morphTargets displace position out of attribute 0\n\t\tgl.bindAttribLocation( program, 0, 'position' );\n\n\t}\n\n\tgl.linkProgram( program );\n\n\tfunction onFirstUse( self ) {\n\n\t\t// check for link errors\n\t\tif ( renderer.debug.checkShaderErrors ) {\n\n\t\t\tconst programLog = gl.getProgramInfoLog( program ).trim();\n\t\t\tconst vertexLog = gl.getShaderInfoLog( glVertexShader ).trim();\n\t\t\tconst fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim();\n\n\t\t\tlet runnable = true;\n\t\t\tlet haveDiagnostics = true;\n\n\t\t\tif ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) {\n\n\t\t\t\trunnable = false;\n\n\t\t\t\tif ( typeof renderer.debug.onShaderError === 'function' ) {\n\n\t\t\t\t\trenderer.debug.onShaderError( gl, program, glVertexShader, glFragmentShader );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// default error reporting\n\n\t\t\t\t\tconst vertexErrors = getShaderErrors( gl, glVertexShader, 'vertex' );\n\t\t\t\t\tconst fragmentErrors = getShaderErrors( gl, glFragmentShader, 'fragment' );\n\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t'THREE.WebGLProgram: Shader Error ' + gl.getError() + ' - ' +\n\t\t\t\t\t\t'VALIDATE_STATUS ' + gl.getProgramParameter( program, gl.VALIDATE_STATUS ) + '\\n\\n' +\n\t\t\t\t\t\t'Program Info Log: ' + programLog + '\\n' +\n\t\t\t\t\t\tvertexErrors + '\\n' +\n\t\t\t\t\t\tfragmentErrors\n\t\t\t\t\t);\n\n\t\t\t\t}\n\n\t\t\t} else if ( programLog !== '' ) {\n\n\t\t\t\tconsole.warn( 'THREE.WebGLProgram: Program Info Log:', programLog );\n\n\t\t\t} else if ( vertexLog === '' || fragmentLog === '' ) {\n\n\t\t\t\thaveDiagnostics = false;\n\n\t\t\t}\n\n\t\t\tif ( haveDiagnostics ) {\n\n\t\t\t\tself.diagnostics = {\n\n\t\t\t\t\trunnable: runnable,\n\n\t\t\t\t\tprogramLog: programLog,\n\n\t\t\t\t\tvertexShader: {\n\n\t\t\t\t\t\tlog: vertexLog,\n\t\t\t\t\t\tprefix: prefixVertex\n\n\t\t\t\t\t},\n\n\t\t\t\t\tfragmentShader: {\n\n\t\t\t\t\t\tlog: fragmentLog,\n\t\t\t\t\t\tprefix: prefixFragment\n\n\t\t\t\t\t}\n\n\t\t\t\t};\n\n\t\t\t}\n\n\t\t}\n\n\t\t// Clean up\n\n\t\t// Crashes in iOS9 and iOS10. #18402\n\t\t// gl.detachShader( program, glVertexShader );\n\t\t// gl.detachShader( program, glFragmentShader );\n\n\t\tgl.deleteShader( glVertexShader );\n\t\tgl.deleteShader( glFragmentShader );\n\n\t\tcachedUniforms = new WebGLUniforms( gl, program );\n\t\tcachedAttributes = fetchAttributeLocations( gl, program );\n\n\t}\n\n\t// set up caching for uniform locations\n\n\tlet cachedUniforms;\n\n\tthis.getUniforms = function () {\n\n\t\tif ( cachedUniforms === undefined ) {\n\n\t\t\t// Populates cachedUniforms and cachedAttributes\n\t\t\tonFirstUse( this );\n\n\t\t}\n\n\t\treturn cachedUniforms;\n\n\t};\n\n\t// set up caching for attribute locations\n\n\tlet cachedAttributes;\n\n\tthis.getAttributes = function () {\n\n\t\tif ( cachedAttributes === undefined ) {\n\n\t\t\t// Populates cachedAttributes and cachedUniforms\n\t\t\tonFirstUse( this );\n\n\t\t}\n\n\t\treturn cachedAttributes;\n\n\t};\n\n\t// indicate when the program is ready to be used. if the KHR_parallel_shader_compile extension isn't supported,\n\t// flag the program as ready immediately. It may cause a stall when it's first used.\n\n\tlet programReady = ( parameters.rendererExtensionParallelShaderCompile === false );\n\n\tthis.isReady = function () {\n\n\t\tif ( programReady === false ) {\n\n\t\t\tprogramReady = gl.getProgramParameter( program, COMPLETION_STATUS_KHR );\n\n\t\t}\n\n\t\treturn programReady;\n\n\t};\n\n\t// free resource\n\n\tthis.destroy = function () {\n\n\t\tbindingStates.releaseStatesOfProgram( this );\n\n\t\tgl.deleteProgram( program );\n\t\tthis.program = undefined;\n\n\t};\n\n\t//\n\n\tthis.type = parameters.shaderType;\n\tthis.name = parameters.shaderName;\n\tthis.id = programIdCount ++;\n\tthis.cacheKey = cacheKey;\n\tthis.usedTimes = 1;\n\tthis.program = program;\n\tthis.vertexShader = glVertexShader;\n\tthis.fragmentShader = glFragmentShader;\n\n\treturn this;\n\n}\n\nexport { WebGLProgram };\n","let _id = 0;\n\nclass WebGLShaderCache {\n\n\tconstructor() {\n\n\t\tthis.shaderCache = new Map();\n\t\tthis.materialCache = new Map();\n\n\t}\n\n\tupdate( material ) {\n\n\t\tconst vertexShader = material.vertexShader;\n\t\tconst fragmentShader = material.fragmentShader;\n\n\t\tconst vertexShaderStage = this._getShaderStage( vertexShader );\n\t\tconst fragmentShaderStage = this._getShaderStage( fragmentShader );\n\n\t\tconst materialShaders = this._getShaderCacheForMaterial( material );\n\n\t\tif ( materialShaders.has( vertexShaderStage ) === false ) {\n\n\t\t\tmaterialShaders.add( vertexShaderStage );\n\t\t\tvertexShaderStage.usedTimes ++;\n\n\t\t}\n\n\t\tif ( materialShaders.has( fragmentShaderStage ) === false ) {\n\n\t\t\tmaterialShaders.add( fragmentShaderStage );\n\t\t\tfragmentShaderStage.usedTimes ++;\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tremove( material ) {\n\n\t\tconst materialShaders = this.materialCache.get( material );\n\n\t\tfor ( const shaderStage of materialShaders ) {\n\n\t\t\tshaderStage.usedTimes --;\n\n\t\t\tif ( shaderStage.usedTimes === 0 ) this.shaderCache.delete( shaderStage.code );\n\n\t\t}\n\n\t\tthis.materialCache.delete( material );\n\n\t\treturn this;\n\n\t}\n\n\tgetVertexShaderID( material ) {\n\n\t\treturn this._getShaderStage( material.vertexShader ).id;\n\n\t}\n\n\tgetFragmentShaderID( material ) {\n\n\t\treturn this._getShaderStage( material.fragmentShader ).id;\n\n\t}\n\n\tdispose() {\n\n\t\tthis.shaderCache.clear();\n\t\tthis.materialCache.clear();\n\n\t}\n\n\t_getShaderCacheForMaterial( material ) {\n\n\t\tconst cache = this.materialCache;\n\t\tlet set = cache.get( material );\n\n\t\tif ( set === undefined ) {\n\n\t\t\tset = new Set();\n\t\t\tcache.set( material, set );\n\n\t\t}\n\n\t\treturn set;\n\n\t}\n\n\t_getShaderStage( code ) {\n\n\t\tconst cache = this.shaderCache;\n\t\tlet stage = cache.get( code );\n\n\t\tif ( stage === undefined ) {\n\n\t\t\tstage = new WebGLShaderStage( code );\n\t\t\tcache.set( code, stage );\n\n\t\t}\n\n\t\treturn stage;\n\n\t}\n\n}\n\nclass WebGLShaderStage {\n\n\tconstructor( code ) {\n\n\t\tthis.id = _id ++;\n\n\t\tthis.code = code;\n\t\tthis.usedTimes = 0;\n\n\t}\n\n}\n\nexport { WebGLShaderCache };\n","import { BackSide, DoubleSide, CubeUVReflectionMapping, ObjectSpaceNormalMap, TangentSpaceNormalMap, NoToneMapping, NormalBlending, LinearSRGBColorSpace, SRGBTransfer } from '../../constants.js';\nimport { Layers } from '../../core/Layers.js';\nimport { WebGLProgram } from './WebGLProgram.js';\nimport { WebGLShaderCache } from './WebGLShaderCache.js';\nimport { ShaderLib } from '../shaders/ShaderLib.js';\nimport { UniformsUtils } from '../shaders/UniformsUtils.js';\nimport { ColorManagement } from '../../math/ColorManagement.js';\n\nfunction WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping ) {\n\n\tconst _programLayers = new Layers();\n\tconst _customShaders = new WebGLShaderCache();\n\tconst programs = [];\n\n\tconst IS_WEBGL2 = capabilities.isWebGL2;\n\tconst logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer;\n\tconst SUPPORTS_VERTEX_TEXTURES = capabilities.vertexTextures;\n\n\tlet precision = capabilities.precision;\n\n\tconst shaderIDs = {\n\t\tMeshDepthMaterial: 'depth',\n\t\tMeshDistanceMaterial: 'distanceRGBA',\n\t\tMeshNormalMaterial: 'normal',\n\t\tMeshBasicMaterial: 'basic',\n\t\tMeshLambertMaterial: 'lambert',\n\t\tMeshPhongMaterial: 'phong',\n\t\tMeshToonMaterial: 'toon',\n\t\tMeshStandardMaterial: 'physical',\n\t\tMeshPhysicalMaterial: 'physical',\n\t\tMeshMatcapMaterial: 'matcap',\n\t\tLineBasicMaterial: 'basic',\n\t\tLineDashedMaterial: 'dashed',\n\t\tPointsMaterial: 'points',\n\t\tShadowMaterial: 'shadow',\n\t\tSpriteMaterial: 'sprite'\n\t};\n\n\tfunction getChannel( value ) {\n\n\t\tif ( value === 0 ) return 'uv';\n\n\t\treturn `uv${ value }`;\n\n\t}\n\n\tfunction getParameters( material, lights, shadows, scene, object ) {\n\n\t\tconst fog = scene.fog;\n\t\tconst geometry = object.geometry;\n\t\tconst environment = material.isMeshStandardMaterial ? scene.environment : null;\n\n\t\tconst envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment );\n\t\tconst envMapCubeUVHeight = ( !! envMap ) && ( envMap.mapping === CubeUVReflectionMapping ) ? envMap.image.height : null;\n\n\t\tconst shaderID = shaderIDs[ material.type ];\n\n\t\t// heuristics to create shader parameters according to lights in the scene\n\t\t// (not to blow over maxLights budget)\n\n\t\tif ( material.precision !== null ) {\n\n\t\t\tprecision = capabilities.getMaxPrecision( material.precision );\n\n\t\t\tif ( precision !== material.precision ) {\n\n\t\t\t\tconsole.warn( 'THREE.WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.' );\n\n\t\t\t}\n\n\t\t}\n\n\t\t//\n\n\t\tconst morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color;\n\t\tconst morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0;\n\n\t\tlet morphTextureStride = 0;\n\n\t\tif ( geometry.morphAttributes.position !== undefined ) morphTextureStride = 1;\n\t\tif ( geometry.morphAttributes.normal !== undefined ) morphTextureStride = 2;\n\t\tif ( geometry.morphAttributes.color !== undefined ) morphTextureStride = 3;\n\n\t\t//\n\n\t\tlet vertexShader, fragmentShader;\n\t\tlet customVertexShaderID, customFragmentShaderID;\n\n\t\tif ( shaderID ) {\n\n\t\t\tconst shader = ShaderLib[ shaderID ];\n\n\t\t\tvertexShader = shader.vertexShader;\n\t\t\tfragmentShader = shader.fragmentShader;\n\n\t\t} else {\n\n\t\t\tvertexShader = material.vertexShader;\n\t\t\tfragmentShader = material.fragmentShader;\n\n\t\t\t_customShaders.update( material );\n\n\t\t\tcustomVertexShaderID = _customShaders.getVertexShaderID( material );\n\t\t\tcustomFragmentShaderID = _customShaders.getFragmentShaderID( material );\n\n\t\t}\n\n\t\tconst currentRenderTarget = renderer.getRenderTarget();\n\n\t\tconst IS_INSTANCEDMESH = object.isInstancedMesh === true;\n\n\t\tconst HAS_MAP = !! material.map;\n\t\tconst HAS_MATCAP = !! material.matcap;\n\t\tconst HAS_ENVMAP = !! envMap;\n\t\tconst HAS_AOMAP = !! material.aoMap;\n\t\tconst HAS_LIGHTMAP = !! material.lightMap;\n\t\tconst HAS_BUMPMAP = !! material.bumpMap;\n\t\tconst HAS_NORMALMAP = !! material.normalMap;\n\t\tconst HAS_DISPLACEMENTMAP = !! material.displacementMap;\n\t\tconst HAS_EMISSIVEMAP = !! material.emissiveMap;\n\n\t\tconst HAS_METALNESSMAP = !! material.metalnessMap;\n\t\tconst HAS_ROUGHNESSMAP = !! material.roughnessMap;\n\n\t\tconst HAS_ANISOTROPY = material.anisotropy > 0;\n\t\tconst HAS_CLEARCOAT = material.clearcoat > 0;\n\t\tconst HAS_IRIDESCENCE = material.iridescence > 0;\n\t\tconst HAS_SHEEN = material.sheen > 0;\n\t\tconst HAS_TRANSMISSION = material.transmission > 0;\n\n\t\tconst HAS_ANISOTROPYMAP = HAS_ANISOTROPY && !! material.anisotropyMap;\n\n\t\tconst HAS_CLEARCOATMAP = HAS_CLEARCOAT && !! material.clearcoatMap;\n\t\tconst HAS_CLEARCOAT_NORMALMAP = HAS_CLEARCOAT && !! material.clearcoatNormalMap;\n\t\tconst HAS_CLEARCOAT_ROUGHNESSMAP = HAS_CLEARCOAT && !! material.clearcoatRoughnessMap;\n\n\t\tconst HAS_IRIDESCENCEMAP = HAS_IRIDESCENCE && !! material.iridescenceMap;\n\t\tconst HAS_IRIDESCENCE_THICKNESSMAP = HAS_IRIDESCENCE && !! material.iridescenceThicknessMap;\n\n\t\tconst HAS_SHEEN_COLORMAP = HAS_SHEEN && !! material.sheenColorMap;\n\t\tconst HAS_SHEEN_ROUGHNESSMAP = HAS_SHEEN && !! material.sheenRoughnessMap;\n\n\t\tconst HAS_SPECULARMAP = !! material.specularMap;\n\t\tconst HAS_SPECULAR_COLORMAP = !! material.specularColorMap;\n\t\tconst HAS_SPECULAR_INTENSITYMAP = !! material.specularIntensityMap;\n\n\t\tconst HAS_TRANSMISSIONMAP = HAS_TRANSMISSION && !! material.transmissionMap;\n\t\tconst HAS_THICKNESSMAP = HAS_TRANSMISSION && !! material.thicknessMap;\n\n\t\tconst HAS_GRADIENTMAP = !! material.gradientMap;\n\n\t\tconst HAS_ALPHAMAP = !! material.alphaMap;\n\n\t\tconst HAS_ALPHATEST = material.alphaTest > 0;\n\n\t\tconst HAS_ALPHAHASH = !! material.alphaHash;\n\n\t\tconst HAS_EXTENSIONS = !! material.extensions;\n\n\t\tconst HAS_ATTRIBUTE_UV1 = !! geometry.attributes.uv1;\n\t\tconst HAS_ATTRIBUTE_UV2 = !! geometry.attributes.uv2;\n\t\tconst HAS_ATTRIBUTE_UV3 = !! geometry.attributes.uv3;\n\n\t\tlet toneMapping = NoToneMapping;\n\n\t\tif ( material.toneMapped ) {\n\n\t\t\tif ( currentRenderTarget === null || currentRenderTarget.isXRRenderTarget === true ) {\n\n\t\t\t\ttoneMapping = renderer.toneMapping;\n\n\t\t\t}\n\n\t\t}\n\n\t\tconst parameters = {\n\n\t\t\tisWebGL2: IS_WEBGL2,\n\n\t\t\tshaderID: shaderID,\n\t\t\tshaderType: material.type,\n\t\t\tshaderName: material.name,\n\n\t\t\tvertexShader: vertexShader,\n\t\t\tfragmentShader: fragmentShader,\n\t\t\tdefines: material.defines,\n\n\t\t\tcustomVertexShaderID: customVertexShaderID,\n\t\t\tcustomFragmentShaderID: customFragmentShaderID,\n\n\t\t\tisRawShaderMaterial: material.isRawShaderMaterial === true,\n\t\t\tglslVersion: material.glslVersion,\n\n\t\t\tprecision: precision,\n\n\t\t\tinstancing: IS_INSTANCEDMESH,\n\t\t\tinstancingColor: IS_INSTANCEDMESH && object.instanceColor !== null,\n\n\t\t\tsupportsVertexTextures: SUPPORTS_VERTEX_TEXTURES,\n\t\t\toutputColorSpace: ( currentRenderTarget === null ) ? renderer.outputColorSpace : ( currentRenderTarget.isXRRenderTarget === true ? currentRenderTarget.texture.colorSpace : LinearSRGBColorSpace ),\n\n\t\t\tmap: HAS_MAP,\n\t\t\tmatcap: HAS_MATCAP,\n\t\t\tenvMap: HAS_ENVMAP,\n\t\t\tenvMapMode: HAS_ENVMAP && envMap.mapping,\n\t\t\tenvMapCubeUVHeight: envMapCubeUVHeight,\n\t\t\taoMap: HAS_AOMAP,\n\t\t\tlightMap: HAS_LIGHTMAP,\n\t\t\tbumpMap: HAS_BUMPMAP,\n\t\t\tnormalMap: HAS_NORMALMAP,\n\t\t\tdisplacementMap: SUPPORTS_VERTEX_TEXTURES && HAS_DISPLACEMENTMAP,\n\t\t\temissiveMap: HAS_EMISSIVEMAP,\n\n\t\t\tnormalMapObjectSpace: HAS_NORMALMAP && material.normalMapType === ObjectSpaceNormalMap,\n\t\t\tnormalMapTangentSpace: HAS_NORMALMAP && material.normalMapType === TangentSpaceNormalMap,\n\n\t\t\tmetalnessMap: HAS_METALNESSMAP,\n\t\t\troughnessMap: HAS_ROUGHNESSMAP,\n\n\t\t\tanisotropy: HAS_ANISOTROPY,\n\t\t\tanisotropyMap: HAS_ANISOTROPYMAP,\n\n\t\t\tclearcoat: HAS_CLEARCOAT,\n\t\t\tclearcoatMap: HAS_CLEARCOATMAP,\n\t\t\tclearcoatNormalMap: HAS_CLEARCOAT_NORMALMAP,\n\t\t\tclearcoatRoughnessMap: HAS_CLEARCOAT_ROUGHNESSMAP,\n\n\t\t\tiridescence: HAS_IRIDESCENCE,\n\t\t\tiridescenceMap: HAS_IRIDESCENCEMAP,\n\t\t\tiridescenceThicknessMap: HAS_IRIDESCENCE_THICKNESSMAP,\n\n\t\t\tsheen: HAS_SHEEN,\n\t\t\tsheenColorMap: HAS_SHEEN_COLORMAP,\n\t\t\tsheenRoughnessMap: HAS_SHEEN_ROUGHNESSMAP,\n\n\t\t\tspecularMap: HAS_SPECULARMAP,\n\t\t\tspecularColorMap: HAS_SPECULAR_COLORMAP,\n\t\t\tspecularIntensityMap: HAS_SPECULAR_INTENSITYMAP,\n\n\t\t\ttransmission: HAS_TRANSMISSION,\n\t\t\ttransmissionMap: HAS_TRANSMISSIONMAP,\n\t\t\tthicknessMap: HAS_THICKNESSMAP,\n\n\t\t\tgradientMap: HAS_GRADIENTMAP,\n\n\t\t\topaque: material.transparent === false && material.blending === NormalBlending,\n\n\t\t\talphaMap: HAS_ALPHAMAP,\n\t\t\talphaTest: HAS_ALPHATEST,\n\t\t\talphaHash: HAS_ALPHAHASH,\n\n\t\t\tcombine: material.combine,\n\n\t\t\t//\n\n\t\t\tmapUv: HAS_MAP && getChannel( material.map.channel ),\n\t\t\taoMapUv: HAS_AOMAP && getChannel( material.aoMap.channel ),\n\t\t\tlightMapUv: HAS_LIGHTMAP && getChannel( material.lightMap.channel ),\n\t\t\tbumpMapUv: HAS_BUMPMAP && getChannel( material.bumpMap.channel ),\n\t\t\tnormalMapUv: HAS_NORMALMAP && getChannel( material.normalMap.channel ),\n\t\t\tdisplacementMapUv: HAS_DISPLACEMENTMAP && getChannel( material.displacementMap.channel ),\n\t\t\temissiveMapUv: HAS_EMISSIVEMAP && getChannel( material.emissiveMap.channel ),\n\n\t\t\tmetalnessMapUv: HAS_METALNESSMAP && getChannel( material.metalnessMap.channel ),\n\t\t\troughnessMapUv: HAS_ROUGHNESSMAP && getChannel( material.roughnessMap.channel ),\n\n\t\t\tanisotropyMapUv: HAS_ANISOTROPYMAP && getChannel( material.anisotropyMap.channel ),\n\n\t\t\tclearcoatMapUv: HAS_CLEARCOATMAP && getChannel( material.clearcoatMap.channel ),\n\t\t\tclearcoatNormalMapUv: HAS_CLEARCOAT_NORMALMAP && getChannel( material.clearcoatNormalMap.channel ),\n\t\t\tclearcoatRoughnessMapUv: HAS_CLEARCOAT_ROUGHNESSMAP && getChannel( material.clearcoatRoughnessMap.channel ),\n\n\t\t\tiridescenceMapUv: HAS_IRIDESCENCEMAP && getChannel( material.iridescenceMap.channel ),\n\t\t\tiridescenceThicknessMapUv: HAS_IRIDESCENCE_THICKNESSMAP && getChannel( material.iridescenceThicknessMap.channel ),\n\n\t\t\tsheenColorMapUv: HAS_SHEEN_COLORMAP && getChannel( material.sheenColorMap.channel ),\n\t\t\tsheenRoughnessMapUv: HAS_SHEEN_ROUGHNESSMAP && getChannel( material.sheenRoughnessMap.channel ),\n\n\t\t\tspecularMapUv: HAS_SPECULARMAP && getChannel( material.specularMap.channel ),\n\t\t\tspecularColorMapUv: HAS_SPECULAR_COLORMAP && getChannel( material.specularColorMap.channel ),\n\t\t\tspecularIntensityMapUv: HAS_SPECULAR_INTENSITYMAP && getChannel( material.specularIntensityMap.channel ),\n\n\t\t\ttransmissionMapUv: HAS_TRANSMISSIONMAP && getChannel( material.transmissionMap.channel ),\n\t\t\tthicknessMapUv: HAS_THICKNESSMAP && getChannel( material.thicknessMap.channel ),\n\n\t\t\talphaMapUv: HAS_ALPHAMAP && getChannel( material.alphaMap.channel ),\n\n\t\t\t//\n\n\t\t\tvertexTangents: !! geometry.attributes.tangent && ( HAS_NORMALMAP || HAS_ANISOTROPY ),\n\t\t\tvertexColors: material.vertexColors,\n\t\t\tvertexAlphas: material.vertexColors === true && !! geometry.attributes.color && geometry.attributes.color.itemSize === 4,\n\t\t\tvertexUv1s: HAS_ATTRIBUTE_UV1,\n\t\t\tvertexUv2s: HAS_ATTRIBUTE_UV2,\n\t\t\tvertexUv3s: HAS_ATTRIBUTE_UV3,\n\n\t\t\tpointsUvs: object.isPoints === true && !! geometry.attributes.uv && ( HAS_MAP || HAS_ALPHAMAP ),\n\n\t\t\tfog: !! fog,\n\t\t\tuseFog: material.fog === true,\n\t\t\tfogExp2: ( fog && fog.isFogExp2 ),\n\n\t\t\tflatShading: material.flatShading === true,\n\n\t\t\tsizeAttenuation: material.sizeAttenuation === true,\n\t\t\tlogarithmicDepthBuffer: logarithmicDepthBuffer,\n\n\t\t\tskinning: object.isSkinnedMesh === true,\n\n\t\t\tmorphTargets: geometry.morphAttributes.position !== undefined,\n\t\t\tmorphNormals: geometry.morphAttributes.normal !== undefined,\n\t\t\tmorphColors: geometry.morphAttributes.color !== undefined,\n\t\t\tmorphTargetsCount: morphTargetsCount,\n\t\t\tmorphTextureStride: morphTextureStride,\n\n\t\t\tnumDirLights: lights.directional.length,\n\t\t\tnumPointLights: lights.point.length,\n\t\t\tnumSpotLights: lights.spot.length,\n\t\t\tnumSpotLightMaps: lights.spotLightMap.length,\n\t\t\tnumRectAreaLights: lights.rectArea.length,\n\t\t\tnumHemiLights: lights.hemi.length,\n\n\t\t\tnumDirLightShadows: lights.directionalShadowMap.length,\n\t\t\tnumPointLightShadows: lights.pointShadowMap.length,\n\t\t\tnumSpotLightShadows: lights.spotShadowMap.length,\n\t\t\tnumSpotLightShadowsWithMaps: lights.numSpotLightShadowsWithMaps,\n\n\t\t\tnumLightProbes: lights.numLightProbes,\n\n\t\t\tnumClippingPlanes: clipping.numPlanes,\n\t\t\tnumClipIntersection: clipping.numIntersection,\n\n\t\t\tdithering: material.dithering,\n\n\t\t\tshadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0,\n\t\t\tshadowMapType: renderer.shadowMap.type,\n\n\t\t\ttoneMapping: toneMapping,\n\t\t\tuseLegacyLights: renderer._useLegacyLights,\n\n\t\t\tdecodeVideoTexture: HAS_MAP && ( material.map.isVideoTexture === true ) && ( ColorManagement.getTransfer( material.map.colorSpace ) === SRGBTransfer ),\n\n\t\t\tpremultipliedAlpha: material.premultipliedAlpha,\n\n\t\t\tdoubleSided: material.side === DoubleSide,\n\t\t\tflipSided: material.side === BackSide,\n\n\t\t\tuseDepthPacking: material.depthPacking >= 0,\n\t\t\tdepthPacking: material.depthPacking || 0,\n\n\t\t\tindex0AttributeName: material.index0AttributeName,\n\n\t\t\textensionDerivatives: HAS_EXTENSIONS && material.extensions.derivatives === true,\n\t\t\textensionFragDepth: HAS_EXTENSIONS && material.extensions.fragDepth === true,\n\t\t\textensionDrawBuffers: HAS_EXTENSIONS && material.extensions.drawBuffers === true,\n\t\t\textensionShaderTextureLOD: HAS_EXTENSIONS && material.extensions.shaderTextureLOD === true,\n\n\t\t\trendererExtensionFragDepth: IS_WEBGL2 || extensions.has( 'EXT_frag_depth' ),\n\t\t\trendererExtensionDrawBuffers: IS_WEBGL2 || extensions.has( 'WEBGL_draw_buffers' ),\n\t\t\trendererExtensionShaderTextureLod: IS_WEBGL2 || extensions.has( 'EXT_shader_texture_lod' ),\n\t\t\trendererExtensionParallelShaderCompile: extensions.has( 'KHR_parallel_shader_compile' ),\n\n\t\t\tcustomProgramCacheKey: material.customProgramCacheKey()\n\n\t\t};\n\n\t\treturn parameters;\n\n\t}\n\n\tfunction getProgramCacheKey( parameters ) {\n\n\t\tconst array = [];\n\n\t\tif ( parameters.shaderID ) {\n\n\t\t\tarray.push( parameters.shaderID );\n\n\t\t} else {\n\n\t\t\tarray.push( parameters.customVertexShaderID );\n\t\t\tarray.push( parameters.customFragmentShaderID );\n\n\t\t}\n\n\t\tif ( parameters.defines !== undefined ) {\n\n\t\t\tfor ( const name in parameters.defines ) {\n\n\t\t\t\tarray.push( name );\n\t\t\t\tarray.push( parameters.defines[ name ] );\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( parameters.isRawShaderMaterial === false ) {\n\n\t\t\tgetProgramCacheKeyParameters( array, parameters );\n\t\t\tgetProgramCacheKeyBooleans( array, parameters );\n\t\t\tarray.push( renderer.outputColorSpace );\n\n\t\t}\n\n\t\tarray.push( parameters.customProgramCacheKey );\n\n\t\treturn array.join();\n\n\t}\n\n\tfunction getProgramCacheKeyParameters( array, parameters ) {\n\n\t\tarray.push( parameters.precision );\n\t\tarray.push( parameters.outputColorSpace );\n\t\tarray.push( parameters.envMapMode );\n\t\tarray.push( parameters.envMapCubeUVHeight );\n\t\tarray.push( parameters.mapUv );\n\t\tarray.push( parameters.alphaMapUv );\n\t\tarray.push( parameters.lightMapUv );\n\t\tarray.push( parameters.aoMapUv );\n\t\tarray.push( parameters.bumpMapUv );\n\t\tarray.push( parameters.normalMapUv );\n\t\tarray.push( parameters.displacementMapUv );\n\t\tarray.push( parameters.emissiveMapUv );\n\t\tarray.push( parameters.metalnessMapUv );\n\t\tarray.push( parameters.roughnessMapUv );\n\t\tarray.push( parameters.anisotropyMapUv );\n\t\tarray.push( parameters.clearcoatMapUv );\n\t\tarray.push( parameters.clearcoatNormalMapUv );\n\t\tarray.push( parameters.clearcoatRoughnessMapUv );\n\t\tarray.push( parameters.iridescenceMapUv );\n\t\tarray.push( parameters.iridescenceThicknessMapUv );\n\t\tarray.push( parameters.sheenColorMapUv );\n\t\tarray.push( parameters.sheenRoughnessMapUv );\n\t\tarray.push( parameters.specularMapUv );\n\t\tarray.push( parameters.specularColorMapUv );\n\t\tarray.push( parameters.specularIntensityMapUv );\n\t\tarray.push( parameters.transmissionMapUv );\n\t\tarray.push( parameters.thicknessMapUv );\n\t\tarray.push( parameters.combine );\n\t\tarray.push( parameters.fogExp2 );\n\t\tarray.push( parameters.sizeAttenuation );\n\t\tarray.push( parameters.morphTargetsCount );\n\t\tarray.push( parameters.morphAttributeCount );\n\t\tarray.push( parameters.numDirLights );\n\t\tarray.push( parameters.numPointLights );\n\t\tarray.push( parameters.numSpotLights );\n\t\tarray.push( parameters.numSpotLightMaps );\n\t\tarray.push( parameters.numHemiLights );\n\t\tarray.push( parameters.numRectAreaLights );\n\t\tarray.push( parameters.numDirLightShadows );\n\t\tarray.push( parameters.numPointLightShadows );\n\t\tarray.push( parameters.numSpotLightShadows );\n\t\tarray.push( parameters.numSpotLightShadowsWithMaps );\n\t\tarray.push( parameters.numLightProbes );\n\t\tarray.push( parameters.shadowMapType );\n\t\tarray.push( parameters.toneMapping );\n\t\tarray.push( parameters.numClippingPlanes );\n\t\tarray.push( parameters.numClipIntersection );\n\t\tarray.push( parameters.depthPacking );\n\n\t}\n\n\tfunction getProgramCacheKeyBooleans( array, parameters ) {\n\n\t\t_programLayers.disableAll();\n\n\t\tif ( parameters.isWebGL2 )\n\t\t\t_programLayers.enable( 0 );\n\t\tif ( parameters.supportsVertexTextures )\n\t\t\t_programLayers.enable( 1 );\n\t\tif ( parameters.instancing )\n\t\t\t_programLayers.enable( 2 );\n\t\tif ( parameters.instancingColor )\n\t\t\t_programLayers.enable( 3 );\n\t\tif ( parameters.matcap )\n\t\t\t_programLayers.enable( 4 );\n\t\tif ( parameters.envMap )\n\t\t\t_programLayers.enable( 5 );\n\t\tif ( parameters.normalMapObjectSpace )\n\t\t\t_programLayers.enable( 6 );\n\t\tif ( parameters.normalMapTangentSpace )\n\t\t\t_programLayers.enable( 7 );\n\t\tif ( parameters.clearcoat )\n\t\t\t_programLayers.enable( 8 );\n\t\tif ( parameters.iridescence )\n\t\t\t_programLayers.enable( 9 );\n\t\tif ( parameters.alphaTest )\n\t\t\t_programLayers.enable( 10 );\n\t\tif ( parameters.vertexColors )\n\t\t\t_programLayers.enable( 11 );\n\t\tif ( parameters.vertexAlphas )\n\t\t\t_programLayers.enable( 12 );\n\t\tif ( parameters.vertexUv1s )\n\t\t\t_programLayers.enable( 13 );\n\t\tif ( parameters.vertexUv2s )\n\t\t\t_programLayers.enable( 14 );\n\t\tif ( parameters.vertexUv3s )\n\t\t\t_programLayers.enable( 15 );\n\t\tif ( parameters.vertexTangents )\n\t\t\t_programLayers.enable( 16 );\n\t\tif ( parameters.anisotropy )\n\t\t\t_programLayers.enable( 17 );\n\t\tif ( parameters.alphaHash )\n\t\t\t_programLayers.enable( 18 );\n\n\t\tarray.push( _programLayers.mask );\n\t\t_programLayers.disableAll();\n\n\t\tif ( parameters.fog )\n\t\t\t_programLayers.enable( 0 );\n\t\tif ( parameters.useFog )\n\t\t\t_programLayers.enable( 1 );\n\t\tif ( parameters.flatShading )\n\t\t\t_programLayers.enable( 2 );\n\t\tif ( parameters.logarithmicDepthBuffer )\n\t\t\t_programLayers.enable( 3 );\n\t\tif ( parameters.skinning )\n\t\t\t_programLayers.enable( 4 );\n\t\tif ( parameters.morphTargets )\n\t\t\t_programLayers.enable( 5 );\n\t\tif ( parameters.morphNormals )\n\t\t\t_programLayers.enable( 6 );\n\t\tif ( parameters.morphColors )\n\t\t\t_programLayers.enable( 7 );\n\t\tif ( parameters.premultipliedAlpha )\n\t\t\t_programLayers.enable( 8 );\n\t\tif ( parameters.shadowMapEnabled )\n\t\t\t_programLayers.enable( 9 );\n\t\tif ( parameters.useLegacyLights )\n\t\t\t_programLayers.enable( 10 );\n\t\tif ( parameters.doubleSided )\n\t\t\t_programLayers.enable( 11 );\n\t\tif ( parameters.flipSided )\n\t\t\t_programLayers.enable( 12 );\n\t\tif ( parameters.useDepthPacking )\n\t\t\t_programLayers.enable( 13 );\n\t\tif ( parameters.dithering )\n\t\t\t_programLayers.enable( 14 );\n\t\tif ( parameters.transmission )\n\t\t\t_programLayers.enable( 15 );\n\t\tif ( parameters.sheen )\n\t\t\t_programLayers.enable( 16 );\n\t\tif ( parameters.opaque )\n\t\t\t_programLayers.enable( 17 );\n\t\tif ( parameters.pointsUvs )\n\t\t\t_programLayers.enable( 18 );\n\t\tif ( parameters.decodeVideoTexture )\n\t\t\t_programLayers.enable( 19 );\n\n\t\tarray.push( _programLayers.mask );\n\n\t}\n\n\tfunction getUniforms( material ) {\n\n\t\tconst shaderID = shaderIDs[ material.type ];\n\t\tlet uniforms;\n\n\t\tif ( shaderID ) {\n\n\t\t\tconst shader = ShaderLib[ shaderID ];\n\t\t\tuniforms = UniformsUtils.clone( shader.uniforms );\n\n\t\t} else {\n\n\t\t\tuniforms = material.uniforms;\n\n\t\t}\n\n\t\treturn uniforms;\n\n\t}\n\n\tfunction acquireProgram( parameters, cacheKey ) {\n\n\t\tlet program;\n\n\t\t// Check if code has been already compiled\n\t\tfor ( let p = 0, pl = programs.length; p < pl; p ++ ) {\n\n\t\t\tconst preexistingProgram = programs[ p ];\n\n\t\t\tif ( preexistingProgram.cacheKey === cacheKey ) {\n\n\t\t\t\tprogram = preexistingProgram;\n\t\t\t\t++ program.usedTimes;\n\n\t\t\t\tbreak;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( program === undefined ) {\n\n\t\t\tprogram = new WebGLProgram( renderer, cacheKey, parameters, bindingStates );\n\t\t\tprograms.push( program );\n\n\t\t}\n\n\t\treturn program;\n\n\t}\n\n\tfunction releaseProgram( program ) {\n\n\t\tif ( -- program.usedTimes === 0 ) {\n\n\t\t\t// Remove from unordered set\n\t\t\tconst i = programs.indexOf( program );\n\t\t\tprograms[ i ] = programs[ programs.length - 1 ];\n\t\t\tprograms.pop();\n\n\t\t\t// Free WebGL resources\n\t\t\tprogram.destroy();\n\n\t\t}\n\n\t}\n\n\tfunction releaseShaderCache( material ) {\n\n\t\t_customShaders.remove( material );\n\n\t}\n\n\tfunction dispose() {\n\n\t\t_customShaders.dispose();\n\n\t}\n\n\treturn {\n\t\tgetParameters: getParameters,\n\t\tgetProgramCacheKey: getProgramCacheKey,\n\t\tgetUniforms: getUniforms,\n\t\tacquireProgram: acquireProgram,\n\t\treleaseProgram: releaseProgram,\n\t\treleaseShaderCache: releaseShaderCache,\n\t\t// Exposed for resource monitoring & error feedback via renderer.info:\n\t\tprograms: programs,\n\t\tdispose: dispose\n\t};\n\n}\n\nexport { WebGLPrograms };\n","function WebGLProperties() {\n\n\tlet properties = new WeakMap();\n\n\tfunction get( object ) {\n\n\t\tlet map = properties.get( object );\n\n\t\tif ( map === undefined ) {\n\n\t\t\tmap = {};\n\t\t\tproperties.set( object, map );\n\n\t\t}\n\n\t\treturn map;\n\n\t}\n\n\tfunction remove( object ) {\n\n\t\tproperties.delete( object );\n\n\t}\n\n\tfunction update( object, key, value ) {\n\n\t\tproperties.get( object )[ key ] = value;\n\n\t}\n\n\tfunction dispose() {\n\n\t\tproperties = new WeakMap();\n\n\t}\n\n\treturn {\n\t\tget: get,\n\t\tremove: remove,\n\t\tupdate: update,\n\t\tdispose: dispose\n\t};\n\n}\n\n\nexport { WebGLProperties };\n","function painterSortStable( a, b ) {\n\n\tif ( a.groupOrder !== b.groupOrder ) {\n\n\t\treturn a.groupOrder - b.groupOrder;\n\n\t} else if ( a.renderOrder !== b.renderOrder ) {\n\n\t\treturn a.renderOrder - b.renderOrder;\n\n\t} else if ( a.material.id !== b.material.id ) {\n\n\t\treturn a.material.id - b.material.id;\n\n\t} else if ( a.z !== b.z ) {\n\n\t\treturn a.z - b.z;\n\n\t} else {\n\n\t\treturn a.id - b.id;\n\n\t}\n\n}\n\nfunction reversePainterSortStable( a, b ) {\n\n\tif ( a.groupOrder !== b.groupOrder ) {\n\n\t\treturn a.groupOrder - b.groupOrder;\n\n\t} else if ( a.renderOrder !== b.renderOrder ) {\n\n\t\treturn a.renderOrder - b.renderOrder;\n\n\t} else if ( a.z !== b.z ) {\n\n\t\treturn b.z - a.z;\n\n\t} else {\n\n\t\treturn a.id - b.id;\n\n\t}\n\n}\n\n\nfunction WebGLRenderList() {\n\n\tconst renderItems = [];\n\tlet renderItemsIndex = 0;\n\n\tconst opaque = [];\n\tconst transmissive = [];\n\tconst transparent = [];\n\n\tfunction init() {\n\n\t\trenderItemsIndex = 0;\n\n\t\topaque.length = 0;\n\t\ttransmissive.length = 0;\n\t\ttransparent.length = 0;\n\n\t}\n\n\tfunction getNextRenderItem( object, geometry, material, groupOrder, z, group ) {\n\n\t\tlet renderItem = renderItems[ renderItemsIndex ];\n\n\t\tif ( renderItem === undefined ) {\n\n\t\t\trenderItem = {\n\t\t\t\tid: object.id,\n\t\t\t\tobject: object,\n\t\t\t\tgeometry: geometry,\n\t\t\t\tmaterial: material,\n\t\t\t\tgroupOrder: groupOrder,\n\t\t\t\trenderOrder: object.renderOrder,\n\t\t\t\tz: z,\n\t\t\t\tgroup: group\n\t\t\t};\n\n\t\t\trenderItems[ renderItemsIndex ] = renderItem;\n\n\t\t} else {\n\n\t\t\trenderItem.id = object.id;\n\t\t\trenderItem.object = object;\n\t\t\trenderItem.geometry = geometry;\n\t\t\trenderItem.material = material;\n\t\t\trenderItem.groupOrder = groupOrder;\n\t\t\trenderItem.renderOrder = object.renderOrder;\n\t\t\trenderItem.z = z;\n\t\t\trenderItem.group = group;\n\n\t\t}\n\n\t\trenderItemsIndex ++;\n\n\t\treturn renderItem;\n\n\t}\n\n\tfunction push( object, geometry, material, groupOrder, z, group ) {\n\n\t\tconst renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group );\n\n\t\tif ( material.transmission > 0.0 ) {\n\n\t\t\ttransmissive.push( renderItem );\n\n\t\t} else if ( material.transparent === true ) {\n\n\t\t\ttransparent.push( renderItem );\n\n\t\t} else {\n\n\t\t\topaque.push( renderItem );\n\n\t\t}\n\n\t}\n\n\tfunction unshift( object, geometry, material, groupOrder, z, group ) {\n\n\t\tconst renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group );\n\n\t\tif ( material.transmission > 0.0 ) {\n\n\t\t\ttransmissive.unshift( renderItem );\n\n\t\t} else if ( material.transparent === true ) {\n\n\t\t\ttransparent.unshift( renderItem );\n\n\t\t} else {\n\n\t\t\topaque.unshift( renderItem );\n\n\t\t}\n\n\t}\n\n\tfunction sort( customOpaqueSort, customTransparentSort ) {\n\n\t\tif ( opaque.length > 1 ) opaque.sort( customOpaqueSort || painterSortStable );\n\t\tif ( transmissive.length > 1 ) transmissive.sort( customTransparentSort || reversePainterSortStable );\n\t\tif ( transparent.length > 1 ) transparent.sort( customTransparentSort || reversePainterSortStable );\n\n\t}\n\n\tfunction finish() {\n\n\t\t// Clear references from inactive renderItems in the list\n\n\t\tfor ( let i = renderItemsIndex, il = renderItems.length; i < il; i ++ ) {\n\n\t\t\tconst renderItem = renderItems[ i ];\n\n\t\t\tif ( renderItem.id === null ) break;\n\n\t\t\trenderItem.id = null;\n\t\t\trenderItem.object = null;\n\t\t\trenderItem.geometry = null;\n\t\t\trenderItem.material = null;\n\t\t\trenderItem.group = null;\n\n\t\t}\n\n\t}\n\n\treturn {\n\n\t\topaque: opaque,\n\t\ttransmissive: transmissive,\n\t\ttransparent: transparent,\n\n\t\tinit: init,\n\t\tpush: push,\n\t\tunshift: unshift,\n\t\tfinish: finish,\n\n\t\tsort: sort\n\t};\n\n}\n\nfunction WebGLRenderLists() {\n\n\tlet lists = new WeakMap();\n\n\tfunction get( scene, renderCallDepth ) {\n\n\t\tconst listArray = lists.get( scene );\n\t\tlet list;\n\n\t\tif ( listArray === undefined ) {\n\n\t\t\tlist = new WebGLRenderList();\n\t\t\tlists.set( scene, [ list ] );\n\n\t\t} else {\n\n\t\t\tif ( renderCallDepth >= listArray.length ) {\n\n\t\t\t\tlist = new WebGLRenderList();\n\t\t\t\tlistArray.push( list );\n\n\t\t\t} else {\n\n\t\t\t\tlist = listArray[ renderCallDepth ];\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn list;\n\n\t}\n\n\tfunction dispose() {\n\n\t\tlists = new WeakMap();\n\n\t}\n\n\treturn {\n\t\tget: get,\n\t\tdispose: dispose\n\t};\n\n}\n\n\nexport { WebGLRenderLists, WebGLRenderList };\n","import { Color } from '../../math/Color.js';\nimport { Matrix4 } from '../../math/Matrix4.js';\nimport { Vector2 } from '../../math/Vector2.js';\nimport { Vector3 } from '../../math/Vector3.js';\nimport { UniformsLib } from '../shaders/UniformsLib.js';\n\nfunction UniformsCache() {\n\n\tconst lights = {};\n\n\treturn {\n\n\t\tget: function ( light ) {\n\n\t\t\tif ( lights[ light.id ] !== undefined ) {\n\n\t\t\t\treturn lights[ light.id ];\n\n\t\t\t}\n\n\t\t\tlet uniforms;\n\n\t\t\tswitch ( light.type ) {\n\n\t\t\t\tcase 'DirectionalLight':\n\t\t\t\t\tuniforms = {\n\t\t\t\t\t\tdirection: new Vector3(),\n\t\t\t\t\t\tcolor: new Color()\n\t\t\t\t\t};\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'SpotLight':\n\t\t\t\t\tuniforms = {\n\t\t\t\t\t\tposition: new Vector3(),\n\t\t\t\t\t\tdirection: new Vector3(),\n\t\t\t\t\t\tcolor: new Color(),\n\t\t\t\t\t\tdistance: 0,\n\t\t\t\t\t\tconeCos: 0,\n\t\t\t\t\t\tpenumbraCos: 0,\n\t\t\t\t\t\tdecay: 0\n\t\t\t\t\t};\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'PointLight':\n\t\t\t\t\tuniforms = {\n\t\t\t\t\t\tposition: new Vector3(),\n\t\t\t\t\t\tcolor: new Color(),\n\t\t\t\t\t\tdistance: 0,\n\t\t\t\t\t\tdecay: 0\n\t\t\t\t\t};\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'HemisphereLight':\n\t\t\t\t\tuniforms = {\n\t\t\t\t\t\tdirection: new Vector3(),\n\t\t\t\t\t\tskyColor: new Color(),\n\t\t\t\t\t\tgroundColor: new Color()\n\t\t\t\t\t};\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'RectAreaLight':\n\t\t\t\t\tuniforms = {\n\t\t\t\t\t\tcolor: new Color(),\n\t\t\t\t\t\tposition: new Vector3(),\n\t\t\t\t\t\thalfWidth: new Vector3(),\n\t\t\t\t\t\thalfHeight: new Vector3()\n\t\t\t\t\t};\n\t\t\t\t\tbreak;\n\n\t\t\t}\n\n\t\t\tlights[ light.id ] = uniforms;\n\n\t\t\treturn uniforms;\n\n\t\t}\n\n\t};\n\n}\n\nfunction ShadowUniformsCache() {\n\n\tconst lights = {};\n\n\treturn {\n\n\t\tget: function ( light ) {\n\n\t\t\tif ( lights[ light.id ] !== undefined ) {\n\n\t\t\t\treturn lights[ light.id ];\n\n\t\t\t}\n\n\t\t\tlet uniforms;\n\n\t\t\tswitch ( light.type ) {\n\n\t\t\t\tcase 'DirectionalLight':\n\t\t\t\t\tuniforms = {\n\t\t\t\t\t\tshadowBias: 0,\n\t\t\t\t\t\tshadowNormalBias: 0,\n\t\t\t\t\t\tshadowRadius: 1,\n\t\t\t\t\t\tshadowMapSize: new Vector2()\n\t\t\t\t\t};\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'SpotLight':\n\t\t\t\t\tuniforms = {\n\t\t\t\t\t\tshadowBias: 0,\n\t\t\t\t\t\tshadowNormalBias: 0,\n\t\t\t\t\t\tshadowRadius: 1,\n\t\t\t\t\t\tshadowMapSize: new Vector2()\n\t\t\t\t\t};\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'PointLight':\n\t\t\t\t\tuniforms = {\n\t\t\t\t\t\tshadowBias: 0,\n\t\t\t\t\t\tshadowNormalBias: 0,\n\t\t\t\t\t\tshadowRadius: 1,\n\t\t\t\t\t\tshadowMapSize: new Vector2(),\n\t\t\t\t\t\tshadowCameraNear: 1,\n\t\t\t\t\t\tshadowCameraFar: 1000\n\t\t\t\t\t};\n\t\t\t\t\tbreak;\n\n\t\t\t\t// TODO (abelnation): set RectAreaLight shadow uniforms\n\n\t\t\t}\n\n\t\t\tlights[ light.id ] = uniforms;\n\n\t\t\treturn uniforms;\n\n\t\t}\n\n\t};\n\n}\n\n\n\nlet nextVersion = 0;\n\nfunction shadowCastingAndTexturingLightsFirst( lightA, lightB ) {\n\n\treturn ( lightB.castShadow ? 2 : 0 ) - ( lightA.castShadow ? 2 : 0 ) + ( lightB.map ? 1 : 0 ) - ( lightA.map ? 1 : 0 );\n\n}\n\nfunction WebGLLights( extensions, capabilities ) {\n\n\tconst cache = new UniformsCache();\n\n\tconst shadowCache = ShadowUniformsCache();\n\n\tconst state = {\n\n\t\tversion: 0,\n\n\t\thash: {\n\t\t\tdirectionalLength: - 1,\n\t\t\tpointLength: - 1,\n\t\t\tspotLength: - 1,\n\t\t\trectAreaLength: - 1,\n\t\t\themiLength: - 1,\n\n\t\t\tnumDirectionalShadows: - 1,\n\t\t\tnumPointShadows: - 1,\n\t\t\tnumSpotShadows: - 1,\n\t\t\tnumSpotMaps: - 1,\n\n\t\t\tnumLightProbes: - 1\n\t\t},\n\n\t\tambient: [ 0, 0, 0 ],\n\t\tprobe: [],\n\t\tdirectional: [],\n\t\tdirectionalShadow: [],\n\t\tdirectionalShadowMap: [],\n\t\tdirectionalShadowMatrix: [],\n\t\tspot: [],\n\t\tspotLightMap: [],\n\t\tspotShadow: [],\n\t\tspotShadowMap: [],\n\t\tspotLightMatrix: [],\n\t\trectArea: [],\n\t\trectAreaLTC1: null,\n\t\trectAreaLTC2: null,\n\t\tpoint: [],\n\t\tpointShadow: [],\n\t\tpointShadowMap: [],\n\t\tpointShadowMatrix: [],\n\t\themi: [],\n\t\tnumSpotLightShadowsWithMaps: 0,\n\t\tnumLightProbes: 0\n\n\t};\n\n\tfor ( let i = 0; i < 9; i ++ ) state.probe.push( new Vector3() );\n\n\tconst vector3 = new Vector3();\n\tconst matrix4 = new Matrix4();\n\tconst matrix42 = new Matrix4();\n\n\tfunction setup( lights, useLegacyLights ) {\n\n\t\tlet r = 0, g = 0, b = 0;\n\n\t\tfor ( let i = 0; i < 9; i ++ ) state.probe[ i ].set( 0, 0, 0 );\n\n\t\tlet directionalLength = 0;\n\t\tlet pointLength = 0;\n\t\tlet spotLength = 0;\n\t\tlet rectAreaLength = 0;\n\t\tlet hemiLength = 0;\n\n\t\tlet numDirectionalShadows = 0;\n\t\tlet numPointShadows = 0;\n\t\tlet numSpotShadows = 0;\n\t\tlet numSpotMaps = 0;\n\t\tlet numSpotShadowsWithMaps = 0;\n\n\t\tlet numLightProbes = 0;\n\n\t\t// ordering : [shadow casting + map texturing, map texturing, shadow casting, none ]\n\t\tlights.sort( shadowCastingAndTexturingLightsFirst );\n\n\t\t// artist-friendly light intensity scaling factor\n\t\tconst scaleFactor = ( useLegacyLights === true ) ? Math.PI : 1;\n\n\t\tfor ( let i = 0, l = lights.length; i < l; i ++ ) {\n\n\t\t\tconst light = lights[ i ];\n\n\t\t\tconst color = light.color;\n\t\t\tconst intensity = light.intensity;\n\t\t\tconst distance = light.distance;\n\n\t\t\tconst shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null;\n\n\t\t\tif ( light.isAmbientLight ) {\n\n\t\t\t\tr += color.r * intensity * scaleFactor;\n\t\t\t\tg += color.g * intensity * scaleFactor;\n\t\t\t\tb += color.b * intensity * scaleFactor;\n\n\t\t\t} else if ( light.isLightProbe ) {\n\n\t\t\t\tfor ( let j = 0; j < 9; j ++ ) {\n\n\t\t\t\t\tstate.probe[ j ].addScaledVector( light.sh.coefficients[ j ], intensity );\n\n\t\t\t\t}\n\n\t\t\t\tnumLightProbes ++;\n\n\t\t\t} else if ( light.isDirectionalLight ) {\n\n\t\t\t\tconst uniforms = cache.get( light );\n\n\t\t\t\tuniforms.color.copy( light.color ).multiplyScalar( light.intensity * scaleFactor );\n\n\t\t\t\tif ( light.castShadow ) {\n\n\t\t\t\t\tconst shadow = light.shadow;\n\n\t\t\t\t\tconst shadowUniforms = shadowCache.get( light );\n\n\t\t\t\t\tshadowUniforms.shadowBias = shadow.bias;\n\t\t\t\t\tshadowUniforms.shadowNormalBias = shadow.normalBias;\n\t\t\t\t\tshadowUniforms.shadowRadius = shadow.radius;\n\t\t\t\t\tshadowUniforms.shadowMapSize = shadow.mapSize;\n\n\t\t\t\t\tstate.directionalShadow[ directionalLength ] = shadowUniforms;\n\t\t\t\t\tstate.directionalShadowMap[ directionalLength ] = shadowMap;\n\t\t\t\t\tstate.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix;\n\n\t\t\t\t\tnumDirectionalShadows ++;\n\n\t\t\t\t}\n\n\t\t\t\tstate.directional[ directionalLength ] = uniforms;\n\n\t\t\t\tdirectionalLength ++;\n\n\t\t\t} else if ( light.isSpotLight ) {\n\n\t\t\t\tconst uniforms = cache.get( light );\n\n\t\t\t\tuniforms.position.setFromMatrixPosition( light.matrixWorld );\n\n\t\t\t\tuniforms.color.copy( color ).multiplyScalar( intensity * scaleFactor );\n\t\t\t\tuniforms.distance = distance;\n\n\t\t\t\tuniforms.coneCos = Math.cos( light.angle );\n\t\t\t\tuniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) );\n\t\t\t\tuniforms.decay = light.decay;\n\n\t\t\t\tstate.spot[ spotLength ] = uniforms;\n\n\t\t\t\tconst shadow = light.shadow;\n\n\t\t\t\tif ( light.map ) {\n\n\t\t\t\t\tstate.spotLightMap[ numSpotMaps ] = light.map;\n\t\t\t\t\tnumSpotMaps ++;\n\n\t\t\t\t\t// make sure the lightMatrix is up to date\n\t\t\t\t\t// TODO : do it if required only\n\t\t\t\t\tshadow.updateMatrices( light );\n\n\t\t\t\t\tif ( light.castShadow ) numSpotShadowsWithMaps ++;\n\n\t\t\t\t}\n\n\t\t\t\tstate.spotLightMatrix[ spotLength ] = shadow.matrix;\n\n\t\t\t\tif ( light.castShadow ) {\n\n\t\t\t\t\tconst shadowUniforms = shadowCache.get( light );\n\n\t\t\t\t\tshadowUniforms.shadowBias = shadow.bias;\n\t\t\t\t\tshadowUniforms.shadowNormalBias = shadow.normalBias;\n\t\t\t\t\tshadowUniforms.shadowRadius = shadow.radius;\n\t\t\t\t\tshadowUniforms.shadowMapSize = shadow.mapSize;\n\n\t\t\t\t\tstate.spotShadow[ spotLength ] = shadowUniforms;\n\t\t\t\t\tstate.spotShadowMap[ spotLength ] = shadowMap;\n\n\t\t\t\t\tnumSpotShadows ++;\n\n\t\t\t\t}\n\n\t\t\t\tspotLength ++;\n\n\t\t\t} else if ( light.isRectAreaLight ) {\n\n\t\t\t\tconst uniforms = cache.get( light );\n\n\t\t\t\tuniforms.color.copy( color ).multiplyScalar( intensity );\n\n\t\t\t\tuniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 );\n\t\t\t\tuniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 );\n\n\t\t\t\tstate.rectArea[ rectAreaLength ] = uniforms;\n\n\t\t\t\trectAreaLength ++;\n\n\t\t\t} else if ( light.isPointLight ) {\n\n\t\t\t\tconst uniforms = cache.get( light );\n\n\t\t\t\tuniforms.color.copy( light.color ).multiplyScalar( light.intensity * scaleFactor );\n\t\t\t\tuniforms.distance = light.distance;\n\t\t\t\tuniforms.decay = light.decay;\n\n\t\t\t\tif ( light.castShadow ) {\n\n\t\t\t\t\tconst shadow = light.shadow;\n\n\t\t\t\t\tconst shadowUniforms = shadowCache.get( light );\n\n\t\t\t\t\tshadowUniforms.shadowBias = shadow.bias;\n\t\t\t\t\tshadowUniforms.shadowNormalBias = shadow.normalBias;\n\t\t\t\t\tshadowUniforms.shadowRadius = shadow.radius;\n\t\t\t\t\tshadowUniforms.shadowMapSize = shadow.mapSize;\n\t\t\t\t\tshadowUniforms.shadowCameraNear = shadow.camera.near;\n\t\t\t\t\tshadowUniforms.shadowCameraFar = shadow.camera.far;\n\n\t\t\t\t\tstate.pointShadow[ pointLength ] = shadowUniforms;\n\t\t\t\t\tstate.pointShadowMap[ pointLength ] = shadowMap;\n\t\t\t\t\tstate.pointShadowMatrix[ pointLength ] = light.shadow.matrix;\n\n\t\t\t\t\tnumPointShadows ++;\n\n\t\t\t\t}\n\n\t\t\t\tstate.point[ pointLength ] = uniforms;\n\n\t\t\t\tpointLength ++;\n\n\t\t\t} else if ( light.isHemisphereLight ) {\n\n\t\t\t\tconst uniforms = cache.get( light );\n\n\t\t\t\tuniforms.skyColor.copy( light.color ).multiplyScalar( intensity * scaleFactor );\n\t\t\t\tuniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity * scaleFactor );\n\n\t\t\t\tstate.hemi[ hemiLength ] = uniforms;\n\n\t\t\t\themiLength ++;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( rectAreaLength > 0 ) {\n\n\t\t\tif ( capabilities.isWebGL2 ) {\n\n\t\t\t\t// WebGL 2\n\n\t\t\t\tstate.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1;\n\t\t\t\tstate.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2;\n\n\t\t\t} else {\n\n\t\t\t\t// WebGL 1\n\n\t\t\t\tif ( extensions.has( 'OES_texture_float_linear' ) === true ) {\n\n\t\t\t\t\tstate.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1;\n\t\t\t\t\tstate.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2;\n\n\t\t\t\t} else if ( extensions.has( 'OES_texture_half_float_linear' ) === true ) {\n\n\t\t\t\t\tstate.rectAreaLTC1 = UniformsLib.LTC_HALF_1;\n\t\t\t\t\tstate.rectAreaLTC2 = UniformsLib.LTC_HALF_2;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tconsole.error( 'THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions.' );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tstate.ambient[ 0 ] = r;\n\t\tstate.ambient[ 1 ] = g;\n\t\tstate.ambient[ 2 ] = b;\n\n\t\tconst hash = state.hash;\n\n\t\tif ( hash.directionalLength !== directionalLength ||\n\t\t\thash.pointLength !== pointLength ||\n\t\t\thash.spotLength !== spotLength ||\n\t\t\thash.rectAreaLength !== rectAreaLength ||\n\t\t\thash.hemiLength !== hemiLength ||\n\t\t\thash.numDirectionalShadows !== numDirectionalShadows ||\n\t\t\thash.numPointShadows !== numPointShadows ||\n\t\t\thash.numSpotShadows !== numSpotShadows ||\n\t\t\thash.numSpotMaps !== numSpotMaps ||\n\t\t\thash.numLightProbes !== numLightProbes ) {\n\n\t\t\tstate.directional.length = directionalLength;\n\t\t\tstate.spot.length = spotLength;\n\t\t\tstate.rectArea.length = rectAreaLength;\n\t\t\tstate.point.length = pointLength;\n\t\t\tstate.hemi.length = hemiLength;\n\n\t\t\tstate.directionalShadow.length = numDirectionalShadows;\n\t\t\tstate.directionalShadowMap.length = numDirectionalShadows;\n\t\t\tstate.pointShadow.length = numPointShadows;\n\t\t\tstate.pointShadowMap.length = numPointShadows;\n\t\t\tstate.spotShadow.length = numSpotShadows;\n\t\t\tstate.spotShadowMap.length = numSpotShadows;\n\t\t\tstate.directionalShadowMatrix.length = numDirectionalShadows;\n\t\t\tstate.pointShadowMatrix.length = numPointShadows;\n\t\t\tstate.spotLightMatrix.length = numSpotShadows + numSpotMaps - numSpotShadowsWithMaps;\n\t\t\tstate.spotLightMap.length = numSpotMaps;\n\t\t\tstate.numSpotLightShadowsWithMaps = numSpotShadowsWithMaps;\n\t\t\tstate.numLightProbes = numLightProbes;\n\n\t\t\thash.directionalLength = directionalLength;\n\t\t\thash.pointLength = pointLength;\n\t\t\thash.spotLength = spotLength;\n\t\t\thash.rectAreaLength = rectAreaLength;\n\t\t\thash.hemiLength = hemiLength;\n\n\t\t\thash.numDirectionalShadows = numDirectionalShadows;\n\t\t\thash.numPointShadows = numPointShadows;\n\t\t\thash.numSpotShadows = numSpotShadows;\n\t\t\thash.numSpotMaps = numSpotMaps;\n\n\t\t\thash.numLightProbes = numLightProbes;\n\n\t\t\tstate.version = nextVersion ++;\n\n\t\t}\n\n\t}\n\n\tfunction setupView( lights, camera ) {\n\n\t\tlet directionalLength = 0;\n\t\tlet pointLength = 0;\n\t\tlet spotLength = 0;\n\t\tlet rectAreaLength = 0;\n\t\tlet hemiLength = 0;\n\n\t\tconst viewMatrix = camera.matrixWorldInverse;\n\n\t\tfor ( let i = 0, l = lights.length; i < l; i ++ ) {\n\n\t\t\tconst light = lights[ i ];\n\n\t\t\tif ( light.isDirectionalLight ) {\n\n\t\t\t\tconst uniforms = state.directional[ directionalLength ];\n\n\t\t\t\tuniforms.direction.setFromMatrixPosition( light.matrixWorld );\n\t\t\t\tvector3.setFromMatrixPosition( light.target.matrixWorld );\n\t\t\t\tuniforms.direction.sub( vector3 );\n\t\t\t\tuniforms.direction.transformDirection( viewMatrix );\n\n\t\t\t\tdirectionalLength ++;\n\n\t\t\t} else if ( light.isSpotLight ) {\n\n\t\t\t\tconst uniforms = state.spot[ spotLength ];\n\n\t\t\t\tuniforms.position.setFromMatrixPosition( light.matrixWorld );\n\t\t\t\tuniforms.position.applyMatrix4( viewMatrix );\n\n\t\t\t\tuniforms.direction.setFromMatrixPosition( light.matrixWorld );\n\t\t\t\tvector3.setFromMatrixPosition( light.target.matrixWorld );\n\t\t\t\tuniforms.direction.sub( vector3 );\n\t\t\t\tuniforms.direction.transformDirection( viewMatrix );\n\n\t\t\t\tspotLength ++;\n\n\t\t\t} else if ( light.isRectAreaLight ) {\n\n\t\t\t\tconst uniforms = state.rectArea[ rectAreaLength ];\n\n\t\t\t\tuniforms.position.setFromMatrixPosition( light.matrixWorld );\n\t\t\t\tuniforms.position.applyMatrix4( viewMatrix );\n\n\t\t\t\t// extract local rotation of light to derive width/height half vectors\n\t\t\t\tmatrix42.identity();\n\t\t\t\tmatrix4.copy( light.matrixWorld );\n\t\t\t\tmatrix4.premultiply( viewMatrix );\n\t\t\t\tmatrix42.extractRotation( matrix4 );\n\n\t\t\t\tuniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 );\n\t\t\t\tuniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 );\n\n\t\t\t\tuniforms.halfWidth.applyMatrix4( matrix42 );\n\t\t\t\tuniforms.halfHeight.applyMatrix4( matrix42 );\n\n\t\t\t\trectAreaLength ++;\n\n\t\t\t} else if ( light.isPointLight ) {\n\n\t\t\t\tconst uniforms = state.point[ pointLength ];\n\n\t\t\t\tuniforms.position.setFromMatrixPosition( light.matrixWorld );\n\t\t\t\tuniforms.position.applyMatrix4( viewMatrix );\n\n\t\t\t\tpointLength ++;\n\n\t\t\t} else if ( light.isHemisphereLight ) {\n\n\t\t\t\tconst uniforms = state.hemi[ hemiLength ];\n\n\t\t\t\tuniforms.direction.setFromMatrixPosition( light.matrixWorld );\n\t\t\t\tuniforms.direction.transformDirection( viewMatrix );\n\n\t\t\t\themiLength ++;\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\treturn {\n\t\tsetup: setup,\n\t\tsetupView: setupView,\n\t\tstate: state\n\t};\n\n}\n\n\nexport { WebGLLights };\n","import { WebGLLights } from './WebGLLights.js';\n\nfunction WebGLRenderState( extensions, capabilities ) {\n\n\tconst lights = new WebGLLights( extensions, capabilities );\n\n\tconst lightsArray = [];\n\tconst shadowsArray = [];\n\n\tfunction init() {\n\n\t\tlightsArray.length = 0;\n\t\tshadowsArray.length = 0;\n\n\t}\n\n\tfunction pushLight( light ) {\n\n\t\tlightsArray.push( light );\n\n\t}\n\n\tfunction pushShadow( shadowLight ) {\n\n\t\tshadowsArray.push( shadowLight );\n\n\t}\n\n\tfunction setupLights( useLegacyLights ) {\n\n\t\tlights.setup( lightsArray, useLegacyLights );\n\n\t}\n\n\tfunction setupLightsView( camera ) {\n\n\t\tlights.setupView( lightsArray, camera );\n\n\t}\n\n\tconst state = {\n\t\tlightsArray: lightsArray,\n\t\tshadowsArray: shadowsArray,\n\n\t\tlights: lights\n\t};\n\n\treturn {\n\t\tinit: init,\n\t\tstate: state,\n\t\tsetupLights: setupLights,\n\t\tsetupLightsView: setupLightsView,\n\n\t\tpushLight: pushLight,\n\t\tpushShadow: pushShadow\n\t};\n\n}\n\nfunction WebGLRenderStates( extensions, capabilities ) {\n\n\tlet renderStates = new WeakMap();\n\n\tfunction get( scene, renderCallDepth = 0 ) {\n\n\t\tconst renderStateArray = renderStates.get( scene );\n\t\tlet renderState;\n\n\t\tif ( renderStateArray === undefined ) {\n\n\t\t\trenderState = new WebGLRenderState( extensions, capabilities );\n\t\t\trenderStates.set( scene, [ renderState ] );\n\n\t\t} else {\n\n\t\t\tif ( renderCallDepth >= renderStateArray.length ) {\n\n\t\t\t\trenderState = new WebGLRenderState( extensions, capabilities );\n\t\t\t\trenderStateArray.push( renderState );\n\n\t\t\t} else {\n\n\t\t\t\trenderState = renderStateArray[ renderCallDepth ];\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn renderState;\n\n\t}\n\n\tfunction dispose() {\n\n\t\trenderStates = new WeakMap();\n\n\t}\n\n\treturn {\n\t\tget: get,\n\t\tdispose: dispose\n\t};\n\n}\n\n\nexport { WebGLRenderStates };\n","import { Material } from './Material.js';\nimport { BasicDepthPacking } from '../constants.js';\n\nclass MeshDepthMaterial extends Material {\n\n\tconstructor( parameters ) {\n\n\t\tsuper();\n\n\t\tthis.isMeshDepthMaterial = true;\n\n\t\tthis.type = 'MeshDepthMaterial';\n\n\t\tthis.depthPacking = BasicDepthPacking;\n\n\t\tthis.map = null;\n\n\t\tthis.alphaMap = null;\n\n\t\tthis.displacementMap = null;\n\t\tthis.displacementScale = 1;\n\t\tthis.displacementBias = 0;\n\n\t\tthis.wireframe = false;\n\t\tthis.wireframeLinewidth = 1;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.depthPacking = source.depthPacking;\n\n\t\tthis.map = source.map;\n\n\t\tthis.alphaMap = source.alphaMap;\n\n\t\tthis.displacementMap = source.displacementMap;\n\t\tthis.displacementScale = source.displacementScale;\n\t\tthis.displacementBias = source.displacementBias;\n\n\t\tthis.wireframe = source.wireframe;\n\t\tthis.wireframeLinewidth = source.wireframeLinewidth;\n\n\t\treturn this;\n\n\t}\n\n}\n\nexport { MeshDepthMaterial };\n","import { Material } from './Material.js';\n\nclass MeshDistanceMaterial extends Material {\n\n\tconstructor( parameters ) {\n\n\t\tsuper();\n\n\t\tthis.isMeshDistanceMaterial = true;\n\n\t\tthis.type = 'MeshDistanceMaterial';\n\n\t\tthis.map = null;\n\n\t\tthis.alphaMap = null;\n\n\t\tthis.displacementMap = null;\n\t\tthis.displacementScale = 1;\n\t\tthis.displacementBias = 0;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.map = source.map;\n\n\t\tthis.alphaMap = source.alphaMap;\n\n\t\tthis.displacementMap = source.displacementMap;\n\t\tthis.displacementScale = source.displacementScale;\n\t\tthis.displacementBias = source.displacementBias;\n\n\t\treturn this;\n\n\t}\n\n}\n\nexport { MeshDistanceMaterial };\n","import { FrontSide, BackSide, DoubleSide, NearestFilter, PCFShadowMap, VSMShadowMap, RGBADepthPacking, NoBlending } from '../../constants.js';\nimport { WebGLRenderTarget } from '../WebGLRenderTarget.js';\nimport { MeshDepthMaterial } from '../../materials/MeshDepthMaterial.js';\nimport { MeshDistanceMaterial } from '../../materials/MeshDistanceMaterial.js';\nimport { ShaderMaterial } from '../../materials/ShaderMaterial.js';\nimport { BufferAttribute } from '../../core/BufferAttribute.js';\nimport { BufferGeometry } from '../../core/BufferGeometry.js';\nimport { Mesh } from '../../objects/Mesh.js';\nimport { Vector4 } from '../../math/Vector4.js';\nimport { Vector2 } from '../../math/Vector2.js';\nimport { Frustum } from '../../math/Frustum.js';\n\nimport * as vsm from '../shaders/ShaderLib/vsm.glsl.js';\n\nfunction WebGLShadowMap( _renderer, _objects, _capabilities ) {\n\n\tlet _frustum = new Frustum();\n\n\tconst _shadowMapSize = new Vector2(),\n\t\t_viewportSize = new Vector2(),\n\n\t\t_viewport = new Vector4(),\n\n\t\t_depthMaterial = new MeshDepthMaterial( { depthPacking: RGBADepthPacking } ),\n\t\t_distanceMaterial = new MeshDistanceMaterial(),\n\n\t\t_materialCache = {},\n\n\t\t_maxTextureSize = _capabilities.maxTextureSize;\n\n\tconst shadowSide = { [ FrontSide ]: BackSide, [ BackSide ]: FrontSide, [ DoubleSide ]: DoubleSide };\n\n\tconst shadowMaterialVertical = new ShaderMaterial( {\n\t\tdefines: {\n\t\t\tVSM_SAMPLES: 8\n\t\t},\n\t\tuniforms: {\n\t\t\tshadow_pass: { value: null },\n\t\t\tresolution: { value: new Vector2() },\n\t\t\tradius: { value: 4.0 }\n\t\t},\n\n\t\tvertexShader: vsm.vertex,\n\t\tfragmentShader: vsm.fragment\n\n\t} );\n\n\tconst shadowMaterialHorizontal = shadowMaterialVertical.clone();\n\tshadowMaterialHorizontal.defines.HORIZONTAL_PASS = 1;\n\n\tconst fullScreenTri = new BufferGeometry();\n\tfullScreenTri.setAttribute(\n\t\t'position',\n\t\tnew BufferAttribute(\n\t\t\tnew Float32Array( [ - 1, - 1, 0.5, 3, - 1, 0.5, - 1, 3, 0.5 ] ),\n\t\t\t3\n\t\t)\n\t);\n\n\tconst fullScreenMesh = new Mesh( fullScreenTri, shadowMaterialVertical );\n\n\tconst scope = this;\n\n\tthis.enabled = false;\n\n\tthis.autoUpdate = true;\n\tthis.needsUpdate = false;\n\n\tthis.type = PCFShadowMap;\n\tlet _previousType = this.type;\n\n\tthis.render = function ( lights, scene, camera ) {\n\n\t\tif ( scope.enabled === false ) return;\n\t\tif ( scope.autoUpdate === false && scope.needsUpdate === false ) return;\n\n\t\tif ( lights.length === 0 ) return;\n\n\t\tconst currentRenderTarget = _renderer.getRenderTarget();\n\t\tconst activeCubeFace = _renderer.getActiveCubeFace();\n\t\tconst activeMipmapLevel = _renderer.getActiveMipmapLevel();\n\n\t\tconst _state = _renderer.state;\n\n\t\t// Set GL state for depth map.\n\t\t_state.setBlending( NoBlending );\n\t\t_state.buffers.color.setClear( 1, 1, 1, 1 );\n\t\t_state.buffers.depth.setTest( true );\n\t\t_state.setScissorTest( false );\n\n\t\t// check for shadow map type changes\n\n\t\tconst toVSM = ( _previousType !== VSMShadowMap && this.type === VSMShadowMap );\n\t\tconst fromVSM = ( _previousType === VSMShadowMap && this.type !== VSMShadowMap );\n\n\t\t// render depth map\n\n\t\tfor ( let i = 0, il = lights.length; i < il; i ++ ) {\n\n\t\t\tconst light = lights[ i ];\n\t\t\tconst shadow = light.shadow;\n\n\t\t\tif ( shadow === undefined ) {\n\n\t\t\t\tconsole.warn( 'THREE.WebGLShadowMap:', light, 'has no shadow.' );\n\t\t\t\tcontinue;\n\n\t\t\t}\n\n\t\t\tif ( shadow.autoUpdate === false && shadow.needsUpdate === false ) continue;\n\n\t\t\t_shadowMapSize.copy( shadow.mapSize );\n\n\t\t\tconst shadowFrameExtents = shadow.getFrameExtents();\n\n\t\t\t_shadowMapSize.multiply( shadowFrameExtents );\n\n\t\t\t_viewportSize.copy( shadow.mapSize );\n\n\t\t\tif ( _shadowMapSize.x > _maxTextureSize || _shadowMapSize.y > _maxTextureSize ) {\n\n\t\t\t\tif ( _shadowMapSize.x > _maxTextureSize ) {\n\n\t\t\t\t\t_viewportSize.x = Math.floor( _maxTextureSize / shadowFrameExtents.x );\n\t\t\t\t\t_shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x;\n\t\t\t\t\tshadow.mapSize.x = _viewportSize.x;\n\n\t\t\t\t}\n\n\t\t\t\tif ( _shadowMapSize.y > _maxTextureSize ) {\n\n\t\t\t\t\t_viewportSize.y = Math.floor( _maxTextureSize / shadowFrameExtents.y );\n\t\t\t\t\t_shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y;\n\t\t\t\t\tshadow.mapSize.y = _viewportSize.y;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( shadow.map === null || toVSM === true || fromVSM === true ) {\n\n\t\t\t\tconst pars = ( this.type !== VSMShadowMap ) ? { minFilter: NearestFilter, magFilter: NearestFilter } : {};\n\n\t\t\t\tif ( shadow.map !== null ) {\n\n\t\t\t\t\tshadow.map.dispose();\n\n\t\t\t\t}\n\n\t\t\t\tshadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars );\n\t\t\t\tshadow.map.texture.name = light.name + '.shadowMap';\n\n\t\t\t\tshadow.camera.updateProjectionMatrix();\n\n\t\t\t}\n\n\t\t\t_renderer.setRenderTarget( shadow.map );\n\t\t\t_renderer.clear();\n\n\t\t\tconst viewportCount = shadow.getViewportCount();\n\n\t\t\tfor ( let vp = 0; vp < viewportCount; vp ++ ) {\n\n\t\t\t\tconst viewport = shadow.getViewport( vp );\n\n\t\t\t\t_viewport.set(\n\t\t\t\t\t_viewportSize.x * viewport.x,\n\t\t\t\t\t_viewportSize.y * viewport.y,\n\t\t\t\t\t_viewportSize.x * viewport.z,\n\t\t\t\t\t_viewportSize.y * viewport.w\n\t\t\t\t);\n\n\t\t\t\t_state.viewport( _viewport );\n\n\t\t\t\tshadow.updateMatrices( light, vp );\n\n\t\t\t\t_frustum = shadow.getFrustum();\n\n\t\t\t\trenderObject( scene, camera, shadow.camera, light, this.type );\n\n\t\t\t}\n\n\t\t\t// do blur pass for VSM\n\n\t\t\tif ( shadow.isPointLightShadow !== true && this.type === VSMShadowMap ) {\n\n\t\t\t\tVSMPass( shadow, camera );\n\n\t\t\t}\n\n\t\t\tshadow.needsUpdate = false;\n\n\t\t}\n\n\t\t_previousType = this.type;\n\n\t\tscope.needsUpdate = false;\n\n\t\t_renderer.setRenderTarget( currentRenderTarget, activeCubeFace, activeMipmapLevel );\n\n\t};\n\n\tfunction VSMPass( shadow, camera ) {\n\n\t\tconst geometry = _objects.update( fullScreenMesh );\n\n\t\tif ( shadowMaterialVertical.defines.VSM_SAMPLES !== shadow.blurSamples ) {\n\n\t\t\tshadowMaterialVertical.defines.VSM_SAMPLES = shadow.blurSamples;\n\t\t\tshadowMaterialHorizontal.defines.VSM_SAMPLES = shadow.blurSamples;\n\n\t\t\tshadowMaterialVertical.needsUpdate = true;\n\t\t\tshadowMaterialHorizontal.needsUpdate = true;\n\n\t\t}\n\n\t\tif ( shadow.mapPass === null ) {\n\n\t\t\tshadow.mapPass = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y );\n\n\t\t}\n\n\t\t// vertical pass\n\n\t\tshadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture;\n\t\tshadowMaterialVertical.uniforms.resolution.value = shadow.mapSize;\n\t\tshadowMaterialVertical.uniforms.radius.value = shadow.radius;\n\t\t_renderer.setRenderTarget( shadow.mapPass );\n\t\t_renderer.clear();\n\t\t_renderer.renderBufferDirect( camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null );\n\n\t\t// horizontal pass\n\n\t\tshadowMaterialHorizontal.uniforms.shadow_pass.value = shadow.mapPass.texture;\n\t\tshadowMaterialHorizontal.uniforms.resolution.value = shadow.mapSize;\n\t\tshadowMaterialHorizontal.uniforms.radius.value = shadow.radius;\n\t\t_renderer.setRenderTarget( shadow.map );\n\t\t_renderer.clear();\n\t\t_renderer.renderBufferDirect( camera, null, geometry, shadowMaterialHorizontal, fullScreenMesh, null );\n\n\t}\n\n\tfunction getDepthMaterial( object, material, light, type ) {\n\n\t\tlet result = null;\n\n\t\tconst customMaterial = ( light.isPointLight === true ) ? object.customDistanceMaterial : object.customDepthMaterial;\n\n\t\tif ( customMaterial !== undefined ) {\n\n\t\t\tresult = customMaterial;\n\n\t\t} else {\n\n\t\t\tresult = ( light.isPointLight === true ) ? _distanceMaterial : _depthMaterial;\n\n\t\t\tif ( ( _renderer.localClippingEnabled && material.clipShadows === true && Array.isArray( material.clippingPlanes ) && material.clippingPlanes.length !== 0 ) ||\n\t\t\t\t( material.displacementMap && material.displacementScale !== 0 ) ||\n\t\t\t\t( material.alphaMap && material.alphaTest > 0 ) ||\n\t\t\t\t( material.map && material.alphaTest > 0 ) ) {\n\n\t\t\t\t// in this case we need a unique material instance reflecting the\n\t\t\t\t// appropriate state\n\n\t\t\t\tconst keyA = result.uuid, keyB = material.uuid;\n\n\t\t\t\tlet materialsForVariant = _materialCache[ keyA ];\n\n\t\t\t\tif ( materialsForVariant === undefined ) {\n\n\t\t\t\t\tmaterialsForVariant = {};\n\t\t\t\t\t_materialCache[ keyA ] = materialsForVariant;\n\n\t\t\t\t}\n\n\t\t\t\tlet cachedMaterial = materialsForVariant[ keyB ];\n\n\t\t\t\tif ( cachedMaterial === undefined ) {\n\n\t\t\t\t\tcachedMaterial = result.clone();\n\t\t\t\t\tmaterialsForVariant[ keyB ] = cachedMaterial;\n\n\t\t\t\t}\n\n\t\t\t\tresult = cachedMaterial;\n\n\t\t\t}\n\n\t\t}\n\n\t\tresult.visible = material.visible;\n\t\tresult.wireframe = material.wireframe;\n\n\t\tif ( type === VSMShadowMap ) {\n\n\t\t\tresult.side = ( material.shadowSide !== null ) ? material.shadowSide : material.side;\n\n\t\t} else {\n\n\t\t\tresult.side = ( material.shadowSide !== null ) ? material.shadowSide : shadowSide[ material.side ];\n\n\t\t}\n\n\t\tresult.alphaMap = material.alphaMap;\n\t\tresult.alphaTest = material.alphaTest;\n\t\tresult.map = material.map;\n\n\t\tresult.clipShadows = material.clipShadows;\n\t\tresult.clippingPlanes = material.clippingPlanes;\n\t\tresult.clipIntersection = material.clipIntersection;\n\n\t\tresult.displacementMap = material.displacementMap;\n\t\tresult.displacementScale = material.displacementScale;\n\t\tresult.displacementBias = material.displacementBias;\n\n\t\tresult.wireframeLinewidth = material.wireframeLinewidth;\n\t\tresult.linewidth = material.linewidth;\n\n\t\tif ( light.isPointLight === true && result.isMeshDistanceMaterial === true ) {\n\n\t\t\tconst materialProperties = _renderer.properties.get( result );\n\t\t\tmaterialProperties.light = light;\n\n\t\t}\n\n\t\treturn result;\n\n\t}\n\n\tfunction renderObject( object, camera, shadowCamera, light, type ) {\n\n\t\tif ( object.visible === false ) return;\n\n\t\tconst visible = object.layers.test( camera.layers );\n\n\t\tif ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) {\n\n\t\t\tif ( ( object.castShadow || ( object.receiveShadow && type === VSMShadowMap ) ) && ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) ) {\n\n\t\t\t\tobject.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld );\n\n\t\t\t\tconst geometry = _objects.update( object );\n\t\t\t\tconst material = object.material;\n\n\t\t\t\tif ( Array.isArray( material ) ) {\n\n\t\t\t\t\tconst groups = geometry.groups;\n\n\t\t\t\t\tfor ( let k = 0, kl = groups.length; k < kl; k ++ ) {\n\n\t\t\t\t\t\tconst group = groups[ k ];\n\t\t\t\t\t\tconst groupMaterial = material[ group.materialIndex ];\n\n\t\t\t\t\t\tif ( groupMaterial && groupMaterial.visible ) {\n\n\t\t\t\t\t\t\tconst depthMaterial = getDepthMaterial( object, groupMaterial, light, type );\n\n\t\t\t\t\t\t\t_renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t} else if ( material.visible ) {\n\n\t\t\t\t\tconst depthMaterial = getDepthMaterial( object, material, light, type );\n\n\t\t\t\t\t_renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tconst children = object.children;\n\n\t\tfor ( let i = 0, l = children.length; i < l; i ++ ) {\n\n\t\t\trenderObject( children[ i ], camera, shadowCamera, light, type );\n\n\t\t}\n\n\t}\n\n}\n\n\nexport { WebGLShadowMap };\n","export const vertex = /* glsl */`\nvoid main() {\n\n\tgl_Position = vec4( position, 1.0 );\n\n}\n`;\n\nexport const fragment = /* glsl */`\nuniform sampler2D shadow_pass;\nuniform vec2 resolution;\nuniform float radius;\n\n#include \n\nvoid main() {\n\n\tconst float samples = float( VSM_SAMPLES );\n\n\tfloat mean = 0.0;\n\tfloat squared_mean = 0.0;\n\n\tfloat uvStride = samples <= 1.0 ? 0.0 : 2.0 / ( samples - 1.0 );\n\tfloat uvStart = samples <= 1.0 ? 0.0 : - 1.0;\n\tfor ( float i = 0.0; i < samples; i ++ ) {\n\n\t\tfloat uvOffset = uvStart + i * uvStride;\n\n\t\t#ifdef HORIZONTAL_PASS\n\n\t\t\tvec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ) );\n\t\t\tmean += distribution.x;\n\t\t\tsquared_mean += distribution.y * distribution.y + distribution.x * distribution.x;\n\n\t\t#else\n\n\t\t\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ) );\n\t\t\tmean += depth;\n\t\t\tsquared_mean += depth * depth;\n\n\t\t#endif\n\n\t}\n\n\tmean = mean / samples;\n\tsquared_mean = squared_mean / samples;\n\n\tfloat std_dev = sqrt( squared_mean - mean * mean );\n\n\tgl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) );\n\n}\n`;\n","import { NotEqualDepth, GreaterDepth, GreaterEqualDepth, EqualDepth, LessEqualDepth, LessDepth, AlwaysDepth, NeverDepth, CullFaceFront, CullFaceBack, CullFaceNone, DoubleSide, BackSide, CustomBlending, MultiplyBlending, SubtractiveBlending, AdditiveBlending, NoBlending, NormalBlending, AddEquation, SubtractEquation, ReverseSubtractEquation, MinEquation, MaxEquation, ZeroFactor, OneFactor, SrcColorFactor, SrcAlphaFactor, SrcAlphaSaturateFactor, DstColorFactor, DstAlphaFactor, OneMinusSrcColorFactor, OneMinusSrcAlphaFactor, OneMinusDstColorFactor, OneMinusDstAlphaFactor, ConstantColorFactor, OneMinusConstantColorFactor, ConstantAlphaFactor, OneMinusConstantAlphaFactor } from '../../constants.js';\nimport { Color } from '../../math/Color.js';\nimport { Vector4 } from '../../math/Vector4.js';\n\nfunction WebGLState( gl, extensions, capabilities ) {\n\n\tconst isWebGL2 = capabilities.isWebGL2;\n\n\tfunction ColorBuffer() {\n\n\t\tlet locked = false;\n\n\t\tconst color = new Vector4();\n\t\tlet currentColorMask = null;\n\t\tconst currentColorClear = new Vector4( 0, 0, 0, 0 );\n\n\t\treturn {\n\n\t\t\tsetMask: function ( colorMask ) {\n\n\t\t\t\tif ( currentColorMask !== colorMask && ! locked ) {\n\n\t\t\t\t\tgl.colorMask( colorMask, colorMask, colorMask, colorMask );\n\t\t\t\t\tcurrentColorMask = colorMask;\n\n\t\t\t\t}\n\n\t\t\t},\n\n\t\t\tsetLocked: function ( lock ) {\n\n\t\t\t\tlocked = lock;\n\n\t\t\t},\n\n\t\t\tsetClear: function ( r, g, b, a, premultipliedAlpha ) {\n\n\t\t\t\tif ( premultipliedAlpha === true ) {\n\n\t\t\t\t\tr *= a; g *= a; b *= a;\n\n\t\t\t\t}\n\n\t\t\t\tcolor.set( r, g, b, a );\n\n\t\t\t\tif ( currentColorClear.equals( color ) === false ) {\n\n\t\t\t\t\tgl.clearColor( r, g, b, a );\n\t\t\t\t\tcurrentColorClear.copy( color );\n\n\t\t\t\t}\n\n\t\t\t},\n\n\t\t\treset: function () {\n\n\t\t\t\tlocked = false;\n\n\t\t\t\tcurrentColorMask = null;\n\t\t\t\tcurrentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state\n\n\t\t\t}\n\n\t\t};\n\n\t}\n\n\tfunction DepthBuffer() {\n\n\t\tlet locked = false;\n\n\t\tlet currentDepthMask = null;\n\t\tlet currentDepthFunc = null;\n\t\tlet currentDepthClear = null;\n\n\t\treturn {\n\n\t\t\tsetTest: function ( depthTest ) {\n\n\t\t\t\tif ( depthTest ) {\n\n\t\t\t\t\tenable( gl.DEPTH_TEST );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tdisable( gl.DEPTH_TEST );\n\n\t\t\t\t}\n\n\t\t\t},\n\n\t\t\tsetMask: function ( depthMask ) {\n\n\t\t\t\tif ( currentDepthMask !== depthMask && ! locked ) {\n\n\t\t\t\t\tgl.depthMask( depthMask );\n\t\t\t\t\tcurrentDepthMask = depthMask;\n\n\t\t\t\t}\n\n\t\t\t},\n\n\t\t\tsetFunc: function ( depthFunc ) {\n\n\t\t\t\tif ( currentDepthFunc !== depthFunc ) {\n\n\t\t\t\t\tswitch ( depthFunc ) {\n\n\t\t\t\t\t\tcase NeverDepth:\n\n\t\t\t\t\t\t\tgl.depthFunc( gl.NEVER );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase AlwaysDepth:\n\n\t\t\t\t\t\t\tgl.depthFunc( gl.ALWAYS );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase LessDepth:\n\n\t\t\t\t\t\t\tgl.depthFunc( gl.LESS );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase LessEqualDepth:\n\n\t\t\t\t\t\t\tgl.depthFunc( gl.LEQUAL );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase EqualDepth:\n\n\t\t\t\t\t\t\tgl.depthFunc( gl.EQUAL );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase GreaterEqualDepth:\n\n\t\t\t\t\t\t\tgl.depthFunc( gl.GEQUAL );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase GreaterDepth:\n\n\t\t\t\t\t\t\tgl.depthFunc( gl.GREATER );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase NotEqualDepth:\n\n\t\t\t\t\t\t\tgl.depthFunc( gl.NOTEQUAL );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tdefault:\n\n\t\t\t\t\t\t\tgl.depthFunc( gl.LEQUAL );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tcurrentDepthFunc = depthFunc;\n\n\t\t\t\t}\n\n\t\t\t},\n\n\t\t\tsetLocked: function ( lock ) {\n\n\t\t\t\tlocked = lock;\n\n\t\t\t},\n\n\t\t\tsetClear: function ( depth ) {\n\n\t\t\t\tif ( currentDepthClear !== depth ) {\n\n\t\t\t\t\tgl.clearDepth( depth );\n\t\t\t\t\tcurrentDepthClear = depth;\n\n\t\t\t\t}\n\n\t\t\t},\n\n\t\t\treset: function () {\n\n\t\t\t\tlocked = false;\n\n\t\t\t\tcurrentDepthMask = null;\n\t\t\t\tcurrentDepthFunc = null;\n\t\t\t\tcurrentDepthClear = null;\n\n\t\t\t}\n\n\t\t};\n\n\t}\n\n\tfunction StencilBuffer() {\n\n\t\tlet locked = false;\n\n\t\tlet currentStencilMask = null;\n\t\tlet currentStencilFunc = null;\n\t\tlet currentStencilRef = null;\n\t\tlet currentStencilFuncMask = null;\n\t\tlet currentStencilFail = null;\n\t\tlet currentStencilZFail = null;\n\t\tlet currentStencilZPass = null;\n\t\tlet currentStencilClear = null;\n\n\t\treturn {\n\n\t\t\tsetTest: function ( stencilTest ) {\n\n\t\t\t\tif ( ! locked ) {\n\n\t\t\t\t\tif ( stencilTest ) {\n\n\t\t\t\t\t\tenable( gl.STENCIL_TEST );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tdisable( gl.STENCIL_TEST );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t},\n\n\t\t\tsetMask: function ( stencilMask ) {\n\n\t\t\t\tif ( currentStencilMask !== stencilMask && ! locked ) {\n\n\t\t\t\t\tgl.stencilMask( stencilMask );\n\t\t\t\t\tcurrentStencilMask = stencilMask;\n\n\t\t\t\t}\n\n\t\t\t},\n\n\t\t\tsetFunc: function ( stencilFunc, stencilRef, stencilMask ) {\n\n\t\t\t\tif ( currentStencilFunc !== stencilFunc ||\n\t\t\t\t currentStencilRef !== stencilRef ||\n\t\t\t\t currentStencilFuncMask !== stencilMask ) {\n\n\t\t\t\t\tgl.stencilFunc( stencilFunc, stencilRef, stencilMask );\n\n\t\t\t\t\tcurrentStencilFunc = stencilFunc;\n\t\t\t\t\tcurrentStencilRef = stencilRef;\n\t\t\t\t\tcurrentStencilFuncMask = stencilMask;\n\n\t\t\t\t}\n\n\t\t\t},\n\n\t\t\tsetOp: function ( stencilFail, stencilZFail, stencilZPass ) {\n\n\t\t\t\tif ( currentStencilFail !== stencilFail ||\n\t\t\t\t currentStencilZFail !== stencilZFail ||\n\t\t\t\t currentStencilZPass !== stencilZPass ) {\n\n\t\t\t\t\tgl.stencilOp( stencilFail, stencilZFail, stencilZPass );\n\n\t\t\t\t\tcurrentStencilFail = stencilFail;\n\t\t\t\t\tcurrentStencilZFail = stencilZFail;\n\t\t\t\t\tcurrentStencilZPass = stencilZPass;\n\n\t\t\t\t}\n\n\t\t\t},\n\n\t\t\tsetLocked: function ( lock ) {\n\n\t\t\t\tlocked = lock;\n\n\t\t\t},\n\n\t\t\tsetClear: function ( stencil ) {\n\n\t\t\t\tif ( currentStencilClear !== stencil ) {\n\n\t\t\t\t\tgl.clearStencil( stencil );\n\t\t\t\t\tcurrentStencilClear = stencil;\n\n\t\t\t\t}\n\n\t\t\t},\n\n\t\t\treset: function () {\n\n\t\t\t\tlocked = false;\n\n\t\t\t\tcurrentStencilMask = null;\n\t\t\t\tcurrentStencilFunc = null;\n\t\t\t\tcurrentStencilRef = null;\n\t\t\t\tcurrentStencilFuncMask = null;\n\t\t\t\tcurrentStencilFail = null;\n\t\t\t\tcurrentStencilZFail = null;\n\t\t\t\tcurrentStencilZPass = null;\n\t\t\t\tcurrentStencilClear = null;\n\n\t\t\t}\n\n\t\t};\n\n\t}\n\n\t//\n\n\tconst colorBuffer = new ColorBuffer();\n\tconst depthBuffer = new DepthBuffer();\n\tconst stencilBuffer = new StencilBuffer();\n\n\tconst uboBindings = new WeakMap();\n\tconst uboProgramMap = new WeakMap();\n\n\tlet enabledCapabilities = {};\n\n\tlet currentBoundFramebuffers = {};\n\tlet currentDrawbuffers = new WeakMap();\n\tlet defaultDrawbuffers = [];\n\n\tlet currentProgram = null;\n\n\tlet currentBlendingEnabled = false;\n\tlet currentBlending = null;\n\tlet currentBlendEquation = null;\n\tlet currentBlendSrc = null;\n\tlet currentBlendDst = null;\n\tlet currentBlendEquationAlpha = null;\n\tlet currentBlendSrcAlpha = null;\n\tlet currentBlendDstAlpha = null;\n\tlet currentBlendColor = new Color( 0, 0, 0 );\n\tlet currentBlendAlpha = 0;\n\tlet currentPremultipledAlpha = false;\n\n\tlet currentFlipSided = null;\n\tlet currentCullFace = null;\n\n\tlet currentLineWidth = null;\n\n\tlet currentPolygonOffsetFactor = null;\n\tlet currentPolygonOffsetUnits = null;\n\n\tconst maxTextures = gl.getParameter( gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS );\n\n\tlet lineWidthAvailable = false;\n\tlet version = 0;\n\tconst glVersion = gl.getParameter( gl.VERSION );\n\n\tif ( glVersion.indexOf( 'WebGL' ) !== - 1 ) {\n\n\t\tversion = parseFloat( /^WebGL (\\d)/.exec( glVersion )[ 1 ] );\n\t\tlineWidthAvailable = ( version >= 1.0 );\n\n\t} else if ( glVersion.indexOf( 'OpenGL ES' ) !== - 1 ) {\n\n\t\tversion = parseFloat( /^OpenGL ES (\\d)/.exec( glVersion )[ 1 ] );\n\t\tlineWidthAvailable = ( version >= 2.0 );\n\n\t}\n\n\tlet currentTextureSlot = null;\n\tlet currentBoundTextures = {};\n\n\tconst scissorParam = gl.getParameter( gl.SCISSOR_BOX );\n\tconst viewportParam = gl.getParameter( gl.VIEWPORT );\n\n\tconst currentScissor = new Vector4().fromArray( scissorParam );\n\tconst currentViewport = new Vector4().fromArray( viewportParam );\n\n\tfunction createTexture( type, target, count, dimensions ) {\n\n\t\tconst data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4.\n\t\tconst texture = gl.createTexture();\n\n\t\tgl.bindTexture( type, texture );\n\t\tgl.texParameteri( type, gl.TEXTURE_MIN_FILTER, gl.NEAREST );\n\t\tgl.texParameteri( type, gl.TEXTURE_MAG_FILTER, gl.NEAREST );\n\n\t\tfor ( let i = 0; i < count; i ++ ) {\n\n\t\t\tif ( isWebGL2 && ( type === gl.TEXTURE_3D || type === gl.TEXTURE_2D_ARRAY ) ) {\n\n\t\t\t\tgl.texImage3D( target, 0, gl.RGBA, 1, 1, dimensions, 0, gl.RGBA, gl.UNSIGNED_BYTE, data );\n\n\t\t\t} else {\n\n\t\t\t\tgl.texImage2D( target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data );\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn texture;\n\n\t}\n\n\tconst emptyTextures = {};\n\temptyTextures[ gl.TEXTURE_2D ] = createTexture( gl.TEXTURE_2D, gl.TEXTURE_2D, 1 );\n\temptyTextures[ gl.TEXTURE_CUBE_MAP ] = createTexture( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6 );\n\n\tif ( isWebGL2 ) {\n\n\t\temptyTextures[ gl.TEXTURE_2D_ARRAY ] = createTexture( gl.TEXTURE_2D_ARRAY, gl.TEXTURE_2D_ARRAY, 1, 1 );\n\t\temptyTextures[ gl.TEXTURE_3D ] = createTexture( gl.TEXTURE_3D, gl.TEXTURE_3D, 1, 1 );\n\n\t}\n\n\t// init\n\n\tcolorBuffer.setClear( 0, 0, 0, 1 );\n\tdepthBuffer.setClear( 1 );\n\tstencilBuffer.setClear( 0 );\n\n\tenable( gl.DEPTH_TEST );\n\tdepthBuffer.setFunc( LessEqualDepth );\n\n\tsetFlipSided( false );\n\tsetCullFace( CullFaceBack );\n\tenable( gl.CULL_FACE );\n\n\tsetBlending( NoBlending );\n\n\t//\n\n\tfunction enable( id ) {\n\n\t\tif ( enabledCapabilities[ id ] !== true ) {\n\n\t\t\tgl.enable( id );\n\t\t\tenabledCapabilities[ id ] = true;\n\n\t\t}\n\n\t}\n\n\tfunction disable( id ) {\n\n\t\tif ( enabledCapabilities[ id ] !== false ) {\n\n\t\t\tgl.disable( id );\n\t\t\tenabledCapabilities[ id ] = false;\n\n\t\t}\n\n\t}\n\n\tfunction bindFramebuffer( target, framebuffer ) {\n\n\t\tif ( currentBoundFramebuffers[ target ] !== framebuffer ) {\n\n\t\t\tgl.bindFramebuffer( target, framebuffer );\n\n\t\t\tcurrentBoundFramebuffers[ target ] = framebuffer;\n\n\t\t\tif ( isWebGL2 ) {\n\n\t\t\t\t// gl.DRAW_FRAMEBUFFER is equivalent to gl.FRAMEBUFFER\n\n\t\t\t\tif ( target === gl.DRAW_FRAMEBUFFER ) {\n\n\t\t\t\t\tcurrentBoundFramebuffers[ gl.FRAMEBUFFER ] = framebuffer;\n\n\t\t\t\t}\n\n\t\t\t\tif ( target === gl.FRAMEBUFFER ) {\n\n\t\t\t\t\tcurrentBoundFramebuffers[ gl.DRAW_FRAMEBUFFER ] = framebuffer;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn true;\n\n\t\t}\n\n\t\treturn false;\n\n\t}\n\n\tfunction drawBuffers( renderTarget, framebuffer ) {\n\n\t\tlet drawBuffers = defaultDrawbuffers;\n\n\t\tlet needsUpdate = false;\n\n\t\tif ( renderTarget ) {\n\n\t\t\tdrawBuffers = currentDrawbuffers.get( framebuffer );\n\n\t\t\tif ( drawBuffers === undefined ) {\n\n\t\t\t\tdrawBuffers = [];\n\t\t\t\tcurrentDrawbuffers.set( framebuffer, drawBuffers );\n\n\t\t\t}\n\n\t\t\tif ( renderTarget.isWebGLMultipleRenderTargets ) {\n\n\t\t\t\tconst textures = renderTarget.texture;\n\n\t\t\t\tif ( drawBuffers.length !== textures.length || drawBuffers[ 0 ] !== gl.COLOR_ATTACHMENT0 ) {\n\n\t\t\t\t\tfor ( let i = 0, il = textures.length; i < il; i ++ ) {\n\n\t\t\t\t\t\tdrawBuffers[ i ] = gl.COLOR_ATTACHMENT0 + i;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tdrawBuffers.length = textures.length;\n\n\t\t\t\t\tneedsUpdate = true;\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tif ( drawBuffers[ 0 ] !== gl.COLOR_ATTACHMENT0 ) {\n\n\t\t\t\t\tdrawBuffers[ 0 ] = gl.COLOR_ATTACHMENT0;\n\n\t\t\t\t\tneedsUpdate = true;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\tif ( drawBuffers[ 0 ] !== gl.BACK ) {\n\n\t\t\t\tdrawBuffers[ 0 ] = gl.BACK;\n\n\t\t\t\tneedsUpdate = true;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( needsUpdate ) {\n\n\t\t\tif ( capabilities.isWebGL2 ) {\n\n\t\t\t\tgl.drawBuffers( drawBuffers );\n\n\t\t\t} else {\n\n\t\t\t\textensions.get( 'WEBGL_draw_buffers' ).drawBuffersWEBGL( drawBuffers );\n\n\t\t\t}\n\n\t\t}\n\n\n\t}\n\n\tfunction useProgram( program ) {\n\n\t\tif ( currentProgram !== program ) {\n\n\t\t\tgl.useProgram( program );\n\n\t\t\tcurrentProgram = program;\n\n\t\t\treturn true;\n\n\t\t}\n\n\t\treturn false;\n\n\t}\n\n\tconst equationToGL = {\n\t\t[ AddEquation ]: gl.FUNC_ADD,\n\t\t[ SubtractEquation ]: gl.FUNC_SUBTRACT,\n\t\t[ ReverseSubtractEquation ]: gl.FUNC_REVERSE_SUBTRACT\n\t};\n\n\tif ( isWebGL2 ) {\n\n\t\tequationToGL[ MinEquation ] = gl.MIN;\n\t\tequationToGL[ MaxEquation ] = gl.MAX;\n\n\t} else {\n\n\t\tconst extension = extensions.get( 'EXT_blend_minmax' );\n\n\t\tif ( extension !== null ) {\n\n\t\t\tequationToGL[ MinEquation ] = extension.MIN_EXT;\n\t\t\tequationToGL[ MaxEquation ] = extension.MAX_EXT;\n\n\t\t}\n\n\t}\n\n\tconst factorToGL = {\n\t\t[ ZeroFactor ]: gl.ZERO,\n\t\t[ OneFactor ]: gl.ONE,\n\t\t[ SrcColorFactor ]: gl.SRC_COLOR,\n\t\t[ SrcAlphaFactor ]: gl.SRC_ALPHA,\n\t\t[ SrcAlphaSaturateFactor ]: gl.SRC_ALPHA_SATURATE,\n\t\t[ DstColorFactor ]: gl.DST_COLOR,\n\t\t[ DstAlphaFactor ]: gl.DST_ALPHA,\n\t\t[ OneMinusSrcColorFactor ]: gl.ONE_MINUS_SRC_COLOR,\n\t\t[ OneMinusSrcAlphaFactor ]: gl.ONE_MINUS_SRC_ALPHA,\n\t\t[ OneMinusDstColorFactor ]: gl.ONE_MINUS_DST_COLOR,\n\t\t[ OneMinusDstAlphaFactor ]: gl.ONE_MINUS_DST_ALPHA,\n\t\t[ ConstantColorFactor ]: gl.CONSTANT_COLOR,\n\t\t[ OneMinusConstantColorFactor ]: gl.ONE_MINUS_CONSTANT_COLOR,\n\t\t[ ConstantAlphaFactor ]: gl.CONSTANT_ALPHA,\n\t\t[ OneMinusConstantAlphaFactor ]: gl.ONE_MINUS_CONSTANT_ALPHA\n\t};\n\n\tfunction setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, blendColor, blendAlpha, premultipliedAlpha ) {\n\n\t\tif ( blending === NoBlending ) {\n\n\t\t\tif ( currentBlendingEnabled === true ) {\n\n\t\t\t\tdisable( gl.BLEND );\n\t\t\t\tcurrentBlendingEnabled = false;\n\n\t\t\t}\n\n\t\t\treturn;\n\n\t\t}\n\n\t\tif ( currentBlendingEnabled === false ) {\n\n\t\t\tenable( gl.BLEND );\n\t\t\tcurrentBlendingEnabled = true;\n\n\t\t}\n\n\t\tif ( blending !== CustomBlending ) {\n\n\t\t\tif ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) {\n\n\t\t\t\tif ( currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation ) {\n\n\t\t\t\t\tgl.blendEquation( gl.FUNC_ADD );\n\n\t\t\t\t\tcurrentBlendEquation = AddEquation;\n\t\t\t\t\tcurrentBlendEquationAlpha = AddEquation;\n\n\t\t\t\t}\n\n\t\t\t\tif ( premultipliedAlpha ) {\n\n\t\t\t\t\tswitch ( blending ) {\n\n\t\t\t\t\t\tcase NormalBlending:\n\t\t\t\t\t\t\tgl.blendFuncSeparate( gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase AdditiveBlending:\n\t\t\t\t\t\t\tgl.blendFunc( gl.ONE, gl.ONE );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase SubtractiveBlending:\n\t\t\t\t\t\t\tgl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase MultiplyBlending:\n\t\t\t\t\t\t\tgl.blendFuncSeparate( gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tconsole.error( 'THREE.WebGLState: Invalid blending: ', blending );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\tswitch ( blending ) {\n\n\t\t\t\t\t\tcase NormalBlending:\n\t\t\t\t\t\t\tgl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase AdditiveBlending:\n\t\t\t\t\t\t\tgl.blendFunc( gl.SRC_ALPHA, gl.ONE );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase SubtractiveBlending:\n\t\t\t\t\t\t\tgl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase MultiplyBlending:\n\t\t\t\t\t\t\tgl.blendFunc( gl.ZERO, gl.SRC_COLOR );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tconsole.error( 'THREE.WebGLState: Invalid blending: ', blending );\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tcurrentBlendSrc = null;\n\t\t\t\tcurrentBlendDst = null;\n\t\t\t\tcurrentBlendSrcAlpha = null;\n\t\t\t\tcurrentBlendDstAlpha = null;\n\t\t\t\tcurrentBlendColor.set( 0, 0, 0 );\n\t\t\t\tcurrentBlendAlpha = 0;\n\n\t\t\t\tcurrentBlending = blending;\n\t\t\t\tcurrentPremultipledAlpha = premultipliedAlpha;\n\n\t\t\t}\n\n\t\t\treturn;\n\n\t\t}\n\n\t\t// custom blending\n\n\t\tblendEquationAlpha = blendEquationAlpha || blendEquation;\n\t\tblendSrcAlpha = blendSrcAlpha || blendSrc;\n\t\tblendDstAlpha = blendDstAlpha || blendDst;\n\n\t\tif ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) {\n\n\t\t\tgl.blendEquationSeparate( equationToGL[ blendEquation ], equationToGL[ blendEquationAlpha ] );\n\n\t\t\tcurrentBlendEquation = blendEquation;\n\t\t\tcurrentBlendEquationAlpha = blendEquationAlpha;\n\n\t\t}\n\n\t\tif ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) {\n\n\t\t\tgl.blendFuncSeparate( factorToGL[ blendSrc ], factorToGL[ blendDst ], factorToGL[ blendSrcAlpha ], factorToGL[ blendDstAlpha ] );\n\n\t\t\tcurrentBlendSrc = blendSrc;\n\t\t\tcurrentBlendDst = blendDst;\n\t\t\tcurrentBlendSrcAlpha = blendSrcAlpha;\n\t\t\tcurrentBlendDstAlpha = blendDstAlpha;\n\n\t\t}\n\n\t\tif ( blendColor.equals( currentBlendColor ) === false || blendAlpha !== currentBlendAlpha ) {\n\n\t\t\tgl.blendColor( blendColor.r, blendColor.g, blendColor.b, blendAlpha );\n\n\t\t\tcurrentBlendColor.copy( blendColor );\n\t\t\tcurrentBlendAlpha = blendAlpha;\n\n\t\t}\n\n\t\tcurrentBlending = blending;\n\t\tcurrentPremultipledAlpha = false;\n\n\t}\n\n\tfunction setMaterial( material, frontFaceCW ) {\n\n\t\tmaterial.side === DoubleSide\n\t\t\t? disable( gl.CULL_FACE )\n\t\t\t: enable( gl.CULL_FACE );\n\n\t\tlet flipSided = ( material.side === BackSide );\n\t\tif ( frontFaceCW ) flipSided = ! flipSided;\n\n\t\tsetFlipSided( flipSided );\n\n\t\t( material.blending === NormalBlending && material.transparent === false )\n\t\t\t? setBlending( NoBlending )\n\t\t\t: setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.blendColor, material.blendAlpha, material.premultipliedAlpha );\n\n\t\tdepthBuffer.setFunc( material.depthFunc );\n\t\tdepthBuffer.setTest( material.depthTest );\n\t\tdepthBuffer.setMask( material.depthWrite );\n\t\tcolorBuffer.setMask( material.colorWrite );\n\n\t\tconst stencilWrite = material.stencilWrite;\n\t\tstencilBuffer.setTest( stencilWrite );\n\t\tif ( stencilWrite ) {\n\n\t\t\tstencilBuffer.setMask( material.stencilWriteMask );\n\t\t\tstencilBuffer.setFunc( material.stencilFunc, material.stencilRef, material.stencilFuncMask );\n\t\t\tstencilBuffer.setOp( material.stencilFail, material.stencilZFail, material.stencilZPass );\n\n\t\t}\n\n\t\tsetPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );\n\n\t\tmaterial.alphaToCoverage === true\n\t\t\t? enable( gl.SAMPLE_ALPHA_TO_COVERAGE )\n\t\t\t: disable( gl.SAMPLE_ALPHA_TO_COVERAGE );\n\n\t}\n\n\t//\n\n\tfunction setFlipSided( flipSided ) {\n\n\t\tif ( currentFlipSided !== flipSided ) {\n\n\t\t\tif ( flipSided ) {\n\n\t\t\t\tgl.frontFace( gl.CW );\n\n\t\t\t} else {\n\n\t\t\t\tgl.frontFace( gl.CCW );\n\n\t\t\t}\n\n\t\t\tcurrentFlipSided = flipSided;\n\n\t\t}\n\n\t}\n\n\tfunction setCullFace( cullFace ) {\n\n\t\tif ( cullFace !== CullFaceNone ) {\n\n\t\t\tenable( gl.CULL_FACE );\n\n\t\t\tif ( cullFace !== currentCullFace ) {\n\n\t\t\t\tif ( cullFace === CullFaceBack ) {\n\n\t\t\t\t\tgl.cullFace( gl.BACK );\n\n\t\t\t\t} else if ( cullFace === CullFaceFront ) {\n\n\t\t\t\t\tgl.cullFace( gl.FRONT );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tgl.cullFace( gl.FRONT_AND_BACK );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\tdisable( gl.CULL_FACE );\n\n\t\t}\n\n\t\tcurrentCullFace = cullFace;\n\n\t}\n\n\tfunction setLineWidth( width ) {\n\n\t\tif ( width !== currentLineWidth ) {\n\n\t\t\tif ( lineWidthAvailable ) gl.lineWidth( width );\n\n\t\t\tcurrentLineWidth = width;\n\n\t\t}\n\n\t}\n\n\tfunction setPolygonOffset( polygonOffset, factor, units ) {\n\n\t\tif ( polygonOffset ) {\n\n\t\t\tenable( gl.POLYGON_OFFSET_FILL );\n\n\t\t\tif ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) {\n\n\t\t\t\tgl.polygonOffset( factor, units );\n\n\t\t\t\tcurrentPolygonOffsetFactor = factor;\n\t\t\t\tcurrentPolygonOffsetUnits = units;\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\tdisable( gl.POLYGON_OFFSET_FILL );\n\n\t\t}\n\n\t}\n\n\tfunction setScissorTest( scissorTest ) {\n\n\t\tif ( scissorTest ) {\n\n\t\t\tenable( gl.SCISSOR_TEST );\n\n\t\t} else {\n\n\t\t\tdisable( gl.SCISSOR_TEST );\n\n\t\t}\n\n\t}\n\n\t// texture\n\n\tfunction activeTexture( webglSlot ) {\n\n\t\tif ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1;\n\n\t\tif ( currentTextureSlot !== webglSlot ) {\n\n\t\t\tgl.activeTexture( webglSlot );\n\t\t\tcurrentTextureSlot = webglSlot;\n\n\t\t}\n\n\t}\n\n\tfunction bindTexture( webglType, webglTexture, webglSlot ) {\n\n\t\tif ( webglSlot === undefined ) {\n\n\t\t\tif ( currentTextureSlot === null ) {\n\n\t\t\t\twebglSlot = gl.TEXTURE0 + maxTextures - 1;\n\n\t\t\t} else {\n\n\t\t\t\twebglSlot = currentTextureSlot;\n\n\t\t\t}\n\n\t\t}\n\n\t\tlet boundTexture = currentBoundTextures[ webglSlot ];\n\n\t\tif ( boundTexture === undefined ) {\n\n\t\t\tboundTexture = { type: undefined, texture: undefined };\n\t\t\tcurrentBoundTextures[ webglSlot ] = boundTexture;\n\n\t\t}\n\n\t\tif ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) {\n\n\t\t\tif ( currentTextureSlot !== webglSlot ) {\n\n\t\t\t\tgl.activeTexture( webglSlot );\n\t\t\t\tcurrentTextureSlot = webglSlot;\n\n\t\t\t}\n\n\t\t\tgl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] );\n\n\t\t\tboundTexture.type = webglType;\n\t\t\tboundTexture.texture = webglTexture;\n\n\t\t}\n\n\t}\n\n\tfunction unbindTexture() {\n\n\t\tconst boundTexture = currentBoundTextures[ currentTextureSlot ];\n\n\t\tif ( boundTexture !== undefined && boundTexture.type !== undefined ) {\n\n\t\t\tgl.bindTexture( boundTexture.type, null );\n\n\t\t\tboundTexture.type = undefined;\n\t\t\tboundTexture.texture = undefined;\n\n\t\t}\n\n\t}\n\n\tfunction compressedTexImage2D() {\n\n\t\ttry {\n\n\t\t\tgl.compressedTexImage2D.apply( gl, arguments );\n\n\t\t} catch ( error ) {\n\n\t\t\tconsole.error( 'THREE.WebGLState:', error );\n\n\t\t}\n\n\t}\n\n\tfunction compressedTexImage3D() {\n\n\t\ttry {\n\n\t\t\tgl.compressedTexImage3D.apply( gl, arguments );\n\n\t\t} catch ( error ) {\n\n\t\t\tconsole.error( 'THREE.WebGLState:', error );\n\n\t\t}\n\n\t}\n\n\tfunction texSubImage2D() {\n\n\t\ttry {\n\n\t\t\tgl.texSubImage2D.apply( gl, arguments );\n\n\t\t} catch ( error ) {\n\n\t\t\tconsole.error( 'THREE.WebGLState:', error );\n\n\t\t}\n\n\t}\n\n\tfunction texSubImage3D() {\n\n\t\ttry {\n\n\t\t\tgl.texSubImage3D.apply( gl, arguments );\n\n\t\t} catch ( error ) {\n\n\t\t\tconsole.error( 'THREE.WebGLState:', error );\n\n\t\t}\n\n\t}\n\n\tfunction compressedTexSubImage2D() {\n\n\t\ttry {\n\n\t\t\tgl.compressedTexSubImage2D.apply( gl, arguments );\n\n\t\t} catch ( error ) {\n\n\t\t\tconsole.error( 'THREE.WebGLState:', error );\n\n\t\t}\n\n\t}\n\n\tfunction compressedTexSubImage3D() {\n\n\t\ttry {\n\n\t\t\tgl.compressedTexSubImage3D.apply( gl, arguments );\n\n\t\t} catch ( error ) {\n\n\t\t\tconsole.error( 'THREE.WebGLState:', error );\n\n\t\t}\n\n\t}\n\n\tfunction texStorage2D() {\n\n\t\ttry {\n\n\t\t\tgl.texStorage2D.apply( gl, arguments );\n\n\t\t} catch ( error ) {\n\n\t\t\tconsole.error( 'THREE.WebGLState:', error );\n\n\t\t}\n\n\t}\n\n\tfunction texStorage3D() {\n\n\t\ttry {\n\n\t\t\tgl.texStorage3D.apply( gl, arguments );\n\n\t\t} catch ( error ) {\n\n\t\t\tconsole.error( 'THREE.WebGLState:', error );\n\n\t\t}\n\n\t}\n\n\tfunction texImage2D() {\n\n\t\ttry {\n\n\t\t\tgl.texImage2D.apply( gl, arguments );\n\n\t\t} catch ( error ) {\n\n\t\t\tconsole.error( 'THREE.WebGLState:', error );\n\n\t\t}\n\n\t}\n\n\tfunction texImage3D() {\n\n\t\ttry {\n\n\t\t\tgl.texImage3D.apply( gl, arguments );\n\n\t\t} catch ( error ) {\n\n\t\t\tconsole.error( 'THREE.WebGLState:', error );\n\n\t\t}\n\n\t}\n\n\t//\n\n\tfunction scissor( scissor ) {\n\n\t\tif ( currentScissor.equals( scissor ) === false ) {\n\n\t\t\tgl.scissor( scissor.x, scissor.y, scissor.z, scissor.w );\n\t\t\tcurrentScissor.copy( scissor );\n\n\t\t}\n\n\t}\n\n\tfunction viewport( viewport ) {\n\n\t\tif ( currentViewport.equals( viewport ) === false ) {\n\n\t\t\tgl.viewport( viewport.x, viewport.y, viewport.z, viewport.w );\n\t\t\tcurrentViewport.copy( viewport );\n\n\t\t}\n\n\t}\n\n\tfunction updateUBOMapping( uniformsGroup, program ) {\n\n\t\tlet mapping = uboProgramMap.get( program );\n\n\t\tif ( mapping === undefined ) {\n\n\t\t\tmapping = new WeakMap();\n\n\t\t\tuboProgramMap.set( program, mapping );\n\n\t\t}\n\n\t\tlet blockIndex = mapping.get( uniformsGroup );\n\n\t\tif ( blockIndex === undefined ) {\n\n\t\t\tblockIndex = gl.getUniformBlockIndex( program, uniformsGroup.name );\n\n\t\t\tmapping.set( uniformsGroup, blockIndex );\n\n\t\t}\n\n\t}\n\n\tfunction uniformBlockBinding( uniformsGroup, program ) {\n\n\t\tconst mapping = uboProgramMap.get( program );\n\t\tconst blockIndex = mapping.get( uniformsGroup );\n\n\t\tif ( uboBindings.get( program ) !== blockIndex ) {\n\n\t\t\t// bind shader specific block index to global block point\n\t\t\tgl.uniformBlockBinding( program, blockIndex, uniformsGroup.__bindingPointIndex );\n\n\t\t\tuboBindings.set( program, blockIndex );\n\n\t\t}\n\n\t}\n\n\t//\n\n\tfunction reset() {\n\n\t\t// reset state\n\n\t\tgl.disable( gl.BLEND );\n\t\tgl.disable( gl.CULL_FACE );\n\t\tgl.disable( gl.DEPTH_TEST );\n\t\tgl.disable( gl.POLYGON_OFFSET_FILL );\n\t\tgl.disable( gl.SCISSOR_TEST );\n\t\tgl.disable( gl.STENCIL_TEST );\n\t\tgl.disable( gl.SAMPLE_ALPHA_TO_COVERAGE );\n\n\t\tgl.blendEquation( gl.FUNC_ADD );\n\t\tgl.blendFunc( gl.ONE, gl.ZERO );\n\t\tgl.blendFuncSeparate( gl.ONE, gl.ZERO, gl.ONE, gl.ZERO );\n\t\tgl.blendColor( 0, 0, 0, 0 );\n\n\t\tgl.colorMask( true, true, true, true );\n\t\tgl.clearColor( 0, 0, 0, 0 );\n\n\t\tgl.depthMask( true );\n\t\tgl.depthFunc( gl.LESS );\n\t\tgl.clearDepth( 1 );\n\n\t\tgl.stencilMask( 0xffffffff );\n\t\tgl.stencilFunc( gl.ALWAYS, 0, 0xffffffff );\n\t\tgl.stencilOp( gl.KEEP, gl.KEEP, gl.KEEP );\n\t\tgl.clearStencil( 0 );\n\n\t\tgl.cullFace( gl.BACK );\n\t\tgl.frontFace( gl.CCW );\n\n\t\tgl.polygonOffset( 0, 0 );\n\n\t\tgl.activeTexture( gl.TEXTURE0 );\n\n\t\tgl.bindFramebuffer( gl.FRAMEBUFFER, null );\n\n\t\tif ( isWebGL2 === true ) {\n\n\t\t\tgl.bindFramebuffer( gl.DRAW_FRAMEBUFFER, null );\n\t\t\tgl.bindFramebuffer( gl.READ_FRAMEBUFFER, null );\n\n\t\t}\n\n\t\tgl.useProgram( null );\n\n\t\tgl.lineWidth( 1 );\n\n\t\tgl.scissor( 0, 0, gl.canvas.width, gl.canvas.height );\n\t\tgl.viewport( 0, 0, gl.canvas.width, gl.canvas.height );\n\n\t\t// reset internals\n\n\t\tenabledCapabilities = {};\n\n\t\tcurrentTextureSlot = null;\n\t\tcurrentBoundTextures = {};\n\n\t\tcurrentBoundFramebuffers = {};\n\t\tcurrentDrawbuffers = new WeakMap();\n\t\tdefaultDrawbuffers = [];\n\n\t\tcurrentProgram = null;\n\n\t\tcurrentBlendingEnabled = false;\n\t\tcurrentBlending = null;\n\t\tcurrentBlendEquation = null;\n\t\tcurrentBlendSrc = null;\n\t\tcurrentBlendDst = null;\n\t\tcurrentBlendEquationAlpha = null;\n\t\tcurrentBlendSrcAlpha = null;\n\t\tcurrentBlendDstAlpha = null;\n\t\tcurrentBlendColor = new Color( 0, 0, 0 );\n\t\tcurrentBlendAlpha = 0;\n\t\tcurrentPremultipledAlpha = false;\n\n\t\tcurrentFlipSided = null;\n\t\tcurrentCullFace = null;\n\n\t\tcurrentLineWidth = null;\n\n\t\tcurrentPolygonOffsetFactor = null;\n\t\tcurrentPolygonOffsetUnits = null;\n\n\t\tcurrentScissor.set( 0, 0, gl.canvas.width, gl.canvas.height );\n\t\tcurrentViewport.set( 0, 0, gl.canvas.width, gl.canvas.height );\n\n\t\tcolorBuffer.reset();\n\t\tdepthBuffer.reset();\n\t\tstencilBuffer.reset();\n\n\t}\n\n\treturn {\n\n\t\tbuffers: {\n\t\t\tcolor: colorBuffer,\n\t\t\tdepth: depthBuffer,\n\t\t\tstencil: stencilBuffer\n\t\t},\n\n\t\tenable: enable,\n\t\tdisable: disable,\n\n\t\tbindFramebuffer: bindFramebuffer,\n\t\tdrawBuffers: drawBuffers,\n\n\t\tuseProgram: useProgram,\n\n\t\tsetBlending: setBlending,\n\t\tsetMaterial: setMaterial,\n\n\t\tsetFlipSided: setFlipSided,\n\t\tsetCullFace: setCullFace,\n\n\t\tsetLineWidth: setLineWidth,\n\t\tsetPolygonOffset: setPolygonOffset,\n\n\t\tsetScissorTest: setScissorTest,\n\n\t\tactiveTexture: activeTexture,\n\t\tbindTexture: bindTexture,\n\t\tunbindTexture: unbindTexture,\n\t\tcompressedTexImage2D: compressedTexImage2D,\n\t\tcompressedTexImage3D: compressedTexImage3D,\n\t\ttexImage2D: texImage2D,\n\t\ttexImage3D: texImage3D,\n\n\t\tupdateUBOMapping: updateUBOMapping,\n\t\tuniformBlockBinding: uniformBlockBinding,\n\n\t\ttexStorage2D: texStorage2D,\n\t\ttexStorage3D: texStorage3D,\n\t\ttexSubImage2D: texSubImage2D,\n\t\ttexSubImage3D: texSubImage3D,\n\t\tcompressedTexSubImage2D: compressedTexSubImage2D,\n\t\tcompressedTexSubImage3D: compressedTexSubImage3D,\n\n\t\tscissor: scissor,\n\t\tviewport: viewport,\n\n\t\treset: reset\n\n\t};\n\n}\n\nexport { WebGLState };\n","import { LinearFilter, LinearMipmapLinearFilter, LinearMipmapNearestFilter, NearestFilter, NearestMipmapLinearFilter, NearestMipmapNearestFilter, RGBAFormat, DepthFormat, DepthStencilFormat, UnsignedShortType, UnsignedIntType, UnsignedInt248Type, FloatType, HalfFloatType, MirroredRepeatWrapping, ClampToEdgeWrapping, RepeatWrapping, UnsignedByteType, _SRGBAFormat, NoColorSpace, LinearSRGBColorSpace, NeverCompare, AlwaysCompare, LessCompare, LessEqualCompare, EqualCompare, GreaterEqualCompare, GreaterCompare, NotEqualCompare, SRGBTransfer, LinearTransfer } from '../../constants.js';\nimport * as MathUtils from '../../math/MathUtils.js';\nimport { ImageUtils } from '../../extras/ImageUtils.js';\nimport { createElementNS } from '../../utils.js';\nimport { ColorManagement } from '../../math/ColorManagement.js';\n\nfunction WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ) {\n\n\tconst isWebGL2 = capabilities.isWebGL2;\n\tconst maxTextures = capabilities.maxTextures;\n\tconst maxCubemapSize = capabilities.maxCubemapSize;\n\tconst maxTextureSize = capabilities.maxTextureSize;\n\tconst maxSamples = capabilities.maxSamples;\n\tconst multisampledRTTExt = extensions.has( 'WEBGL_multisampled_render_to_texture' ) ? extensions.get( 'WEBGL_multisampled_render_to_texture' ) : null;\n\tconst supportsInvalidateFramebuffer = typeof navigator === 'undefined' ? false : /OculusBrowser/g.test( navigator.userAgent );\n\n\tconst _videoTextures = new WeakMap();\n\tlet _canvas;\n\n\tconst _sources = new WeakMap(); // maps WebglTexture objects to instances of Source\n\n\t// cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas,\n\t// also OffscreenCanvas.getContext(\"webgl\"), but not OffscreenCanvas.getContext(\"2d\")!\n\t// Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d).\n\n\tlet useOffscreenCanvas = false;\n\n\ttry {\n\n\t\tuseOffscreenCanvas = typeof OffscreenCanvas !== 'undefined'\n\t\t\t// eslint-disable-next-line compat/compat\n\t\t\t&& ( new OffscreenCanvas( 1, 1 ).getContext( '2d' ) ) !== null;\n\n\t} catch ( err ) {\n\n\t\t// Ignore any errors\n\n\t}\n\n\tfunction createCanvas( width, height ) {\n\n\t\t// Use OffscreenCanvas when available. Specially needed in web workers\n\n\t\treturn useOffscreenCanvas ?\n\t\t\t// eslint-disable-next-line compat/compat\n\t\t\tnew OffscreenCanvas( width, height ) : createElementNS( 'canvas' );\n\n\t}\n\n\tfunction resizeImage( image, needsPowerOfTwo, needsNewCanvas, maxSize ) {\n\n\t\tlet scale = 1;\n\n\t\t// handle case if texture exceeds max size\n\n\t\tif ( image.width > maxSize || image.height > maxSize ) {\n\n\t\t\tscale = maxSize / Math.max( image.width, image.height );\n\n\t\t}\n\n\t\t// only perform resize if necessary\n\n\t\tif ( scale < 1 || needsPowerOfTwo === true ) {\n\n\t\t\t// only perform resize for certain image types\n\n\t\t\tif ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||\n\t\t\t\t( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||\n\t\t\t\t( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {\n\n\t\t\t\tconst floor = needsPowerOfTwo ? MathUtils.floorPowerOfTwo : Math.floor;\n\n\t\t\t\tconst width = floor( scale * image.width );\n\t\t\t\tconst height = floor( scale * image.height );\n\n\t\t\t\tif ( _canvas === undefined ) _canvas = createCanvas( width, height );\n\n\t\t\t\t// cube textures can't reuse the same canvas\n\n\t\t\t\tconst canvas = needsNewCanvas ? createCanvas( width, height ) : _canvas;\n\n\t\t\t\tcanvas.width = width;\n\t\t\t\tcanvas.height = height;\n\n\t\t\t\tconst context = canvas.getContext( '2d' );\n\t\t\t\tcontext.drawImage( image, 0, 0, width, height );\n\n\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Texture has been resized from (' + image.width + 'x' + image.height + ') to (' + width + 'x' + height + ').' );\n\n\t\t\t\treturn canvas;\n\n\t\t\t} else {\n\n\t\t\t\tif ( 'data' in image ) {\n\n\t\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Image in DataTexture is too big (' + image.width + 'x' + image.height + ').' );\n\n\t\t\t\t}\n\n\t\t\t\treturn image;\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn image;\n\n\t}\n\n\tfunction isPowerOfTwo( image ) {\n\n\t\treturn MathUtils.isPowerOfTwo( image.width ) && MathUtils.isPowerOfTwo( image.height );\n\n\t}\n\n\tfunction textureNeedsPowerOfTwo( texture ) {\n\n\t\tif ( isWebGL2 ) return false;\n\n\t\treturn ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) ||\n\t\t\t( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter );\n\n\t}\n\n\tfunction textureNeedsGenerateMipmaps( texture, supportsMips ) {\n\n\t\treturn texture.generateMipmaps && supportsMips &&\n\t\t\ttexture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter;\n\n\t}\n\n\tfunction generateMipmap( target ) {\n\n\t\t_gl.generateMipmap( target );\n\n\t}\n\n\tfunction getInternalFormat( internalFormatName, glFormat, glType, colorSpace, forceLinearTransfer = false ) {\n\n\t\tif ( isWebGL2 === false ) return glFormat;\n\n\t\tif ( internalFormatName !== null ) {\n\n\t\t\tif ( _gl[ internalFormatName ] !== undefined ) return _gl[ internalFormatName ];\n\n\t\t\tconsole.warn( 'THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format \\'' + internalFormatName + '\\'' );\n\n\t\t}\n\n\t\tlet internalFormat = glFormat;\n\n\t\tif ( glFormat === _gl.RED ) {\n\n\t\t\tif ( glType === _gl.FLOAT ) internalFormat = _gl.R32F;\n\t\t\tif ( glType === _gl.HALF_FLOAT ) internalFormat = _gl.R16F;\n\t\t\tif ( glType === _gl.UNSIGNED_BYTE ) internalFormat = _gl.R8;\n\n\t\t}\n\n\t\tif ( glFormat === _gl.RED_INTEGER ) {\n\n\t\t\tif ( glType === _gl.UNSIGNED_BYTE ) internalFormat = _gl.R8UI;\n\t\t\tif ( glType === _gl.UNSIGNED_SHORT ) internalFormat = _gl.R16UI;\n\t\t\tif ( glType === _gl.UNSIGNED_INT ) internalFormat = _gl.R32UI;\n\t\t\tif ( glType === _gl.BYTE ) internalFormat = _gl.R8I;\n\t\t\tif ( glType === _gl.SHORT ) internalFormat = _gl.R16I;\n\t\t\tif ( glType === _gl.INT ) internalFormat = _gl.R32I;\n\n\t\t}\n\n\t\tif ( glFormat === _gl.RG ) {\n\n\t\t\tif ( glType === _gl.FLOAT ) internalFormat = _gl.RG32F;\n\t\t\tif ( glType === _gl.HALF_FLOAT ) internalFormat = _gl.RG16F;\n\t\t\tif ( glType === _gl.UNSIGNED_BYTE ) internalFormat = _gl.RG8;\n\n\t\t}\n\n\t\tif ( glFormat === _gl.RGBA ) {\n\n\t\t\tconst transfer = forceLinearTransfer ? LinearTransfer : ColorManagement.getTransfer( colorSpace );\n\n\t\t\tif ( glType === _gl.FLOAT ) internalFormat = _gl.RGBA32F;\n\t\t\tif ( glType === _gl.HALF_FLOAT ) internalFormat = _gl.RGBA16F;\n\t\t\tif ( glType === _gl.UNSIGNED_BYTE ) internalFormat = ( transfer === SRGBTransfer ) ? _gl.SRGB8_ALPHA8 : _gl.RGBA8;\n\t\t\tif ( glType === _gl.UNSIGNED_SHORT_4_4_4_4 ) internalFormat = _gl.RGBA4;\n\t\t\tif ( glType === _gl.UNSIGNED_SHORT_5_5_5_1 ) internalFormat = _gl.RGB5_A1;\n\n\t\t}\n\n\t\tif ( internalFormat === _gl.R16F || internalFormat === _gl.R32F ||\n\t\t\tinternalFormat === _gl.RG16F || internalFormat === _gl.RG32F ||\n\t\t\tinternalFormat === _gl.RGBA16F || internalFormat === _gl.RGBA32F ) {\n\n\t\t\textensions.get( 'EXT_color_buffer_float' );\n\n\t\t}\n\n\t\treturn internalFormat;\n\n\t}\n\n\tfunction getMipLevels( texture, image, supportsMips ) {\n\n\t\tif ( textureNeedsGenerateMipmaps( texture, supportsMips ) === true || ( texture.isFramebufferTexture && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) ) {\n\n\t\t\treturn Math.log2( Math.max( image.width, image.height ) ) + 1;\n\n\t\t} else if ( texture.mipmaps !== undefined && texture.mipmaps.length > 0 ) {\n\n\t\t\t// user-defined mipmaps\n\n\t\t\treturn texture.mipmaps.length;\n\n\t\t} else if ( texture.isCompressedTexture && Array.isArray( texture.image ) ) {\n\n\t\t\treturn image.mipmaps.length;\n\n\t\t} else {\n\n\t\t\t// texture without mipmaps (only base level)\n\n\t\t\treturn 1;\n\n\t\t}\n\n\t}\n\n\t// Fallback filters for non-power-of-2 textures\n\n\tfunction filterFallback( f ) {\n\n\t\tif ( f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter ) {\n\n\t\t\treturn _gl.NEAREST;\n\n\t\t}\n\n\t\treturn _gl.LINEAR;\n\n\t}\n\n\t//\n\n\tfunction onTextureDispose( event ) {\n\n\t\tconst texture = event.target;\n\n\t\ttexture.removeEventListener( 'dispose', onTextureDispose );\n\n\t\tdeallocateTexture( texture );\n\n\t\tif ( texture.isVideoTexture ) {\n\n\t\t\t_videoTextures.delete( texture );\n\n\t\t}\n\n\t}\n\n\tfunction onRenderTargetDispose( event ) {\n\n\t\tconst renderTarget = event.target;\n\n\t\trenderTarget.removeEventListener( 'dispose', onRenderTargetDispose );\n\n\t\tdeallocateRenderTarget( renderTarget );\n\n\t}\n\n\t//\n\n\tfunction deallocateTexture( texture ) {\n\n\t\tconst textureProperties = properties.get( texture );\n\n\t\tif ( textureProperties.__webglInit === undefined ) return;\n\n\t\t// check if it's necessary to remove the WebGLTexture object\n\n\t\tconst source = texture.source;\n\t\tconst webglTextures = _sources.get( source );\n\n\t\tif ( webglTextures ) {\n\n\t\t\tconst webglTexture = webglTextures[ textureProperties.__cacheKey ];\n\t\t\twebglTexture.usedTimes --;\n\n\t\t\t// the WebGLTexture object is not used anymore, remove it\n\n\t\t\tif ( webglTexture.usedTimes === 0 ) {\n\n\t\t\t\tdeleteTexture( texture );\n\n\t\t\t}\n\n\t\t\t// remove the weak map entry if no WebGLTexture uses the source anymore\n\n\t\t\tif ( Object.keys( webglTextures ).length === 0 ) {\n\n\t\t\t\t_sources.delete( source );\n\n\t\t\t}\n\n\t\t}\n\n\t\tproperties.remove( texture );\n\n\t}\n\n\tfunction deleteTexture( texture ) {\n\n\t\tconst textureProperties = properties.get( texture );\n\t\t_gl.deleteTexture( textureProperties.__webglTexture );\n\n\t\tconst source = texture.source;\n\t\tconst webglTextures = _sources.get( source );\n\t\tdelete webglTextures[ textureProperties.__cacheKey ];\n\n\t\tinfo.memory.textures --;\n\n\t}\n\n\tfunction deallocateRenderTarget( renderTarget ) {\n\n\t\tconst texture = renderTarget.texture;\n\n\t\tconst renderTargetProperties = properties.get( renderTarget );\n\t\tconst textureProperties = properties.get( texture );\n\n\t\tif ( textureProperties.__webglTexture !== undefined ) {\n\n\t\t\t_gl.deleteTexture( textureProperties.__webglTexture );\n\n\t\t\tinfo.memory.textures --;\n\n\t\t}\n\n\t\tif ( renderTarget.depthTexture ) {\n\n\t\t\trenderTarget.depthTexture.dispose();\n\n\t\t}\n\n\t\tif ( renderTarget.isWebGLCubeRenderTarget ) {\n\n\t\t\tfor ( let i = 0; i < 6; i ++ ) {\n\n\t\t\t\tif ( Array.isArray( renderTargetProperties.__webglFramebuffer[ i ] ) ) {\n\n\t\t\t\t\tfor ( let level = 0; level < renderTargetProperties.__webglFramebuffer[ i ].length; level ++ ) _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ][ level ] );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t_gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] );\n\n\t\t\t\t}\n\n\t\t\t\tif ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] );\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\tif ( Array.isArray( renderTargetProperties.__webglFramebuffer ) ) {\n\n\t\t\t\tfor ( let level = 0; level < renderTargetProperties.__webglFramebuffer.length; level ++ ) _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ level ] );\n\n\t\t\t} else {\n\n\t\t\t\t_gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer );\n\n\t\t\t}\n\n\t\t\tif ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer );\n\t\t\tif ( renderTargetProperties.__webglMultisampledFramebuffer ) _gl.deleteFramebuffer( renderTargetProperties.__webglMultisampledFramebuffer );\n\n\t\t\tif ( renderTargetProperties.__webglColorRenderbuffer ) {\n\n\t\t\t\tfor ( let i = 0; i < renderTargetProperties.__webglColorRenderbuffer.length; i ++ ) {\n\n\t\t\t\t\tif ( renderTargetProperties.__webglColorRenderbuffer[ i ] ) _gl.deleteRenderbuffer( renderTargetProperties.__webglColorRenderbuffer[ i ] );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( renderTargetProperties.__webglDepthRenderbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthRenderbuffer );\n\n\t\t}\n\n\t\tif ( renderTarget.isWebGLMultipleRenderTargets ) {\n\n\t\t\tfor ( let i = 0, il = texture.length; i < il; i ++ ) {\n\n\t\t\t\tconst attachmentProperties = properties.get( texture[ i ] );\n\n\t\t\t\tif ( attachmentProperties.__webglTexture ) {\n\n\t\t\t\t\t_gl.deleteTexture( attachmentProperties.__webglTexture );\n\n\t\t\t\t\tinfo.memory.textures --;\n\n\t\t\t\t}\n\n\t\t\t\tproperties.remove( texture[ i ] );\n\n\t\t\t}\n\n\t\t}\n\n\t\tproperties.remove( texture );\n\t\tproperties.remove( renderTarget );\n\n\t}\n\n\t//\n\n\tlet textureUnits = 0;\n\n\tfunction resetTextureUnits() {\n\n\t\ttextureUnits = 0;\n\n\t}\n\n\tfunction allocateTextureUnit() {\n\n\t\tconst textureUnit = textureUnits;\n\n\t\tif ( textureUnit >= maxTextures ) {\n\n\t\t\tconsole.warn( 'THREE.WebGLTextures: Trying to use ' + textureUnit + ' texture units while this GPU supports only ' + maxTextures );\n\n\t\t}\n\n\t\ttextureUnits += 1;\n\n\t\treturn textureUnit;\n\n\t}\n\n\tfunction getTextureCacheKey( texture ) {\n\n\t\tconst array = [];\n\n\t\tarray.push( texture.wrapS );\n\t\tarray.push( texture.wrapT );\n\t\tarray.push( texture.wrapR || 0 );\n\t\tarray.push( texture.magFilter );\n\t\tarray.push( texture.minFilter );\n\t\tarray.push( texture.anisotropy );\n\t\tarray.push( texture.internalFormat );\n\t\tarray.push( texture.format );\n\t\tarray.push( texture.type );\n\t\tarray.push( texture.generateMipmaps );\n\t\tarray.push( texture.premultiplyAlpha );\n\t\tarray.push( texture.flipY );\n\t\tarray.push( texture.unpackAlignment );\n\t\tarray.push( texture.colorSpace );\n\n\t\treturn array.join();\n\n\t}\n\n\t//\n\n\tfunction setTexture2D( texture, slot ) {\n\n\t\tconst textureProperties = properties.get( texture );\n\n\t\tif ( texture.isVideoTexture ) updateVideoTexture( texture );\n\n\t\tif ( texture.isRenderTargetTexture === false && texture.version > 0 && textureProperties.__version !== texture.version ) {\n\n\t\t\tconst image = texture.image;\n\n\t\t\tif ( image === null ) {\n\n\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Texture marked for update but no image data found.' );\n\n\t\t\t} else if ( image.complete === false ) {\n\n\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete' );\n\n\t\t\t} else {\n\n\t\t\t\tuploadTexture( textureProperties, texture, slot );\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t}\n\n\t\tstate.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture, _gl.TEXTURE0 + slot );\n\n\t}\n\n\tfunction setTexture2DArray( texture, slot ) {\n\n\t\tconst textureProperties = properties.get( texture );\n\n\t\tif ( texture.version > 0 && textureProperties.__version !== texture.version ) {\n\n\t\t\tuploadTexture( textureProperties, texture, slot );\n\t\t\treturn;\n\n\t\t}\n\n\t\tstate.bindTexture( _gl.TEXTURE_2D_ARRAY, textureProperties.__webglTexture, _gl.TEXTURE0 + slot );\n\n\t}\n\n\tfunction setTexture3D( texture, slot ) {\n\n\t\tconst textureProperties = properties.get( texture );\n\n\t\tif ( texture.version > 0 && textureProperties.__version !== texture.version ) {\n\n\t\t\tuploadTexture( textureProperties, texture, slot );\n\t\t\treturn;\n\n\t\t}\n\n\t\tstate.bindTexture( _gl.TEXTURE_3D, textureProperties.__webglTexture, _gl.TEXTURE0 + slot );\n\n\t}\n\n\tfunction setTextureCube( texture, slot ) {\n\n\t\tconst textureProperties = properties.get( texture );\n\n\t\tif ( texture.version > 0 && textureProperties.__version !== texture.version ) {\n\n\t\t\tuploadCubeTexture( textureProperties, texture, slot );\n\t\t\treturn;\n\n\t\t}\n\n\t\tstate.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture, _gl.TEXTURE0 + slot );\n\n\t}\n\n\tconst wrappingToGL = {\n\t\t[ RepeatWrapping ]: _gl.REPEAT,\n\t\t[ ClampToEdgeWrapping ]: _gl.CLAMP_TO_EDGE,\n\t\t[ MirroredRepeatWrapping ]: _gl.MIRRORED_REPEAT\n\t};\n\n\tconst filterToGL = {\n\t\t[ NearestFilter ]: _gl.NEAREST,\n\t\t[ NearestMipmapNearestFilter ]: _gl.NEAREST_MIPMAP_NEAREST,\n\t\t[ NearestMipmapLinearFilter ]: _gl.NEAREST_MIPMAP_LINEAR,\n\n\t\t[ LinearFilter ]: _gl.LINEAR,\n\t\t[ LinearMipmapNearestFilter ]: _gl.LINEAR_MIPMAP_NEAREST,\n\t\t[ LinearMipmapLinearFilter ]: _gl.LINEAR_MIPMAP_LINEAR\n\t};\n\n\tconst compareToGL = {\n\t\t[ NeverCompare ]: _gl.NEVER,\n\t\t[ AlwaysCompare ]: _gl.ALWAYS,\n\t\t[ LessCompare ]: _gl.LESS,\n\t\t[ LessEqualCompare ]: _gl.LEQUAL,\n\t\t[ EqualCompare ]: _gl.EQUAL,\n\t\t[ GreaterEqualCompare ]: _gl.GEQUAL,\n\t\t[ GreaterCompare ]: _gl.GREATER,\n\t\t[ NotEqualCompare ]: _gl.NOTEQUAL\n\t};\n\n\tfunction setTextureParameters( textureType, texture, supportsMips ) {\n\n\t\tif ( supportsMips ) {\n\n\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, wrappingToGL[ texture.wrapS ] );\n\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, wrappingToGL[ texture.wrapT ] );\n\n\t\t\tif ( textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY ) {\n\n\t\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_R, wrappingToGL[ texture.wrapR ] );\n\n\t\t\t}\n\n\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterToGL[ texture.magFilter ] );\n\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterToGL[ texture.minFilter ] );\n\n\t\t} else {\n\n\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE );\n\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE );\n\n\t\t\tif ( textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY ) {\n\n\t\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_R, _gl.CLAMP_TO_EDGE );\n\n\t\t\t}\n\n\t\t\tif ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) {\n\n\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.' );\n\n\t\t\t}\n\n\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) );\n\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) );\n\n\t\t\tif ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) {\n\n\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.' );\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( texture.compareFunction ) {\n\n\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_COMPARE_MODE, _gl.COMPARE_REF_TO_TEXTURE );\n\t\t\t_gl.texParameteri( textureType, _gl.TEXTURE_COMPARE_FUNC, compareToGL[ texture.compareFunction ] );\n\n\t\t}\n\n\t\tif ( extensions.has( 'EXT_texture_filter_anisotropic' ) === true ) {\n\n\t\t\tconst extension = extensions.get( 'EXT_texture_filter_anisotropic' );\n\n\t\t\tif ( texture.magFilter === NearestFilter ) return;\n\t\t\tif ( texture.minFilter !== NearestMipmapLinearFilter && texture.minFilter !== LinearMipmapLinearFilter ) return;\n\t\t\tif ( texture.type === FloatType && extensions.has( 'OES_texture_float_linear' ) === false ) return; // verify extension for WebGL 1 and WebGL 2\n\t\t\tif ( isWebGL2 === false && ( texture.type === HalfFloatType && extensions.has( 'OES_texture_half_float_linear' ) === false ) ) return; // verify extension for WebGL 1 only\n\n\t\t\tif ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) {\n\n\t\t\t\t_gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) );\n\t\t\t\tproperties.get( texture ).__currentAnisotropy = texture.anisotropy;\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tfunction initTexture( textureProperties, texture ) {\n\n\t\tlet forceUpload = false;\n\n\t\tif ( textureProperties.__webglInit === undefined ) {\n\n\t\t\ttextureProperties.__webglInit = true;\n\n\t\t\ttexture.addEventListener( 'dispose', onTextureDispose );\n\n\t\t}\n\n\t\t// create Source <-> WebGLTextures mapping if necessary\n\n\t\tconst source = texture.source;\n\t\tlet webglTextures = _sources.get( source );\n\n\t\tif ( webglTextures === undefined ) {\n\n\t\t\twebglTextures = {};\n\t\t\t_sources.set( source, webglTextures );\n\n\t\t}\n\n\t\t// check if there is already a WebGLTexture object for the given texture parameters\n\n\t\tconst textureCacheKey = getTextureCacheKey( texture );\n\n\t\tif ( textureCacheKey !== textureProperties.__cacheKey ) {\n\n\t\t\t// if not, create a new instance of WebGLTexture\n\n\t\t\tif ( webglTextures[ textureCacheKey ] === undefined ) {\n\n\t\t\t\t// create new entry\n\n\t\t\t\twebglTextures[ textureCacheKey ] = {\n\t\t\t\t\ttexture: _gl.createTexture(),\n\t\t\t\t\tusedTimes: 0\n\t\t\t\t};\n\n\t\t\t\tinfo.memory.textures ++;\n\n\t\t\t\t// when a new instance of WebGLTexture was created, a texture upload is required\n\t\t\t\t// even if the image contents are identical\n\n\t\t\t\tforceUpload = true;\n\n\t\t\t}\n\n\t\t\twebglTextures[ textureCacheKey ].usedTimes ++;\n\n\t\t\t// every time the texture cache key changes, it's necessary to check if an instance of\n\t\t\t// WebGLTexture can be deleted in order to avoid a memory leak.\n\n\t\t\tconst webglTexture = webglTextures[ textureProperties.__cacheKey ];\n\n\t\t\tif ( webglTexture !== undefined ) {\n\n\t\t\t\twebglTextures[ textureProperties.__cacheKey ].usedTimes --;\n\n\t\t\t\tif ( webglTexture.usedTimes === 0 ) {\n\n\t\t\t\t\tdeleteTexture( texture );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// store references to cache key and WebGLTexture object\n\n\t\t\ttextureProperties.__cacheKey = textureCacheKey;\n\t\t\ttextureProperties.__webglTexture = webglTextures[ textureCacheKey ].texture;\n\n\t\t}\n\n\t\treturn forceUpload;\n\n\t}\n\n\tfunction uploadTexture( textureProperties, texture, slot ) {\n\n\t\tlet textureType = _gl.TEXTURE_2D;\n\n\t\tif ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) textureType = _gl.TEXTURE_2D_ARRAY;\n\t\tif ( texture.isData3DTexture ) textureType = _gl.TEXTURE_3D;\n\n\t\tconst forceUpload = initTexture( textureProperties, texture );\n\t\tconst source = texture.source;\n\n\t\tstate.bindTexture( textureType, textureProperties.__webglTexture, _gl.TEXTURE0 + slot );\n\n\t\tconst sourceProperties = properties.get( source );\n\n\t\tif ( source.version !== sourceProperties.__version || forceUpload === true ) {\n\n\t\t\tstate.activeTexture( _gl.TEXTURE0 + slot );\n\n\t\t\tconst workingPrimaries = ColorManagement.getPrimaries( ColorManagement.workingColorSpace );\n\t\t\tconst texturePrimaries = texture.colorSpace === NoColorSpace ? null : ColorManagement.getPrimaries( texture.colorSpace );\n\t\t\tconst unpackConversion = texture.colorSpace === NoColorSpace || workingPrimaries === texturePrimaries ? _gl.NONE : _gl.BROWSER_DEFAULT_WEBGL;\n\n\t\t\t_gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, unpackConversion );\n\n\t\t\tconst needsPowerOfTwo = textureNeedsPowerOfTwo( texture ) && isPowerOfTwo( texture.image ) === false;\n\t\t\tlet image = resizeImage( texture.image, needsPowerOfTwo, false, maxTextureSize );\n\t\t\timage = verifyColorSpace( texture, image );\n\n\t\t\tconst supportsMips = isPowerOfTwo( image ) || isWebGL2,\n\t\t\t\tglFormat = utils.convert( texture.format, texture.colorSpace );\n\n\t\t\tlet glType = utils.convert( texture.type ),\n\t\t\t\tglInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace, texture.isVideoTexture );\n\n\t\t\tsetTextureParameters( textureType, texture, supportsMips );\n\n\t\t\tlet mipmap;\n\t\t\tconst mipmaps = texture.mipmaps;\n\n\t\t\tconst useTexStorage = ( isWebGL2 && texture.isVideoTexture !== true );\n\t\t\tconst allocateMemory = ( sourceProperties.__version === undefined ) || ( forceUpload === true );\n\t\t\tconst levels = getMipLevels( texture, image, supportsMips );\n\n\t\t\tif ( texture.isDepthTexture ) {\n\n\t\t\t\t// populate depth texture with dummy data\n\n\t\t\t\tglInternalFormat = _gl.DEPTH_COMPONENT;\n\n\t\t\t\tif ( isWebGL2 ) {\n\n\t\t\t\t\tif ( texture.type === FloatType ) {\n\n\t\t\t\t\t\tglInternalFormat = _gl.DEPTH_COMPONENT32F;\n\n\t\t\t\t\t} else if ( texture.type === UnsignedIntType ) {\n\n\t\t\t\t\t\tglInternalFormat = _gl.DEPTH_COMPONENT24;\n\n\t\t\t\t\t} else if ( texture.type === UnsignedInt248Type ) {\n\n\t\t\t\t\t\tglInternalFormat = _gl.DEPTH24_STENCIL8;\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tglInternalFormat = _gl.DEPTH_COMPONENT16; // WebGL2 requires sized internalformat for glTexImage2D\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\tif ( texture.type === FloatType ) {\n\n\t\t\t\t\t\tconsole.error( 'WebGLRenderer: Floating point depth texture requires WebGL2.' );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\t// validation checks for WebGL 1\n\n\t\t\t\tif ( texture.format === DepthFormat && glInternalFormat === _gl.DEPTH_COMPONENT ) {\n\n\t\t\t\t\t// The error INVALID_OPERATION is generated by texImage2D if format and internalformat are\n\t\t\t\t\t// DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT\n\t\t\t\t\t// (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/)\n\t\t\t\t\tif ( texture.type !== UnsignedShortType && texture.type !== UnsignedIntType ) {\n\n\t\t\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture.' );\n\n\t\t\t\t\t\ttexture.type = UnsignedIntType;\n\t\t\t\t\t\tglType = utils.convert( texture.type );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tif ( texture.format === DepthStencilFormat && glInternalFormat === _gl.DEPTH_COMPONENT ) {\n\n\t\t\t\t\t// Depth stencil textures need the DEPTH_STENCIL internal format\n\t\t\t\t\t// (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/)\n\t\t\t\t\tglInternalFormat = _gl.DEPTH_STENCIL;\n\n\t\t\t\t\t// The error INVALID_OPERATION is generated by texImage2D if format and internalformat are\n\t\t\t\t\t// DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL.\n\t\t\t\t\t// (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/)\n\t\t\t\t\tif ( texture.type !== UnsignedInt248Type ) {\n\n\t\t\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture.' );\n\n\t\t\t\t\t\ttexture.type = UnsignedInt248Type;\n\t\t\t\t\t\tglType = utils.convert( texture.type );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\t//\n\n\t\t\t\tif ( allocateMemory ) {\n\n\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\tstate.texStorage2D( _gl.TEXTURE_2D, 1, glInternalFormat, image.width, image.height );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} else if ( texture.isDataTexture ) {\n\n\t\t\t\t// use manually created mipmaps if available\n\t\t\t\t// if there are no manual mipmaps\n\t\t\t\t// set 0 level mipmap and then use GL to generate other mipmap levels\n\n\t\t\t\tif ( mipmaps.length > 0 && supportsMips ) {\n\n\t\t\t\t\tif ( useTexStorage && allocateMemory ) {\n\n\t\t\t\t\t\tstate.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tfor ( let i = 0, il = mipmaps.length; i < il; i ++ ) {\n\n\t\t\t\t\t\tmipmap = mipmaps[ i ];\n\n\t\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\t\tstate.texSubImage2D( _gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data );\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t\ttexture.generateMipmaps = false;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\tif ( allocateMemory ) {\n\n\t\t\t\t\t\t\tstate.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tstate.texSubImage2D( _gl.TEXTURE_2D, 0, 0, 0, image.width, image.height, glFormat, glType, image.data );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} else if ( texture.isCompressedTexture ) {\n\n\t\t\t\tif ( texture.isCompressedArrayTexture ) {\n\n\t\t\t\t\tif ( useTexStorage && allocateMemory ) {\n\n\t\t\t\t\t\tstate.texStorage3D( _gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height, image.depth );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tfor ( let i = 0, il = mipmaps.length; i < il; i ++ ) {\n\n\t\t\t\t\t\tmipmap = mipmaps[ i ];\n\n\t\t\t\t\t\tif ( texture.format !== RGBAFormat ) {\n\n\t\t\t\t\t\t\tif ( glFormat !== null ) {\n\n\t\t\t\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\t\t\t\tstate.compressedTexSubImage3D( _gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, mipmap.data, 0, 0 );\n\n\t\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t\tstate.compressedTexImage3D( _gl.TEXTURE_2D_ARRAY, i, glInternalFormat, mipmap.width, mipmap.height, image.depth, 0, mipmap.data, 0, 0 );\n\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\t\t\tstate.texSubImage3D( _gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, glType, mipmap.data );\n\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\tstate.texImage3D( _gl.TEXTURE_2D_ARRAY, i, glInternalFormat, mipmap.width, mipmap.height, image.depth, 0, glFormat, glType, mipmap.data );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\tif ( useTexStorage && allocateMemory ) {\n\n\t\t\t\t\t\tstate.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tfor ( let i = 0, il = mipmaps.length; i < il; i ++ ) {\n\n\t\t\t\t\t\tmipmap = mipmaps[ i ];\n\n\t\t\t\t\t\tif ( texture.format !== RGBAFormat ) {\n\n\t\t\t\t\t\t\tif ( glFormat !== null ) {\n\n\t\t\t\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\t\t\t\tstate.compressedTexSubImage2D( _gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data );\n\n\t\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t\tstate.compressedTexImage2D( _gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data );\n\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\t\t\tstate.texSubImage2D( _gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data );\n\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} else if ( texture.isDataArrayTexture ) {\n\n\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\tif ( allocateMemory ) {\n\n\t\t\t\t\t\tstate.texStorage3D( _gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, image.width, image.height, image.depth );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tstate.texSubImage3D( _gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tstate.texImage3D( _gl.TEXTURE_2D_ARRAY, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data );\n\n\t\t\t\t}\n\n\t\t\t} else if ( texture.isData3DTexture ) {\n\n\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\tif ( allocateMemory ) {\n\n\t\t\t\t\t\tstate.texStorage3D( _gl.TEXTURE_3D, levels, glInternalFormat, image.width, image.height, image.depth );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tstate.texSubImage3D( _gl.TEXTURE_3D, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tstate.texImage3D( _gl.TEXTURE_3D, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data );\n\n\t\t\t\t}\n\n\t\t\t} else if ( texture.isFramebufferTexture ) {\n\n\t\t\t\tif ( allocateMemory ) {\n\n\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\tstate.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tlet width = image.width, height = image.height;\n\n\t\t\t\t\t\tfor ( let i = 0; i < levels; i ++ ) {\n\n\t\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, width, height, 0, glFormat, glType, null );\n\n\t\t\t\t\t\t\twidth >>= 1;\n\t\t\t\t\t\t\theight >>= 1;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\t// regular Texture (image, video, canvas)\n\n\t\t\t\t// use manually created mipmaps if available\n\t\t\t\t// if there are no manual mipmaps\n\t\t\t\t// set 0 level mipmap and then use GL to generate other mipmap levels\n\n\t\t\t\tif ( mipmaps.length > 0 && supportsMips ) {\n\n\t\t\t\t\tif ( useTexStorage && allocateMemory ) {\n\n\t\t\t\t\t\tstate.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tfor ( let i = 0, il = mipmaps.length; i < il; i ++ ) {\n\n\t\t\t\t\t\tmipmap = mipmaps[ i ];\n\n\t\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\t\tstate.texSubImage2D( _gl.TEXTURE_2D, i, 0, 0, glFormat, glType, mipmap );\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, glFormat, glType, mipmap );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t\ttexture.generateMipmaps = false;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\tif ( allocateMemory ) {\n\n\t\t\t\t\t\t\tstate.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tstate.texSubImage2D( _gl.TEXTURE_2D, 0, 0, 0, glFormat, glType, image );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_2D, 0, glInternalFormat, glFormat, glType, image );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) {\n\n\t\t\t\tgenerateMipmap( textureType );\n\n\t\t\t}\n\n\t\t\tsourceProperties.__version = source.version;\n\n\t\t\tif ( texture.onUpdate ) texture.onUpdate( texture );\n\n\t\t}\n\n\t\ttextureProperties.__version = texture.version;\n\n\t}\n\n\tfunction uploadCubeTexture( textureProperties, texture, slot ) {\n\n\t\tif ( texture.image.length !== 6 ) return;\n\n\t\tconst forceUpload = initTexture( textureProperties, texture );\n\t\tconst source = texture.source;\n\n\t\tstate.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture, _gl.TEXTURE0 + slot );\n\n\t\tconst sourceProperties = properties.get( source );\n\n\t\tif ( source.version !== sourceProperties.__version || forceUpload === true ) {\n\n\t\t\tstate.activeTexture( _gl.TEXTURE0 + slot );\n\n\t\t\tconst workingPrimaries = ColorManagement.getPrimaries( ColorManagement.workingColorSpace );\n\t\t\tconst texturePrimaries = texture.colorSpace === NoColorSpace ? null : ColorManagement.getPrimaries( texture.colorSpace );\n\t\t\tconst unpackConversion = texture.colorSpace === NoColorSpace || workingPrimaries === texturePrimaries ? _gl.NONE : _gl.BROWSER_DEFAULT_WEBGL;\n\n\t\t\t_gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, unpackConversion );\n\n\t\t\tconst isCompressed = ( texture.isCompressedTexture || texture.image[ 0 ].isCompressedTexture );\n\t\t\tconst isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture );\n\n\t\t\tconst cubeImage = [];\n\n\t\t\tfor ( let i = 0; i < 6; i ++ ) {\n\n\t\t\t\tif ( ! isCompressed && ! isDataTexture ) {\n\n\t\t\t\t\tcubeImage[ i ] = resizeImage( texture.image[ i ], false, true, maxCubemapSize );\n\n\t\t\t\t} else {\n\n\t\t\t\t\tcubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ];\n\n\t\t\t\t}\n\n\t\t\t\tcubeImage[ i ] = verifyColorSpace( texture, cubeImage[ i ] );\n\n\t\t\t}\n\n\t\t\tconst image = cubeImage[ 0 ],\n\t\t\t\tsupportsMips = isPowerOfTwo( image ) || isWebGL2,\n\t\t\t\tglFormat = utils.convert( texture.format, texture.colorSpace ),\n\t\t\t\tglType = utils.convert( texture.type ),\n\t\t\t\tglInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace );\n\n\t\t\tconst useTexStorage = ( isWebGL2 && texture.isVideoTexture !== true );\n\t\t\tconst allocateMemory = ( sourceProperties.__version === undefined ) || ( forceUpload === true );\n\t\t\tlet levels = getMipLevels( texture, image, supportsMips );\n\n\t\t\tsetTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, supportsMips );\n\n\t\t\tlet mipmaps;\n\n\t\t\tif ( isCompressed ) {\n\n\t\t\t\tif ( useTexStorage && allocateMemory ) {\n\n\t\t\t\t\tstate.texStorage2D( _gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, image.width, image.height );\n\n\t\t\t\t}\n\n\t\t\t\tfor ( let i = 0; i < 6; i ++ ) {\n\n\t\t\t\t\tmipmaps = cubeImage[ i ].mipmaps;\n\n\t\t\t\t\tfor ( let j = 0; j < mipmaps.length; j ++ ) {\n\n\t\t\t\t\t\tconst mipmap = mipmaps[ j ];\n\n\t\t\t\t\t\tif ( texture.format !== RGBAFormat ) {\n\n\t\t\t\t\t\t\tif ( glFormat !== null ) {\n\n\t\t\t\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\t\t\t\tstate.compressedTexSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data );\n\n\t\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t\tstate.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data );\n\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()' );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\t\t\tstate.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data );\n\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tmipmaps = texture.mipmaps;\n\n\t\t\t\tif ( useTexStorage && allocateMemory ) {\n\n\t\t\t\t\t// TODO: Uniformly handle mipmap definitions\n\t\t\t\t\t// Normal textures and compressed cube textures define base level + mips with their mipmap array\n\t\t\t\t\t// Uncompressed cube textures use their mipmap array only for mips (no base level)\n\n\t\t\t\t\tif ( mipmaps.length > 0 ) levels ++;\n\n\t\t\t\t\tstate.texStorage2D( _gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, cubeImage[ 0 ].width, cubeImage[ 0 ].height );\n\n\t\t\t\t}\n\n\t\t\t\tfor ( let i = 0; i < 6; i ++ ) {\n\n\t\t\t\t\tif ( isDataTexture ) {\n\n\t\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\t\tstate.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, cubeImage[ i ].width, cubeImage[ i ].height, glFormat, glType, cubeImage[ i ].data );\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor ( let j = 0; j < mipmaps.length; j ++ ) {\n\n\t\t\t\t\t\t\tconst mipmap = mipmaps[ j ];\n\t\t\t\t\t\t\tconst mipmapImage = mipmap.image[ i ].image;\n\n\t\t\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\t\t\tstate.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, mipmapImage.width, mipmapImage.height, glFormat, glType, mipmapImage.data );\n\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\t\tstate.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, glFormat, glType, cubeImage[ i ] );\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, glFormat, glType, cubeImage[ i ] );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor ( let j = 0; j < mipmaps.length; j ++ ) {\n\n\t\t\t\t\t\t\tconst mipmap = mipmaps[ j ];\n\n\t\t\t\t\t\t\tif ( useTexStorage ) {\n\n\t\t\t\t\t\t\t\tstate.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, glFormat, glType, mipmap.image[ i ] );\n\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\tstate.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[ i ] );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) {\n\n\t\t\t\t// We assume images for cube map have the same size.\n\t\t\t\tgenerateMipmap( _gl.TEXTURE_CUBE_MAP );\n\n\t\t\t}\n\n\t\t\tsourceProperties.__version = source.version;\n\n\t\t\tif ( texture.onUpdate ) texture.onUpdate( texture );\n\n\t\t}\n\n\t\ttextureProperties.__version = texture.version;\n\n\t}\n\n\t// Render targets\n\n\t// Setup storage for target texture and bind it to correct framebuffer\n\tfunction setupFrameBufferTexture( framebuffer, renderTarget, texture, attachment, textureTarget, level ) {\n\n\t\tconst glFormat = utils.convert( texture.format, texture.colorSpace );\n\t\tconst glType = utils.convert( texture.type );\n\t\tconst glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace );\n\t\tconst renderTargetProperties = properties.get( renderTarget );\n\n\t\tif ( ! renderTargetProperties.__hasExternalTextures ) {\n\n\t\t\tconst width = Math.max( 1, renderTarget.width >> level );\n\t\t\tconst height = Math.max( 1, renderTarget.height >> level );\n\n\t\t\tif ( textureTarget === _gl.TEXTURE_3D || textureTarget === _gl.TEXTURE_2D_ARRAY ) {\n\n\t\t\t\tstate.texImage3D( textureTarget, level, glInternalFormat, width, height, renderTarget.depth, 0, glFormat, glType, null );\n\n\t\t\t} else {\n\n\t\t\t\tstate.texImage2D( textureTarget, level, glInternalFormat, width, height, 0, glFormat, glType, null );\n\n\t\t\t}\n\n\t\t}\n\n\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );\n\n\t\tif ( useMultisampledRTT( renderTarget ) ) {\n\n\t\t\tmultisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( texture ).__webglTexture, 0, getRenderTargetSamples( renderTarget ) );\n\n\t\t} else if ( textureTarget === _gl.TEXTURE_2D || ( textureTarget >= _gl.TEXTURE_CUBE_MAP_POSITIVE_X && textureTarget <= _gl.TEXTURE_CUBE_MAP_NEGATIVE_Z ) ) { // see #24753\n\n\t\t\t_gl.framebufferTexture2D( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( texture ).__webglTexture, level );\n\n\t\t}\n\n\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, null );\n\n\t}\n\n\n\t// Setup storage for internal depth/stencil buffers and bind to correct framebuffer\n\tfunction setupRenderBufferStorage( renderbuffer, renderTarget, isMultisample ) {\n\n\t\t_gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer );\n\n\t\tif ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) {\n\n\t\t\tlet glInternalFormat = ( isWebGL2 === true ) ? _gl.DEPTH_COMPONENT24 : _gl.DEPTH_COMPONENT16;\n\n\t\t\tif ( isMultisample || useMultisampledRTT( renderTarget ) ) {\n\n\t\t\t\tconst depthTexture = renderTarget.depthTexture;\n\n\t\t\t\tif ( depthTexture && depthTexture.isDepthTexture ) {\n\n\t\t\t\t\tif ( depthTexture.type === FloatType ) {\n\n\t\t\t\t\t\tglInternalFormat = _gl.DEPTH_COMPONENT32F;\n\n\t\t\t\t\t} else if ( depthTexture.type === UnsignedIntType ) {\n\n\t\t\t\t\t\tglInternalFormat = _gl.DEPTH_COMPONENT24;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tconst samples = getRenderTargetSamples( renderTarget );\n\n\t\t\t\tif ( useMultisampledRTT( renderTarget ) ) {\n\n\t\t\t\t\tmultisampledRTTExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t_gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height );\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\t_gl.renderbufferStorage( _gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height );\n\n\t\t\t}\n\n\t\t\t_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );\n\n\t\t} else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) {\n\n\t\t\tconst samples = getRenderTargetSamples( renderTarget );\n\n\t\t\tif ( isMultisample && useMultisampledRTT( renderTarget ) === false ) {\n\n\t\t\t\t_gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height );\n\n\t\t\t} else if ( useMultisampledRTT( renderTarget ) ) {\n\n\t\t\t\tmultisampledRTTExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height );\n\n\t\t\t} else {\n\n\t\t\t\t_gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height );\n\n\t\t\t}\n\n\n\t\t\t_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );\n\n\t\t} else {\n\n\t\t\tconst textures = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture : [ renderTarget.texture ];\n\n\t\t\tfor ( let i = 0; i < textures.length; i ++ ) {\n\n\t\t\t\tconst texture = textures[ i ];\n\n\t\t\t\tconst glFormat = utils.convert( texture.format, texture.colorSpace );\n\t\t\t\tconst glType = utils.convert( texture.type );\n\t\t\t\tconst glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace );\n\t\t\t\tconst samples = getRenderTargetSamples( renderTarget );\n\n\t\t\t\tif ( isMultisample && useMultisampledRTT( renderTarget ) === false ) {\n\n\t\t\t\t\t_gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height );\n\n\t\t\t\t} else if ( useMultisampledRTT( renderTarget ) ) {\n\n\t\t\t\t\tmultisampledRTTExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t_gl.renderbufferStorage( _gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\t_gl.bindRenderbuffer( _gl.RENDERBUFFER, null );\n\n\t}\n\n\t// Setup resources for a Depth Texture for a FBO (needs an extension)\n\tfunction setupDepthTexture( framebuffer, renderTarget ) {\n\n\t\tconst isCube = ( renderTarget && renderTarget.isWebGLCubeRenderTarget );\n\t\tif ( isCube ) throw new Error( 'Depth Texture with cube render targets is not supported' );\n\n\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );\n\n\t\tif ( ! ( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) {\n\n\t\t\tthrow new Error( 'renderTarget.depthTexture must be an instance of THREE.DepthTexture' );\n\n\t\t}\n\n\t\t// upload an empty depth texture with framebuffer size\n\t\tif ( ! properties.get( renderTarget.depthTexture ).__webglTexture ||\n\t\t\t\trenderTarget.depthTexture.image.width !== renderTarget.width ||\n\t\t\t\trenderTarget.depthTexture.image.height !== renderTarget.height ) {\n\n\t\t\trenderTarget.depthTexture.image.width = renderTarget.width;\n\t\t\trenderTarget.depthTexture.image.height = renderTarget.height;\n\t\t\trenderTarget.depthTexture.needsUpdate = true;\n\n\t\t}\n\n\t\tsetTexture2D( renderTarget.depthTexture, 0 );\n\n\t\tconst webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture;\n\t\tconst samples = getRenderTargetSamples( renderTarget );\n\n\t\tif ( renderTarget.depthTexture.format === DepthFormat ) {\n\n\t\t\tif ( useMultisampledRTT( renderTarget ) ) {\n\n\t\t\t\tmultisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples );\n\n\t\t\t} else {\n\n\t\t\t\t_gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 );\n\n\t\t\t}\n\n\t\t} else if ( renderTarget.depthTexture.format === DepthStencilFormat ) {\n\n\t\t\tif ( useMultisampledRTT( renderTarget ) ) {\n\n\t\t\t\tmultisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples );\n\n\t\t\t} else {\n\n\t\t\t\t_gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 );\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\tthrow new Error( 'Unknown depthTexture format' );\n\n\t\t}\n\n\t}\n\n\t// Setup GL resources for a non-texture depth buffer\n\tfunction setupDepthRenderbuffer( renderTarget ) {\n\n\t\tconst renderTargetProperties = properties.get( renderTarget );\n\t\tconst isCube = ( renderTarget.isWebGLCubeRenderTarget === true );\n\n\t\tif ( renderTarget.depthTexture && ! renderTargetProperties.__autoAllocateDepthBuffer ) {\n\n\t\t\tif ( isCube ) throw new Error( 'target.depthTexture not supported in Cube render targets' );\n\n\t\t\tsetupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget );\n\n\t\t} else {\n\n\t\t\tif ( isCube ) {\n\n\t\t\t\trenderTargetProperties.__webglDepthbuffer = [];\n\n\t\t\t\tfor ( let i = 0; i < 6; i ++ ) {\n\n\t\t\t\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[ i ] );\n\t\t\t\t\trenderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer();\n\t\t\t\t\tsetupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget, false );\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer );\n\t\t\t\trenderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer();\n\t\t\t\tsetupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget, false );\n\n\t\t\t}\n\n\t\t}\n\n\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, null );\n\n\t}\n\n\t// rebind framebuffer with external textures\n\tfunction rebindTextures( renderTarget, colorTexture, depthTexture ) {\n\n\t\tconst renderTargetProperties = properties.get( renderTarget );\n\n\t\tif ( colorTexture !== undefined ) {\n\n\t\t\tsetupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, renderTarget.texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D, 0 );\n\n\t\t}\n\n\t\tif ( depthTexture !== undefined ) {\n\n\t\t\tsetupDepthRenderbuffer( renderTarget );\n\n\t\t}\n\n\t}\n\n\t// Set up GL resources for the render target\n\tfunction setupRenderTarget( renderTarget ) {\n\n\t\tconst texture = renderTarget.texture;\n\n\t\tconst renderTargetProperties = properties.get( renderTarget );\n\t\tconst textureProperties = properties.get( texture );\n\n\t\trenderTarget.addEventListener( 'dispose', onRenderTargetDispose );\n\n\t\tif ( renderTarget.isWebGLMultipleRenderTargets !== true ) {\n\n\t\t\tif ( textureProperties.__webglTexture === undefined ) {\n\n\t\t\t\ttextureProperties.__webglTexture = _gl.createTexture();\n\n\t\t\t}\n\n\t\t\ttextureProperties.__version = texture.version;\n\t\t\tinfo.memory.textures ++;\n\n\t\t}\n\n\t\tconst isCube = ( renderTarget.isWebGLCubeRenderTarget === true );\n\t\tconst isMultipleRenderTargets = ( renderTarget.isWebGLMultipleRenderTargets === true );\n\t\tconst supportsMips = isPowerOfTwo( renderTarget ) || isWebGL2;\n\n\t\t// Setup framebuffer\n\n\t\tif ( isCube ) {\n\n\t\t\trenderTargetProperties.__webglFramebuffer = [];\n\n\t\t\tfor ( let i = 0; i < 6; i ++ ) {\n\n\t\t\t\tif ( isWebGL2 && texture.mipmaps && texture.mipmaps.length > 0 ) {\n\n\t\t\t\t\trenderTargetProperties.__webglFramebuffer[ i ] = [];\n\n\t\t\t\t\tfor ( let level = 0; level < texture.mipmaps.length; level ++ ) {\n\n\t\t\t\t\t\trenderTargetProperties.__webglFramebuffer[ i ][ level ] = _gl.createFramebuffer();\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\trenderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer();\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\tif ( isWebGL2 && texture.mipmaps && texture.mipmaps.length > 0 ) {\n\n\t\t\t\trenderTargetProperties.__webglFramebuffer = [];\n\n\t\t\t\tfor ( let level = 0; level < texture.mipmaps.length; level ++ ) {\n\n\t\t\t\t\trenderTargetProperties.__webglFramebuffer[ level ] = _gl.createFramebuffer();\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\trenderTargetProperties.__webglFramebuffer = _gl.createFramebuffer();\n\n\t\t\t}\n\n\t\t\tif ( isMultipleRenderTargets ) {\n\n\t\t\t\tif ( capabilities.drawBuffers ) {\n\n\t\t\t\t\tconst textures = renderTarget.texture;\n\n\t\t\t\t\tfor ( let i = 0, il = textures.length; i < il; i ++ ) {\n\n\t\t\t\t\t\tconst attachmentProperties = properties.get( textures[ i ] );\n\n\t\t\t\t\t\tif ( attachmentProperties.__webglTexture === undefined ) {\n\n\t\t\t\t\t\t\tattachmentProperties.__webglTexture = _gl.createTexture();\n\n\t\t\t\t\t\t\tinfo.memory.textures ++;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: WebGLMultipleRenderTargets can only be used with WebGL2 or WEBGL_draw_buffers extension.' );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( ( isWebGL2 && renderTarget.samples > 0 ) && useMultisampledRTT( renderTarget ) === false ) {\n\n\t\t\t\tconst textures = isMultipleRenderTargets ? texture : [ texture ];\n\n\t\t\t\trenderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer();\n\t\t\t\trenderTargetProperties.__webglColorRenderbuffer = [];\n\n\t\t\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer );\n\n\t\t\t\tfor ( let i = 0; i < textures.length; i ++ ) {\n\n\t\t\t\t\tconst texture = textures[ i ];\n\t\t\t\t\trenderTargetProperties.__webglColorRenderbuffer[ i ] = _gl.createRenderbuffer();\n\n\t\t\t\t\t_gl.bindRenderbuffer( _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[ i ] );\n\n\t\t\t\t\tconst glFormat = utils.convert( texture.format, texture.colorSpace );\n\t\t\t\t\tconst glType = utils.convert( texture.type );\n\t\t\t\t\tconst glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace, renderTarget.isXRRenderTarget === true );\n\t\t\t\t\tconst samples = getRenderTargetSamples( renderTarget );\n\t\t\t\t\t_gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height );\n\n\t\t\t\t\t_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[ i ] );\n\n\t\t\t\t}\n\n\t\t\t\t_gl.bindRenderbuffer( _gl.RENDERBUFFER, null );\n\n\t\t\t\tif ( renderTarget.depthBuffer ) {\n\n\t\t\t\t\trenderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer();\n\t\t\t\t\tsetupRenderBufferStorage( renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true );\n\n\t\t\t\t}\n\n\t\t\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, null );\n\n\t\t\t}\n\n\t\t}\n\n\t\t// Setup color buffer\n\n\t\tif ( isCube ) {\n\n\t\t\tstate.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture );\n\t\t\tsetTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, supportsMips );\n\n\t\t\tfor ( let i = 0; i < 6; i ++ ) {\n\n\t\t\t\tif ( isWebGL2 && texture.mipmaps && texture.mipmaps.length > 0 ) {\n\n\t\t\t\t\tfor ( let level = 0; level < texture.mipmaps.length; level ++ ) {\n\n\t\t\t\t\t\tsetupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ][ level ], renderTarget, texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, level );\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\tsetupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0 );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) {\n\n\t\t\t\tgenerateMipmap( _gl.TEXTURE_CUBE_MAP );\n\n\t\t\t}\n\n\t\t\tstate.unbindTexture();\n\n\t\t} else if ( isMultipleRenderTargets ) {\n\n\t\t\tconst textures = renderTarget.texture;\n\n\t\t\tfor ( let i = 0, il = textures.length; i < il; i ++ ) {\n\n\t\t\t\tconst attachment = textures[ i ];\n\t\t\t\tconst attachmentProperties = properties.get( attachment );\n\n\t\t\t\tstate.bindTexture( _gl.TEXTURE_2D, attachmentProperties.__webglTexture );\n\t\t\t\tsetTextureParameters( _gl.TEXTURE_2D, attachment, supportsMips );\n\t\t\t\tsetupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, attachment, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D, 0 );\n\n\t\t\t\tif ( textureNeedsGenerateMipmaps( attachment, supportsMips ) ) {\n\n\t\t\t\t\tgenerateMipmap( _gl.TEXTURE_2D );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tstate.unbindTexture();\n\n\t\t} else {\n\n\t\t\tlet glTextureType = _gl.TEXTURE_2D;\n\n\t\t\tif ( renderTarget.isWebGL3DRenderTarget || renderTarget.isWebGLArrayRenderTarget ) {\n\n\t\t\t\tif ( isWebGL2 ) {\n\n\t\t\t\t\tglTextureType = renderTarget.isWebGL3DRenderTarget ? _gl.TEXTURE_3D : _gl.TEXTURE_2D_ARRAY;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tconsole.error( 'THREE.WebGLTextures: THREE.Data3DTexture and THREE.DataArrayTexture only supported with WebGL2.' );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tstate.bindTexture( glTextureType, textureProperties.__webglTexture );\n\t\t\tsetTextureParameters( glTextureType, texture, supportsMips );\n\n\t\t\tif ( isWebGL2 && texture.mipmaps && texture.mipmaps.length > 0 ) {\n\n\t\t\t\tfor ( let level = 0; level < texture.mipmaps.length; level ++ ) {\n\n\t\t\t\t\tsetupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ level ], renderTarget, texture, _gl.COLOR_ATTACHMENT0, glTextureType, level );\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tsetupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, texture, _gl.COLOR_ATTACHMENT0, glTextureType, 0 );\n\n\t\t\t}\n\n\t\t\tif ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) {\n\n\t\t\t\tgenerateMipmap( glTextureType );\n\n\t\t\t}\n\n\t\t\tstate.unbindTexture();\n\n\t\t}\n\n\t\t// Setup depth and stencil buffers\n\n\t\tif ( renderTarget.depthBuffer ) {\n\n\t\t\tsetupDepthRenderbuffer( renderTarget );\n\n\t\t}\n\n\t}\n\n\tfunction updateRenderTargetMipmap( renderTarget ) {\n\n\t\tconst supportsMips = isPowerOfTwo( renderTarget ) || isWebGL2;\n\n\t\tconst textures = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture : [ renderTarget.texture ];\n\n\t\tfor ( let i = 0, il = textures.length; i < il; i ++ ) {\n\n\t\t\tconst texture = textures[ i ];\n\n\t\t\tif ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) {\n\n\t\t\t\tconst target = renderTarget.isWebGLCubeRenderTarget ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D;\n\t\t\t\tconst webglTexture = properties.get( texture ).__webglTexture;\n\n\t\t\t\tstate.bindTexture( target, webglTexture );\n\t\t\t\tgenerateMipmap( target );\n\t\t\t\tstate.unbindTexture();\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tfunction updateMultisampleRenderTarget( renderTarget ) {\n\n\t\tif ( ( isWebGL2 && renderTarget.samples > 0 ) && useMultisampledRTT( renderTarget ) === false ) {\n\n\t\t\tconst textures = renderTarget.isWebGLMultipleRenderTargets ? renderTarget.texture : [ renderTarget.texture ];\n\t\t\tconst width = renderTarget.width;\n\t\t\tconst height = renderTarget.height;\n\t\t\tlet mask = _gl.COLOR_BUFFER_BIT;\n\t\t\tconst invalidationArray = [];\n\t\t\tconst depthStyle = renderTarget.stencilBuffer ? _gl.DEPTH_STENCIL_ATTACHMENT : _gl.DEPTH_ATTACHMENT;\n\t\t\tconst renderTargetProperties = properties.get( renderTarget );\n\t\t\tconst isMultipleRenderTargets = ( renderTarget.isWebGLMultipleRenderTargets === true );\n\n\t\t\t// If MRT we need to remove FBO attachments\n\t\t\tif ( isMultipleRenderTargets ) {\n\n\t\t\t\tfor ( let i = 0; i < textures.length; i ++ ) {\n\n\t\t\t\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer );\n\t\t\t\t\t_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.RENDERBUFFER, null );\n\n\t\t\t\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer );\n\t\t\t\t\t_gl.framebufferTexture2D( _gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D, null, 0 );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tstate.bindFramebuffer( _gl.READ_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer );\n\t\t\tstate.bindFramebuffer( _gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglFramebuffer );\n\n\t\t\tfor ( let i = 0; i < textures.length; i ++ ) {\n\n\t\t\t\tinvalidationArray.push( _gl.COLOR_ATTACHMENT0 + i );\n\n\t\t\t\tif ( renderTarget.depthBuffer ) {\n\n\t\t\t\t\tinvalidationArray.push( depthStyle );\n\n\t\t\t\t}\n\n\t\t\t\tconst ignoreDepthValues = ( renderTargetProperties.__ignoreDepthValues !== undefined ) ? renderTargetProperties.__ignoreDepthValues : false;\n\n\t\t\t\tif ( ignoreDepthValues === false ) {\n\n\t\t\t\t\tif ( renderTarget.depthBuffer ) mask |= _gl.DEPTH_BUFFER_BIT;\n\t\t\t\t\tif ( renderTarget.stencilBuffer ) mask |= _gl.STENCIL_BUFFER_BIT;\n\n\t\t\t\t}\n\n\t\t\t\tif ( isMultipleRenderTargets ) {\n\n\t\t\t\t\t_gl.framebufferRenderbuffer( _gl.READ_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[ i ] );\n\n\t\t\t\t}\n\n\t\t\t\tif ( ignoreDepthValues === true ) {\n\n\t\t\t\t\t_gl.invalidateFramebuffer( _gl.READ_FRAMEBUFFER, [ depthStyle ] );\n\t\t\t\t\t_gl.invalidateFramebuffer( _gl.DRAW_FRAMEBUFFER, [ depthStyle ] );\n\n\t\t\t\t}\n\n\t\t\t\tif ( isMultipleRenderTargets ) {\n\n\t\t\t\t\tconst webglTexture = properties.get( textures[ i ] ).__webglTexture;\n\t\t\t\t\t_gl.framebufferTexture2D( _gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D, webglTexture, 0 );\n\n\t\t\t\t}\n\n\t\t\t\t_gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, _gl.NEAREST );\n\n\t\t\t\tif ( supportsInvalidateFramebuffer ) {\n\n\t\t\t\t\t_gl.invalidateFramebuffer( _gl.READ_FRAMEBUFFER, invalidationArray );\n\n\t\t\t\t}\n\n\n\t\t\t}\n\n\t\t\tstate.bindFramebuffer( _gl.READ_FRAMEBUFFER, null );\n\t\t\tstate.bindFramebuffer( _gl.DRAW_FRAMEBUFFER, null );\n\n\t\t\t// If MRT since pre-blit we removed the FBO we need to reconstruct the attachments\n\t\t\tif ( isMultipleRenderTargets ) {\n\n\t\t\t\tfor ( let i = 0; i < textures.length; i ++ ) {\n\n\t\t\t\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer );\n\t\t\t\t\t_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[ i ] );\n\n\t\t\t\t\tconst webglTexture = properties.get( textures[ i ] ).__webglTexture;\n\n\t\t\t\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer );\n\t\t\t\t\t_gl.framebufferTexture2D( _gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D, webglTexture, 0 );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tstate.bindFramebuffer( _gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer );\n\n\t\t}\n\n\t}\n\n\tfunction getRenderTargetSamples( renderTarget ) {\n\n\t\treturn Math.min( maxSamples, renderTarget.samples );\n\n\t}\n\n\tfunction useMultisampledRTT( renderTarget ) {\n\n\t\tconst renderTargetProperties = properties.get( renderTarget );\n\n\t\treturn isWebGL2 && renderTarget.samples > 0 && extensions.has( 'WEBGL_multisampled_render_to_texture' ) === true && renderTargetProperties.__useRenderToTexture !== false;\n\n\t}\n\n\tfunction updateVideoTexture( texture ) {\n\n\t\tconst frame = info.render.frame;\n\n\t\t// Check the last frame we updated the VideoTexture\n\n\t\tif ( _videoTextures.get( texture ) !== frame ) {\n\n\t\t\t_videoTextures.set( texture, frame );\n\t\t\ttexture.update();\n\n\t\t}\n\n\t}\n\n\tfunction verifyColorSpace( texture, image ) {\n\n\t\tconst colorSpace = texture.colorSpace;\n\t\tconst format = texture.format;\n\t\tconst type = texture.type;\n\n\t\tif ( texture.isCompressedTexture === true || texture.isVideoTexture === true || texture.format === _SRGBAFormat ) return image;\n\n\t\tif ( colorSpace !== LinearSRGBColorSpace && colorSpace !== NoColorSpace ) {\n\n\t\t\t// sRGB\n\n\t\t\tif ( ColorManagement.getTransfer( colorSpace ) === SRGBTransfer ) {\n\n\t\t\t\tif ( isWebGL2 === false ) {\n\n\t\t\t\t\t// in WebGL 1, try to use EXT_sRGB extension and unsized formats\n\n\t\t\t\t\tif ( extensions.has( 'EXT_sRGB' ) === true && format === RGBAFormat ) {\n\n\t\t\t\t\t\ttexture.format = _SRGBAFormat;\n\n\t\t\t\t\t\t// it's not possible to generate mips in WebGL 1 with this extension\n\n\t\t\t\t\t\ttexture.minFilter = LinearFilter;\n\t\t\t\t\t\ttexture.generateMipmaps = false;\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t// slow fallback (CPU decode)\n\n\t\t\t\t\t\timage = ImageUtils.sRGBToLinear( image );\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// in WebGL 2 uncompressed textures can only be sRGB encoded if they have the RGBA8 format\n\n\t\t\t\t\tif ( format !== RGBAFormat || type !== UnsignedByteType ) {\n\n\t\t\t\t\t\tconsole.warn( 'THREE.WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType.' );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tconsole.error( 'THREE.WebGLTextures: Unsupported texture color space:', colorSpace );\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn image;\n\n\t}\n\n\t//\n\n\tthis.allocateTextureUnit = allocateTextureUnit;\n\tthis.resetTextureUnits = resetTextureUnits;\n\n\tthis.setTexture2D = setTexture2D;\n\tthis.setTexture2DArray = setTexture2DArray;\n\tthis.setTexture3D = setTexture3D;\n\tthis.setTextureCube = setTextureCube;\n\tthis.rebindTextures = rebindTextures;\n\tthis.setupRenderTarget = setupRenderTarget;\n\tthis.updateRenderTargetMipmap = updateRenderTargetMipmap;\n\tthis.updateMultisampleRenderTarget = updateMultisampleRenderTarget;\n\tthis.setupDepthRenderbuffer = setupDepthRenderbuffer;\n\tthis.setupFrameBufferTexture = setupFrameBufferTexture;\n\tthis.useMultisampledRTT = useMultisampledRTT;\n\n}\n\nexport { WebGLTextures };\n","import { RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_ASTC_10x5_Format, RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_10x10_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, RGB_ETC1_Format, RGB_ETC2_Format, RGBA_ETC2_EAC_Format, RGBA_PVRTC_2BPPV1_Format, RGBA_PVRTC_4BPPV1_Format, RGB_PVRTC_2BPPV1_Format, RGB_PVRTC_4BPPV1_Format, RGBA_S3TC_DXT5_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT1_Format, RGB_S3TC_DXT1_Format, DepthFormat, DepthStencilFormat, LuminanceAlphaFormat, LuminanceFormat, RedFormat, RGBAFormat, AlphaFormat, RedIntegerFormat, RGFormat, RGIntegerFormat, RGBAIntegerFormat, HalfFloatType, FloatType, UnsignedIntType, IntType, UnsignedShortType, ShortType, ByteType, UnsignedInt248Type, UnsignedShort5551Type, UnsignedShort4444Type, UnsignedByteType, RGBA_BPTC_Format, RGB_BPTC_SIGNED_Format, RGB_BPTC_UNSIGNED_Format, _SRGBAFormat, RED_RGTC1_Format, SIGNED_RED_RGTC1_Format, RED_GREEN_RGTC2_Format, SIGNED_RED_GREEN_RGTC2_Format, NoColorSpace, SRGBTransfer } from '../../constants.js';\nimport { ColorManagement } from '../../math/ColorManagement.js';\n\nfunction WebGLUtils( gl, extensions, capabilities ) {\n\n\tconst isWebGL2 = capabilities.isWebGL2;\n\n\tfunction convert( p, colorSpace = NoColorSpace ) {\n\n\t\tlet extension;\n\n\t\tconst transfer = ColorManagement.getTransfer( colorSpace );\n\n\t\tif ( p === UnsignedByteType ) return gl.UNSIGNED_BYTE;\n\t\tif ( p === UnsignedShort4444Type ) return gl.UNSIGNED_SHORT_4_4_4_4;\n\t\tif ( p === UnsignedShort5551Type ) return gl.UNSIGNED_SHORT_5_5_5_1;\n\n\t\tif ( p === ByteType ) return gl.BYTE;\n\t\tif ( p === ShortType ) return gl.SHORT;\n\t\tif ( p === UnsignedShortType ) return gl.UNSIGNED_SHORT;\n\t\tif ( p === IntType ) return gl.INT;\n\t\tif ( p === UnsignedIntType ) return gl.UNSIGNED_INT;\n\t\tif ( p === FloatType ) return gl.FLOAT;\n\n\t\tif ( p === HalfFloatType ) {\n\n\t\t\tif ( isWebGL2 ) return gl.HALF_FLOAT;\n\n\t\t\textension = extensions.get( 'OES_texture_half_float' );\n\n\t\t\tif ( extension !== null ) {\n\n\t\t\t\treturn extension.HALF_FLOAT_OES;\n\n\t\t\t} else {\n\n\t\t\t\treturn null;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( p === AlphaFormat ) return gl.ALPHA;\n\t\tif ( p === RGBAFormat ) return gl.RGBA;\n\t\tif ( p === LuminanceFormat ) return gl.LUMINANCE;\n\t\tif ( p === LuminanceAlphaFormat ) return gl.LUMINANCE_ALPHA;\n\t\tif ( p === DepthFormat ) return gl.DEPTH_COMPONENT;\n\t\tif ( p === DepthStencilFormat ) return gl.DEPTH_STENCIL;\n\n\t\t// WebGL 1 sRGB fallback\n\n\t\tif ( p === _SRGBAFormat ) {\n\n\t\t\textension = extensions.get( 'EXT_sRGB' );\n\n\t\t\tif ( extension !== null ) {\n\n\t\t\t\treturn extension.SRGB_ALPHA_EXT;\n\n\t\t\t} else {\n\n\t\t\t\treturn null;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// WebGL2 formats.\n\n\t\tif ( p === RedFormat ) return gl.RED;\n\t\tif ( p === RedIntegerFormat ) return gl.RED_INTEGER;\n\t\tif ( p === RGFormat ) return gl.RG;\n\t\tif ( p === RGIntegerFormat ) return gl.RG_INTEGER;\n\t\tif ( p === RGBAIntegerFormat ) return gl.RGBA_INTEGER;\n\n\t\t// S3TC\n\n\t\tif ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) {\n\n\t\t\tif ( transfer === SRGBTransfer ) {\n\n\t\t\t\textension = extensions.get( 'WEBGL_compressed_texture_s3tc_srgb' );\n\n\t\t\t\tif ( extension !== null ) {\n\n\t\t\t\t\tif ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_SRGB_S3TC_DXT1_EXT;\n\t\t\t\t\tif ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;\n\t\t\t\t\tif ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;\n\t\t\t\t\tif ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;\n\n\t\t\t\t} else {\n\n\t\t\t\t\treturn null;\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\textension = extensions.get( 'WEBGL_compressed_texture_s3tc' );\n\n\t\t\t\tif ( extension !== null ) {\n\n\t\t\t\t\tif ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT;\n\t\t\t\t\tif ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT;\n\t\t\t\t\tif ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT;\n\t\t\t\t\tif ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT;\n\n\t\t\t\t} else {\n\n\t\t\t\t\treturn null;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\t// PVRTC\n\n\t\tif ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) {\n\n\t\t\textension = extensions.get( 'WEBGL_compressed_texture_pvrtc' );\n\n\t\t\tif ( extension !== null ) {\n\n\t\t\t\tif ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;\n\t\t\t\tif ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;\n\t\t\t\tif ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;\n\t\t\t\tif ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;\n\n\t\t\t} else {\n\n\t\t\t\treturn null;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// ETC1\n\n\t\tif ( p === RGB_ETC1_Format ) {\n\n\t\t\textension = extensions.get( 'WEBGL_compressed_texture_etc1' );\n\n\t\t\tif ( extension !== null ) {\n\n\t\t\t\treturn extension.COMPRESSED_RGB_ETC1_WEBGL;\n\n\t\t\t} else {\n\n\t\t\t\treturn null;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// ETC2\n\n\t\tif ( p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format ) {\n\n\t\t\textension = extensions.get( 'WEBGL_compressed_texture_etc' );\n\n\t\t\tif ( extension !== null ) {\n\n\t\t\t\tif ( p === RGB_ETC2_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ETC2 : extension.COMPRESSED_RGB8_ETC2;\n\t\t\t\tif ( p === RGBA_ETC2_EAC_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : extension.COMPRESSED_RGBA8_ETC2_EAC;\n\n\t\t\t} else {\n\n\t\t\t\treturn null;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// ASTC\n\n\t\tif ( p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format ||\n\t\t\tp === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format ||\n\t\t\tp === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format ||\n\t\t\tp === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format ||\n\t\t\tp === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format ) {\n\n\t\t\textension = extensions.get( 'WEBGL_compressed_texture_astc' );\n\n\t\t\tif ( extension !== null ) {\n\n\t\t\t\tif ( p === RGBA_ASTC_4x4_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : extension.COMPRESSED_RGBA_ASTC_4x4_KHR;\n\t\t\t\tif ( p === RGBA_ASTC_5x4_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : extension.COMPRESSED_RGBA_ASTC_5x4_KHR;\n\t\t\t\tif ( p === RGBA_ASTC_5x5_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : extension.COMPRESSED_RGBA_ASTC_5x5_KHR;\n\t\t\t\tif ( p === RGBA_ASTC_6x5_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : extension.COMPRESSED_RGBA_ASTC_6x5_KHR;\n\t\t\t\tif ( p === RGBA_ASTC_6x6_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : extension.COMPRESSED_RGBA_ASTC_6x6_KHR;\n\t\t\t\tif ( p === RGBA_ASTC_8x5_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : extension.COMPRESSED_RGBA_ASTC_8x5_KHR;\n\t\t\t\tif ( p === RGBA_ASTC_8x6_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : extension.COMPRESSED_RGBA_ASTC_8x6_KHR;\n\t\t\t\tif ( p === RGBA_ASTC_8x8_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : extension.COMPRESSED_RGBA_ASTC_8x8_KHR;\n\t\t\t\tif ( p === RGBA_ASTC_10x5_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : extension.COMPRESSED_RGBA_ASTC_10x5_KHR;\n\t\t\t\tif ( p === RGBA_ASTC_10x6_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : extension.COMPRESSED_RGBA_ASTC_10x6_KHR;\n\t\t\t\tif ( p === RGBA_ASTC_10x8_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : extension.COMPRESSED_RGBA_ASTC_10x8_KHR;\n\t\t\t\tif ( p === RGBA_ASTC_10x10_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : extension.COMPRESSED_RGBA_ASTC_10x10_KHR;\n\t\t\t\tif ( p === RGBA_ASTC_12x10_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : extension.COMPRESSED_RGBA_ASTC_12x10_KHR;\n\t\t\t\tif ( p === RGBA_ASTC_12x12_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : extension.COMPRESSED_RGBA_ASTC_12x12_KHR;\n\n\t\t\t} else {\n\n\t\t\t\treturn null;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// BPTC\n\n\t\tif ( p === RGBA_BPTC_Format || p === RGB_BPTC_SIGNED_Format || p === RGB_BPTC_UNSIGNED_Format ) {\n\n\t\t\textension = extensions.get( 'EXT_texture_compression_bptc' );\n\n\t\t\tif ( extension !== null ) {\n\n\t\t\t\tif ( p === RGBA_BPTC_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT : extension.COMPRESSED_RGBA_BPTC_UNORM_EXT;\n\t\t\t\tif ( p === RGB_BPTC_SIGNED_Format ) return extension.COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT;\n\t\t\t\tif ( p === RGB_BPTC_UNSIGNED_Format ) return extension.COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT;\n\n\t\t\t} else {\n\n\t\t\t\treturn null;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// RGTC\n\n\t\tif ( p === RED_RGTC1_Format || p === SIGNED_RED_RGTC1_Format || p === RED_GREEN_RGTC2_Format || p === SIGNED_RED_GREEN_RGTC2_Format ) {\n\n\t\t\textension = extensions.get( 'EXT_texture_compression_rgtc' );\n\n\t\t\tif ( extension !== null ) {\n\n\t\t\t\tif ( p === RGBA_BPTC_Format ) return extension.COMPRESSED_RED_RGTC1_EXT;\n\t\t\t\tif ( p === SIGNED_RED_RGTC1_Format ) return extension.COMPRESSED_SIGNED_RED_RGTC1_EXT;\n\t\t\t\tif ( p === RED_GREEN_RGTC2_Format ) return extension.COMPRESSED_RED_GREEN_RGTC2_EXT;\n\t\t\t\tif ( p === SIGNED_RED_GREEN_RGTC2_Format ) return extension.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT;\n\n\t\t\t} else {\n\n\t\t\t\treturn null;\n\n\t\t\t}\n\n\t\t}\n\n\t\t//\n\n\t\tif ( p === UnsignedInt248Type ) {\n\n\t\t\tif ( isWebGL2 ) return gl.UNSIGNED_INT_24_8;\n\n\t\t\textension = extensions.get( 'WEBGL_depth_texture' );\n\n\t\t\tif ( extension !== null ) {\n\n\t\t\t\treturn extension.UNSIGNED_INT_24_8_WEBGL;\n\n\t\t\t} else {\n\n\t\t\t\treturn null;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// if \"p\" can't be resolved, assume the user defines a WebGL constant as a string (fallback/workaround for packed RGB formats)\n\n\t\treturn ( gl[ p ] !== undefined ) ? gl[ p ] : null;\n\n\t}\n\n\treturn { convert: convert };\n\n}\n\n\nexport { WebGLUtils };\n","import { PerspectiveCamera } from './PerspectiveCamera.js';\n\nclass ArrayCamera extends PerspectiveCamera {\n\n\tconstructor( array = [] ) {\n\n\t\tsuper();\n\n\t\tthis.isArrayCamera = true;\n\n\t\tthis.cameras = array;\n\n\t}\n\n}\n\nexport { ArrayCamera };\n","import { Object3D } from '../core/Object3D.js';\n\nclass Group extends Object3D {\n\n\tconstructor() {\n\n\t\tsuper();\n\n\t\tthis.isGroup = true;\n\n\t\tthis.type = 'Group';\n\n\t}\n\n}\n\nexport { Group };\n","import { Vector3 } from '../../math/Vector3.js';\nimport { Group } from '../../objects/Group.js';\n\nconst _moveEvent = { type: 'move' };\n\nclass WebXRController {\n\n\tconstructor() {\n\n\t\tthis._targetRay = null;\n\t\tthis._grip = null;\n\t\tthis._hand = null;\n\n\t}\n\n\tgetHandSpace() {\n\n\t\tif ( this._hand === null ) {\n\n\t\t\tthis._hand = new Group();\n\t\t\tthis._hand.matrixAutoUpdate = false;\n\t\t\tthis._hand.visible = false;\n\n\t\t\tthis._hand.joints = {};\n\t\t\tthis._hand.inputState = { pinching: false };\n\n\t\t}\n\n\t\treturn this._hand;\n\n\t}\n\n\tgetTargetRaySpace() {\n\n\t\tif ( this._targetRay === null ) {\n\n\t\t\tthis._targetRay = new Group();\n\t\t\tthis._targetRay.matrixAutoUpdate = false;\n\t\t\tthis._targetRay.visible = false;\n\t\t\tthis._targetRay.hasLinearVelocity = false;\n\t\t\tthis._targetRay.linearVelocity = new Vector3();\n\t\t\tthis._targetRay.hasAngularVelocity = false;\n\t\t\tthis._targetRay.angularVelocity = new Vector3();\n\n\t\t}\n\n\t\treturn this._targetRay;\n\n\t}\n\n\tgetGripSpace() {\n\n\t\tif ( this._grip === null ) {\n\n\t\t\tthis._grip = new Group();\n\t\t\tthis._grip.matrixAutoUpdate = false;\n\t\t\tthis._grip.visible = false;\n\t\t\tthis._grip.hasLinearVelocity = false;\n\t\t\tthis._grip.linearVelocity = new Vector3();\n\t\t\tthis._grip.hasAngularVelocity = false;\n\t\t\tthis._grip.angularVelocity = new Vector3();\n\n\t\t}\n\n\t\treturn this._grip;\n\n\t}\n\n\tdispatchEvent( event ) {\n\n\t\tif ( this._targetRay !== null ) {\n\n\t\t\tthis._targetRay.dispatchEvent( event );\n\n\t\t}\n\n\t\tif ( this._grip !== null ) {\n\n\t\t\tthis._grip.dispatchEvent( event );\n\n\t\t}\n\n\t\tif ( this._hand !== null ) {\n\n\t\t\tthis._hand.dispatchEvent( event );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tconnect( inputSource ) {\n\n\t\tif ( inputSource && inputSource.hand ) {\n\n\t\t\tconst hand = this._hand;\n\n\t\t\tif ( hand ) {\n\n\t\t\t\tfor ( const inputjoint of inputSource.hand.values() ) {\n\n\t\t\t\t\t// Initialize hand with joints when connected\n\t\t\t\t\tthis._getHandJoint( hand, inputjoint );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tthis.dispatchEvent( { type: 'connected', data: inputSource } );\n\n\t\treturn this;\n\n\t}\n\n\tdisconnect( inputSource ) {\n\n\t\tthis.dispatchEvent( { type: 'disconnected', data: inputSource } );\n\n\t\tif ( this._targetRay !== null ) {\n\n\t\t\tthis._targetRay.visible = false;\n\n\t\t}\n\n\t\tif ( this._grip !== null ) {\n\n\t\t\tthis._grip.visible = false;\n\n\t\t}\n\n\t\tif ( this._hand !== null ) {\n\n\t\t\tthis._hand.visible = false;\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\tupdate( inputSource, frame, referenceSpace ) {\n\n\t\tlet inputPose = null;\n\t\tlet gripPose = null;\n\t\tlet handPose = null;\n\n\t\tconst targetRay = this._targetRay;\n\t\tconst grip = this._grip;\n\t\tconst hand = this._hand;\n\n\t\tif ( inputSource && frame.session.visibilityState !== 'visible-blurred' ) {\n\n\t\t\tif ( hand && inputSource.hand ) {\n\n\t\t\t\thandPose = true;\n\n\t\t\t\tfor ( const inputjoint of inputSource.hand.values() ) {\n\n\t\t\t\t\t// Update the joints groups with the XRJoint poses\n\t\t\t\t\tconst jointPose = frame.getJointPose( inputjoint, referenceSpace );\n\n\t\t\t\t\t// The transform of this joint will be updated with the joint pose on each frame\n\t\t\t\t\tconst joint = this._getHandJoint( hand, inputjoint );\n\n\t\t\t\t\tif ( jointPose !== null ) {\n\n\t\t\t\t\t\tjoint.matrix.fromArray( jointPose.transform.matrix );\n\t\t\t\t\t\tjoint.matrix.decompose( joint.position, joint.rotation, joint.scale );\n\t\t\t\t\t\tjoint.matrixWorldNeedsUpdate = true;\n\t\t\t\t\t\tjoint.jointRadius = jointPose.radius;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tjoint.visible = jointPose !== null;\n\n\t\t\t\t}\n\n\t\t\t\t// Custom events\n\n\t\t\t\t// Check pinchz\n\t\t\t\tconst indexTip = hand.joints[ 'index-finger-tip' ];\n\t\t\t\tconst thumbTip = hand.joints[ 'thumb-tip' ];\n\t\t\t\tconst distance = indexTip.position.distanceTo( thumbTip.position );\n\n\t\t\t\tconst distanceToPinch = 0.02;\n\t\t\t\tconst threshold = 0.005;\n\n\t\t\t\tif ( hand.inputState.pinching && distance > distanceToPinch + threshold ) {\n\n\t\t\t\t\thand.inputState.pinching = false;\n\t\t\t\t\tthis.dispatchEvent( {\n\t\t\t\t\t\ttype: 'pinchend',\n\t\t\t\t\t\thandedness: inputSource.handedness,\n\t\t\t\t\t\ttarget: this\n\t\t\t\t\t} );\n\n\t\t\t\t} else if ( ! hand.inputState.pinching && distance <= distanceToPinch - threshold ) {\n\n\t\t\t\t\thand.inputState.pinching = true;\n\t\t\t\t\tthis.dispatchEvent( {\n\t\t\t\t\t\ttype: 'pinchstart',\n\t\t\t\t\t\thandedness: inputSource.handedness,\n\t\t\t\t\t\ttarget: this\n\t\t\t\t\t} );\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tif ( grip !== null && inputSource.gripSpace ) {\n\n\t\t\t\t\tgripPose = frame.getPose( inputSource.gripSpace, referenceSpace );\n\n\t\t\t\t\tif ( gripPose !== null ) {\n\n\t\t\t\t\t\tgrip.matrix.fromArray( gripPose.transform.matrix );\n\t\t\t\t\t\tgrip.matrix.decompose( grip.position, grip.rotation, grip.scale );\n\t\t\t\t\t\tgrip.matrixWorldNeedsUpdate = true;\n\n\t\t\t\t\t\tif ( gripPose.linearVelocity ) {\n\n\t\t\t\t\t\t\tgrip.hasLinearVelocity = true;\n\t\t\t\t\t\t\tgrip.linearVelocity.copy( gripPose.linearVelocity );\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tgrip.hasLinearVelocity = false;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ( gripPose.angularVelocity ) {\n\n\t\t\t\t\t\t\tgrip.hasAngularVelocity = true;\n\t\t\t\t\t\t\tgrip.angularVelocity.copy( gripPose.angularVelocity );\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tgrip.hasAngularVelocity = false;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( targetRay !== null ) {\n\n\t\t\t\tinputPose = frame.getPose( inputSource.targetRaySpace, referenceSpace );\n\n\t\t\t\t// Some runtimes (namely Vive Cosmos with Vive OpenXR Runtime) have only grip space and ray space is equal to it\n\t\t\t\tif ( inputPose === null && gripPose !== null ) {\n\n\t\t\t\t\tinputPose = gripPose;\n\n\t\t\t\t}\n\n\t\t\t\tif ( inputPose !== null ) {\n\n\t\t\t\t\ttargetRay.matrix.fromArray( inputPose.transform.matrix );\n\t\t\t\t\ttargetRay.matrix.decompose( targetRay.position, targetRay.rotation, targetRay.scale );\n\t\t\t\t\ttargetRay.matrixWorldNeedsUpdate = true;\n\n\t\t\t\t\tif ( inputPose.linearVelocity ) {\n\n\t\t\t\t\t\ttargetRay.hasLinearVelocity = true;\n\t\t\t\t\t\ttargetRay.linearVelocity.copy( inputPose.linearVelocity );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\ttargetRay.hasLinearVelocity = false;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( inputPose.angularVelocity ) {\n\n\t\t\t\t\t\ttargetRay.hasAngularVelocity = true;\n\t\t\t\t\t\ttargetRay.angularVelocity.copy( inputPose.angularVelocity );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\ttargetRay.hasAngularVelocity = false;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.dispatchEvent( _moveEvent );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\n\t\t}\n\n\t\tif ( targetRay !== null ) {\n\n\t\t\ttargetRay.visible = ( inputPose !== null );\n\n\t\t}\n\n\t\tif ( grip !== null ) {\n\n\t\t\tgrip.visible = ( gripPose !== null );\n\n\t\t}\n\n\t\tif ( hand !== null ) {\n\n\t\t\thand.visible = ( handPose !== null );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\t// private method\n\n\t_getHandJoint( hand, inputjoint ) {\n\n\t\tif ( hand.joints[ inputjoint.jointName ] === undefined ) {\n\n\t\t\tconst joint = new Group();\n\t\t\tjoint.matrixAutoUpdate = false;\n\t\t\tjoint.visible = false;\n\t\t\thand.joints[ inputjoint.jointName ] = joint;\n\n\t\t\thand.add( joint );\n\n\t\t}\n\n\t\treturn hand.joints[ inputjoint.jointName ];\n\n\t}\n\n}\n\n\nexport { WebXRController };\n","import { Texture } from './Texture.js';\nimport { NearestFilter, UnsignedIntType, UnsignedInt248Type, DepthFormat, DepthStencilFormat } from '../constants.js';\n\nclass DepthTexture extends Texture {\n\n\tconstructor( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) {\n\n\t\tformat = format !== undefined ? format : DepthFormat;\n\n\t\tif ( format !== DepthFormat && format !== DepthStencilFormat ) {\n\n\t\t\tthrow new Error( 'DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat' );\n\n\t\t}\n\n\t\tif ( type === undefined && format === DepthFormat ) type = UnsignedIntType;\n\t\tif ( type === undefined && format === DepthStencilFormat ) type = UnsignedInt248Type;\n\n\t\tsuper( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );\n\n\t\tthis.isDepthTexture = true;\n\n\t\tthis.image = { width: width, height: height };\n\n\t\tthis.magFilter = magFilter !== undefined ? magFilter : NearestFilter;\n\t\tthis.minFilter = minFilter !== undefined ? minFilter : NearestFilter;\n\n\t\tthis.flipY = false;\n\t\tthis.generateMipmaps = false;\n\n\t\tthis.compareFunction = null;\n\n\t}\n\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.compareFunction = source.compareFunction;\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON( meta ) {\n\n\t\tconst data = super.toJSON( meta );\n\n\t\tif ( this.compareFunction !== null ) data.compareFunction = this.compareFunction;\n\n\t\treturn data;\n\n\t}\n\n}\n\nexport { DepthTexture };\n","import { ArrayCamera } from '../../cameras/ArrayCamera.js';\nimport { EventDispatcher } from '../../core/EventDispatcher.js';\nimport { PerspectiveCamera } from '../../cameras/PerspectiveCamera.js';\nimport { Vector3 } from '../../math/Vector3.js';\nimport { Vector4 } from '../../math/Vector4.js';\nimport { RAD2DEG } from '../../math/MathUtils.js';\nimport { WebGLAnimation } from '../webgl/WebGLAnimation.js';\nimport { WebGLRenderTarget } from '../WebGLRenderTarget.js';\nimport { WebXRController } from './WebXRController.js';\nimport { DepthTexture } from '../../textures/DepthTexture.js';\nimport { DepthFormat, DepthStencilFormat, RGBAFormat, UnsignedByteType, UnsignedIntType, UnsignedInt248Type } from '../../constants.js';\n\nclass WebXRManager extends EventDispatcher {\n\n\tconstructor( renderer, gl ) {\n\n\t\tsuper();\n\n\t\tconst scope = this;\n\n\t\tlet session = null;\n\n\t\tlet framebufferScaleFactor = 1.0;\n\n\t\tlet referenceSpace = null;\n\t\tlet referenceSpaceType = 'local-floor';\n\t\t// Set default foveation to maximum.\n\t\tlet foveation = 1.0;\n\t\tlet customReferenceSpace = null;\n\n\t\tlet pose = null;\n\t\tlet glBinding = null;\n\t\tlet glProjLayer = null;\n\t\tlet glBaseLayer = null;\n\t\tlet xrFrame = null;\n\t\tconst attributes = gl.getContextAttributes();\n\t\tlet initialRenderTarget = null;\n\t\tlet newRenderTarget = null;\n\n\t\tconst controllers = [];\n\t\tconst controllerInputSources = [];\n\n\t\t//\n\n\t\tconst cameraL = new PerspectiveCamera();\n\t\tcameraL.layers.enable( 1 );\n\t\tcameraL.viewport = new Vector4();\n\n\t\tconst cameraR = new PerspectiveCamera();\n\t\tcameraR.layers.enable( 2 );\n\t\tcameraR.viewport = new Vector4();\n\n\t\tconst cameras = [ cameraL, cameraR ];\n\n\t\tconst cameraXR = new ArrayCamera();\n\t\tcameraXR.layers.enable( 1 );\n\t\tcameraXR.layers.enable( 2 );\n\n\t\tlet _currentDepthNear = null;\n\t\tlet _currentDepthFar = null;\n\n\t\t//\n\n\t\tthis.cameraAutoUpdate = true;\n\t\tthis.enabled = false;\n\n\t\tthis.isPresenting = false;\n\n\t\tthis.getController = function ( index ) {\n\n\t\t\tlet controller = controllers[ index ];\n\n\t\t\tif ( controller === undefined ) {\n\n\t\t\t\tcontroller = new WebXRController();\n\t\t\t\tcontrollers[ index ] = controller;\n\n\t\t\t}\n\n\t\t\treturn controller.getTargetRaySpace();\n\n\t\t};\n\n\t\tthis.getControllerGrip = function ( index ) {\n\n\t\t\tlet controller = controllers[ index ];\n\n\t\t\tif ( controller === undefined ) {\n\n\t\t\t\tcontroller = new WebXRController();\n\t\t\t\tcontrollers[ index ] = controller;\n\n\t\t\t}\n\n\t\t\treturn controller.getGripSpace();\n\n\t\t};\n\n\t\tthis.getHand = function ( index ) {\n\n\t\t\tlet controller = controllers[ index ];\n\n\t\t\tif ( controller === undefined ) {\n\n\t\t\t\tcontroller = new WebXRController();\n\t\t\t\tcontrollers[ index ] = controller;\n\n\t\t\t}\n\n\t\t\treturn controller.getHandSpace();\n\n\t\t};\n\n\t\t//\n\n\t\tfunction onSessionEvent( event ) {\n\n\t\t\tconst controllerIndex = controllerInputSources.indexOf( event.inputSource );\n\n\t\t\tif ( controllerIndex === - 1 ) {\n\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\tconst controller = controllers[ controllerIndex ];\n\n\t\t\tif ( controller !== undefined ) {\n\n\t\t\t\tcontroller.update( event.inputSource, event.frame, customReferenceSpace || referenceSpace );\n\t\t\t\tcontroller.dispatchEvent( { type: event.type, data: event.inputSource } );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction onSessionEnd() {\n\n\t\t\tsession.removeEventListener( 'select', onSessionEvent );\n\t\t\tsession.removeEventListener( 'selectstart', onSessionEvent );\n\t\t\tsession.removeEventListener( 'selectend', onSessionEvent );\n\t\t\tsession.removeEventListener( 'squeeze', onSessionEvent );\n\t\t\tsession.removeEventListener( 'squeezestart', onSessionEvent );\n\t\t\tsession.removeEventListener( 'squeezeend', onSessionEvent );\n\t\t\tsession.removeEventListener( 'end', onSessionEnd );\n\t\t\tsession.removeEventListener( 'inputsourceschange', onInputSourcesChange );\n\n\t\t\tfor ( let i = 0; i < controllers.length; i ++ ) {\n\n\t\t\t\tconst inputSource = controllerInputSources[ i ];\n\n\t\t\t\tif ( inputSource === null ) continue;\n\n\t\t\t\tcontrollerInputSources[ i ] = null;\n\n\t\t\t\tcontrollers[ i ].disconnect( inputSource );\n\n\t\t\t}\n\n\t\t\t_currentDepthNear = null;\n\t\t\t_currentDepthFar = null;\n\n\t\t\t// restore framebuffer/rendering state\n\n\t\t\trenderer.setRenderTarget( initialRenderTarget );\n\n\t\t\tglBaseLayer = null;\n\t\t\tglProjLayer = null;\n\t\t\tglBinding = null;\n\t\t\tsession = null;\n\t\t\tnewRenderTarget = null;\n\n\t\t\t//\n\n\t\t\tanimation.stop();\n\n\t\t\tscope.isPresenting = false;\n\n\t\t\tscope.dispatchEvent( { type: 'sessionend' } );\n\n\t\t}\n\n\t\tthis.setFramebufferScaleFactor = function ( value ) {\n\n\t\t\tframebufferScaleFactor = value;\n\n\t\t\tif ( scope.isPresenting === true ) {\n\n\t\t\t\tconsole.warn( 'THREE.WebXRManager: Cannot change framebuffer scale while presenting.' );\n\n\t\t\t}\n\n\t\t};\n\n\t\tthis.setReferenceSpaceType = function ( value ) {\n\n\t\t\treferenceSpaceType = value;\n\n\t\t\tif ( scope.isPresenting === true ) {\n\n\t\t\t\tconsole.warn( 'THREE.WebXRManager: Cannot change reference space type while presenting.' );\n\n\t\t\t}\n\n\t\t};\n\n\t\tthis.getReferenceSpace = function () {\n\n\t\t\treturn customReferenceSpace || referenceSpace;\n\n\t\t};\n\n\t\tthis.setReferenceSpace = function ( space ) {\n\n\t\t\tcustomReferenceSpace = space;\n\n\t\t};\n\n\t\tthis.getBaseLayer = function () {\n\n\t\t\treturn glProjLayer !== null ? glProjLayer : glBaseLayer;\n\n\t\t};\n\n\t\tthis.getBinding = function () {\n\n\t\t\treturn glBinding;\n\n\t\t};\n\n\t\tthis.getFrame = function () {\n\n\t\t\treturn xrFrame;\n\n\t\t};\n\n\t\tthis.getSession = function () {\n\n\t\t\treturn session;\n\n\t\t};\n\n\t\tthis.setSession = async function ( value ) {\n\n\t\t\tsession = value;\n\n\t\t\tif ( session !== null ) {\n\n\t\t\t\tinitialRenderTarget = renderer.getRenderTarget();\n\n\t\t\t\tsession.addEventListener( 'select', onSessionEvent );\n\t\t\t\tsession.addEventListener( 'selectstart', onSessionEvent );\n\t\t\t\tsession.addEventListener( 'selectend', onSessionEvent );\n\t\t\t\tsession.addEventListener( 'squeeze', onSessionEvent );\n\t\t\t\tsession.addEventListener( 'squeezestart', onSessionEvent );\n\t\t\t\tsession.addEventListener( 'squeezeend', onSessionEvent );\n\t\t\t\tsession.addEventListener( 'end', onSessionEnd );\n\t\t\t\tsession.addEventListener( 'inputsourceschange', onInputSourcesChange );\n\n\t\t\t\tif ( attributes.xrCompatible !== true ) {\n\n\t\t\t\t\tawait gl.makeXRCompatible();\n\n\t\t\t\t}\n\n\t\t\t\tif ( ( session.renderState.layers === undefined ) || ( renderer.capabilities.isWebGL2 === false ) ) {\n\n\t\t\t\t\tconst layerInit = {\n\t\t\t\t\t\tantialias: ( session.renderState.layers === undefined ) ? attributes.antialias : true,\n\t\t\t\t\t\talpha: true,\n\t\t\t\t\t\tdepth: attributes.depth,\n\t\t\t\t\t\tstencil: attributes.stencil,\n\t\t\t\t\t\tframebufferScaleFactor: framebufferScaleFactor\n\t\t\t\t\t};\n\n\t\t\t\t\tglBaseLayer = new XRWebGLLayer( session, gl, layerInit );\n\n\t\t\t\t\tsession.updateRenderState( { baseLayer: glBaseLayer } );\n\n\t\t\t\t\tnewRenderTarget = new WebGLRenderTarget(\n\t\t\t\t\t\tglBaseLayer.framebufferWidth,\n\t\t\t\t\t\tglBaseLayer.framebufferHeight,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tformat: RGBAFormat,\n\t\t\t\t\t\t\ttype: UnsignedByteType,\n\t\t\t\t\t\t\tcolorSpace: renderer.outputColorSpace,\n\t\t\t\t\t\t\tstencilBuffer: attributes.stencil\n\t\t\t\t\t\t}\n\t\t\t\t\t);\n\n\t\t\t\t} else {\n\n\t\t\t\t\tlet depthFormat = null;\n\t\t\t\t\tlet depthType = null;\n\t\t\t\t\tlet glDepthFormat = null;\n\n\t\t\t\t\tif ( attributes.depth ) {\n\n\t\t\t\t\t\tglDepthFormat = attributes.stencil ? gl.DEPTH24_STENCIL8 : gl.DEPTH_COMPONENT24;\n\t\t\t\t\t\tdepthFormat = attributes.stencil ? DepthStencilFormat : DepthFormat;\n\t\t\t\t\t\tdepthType = attributes.stencil ? UnsignedInt248Type : UnsignedIntType;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tconst projectionlayerInit = {\n\t\t\t\t\t\tcolorFormat: gl.RGBA8,\n\t\t\t\t\t\tdepthFormat: glDepthFormat,\n\t\t\t\t\t\tscaleFactor: framebufferScaleFactor\n\t\t\t\t\t};\n\n\t\t\t\t\tglBinding = new XRWebGLBinding( session, gl );\n\n\t\t\t\t\tglProjLayer = glBinding.createProjectionLayer( projectionlayerInit );\n\n\t\t\t\t\tsession.updateRenderState( { layers: [ glProjLayer ] } );\n\n\t\t\t\t\tnewRenderTarget = new WebGLRenderTarget(\n\t\t\t\t\t\tglProjLayer.textureWidth,\n\t\t\t\t\t\tglProjLayer.textureHeight,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tformat: RGBAFormat,\n\t\t\t\t\t\t\ttype: UnsignedByteType,\n\t\t\t\t\t\t\tdepthTexture: new DepthTexture( glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat ),\n\t\t\t\t\t\t\tstencilBuffer: attributes.stencil,\n\t\t\t\t\t\t\tcolorSpace: renderer.outputColorSpace,\n\t\t\t\t\t\t\tsamples: attributes.antialias ? 4 : 0\n\t\t\t\t\t\t} );\n\n\t\t\t\t\tconst renderTargetProperties = renderer.properties.get( newRenderTarget );\n\t\t\t\t\trenderTargetProperties.__ignoreDepthValues = glProjLayer.ignoreDepthValues;\n\n\t\t\t\t}\n\n\t\t\t\tnewRenderTarget.isXRRenderTarget = true; // TODO Remove this when possible, see #23278\n\n\t\t\t\tthis.setFoveation( foveation );\n\n\t\t\t\tcustomReferenceSpace = null;\n\t\t\t\treferenceSpace = await session.requestReferenceSpace( referenceSpaceType );\n\n\t\t\t\tanimation.setContext( session );\n\t\t\t\tanimation.start();\n\n\t\t\t\tscope.isPresenting = true;\n\n\t\t\t\tscope.dispatchEvent( { type: 'sessionstart' } );\n\n\t\t\t}\n\n\t\t};\n\n\t\tthis.getEnvironmentBlendMode = function () {\n\n\t\t\tif ( session !== null ) {\n\n\t\t\t\treturn session.environmentBlendMode;\n\n\t\t\t}\n\n\t\t};\n\n\t\tfunction onInputSourcesChange( event ) {\n\n\t\t\t// Notify disconnected\n\n\t\t\tfor ( let i = 0; i < event.removed.length; i ++ ) {\n\n\t\t\t\tconst inputSource = event.removed[ i ];\n\t\t\t\tconst index = controllerInputSources.indexOf( inputSource );\n\n\t\t\t\tif ( index >= 0 ) {\n\n\t\t\t\t\tcontrollerInputSources[ index ] = null;\n\t\t\t\t\tcontrollers[ index ].disconnect( inputSource );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// Notify connected\n\n\t\t\tfor ( let i = 0; i < event.added.length; i ++ ) {\n\n\t\t\t\tconst inputSource = event.added[ i ];\n\n\t\t\t\tlet controllerIndex = controllerInputSources.indexOf( inputSource );\n\n\t\t\t\tif ( controllerIndex === - 1 ) {\n\n\t\t\t\t\t// Assign input source a controller that currently has no input source\n\n\t\t\t\t\tfor ( let i = 0; i < controllers.length; i ++ ) {\n\n\t\t\t\t\t\tif ( i >= controllerInputSources.length ) {\n\n\t\t\t\t\t\t\tcontrollerInputSources.push( inputSource );\n\t\t\t\t\t\t\tcontrollerIndex = i;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t} else if ( controllerInputSources[ i ] === null ) {\n\n\t\t\t\t\t\t\tcontrollerInputSources[ i ] = inputSource;\n\t\t\t\t\t\t\tcontrollerIndex = i;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t\t// If all controllers do currently receive input we ignore new ones\n\n\t\t\t\t\tif ( controllerIndex === - 1 ) break;\n\n\t\t\t\t}\n\n\t\t\t\tconst controller = controllers[ controllerIndex ];\n\n\t\t\t\tif ( controller ) {\n\n\t\t\t\t\tcontroller.connect( inputSource );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\t//\n\n\t\tconst cameraLPos = new Vector3();\n\t\tconst cameraRPos = new Vector3();\n\n\t\t/**\n\t\t * Assumes 2 cameras that are parallel and share an X-axis, and that\n\t\t * the cameras' projection and world matrices have already been set.\n\t\t * And that near and far planes are identical for both cameras.\n\t\t * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765\n\t\t */\n\t\tfunction setProjectionFromUnion( camera, cameraL, cameraR ) {\n\n\t\t\tcameraLPos.setFromMatrixPosition( cameraL.matrixWorld );\n\t\t\tcameraRPos.setFromMatrixPosition( cameraR.matrixWorld );\n\n\t\t\tconst ipd = cameraLPos.distanceTo( cameraRPos );\n\n\t\t\tconst projL = cameraL.projectionMatrix.elements;\n\t\t\tconst projR = cameraR.projectionMatrix.elements;\n\n\t\t\t// VR systems will have identical far and near planes, and\n\t\t\t// most likely identical top and bottom frustum extents.\n\t\t\t// Use the left camera for these values.\n\t\t\tconst near = projL[ 14 ] / ( projL[ 10 ] - 1 );\n\t\t\tconst far = projL[ 14 ] / ( projL[ 10 ] + 1 );\n\t\t\tconst topFov = ( projL[ 9 ] + 1 ) / projL[ 5 ];\n\t\t\tconst bottomFov = ( projL[ 9 ] - 1 ) / projL[ 5 ];\n\n\t\t\tconst leftFov = ( projL[ 8 ] - 1 ) / projL[ 0 ];\n\t\t\tconst rightFov = ( projR[ 8 ] + 1 ) / projR[ 0 ];\n\t\t\tconst left = near * leftFov;\n\t\t\tconst right = near * rightFov;\n\n\t\t\t// Calculate the new camera's position offset from the\n\t\t\t// left camera. xOffset should be roughly half `ipd`.\n\t\t\tconst zOffset = ipd / ( - leftFov + rightFov );\n\t\t\tconst xOffset = zOffset * - leftFov;\n\n\t\t\t// TODO: Better way to apply this offset?\n\t\t\tcameraL.matrixWorld.decompose( camera.position, camera.quaternion, camera.scale );\n\t\t\tcamera.translateX( xOffset );\n\t\t\tcamera.translateZ( zOffset );\n\t\t\tcamera.matrixWorld.compose( camera.position, camera.quaternion, camera.scale );\n\t\t\tcamera.matrixWorldInverse.copy( camera.matrixWorld ).invert();\n\n\t\t\t// Find the union of the frustum values of the cameras and scale\n\t\t\t// the values so that the near plane's position does not change in world space,\n\t\t\t// although must now be relative to the new union camera.\n\t\t\tconst near2 = near + zOffset;\n\t\t\tconst far2 = far + zOffset;\n\t\t\tconst left2 = left - xOffset;\n\t\t\tconst right2 = right + ( ipd - xOffset );\n\t\t\tconst top2 = topFov * far / far2 * near2;\n\t\t\tconst bottom2 = bottomFov * far / far2 * near2;\n\n\t\t\tcamera.projectionMatrix.makePerspective( left2, right2, top2, bottom2, near2, far2 );\n\t\t\tcamera.projectionMatrixInverse.copy( camera.projectionMatrix ).invert();\n\n\t\t}\n\n\t\tfunction updateCamera( camera, parent ) {\n\n\t\t\tif ( parent === null ) {\n\n\t\t\t\tcamera.matrixWorld.copy( camera.matrix );\n\n\t\t\t} else {\n\n\t\t\t\tcamera.matrixWorld.multiplyMatrices( parent.matrixWorld, camera.matrix );\n\n\t\t\t}\n\n\t\t\tcamera.matrixWorldInverse.copy( camera.matrixWorld ).invert();\n\n\t\t}\n\n\t\tthis.updateCamera = function ( camera ) {\n\n\t\t\tif ( session === null ) return;\n\n\t\t\tcameraXR.near = cameraR.near = cameraL.near = camera.near;\n\t\t\tcameraXR.far = cameraR.far = cameraL.far = camera.far;\n\n\t\t\tif ( _currentDepthNear !== cameraXR.near || _currentDepthFar !== cameraXR.far ) {\n\n\t\t\t\t// Note that the new renderState won't apply until the next frame. See #18320\n\n\t\t\t\tsession.updateRenderState( {\n\t\t\t\t\tdepthNear: cameraXR.near,\n\t\t\t\t\tdepthFar: cameraXR.far\n\t\t\t\t} );\n\n\t\t\t\t_currentDepthNear = cameraXR.near;\n\t\t\t\t_currentDepthFar = cameraXR.far;\n\n\t\t\t}\n\n\t\t\tconst parent = camera.parent;\n\t\t\tconst cameras = cameraXR.cameras;\n\n\t\t\tupdateCamera( cameraXR, parent );\n\n\t\t\tfor ( let i = 0; i < cameras.length; i ++ ) {\n\n\t\t\t\tupdateCamera( cameras[ i ], parent );\n\n\t\t\t}\n\n\t\t\t// update projection matrix for proper view frustum culling\n\n\t\t\tif ( cameras.length === 2 ) {\n\n\t\t\t\tsetProjectionFromUnion( cameraXR, cameraL, cameraR );\n\n\t\t\t} else {\n\n\t\t\t\t// assume single camera setup (AR)\n\n\t\t\t\tcameraXR.projectionMatrix.copy( cameraL.projectionMatrix );\n\n\t\t\t}\n\n\t\t\t// update user camera and its children\n\n\t\t\tupdateUserCamera( camera, cameraXR, parent );\n\n\t\t};\n\n\t\tfunction updateUserCamera( camera, cameraXR, parent ) {\n\n\t\t\tif ( parent === null ) {\n\n\t\t\t\tcamera.matrix.copy( cameraXR.matrixWorld );\n\n\t\t\t} else {\n\n\t\t\t\tcamera.matrix.copy( parent.matrixWorld );\n\t\t\t\tcamera.matrix.invert();\n\t\t\t\tcamera.matrix.multiply( cameraXR.matrixWorld );\n\n\t\t\t}\n\n\t\t\tcamera.matrix.decompose( camera.position, camera.quaternion, camera.scale );\n\t\t\tcamera.updateMatrixWorld( true );\n\n\t\t\tcamera.projectionMatrix.copy( cameraXR.projectionMatrix );\n\t\t\tcamera.projectionMatrixInverse.copy( cameraXR.projectionMatrixInverse );\n\n\t\t\tif ( camera.isPerspectiveCamera ) {\n\n\t\t\t\tcamera.fov = RAD2DEG * 2 * Math.atan( 1 / camera.projectionMatrix.elements[ 5 ] );\n\t\t\t\tcamera.zoom = 1;\n\n\t\t\t}\n\n\t\t}\n\n\t\tthis.getCamera = function () {\n\n\t\t\treturn cameraXR;\n\n\t\t};\n\n\t\tthis.getFoveation = function () {\n\n\t\t\tif ( glProjLayer === null && glBaseLayer === null ) {\n\n\t\t\t\treturn undefined;\n\n\t\t\t}\n\n\t\t\treturn foveation;\n\n\t\t};\n\n\t\tthis.setFoveation = function ( value ) {\n\n\t\t\t// 0 = no foveation = full resolution\n\t\t\t// 1 = maximum foveation = the edges render at lower resolution\n\n\t\t\tfoveation = value;\n\n\t\t\tif ( glProjLayer !== null ) {\n\n\t\t\t\tglProjLayer.fixedFoveation = value;\n\n\t\t\t}\n\n\t\t\tif ( glBaseLayer !== null && glBaseLayer.fixedFoveation !== undefined ) {\n\n\t\t\t\tglBaseLayer.fixedFoveation = value;\n\n\t\t\t}\n\n\t\t};\n\n\t\t// Animation Loop\n\n\t\tlet onAnimationFrameCallback = null;\n\n\t\tfunction onAnimationFrame( time, frame ) {\n\n\t\t\tpose = frame.getViewerPose( customReferenceSpace || referenceSpace );\n\t\t\txrFrame = frame;\n\n\t\t\tif ( pose !== null ) {\n\n\t\t\t\tconst views = pose.views;\n\n\t\t\t\tif ( glBaseLayer !== null ) {\n\n\t\t\t\t\trenderer.setRenderTargetFramebuffer( newRenderTarget, glBaseLayer.framebuffer );\n\t\t\t\t\trenderer.setRenderTarget( newRenderTarget );\n\n\t\t\t\t}\n\n\t\t\t\tlet cameraXRNeedsUpdate = false;\n\n\t\t\t\t// check if it's necessary to rebuild cameraXR's camera list\n\n\t\t\t\tif ( views.length !== cameraXR.cameras.length ) {\n\n\t\t\t\t\tcameraXR.cameras.length = 0;\n\t\t\t\t\tcameraXRNeedsUpdate = true;\n\n\t\t\t\t}\n\n\t\t\t\tfor ( let i = 0; i < views.length; i ++ ) {\n\n\t\t\t\t\tconst view = views[ i ];\n\n\t\t\t\t\tlet viewport = null;\n\n\t\t\t\t\tif ( glBaseLayer !== null ) {\n\n\t\t\t\t\t\tviewport = glBaseLayer.getViewport( view );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tconst glSubImage = glBinding.getViewSubImage( glProjLayer, view );\n\t\t\t\t\t\tviewport = glSubImage.viewport;\n\n\t\t\t\t\t\t// For side-by-side projection, we only produce a single texture for both eyes.\n\t\t\t\t\t\tif ( i === 0 ) {\n\n\t\t\t\t\t\t\trenderer.setRenderTargetTextures(\n\t\t\t\t\t\t\t\tnewRenderTarget,\n\t\t\t\t\t\t\t\tglSubImage.colorTexture,\n\t\t\t\t\t\t\t\tglProjLayer.ignoreDepthValues ? undefined : glSubImage.depthStencilTexture );\n\n\t\t\t\t\t\t\trenderer.setRenderTarget( newRenderTarget );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t\tlet camera = cameras[ i ];\n\n\t\t\t\t\tif ( camera === undefined ) {\n\n\t\t\t\t\t\tcamera = new PerspectiveCamera();\n\t\t\t\t\t\tcamera.layers.enable( i );\n\t\t\t\t\t\tcamera.viewport = new Vector4();\n\t\t\t\t\t\tcameras[ i ] = camera;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tcamera.matrix.fromArray( view.transform.matrix );\n\t\t\t\t\tcamera.matrix.decompose( camera.position, camera.quaternion, camera.scale );\n\t\t\t\t\tcamera.projectionMatrix.fromArray( view.projectionMatrix );\n\t\t\t\t\tcamera.projectionMatrixInverse.copy( camera.projectionMatrix ).invert();\n\t\t\t\t\tcamera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height );\n\n\t\t\t\t\tif ( i === 0 ) {\n\n\t\t\t\t\t\tcameraXR.matrix.copy( camera.matrix );\n\t\t\t\t\t\tcameraXR.matrix.decompose( cameraXR.position, cameraXR.quaternion, cameraXR.scale );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( cameraXRNeedsUpdate === true ) {\n\n\t\t\t\t\t\tcameraXR.cameras.push( camera );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t//\n\n\t\t\tfor ( let i = 0; i < controllers.length; i ++ ) {\n\n\t\t\t\tconst inputSource = controllerInputSources[ i ];\n\t\t\t\tconst controller = controllers[ i ];\n\n\t\t\t\tif ( inputSource !== null && controller !== undefined ) {\n\n\t\t\t\t\tcontroller.update( inputSource, frame, customReferenceSpace || referenceSpace );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( onAnimationFrameCallback ) onAnimationFrameCallback( time, frame );\n\n\t\t\tif ( frame.detectedPlanes ) {\n\n\t\t\t\tscope.dispatchEvent( { type: 'planesdetected', data: frame } );\n\n\t\t\t}\n\n\t\t\txrFrame = null;\n\n\t\t}\n\n\t\tconst animation = new WebGLAnimation();\n\n\t\tanimation.setAnimationLoop( onAnimationFrame );\n\n\t\tthis.setAnimationLoop = function ( callback ) {\n\n\t\t\tonAnimationFrameCallback = callback;\n\n\t\t};\n\n\t\tthis.dispose = function () {};\n\n\t}\n\n}\n\nexport { WebXRManager };\n","import { BackSide } from '../../constants.js';\nimport { getUnlitUniformColorSpace } from '../shaders/UniformsUtils.js';\n\nfunction WebGLMaterials( renderer, properties ) {\n\n\tfunction refreshTransformUniform( map, uniform ) {\n\n\t\tif ( map.matrixAutoUpdate === true ) {\n\n\t\t\tmap.updateMatrix();\n\n\t\t}\n\n\t\tuniform.value.copy( map.matrix );\n\n\t}\n\n\tfunction refreshFogUniforms( uniforms, fog ) {\n\n\t\tfog.color.getRGB( uniforms.fogColor.value, getUnlitUniformColorSpace( renderer ) );\n\n\t\tif ( fog.isFog ) {\n\n\t\t\tuniforms.fogNear.value = fog.near;\n\t\t\tuniforms.fogFar.value = fog.far;\n\n\t\t} else if ( fog.isFogExp2 ) {\n\n\t\t\tuniforms.fogDensity.value = fog.density;\n\n\t\t}\n\n\t}\n\n\tfunction refreshMaterialUniforms( uniforms, material, pixelRatio, height, transmissionRenderTarget ) {\n\n\t\tif ( material.isMeshBasicMaterial ) {\n\n\t\t\trefreshUniformsCommon( uniforms, material );\n\n\t\t} else if ( material.isMeshLambertMaterial ) {\n\n\t\t\trefreshUniformsCommon( uniforms, material );\n\n\t\t} else if ( material.isMeshToonMaterial ) {\n\n\t\t\trefreshUniformsCommon( uniforms, material );\n\t\t\trefreshUniformsToon( uniforms, material );\n\n\t\t} else if ( material.isMeshPhongMaterial ) {\n\n\t\t\trefreshUniformsCommon( uniforms, material );\n\t\t\trefreshUniformsPhong( uniforms, material );\n\n\t\t} else if ( material.isMeshStandardMaterial ) {\n\n\t\t\trefreshUniformsCommon( uniforms, material );\n\t\t\trefreshUniformsStandard( uniforms, material );\n\n\t\t\tif ( material.isMeshPhysicalMaterial ) {\n\n\t\t\t\trefreshUniformsPhysical( uniforms, material, transmissionRenderTarget );\n\n\t\t\t}\n\n\t\t} else if ( material.isMeshMatcapMaterial ) {\n\n\t\t\trefreshUniformsCommon( uniforms, material );\n\t\t\trefreshUniformsMatcap( uniforms, material );\n\n\t\t} else if ( material.isMeshDepthMaterial ) {\n\n\t\t\trefreshUniformsCommon( uniforms, material );\n\n\t\t} else if ( material.isMeshDistanceMaterial ) {\n\n\t\t\trefreshUniformsCommon( uniforms, material );\n\t\t\trefreshUniformsDistance( uniforms, material );\n\n\t\t} else if ( material.isMeshNormalMaterial ) {\n\n\t\t\trefreshUniformsCommon( uniforms, material );\n\n\t\t} else if ( material.isLineBasicMaterial ) {\n\n\t\t\trefreshUniformsLine( uniforms, material );\n\n\t\t\tif ( material.isLineDashedMaterial ) {\n\n\t\t\t\trefreshUniformsDash( uniforms, material );\n\n\t\t\t}\n\n\t\t} else if ( material.isPointsMaterial ) {\n\n\t\t\trefreshUniformsPoints( uniforms, material, pixelRatio, height );\n\n\t\t} else if ( material.isSpriteMaterial ) {\n\n\t\t\trefreshUniformsSprites( uniforms, material );\n\n\t\t} else if ( material.isShadowMaterial ) {\n\n\t\t\tuniforms.color.value.copy( material.color );\n\t\t\tuniforms.opacity.value = material.opacity;\n\n\t\t} else if ( material.isShaderMaterial ) {\n\n\t\t\tmaterial.uniformsNeedUpdate = false; // #15581\n\n\t\t}\n\n\t}\n\n\tfunction refreshUniformsCommon( uniforms, material ) {\n\n\t\tuniforms.opacity.value = material.opacity;\n\n\t\tif ( material.color ) {\n\n\t\t\tuniforms.diffuse.value.copy( material.color );\n\n\t\t}\n\n\t\tif ( material.emissive ) {\n\n\t\t\tuniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity );\n\n\t\t}\n\n\t\tif ( material.map ) {\n\n\t\t\tuniforms.map.value = material.map;\n\n\t\t\trefreshTransformUniform( material.map, uniforms.mapTransform );\n\n\t\t}\n\n\t\tif ( material.alphaMap ) {\n\n\t\t\tuniforms.alphaMap.value = material.alphaMap;\n\n\t\t\trefreshTransformUniform( material.alphaMap, uniforms.alphaMapTransform );\n\n\t\t}\n\n\t\tif ( material.bumpMap ) {\n\n\t\t\tuniforms.bumpMap.value = material.bumpMap;\n\n\t\t\trefreshTransformUniform( material.bumpMap, uniforms.bumpMapTransform );\n\n\t\t\tuniforms.bumpScale.value = material.bumpScale;\n\n\t\t\tif ( material.side === BackSide ) {\n\n\t\t\t\tuniforms.bumpScale.value *= - 1;\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( material.normalMap ) {\n\n\t\t\tuniforms.normalMap.value = material.normalMap;\n\n\t\t\trefreshTransformUniform( material.normalMap, uniforms.normalMapTransform );\n\n\t\t\tuniforms.normalScale.value.copy( material.normalScale );\n\n\t\t\tif ( material.side === BackSide ) {\n\n\t\t\t\tuniforms.normalScale.value.negate();\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( material.displacementMap ) {\n\n\t\t\tuniforms.displacementMap.value = material.displacementMap;\n\n\t\t\trefreshTransformUniform( material.displacementMap, uniforms.displacementMapTransform );\n\n\t\t\tuniforms.displacementScale.value = material.displacementScale;\n\t\t\tuniforms.displacementBias.value = material.displacementBias;\n\n\t\t}\n\n\t\tif ( material.emissiveMap ) {\n\n\t\t\tuniforms.emissiveMap.value = material.emissiveMap;\n\n\t\t\trefreshTransformUniform( material.emissiveMap, uniforms.emissiveMapTransform );\n\n\t\t}\n\n\t\tif ( material.specularMap ) {\n\n\t\t\tuniforms.specularMap.value = material.specularMap;\n\n\t\t\trefreshTransformUniform( material.specularMap, uniforms.specularMapTransform );\n\n\t\t}\n\n\t\tif ( material.alphaTest > 0 ) {\n\n\t\t\tuniforms.alphaTest.value = material.alphaTest;\n\n\t\t}\n\n\t\tconst envMap = properties.get( material ).envMap;\n\n\t\tif ( envMap ) {\n\n\t\t\tuniforms.envMap.value = envMap;\n\n\t\t\tuniforms.flipEnvMap.value = ( envMap.isCubeTexture && envMap.isRenderTargetTexture === false ) ? - 1 : 1;\n\n\t\t\tuniforms.reflectivity.value = material.reflectivity;\n\t\t\tuniforms.ior.value = material.ior;\n\t\t\tuniforms.refractionRatio.value = material.refractionRatio;\n\n\t\t}\n\n\t\tif ( material.lightMap ) {\n\n\t\t\tuniforms.lightMap.value = material.lightMap;\n\n\t\t\t// artist-friendly light intensity scaling factor\n\t\t\tconst scaleFactor = ( renderer._useLegacyLights === true ) ? Math.PI : 1;\n\n\t\t\tuniforms.lightMapIntensity.value = material.lightMapIntensity * scaleFactor;\n\n\t\t\trefreshTransformUniform( material.lightMap, uniforms.lightMapTransform );\n\n\t\t}\n\n\t\tif ( material.aoMap ) {\n\n\t\t\tuniforms.aoMap.value = material.aoMap;\n\t\t\tuniforms.aoMapIntensity.value = material.aoMapIntensity;\n\n\t\t\trefreshTransformUniform( material.aoMap, uniforms.aoMapTransform );\n\n\t\t}\n\n\t}\n\n\tfunction refreshUniformsLine( uniforms, material ) {\n\n\t\tuniforms.diffuse.value.copy( material.color );\n\t\tuniforms.opacity.value = material.opacity;\n\n\t\tif ( material.map ) {\n\n\t\t\tuniforms.map.value = material.map;\n\n\t\t\trefreshTransformUniform( material.map, uniforms.mapTransform );\n\n\t\t}\n\n\t}\n\n\tfunction refreshUniformsDash( uniforms, material ) {\n\n\t\tuniforms.dashSize.value = material.dashSize;\n\t\tuniforms.totalSize.value = material.dashSize + material.gapSize;\n\t\tuniforms.scale.value = material.scale;\n\n\t}\n\n\tfunction refreshUniformsPoints( uniforms, material, pixelRatio, height ) {\n\n\t\tuniforms.diffuse.value.copy( material.color );\n\t\tuniforms.opacity.value = material.opacity;\n\t\tuniforms.size.value = material.size * pixelRatio;\n\t\tuniforms.scale.value = height * 0.5;\n\n\t\tif ( material.map ) {\n\n\t\t\tuniforms.map.value = material.map;\n\n\t\t\trefreshTransformUniform( material.map, uniforms.uvTransform );\n\n\t\t}\n\n\t\tif ( material.alphaMap ) {\n\n\t\t\tuniforms.alphaMap.value = material.alphaMap;\n\n\t\t\trefreshTransformUniform( material.alphaMap, uniforms.alphaMapTransform );\n\n\t\t}\n\n\t\tif ( material.alphaTest > 0 ) {\n\n\t\t\tuniforms.alphaTest.value = material.alphaTest;\n\n\t\t}\n\n\t}\n\n\tfunction refreshUniformsSprites( uniforms, material ) {\n\n\t\tuniforms.diffuse.value.copy( material.color );\n\t\tuniforms.opacity.value = material.opacity;\n\t\tuniforms.rotation.value = material.rotation;\n\n\t\tif ( material.map ) {\n\n\t\t\tuniforms.map.value = material.map;\n\n\t\t\trefreshTransformUniform( material.map, uniforms.mapTransform );\n\n\t\t}\n\n\t\tif ( material.alphaMap ) {\n\n\t\t\tuniforms.alphaMap.value = material.alphaMap;\n\n\t\t\trefreshTransformUniform( material.alphaMap, uniforms.alphaMapTransform );\n\n\t\t}\n\n\t\tif ( material.alphaTest > 0 ) {\n\n\t\t\tuniforms.alphaTest.value = material.alphaTest;\n\n\t\t}\n\n\t}\n\n\tfunction refreshUniformsPhong( uniforms, material ) {\n\n\t\tuniforms.specular.value.copy( material.specular );\n\t\tuniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 )\n\n\t}\n\n\tfunction refreshUniformsToon( uniforms, material ) {\n\n\t\tif ( material.gradientMap ) {\n\n\t\t\tuniforms.gradientMap.value = material.gradientMap;\n\n\t\t}\n\n\t}\n\n\tfunction refreshUniformsStandard( uniforms, material ) {\n\n\t\tuniforms.metalness.value = material.metalness;\n\n\t\tif ( material.metalnessMap ) {\n\n\t\t\tuniforms.metalnessMap.value = material.metalnessMap;\n\n\t\t\trefreshTransformUniform( material.metalnessMap, uniforms.metalnessMapTransform );\n\n\t\t}\n\n\t\tuniforms.roughness.value = material.roughness;\n\n\t\tif ( material.roughnessMap ) {\n\n\t\t\tuniforms.roughnessMap.value = material.roughnessMap;\n\n\t\t\trefreshTransformUniform( material.roughnessMap, uniforms.roughnessMapTransform );\n\n\t\t}\n\n\t\tconst envMap = properties.get( material ).envMap;\n\n\t\tif ( envMap ) {\n\n\t\t\t//uniforms.envMap.value = material.envMap; // part of uniforms common\n\t\t\tuniforms.envMapIntensity.value = material.envMapIntensity;\n\n\t\t}\n\n\t}\n\n\tfunction refreshUniformsPhysical( uniforms, material, transmissionRenderTarget ) {\n\n\t\tuniforms.ior.value = material.ior; // also part of uniforms common\n\n\t\tif ( material.sheen > 0 ) {\n\n\t\t\tuniforms.sheenColor.value.copy( material.sheenColor ).multiplyScalar( material.sheen );\n\n\t\t\tuniforms.sheenRoughness.value = material.sheenRoughness;\n\n\t\t\tif ( material.sheenColorMap ) {\n\n\t\t\t\tuniforms.sheenColorMap.value = material.sheenColorMap;\n\n\t\t\t\trefreshTransformUniform( material.sheenColorMap, uniforms.sheenColorMapTransform );\n\n\t\t\t}\n\n\t\t\tif ( material.sheenRoughnessMap ) {\n\n\t\t\t\tuniforms.sheenRoughnessMap.value = material.sheenRoughnessMap;\n\n\t\t\t\trefreshTransformUniform( material.sheenRoughnessMap, uniforms.sheenRoughnessMapTransform );\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( material.clearcoat > 0 ) {\n\n\t\t\tuniforms.clearcoat.value = material.clearcoat;\n\t\t\tuniforms.clearcoatRoughness.value = material.clearcoatRoughness;\n\n\t\t\tif ( material.clearcoatMap ) {\n\n\t\t\t\tuniforms.clearcoatMap.value = material.clearcoatMap;\n\n\t\t\t\trefreshTransformUniform( material.clearcoatMap, uniforms.clearcoatMapTransform );\n\n\t\t\t}\n\n\t\t\tif ( material.clearcoatRoughnessMap ) {\n\n\t\t\t\tuniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap;\n\n\t\t\t\trefreshTransformUniform( material.clearcoatRoughnessMap, uniforms.clearcoatRoughnessMapTransform );\n\n\t\t\t}\n\n\t\t\tif ( material.clearcoatNormalMap ) {\n\n\t\t\t\tuniforms.clearcoatNormalMap.value = material.clearcoatNormalMap;\n\n\t\t\t\trefreshTransformUniform( material.clearcoatNormalMap, uniforms.clearcoatNormalMapTransform );\n\n\t\t\t\tuniforms.clearcoatNormalScale.value.copy( material.clearcoatNormalScale );\n\n\t\t\t\tif ( material.side === BackSide ) {\n\n\t\t\t\t\tuniforms.clearcoatNormalScale.value.negate();\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( material.iridescence > 0 ) {\n\n\t\t\tuniforms.iridescence.value = material.iridescence;\n\t\t\tuniforms.iridescenceIOR.value = material.iridescenceIOR;\n\t\t\tuniforms.iridescenceThicknessMinimum.value = material.iridescenceThicknessRange[ 0 ];\n\t\t\tuniforms.iridescenceThicknessMaximum.value = material.iridescenceThicknessRange[ 1 ];\n\n\t\t\tif ( material.iridescenceMap ) {\n\n\t\t\t\tuniforms.iridescenceMap.value = material.iridescenceMap;\n\n\t\t\t\trefreshTransformUniform( material.iridescenceMap, uniforms.iridescenceMapTransform );\n\n\t\t\t}\n\n\t\t\tif ( material.iridescenceThicknessMap ) {\n\n\t\t\t\tuniforms.iridescenceThicknessMap.value = material.iridescenceThicknessMap;\n\n\t\t\t\trefreshTransformUniform( material.iridescenceThicknessMap, uniforms.iridescenceThicknessMapTransform );\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( material.transmission > 0 ) {\n\n\t\t\tuniforms.transmission.value = material.transmission;\n\t\t\tuniforms.transmissionSamplerMap.value = transmissionRenderTarget.texture;\n\t\t\tuniforms.transmissionSamplerSize.value.set( transmissionRenderTarget.width, transmissionRenderTarget.height );\n\n\t\t\tif ( material.transmissionMap ) {\n\n\t\t\t\tuniforms.transmissionMap.value = material.transmissionMap;\n\n\t\t\t\trefreshTransformUniform( material.transmissionMap, uniforms.transmissionMapTransform );\n\n\t\t\t}\n\n\t\t\tuniforms.thickness.value = material.thickness;\n\n\t\t\tif ( material.thicknessMap ) {\n\n\t\t\t\tuniforms.thicknessMap.value = material.thicknessMap;\n\n\t\t\t\trefreshTransformUniform( material.thicknessMap, uniforms.thicknessMapTransform );\n\n\t\t\t}\n\n\t\t\tuniforms.attenuationDistance.value = material.attenuationDistance;\n\t\t\tuniforms.attenuationColor.value.copy( material.attenuationColor );\n\n\t\t}\n\n\t\tif ( material.anisotropy > 0 ) {\n\n\t\t\tuniforms.anisotropyVector.value.set( material.anisotropy * Math.cos( material.anisotropyRotation ), material.anisotropy * Math.sin( material.anisotropyRotation ) );\n\n\t\t\tif ( material.anisotropyMap ) {\n\n\t\t\t\tuniforms.anisotropyMap.value = material.anisotropyMap;\n\n\t\t\t\trefreshTransformUniform( material.anisotropyMap, uniforms.anisotropyMapTransform );\n\n\t\t\t}\n\n\t\t}\n\n\t\tuniforms.specularIntensity.value = material.specularIntensity;\n\t\tuniforms.specularColor.value.copy( material.specularColor );\n\n\t\tif ( material.specularColorMap ) {\n\n\t\t\tuniforms.specularColorMap.value = material.specularColorMap;\n\n\t\t\trefreshTransformUniform( material.specularColorMap, uniforms.specularColorMapTransform );\n\n\t\t}\n\n\t\tif ( material.specularIntensityMap ) {\n\n\t\t\tuniforms.specularIntensityMap.value = material.specularIntensityMap;\n\n\t\t\trefreshTransformUniform( material.specularIntensityMap, uniforms.specularIntensityMapTransform );\n\n\t\t}\n\n\t}\n\n\tfunction refreshUniformsMatcap( uniforms, material ) {\n\n\t\tif ( material.matcap ) {\n\n\t\t\tuniforms.matcap.value = material.matcap;\n\n\t\t}\n\n\t}\n\n\tfunction refreshUniformsDistance( uniforms, material ) {\n\n\t\tconst light = properties.get( material ).light;\n\n\t\tuniforms.referencePosition.value.setFromMatrixPosition( light.matrixWorld );\n\t\tuniforms.nearDistance.value = light.shadow.camera.near;\n\t\tuniforms.farDistance.value = light.shadow.camera.far;\n\n\t}\n\n\treturn {\n\t\trefreshFogUniforms: refreshFogUniforms,\n\t\trefreshMaterialUniforms: refreshMaterialUniforms\n\t};\n\n}\n\nexport { WebGLMaterials };\n","function WebGLUniformsGroups( gl, info, capabilities, state ) {\n\n\tlet buffers = {};\n\tlet updateList = {};\n\tlet allocatedBindingPoints = [];\n\n\tconst maxBindingPoints = ( capabilities.isWebGL2 ) ? gl.getParameter( gl.MAX_UNIFORM_BUFFER_BINDINGS ) : 0; // binding points are global whereas block indices are per shader program\n\n\tfunction bind( uniformsGroup, program ) {\n\n\t\tconst webglProgram = program.program;\n\t\tstate.uniformBlockBinding( uniformsGroup, webglProgram );\n\n\t}\n\n\tfunction update( uniformsGroup, program ) {\n\n\t\tlet buffer = buffers[ uniformsGroup.id ];\n\n\t\tif ( buffer === undefined ) {\n\n\t\t\tprepareUniformsGroup( uniformsGroup );\n\n\t\t\tbuffer = createBuffer( uniformsGroup );\n\t\t\tbuffers[ uniformsGroup.id ] = buffer;\n\n\t\t\tuniformsGroup.addEventListener( 'dispose', onUniformsGroupsDispose );\n\n\t\t}\n\n\t\t// ensure to update the binding points/block indices mapping for this program\n\n\t\tconst webglProgram = program.program;\n\t\tstate.updateUBOMapping( uniformsGroup, webglProgram );\n\n\t\t// update UBO once per frame\n\n\t\tconst frame = info.render.frame;\n\n\t\tif ( updateList[ uniformsGroup.id ] !== frame ) {\n\n\t\t\tupdateBufferData( uniformsGroup );\n\n\t\t\tupdateList[ uniformsGroup.id ] = frame;\n\n\t\t}\n\n\t}\n\n\tfunction createBuffer( uniformsGroup ) {\n\n\t\t// the setup of an UBO is independent of a particular shader program but global\n\n\t\tconst bindingPointIndex = allocateBindingPointIndex();\n\t\tuniformsGroup.__bindingPointIndex = bindingPointIndex;\n\n\t\tconst buffer = gl.createBuffer();\n\t\tconst size = uniformsGroup.__size;\n\t\tconst usage = uniformsGroup.usage;\n\n\t\tgl.bindBuffer( gl.UNIFORM_BUFFER, buffer );\n\t\tgl.bufferData( gl.UNIFORM_BUFFER, size, usage );\n\t\tgl.bindBuffer( gl.UNIFORM_BUFFER, null );\n\t\tgl.bindBufferBase( gl.UNIFORM_BUFFER, bindingPointIndex, buffer );\n\n\t\treturn buffer;\n\n\t}\n\n\tfunction allocateBindingPointIndex() {\n\n\t\tfor ( let i = 0; i < maxBindingPoints; i ++ ) {\n\n\t\t\tif ( allocatedBindingPoints.indexOf( i ) === - 1 ) {\n\n\t\t\t\tallocatedBindingPoints.push( i );\n\t\t\t\treturn i;\n\n\t\t\t}\n\n\t\t}\n\n\t\tconsole.error( 'THREE.WebGLRenderer: Maximum number of simultaneously usable uniforms groups reached.' );\n\n\t\treturn 0;\n\n\t}\n\n\tfunction updateBufferData( uniformsGroup ) {\n\n\t\tconst buffer = buffers[ uniformsGroup.id ];\n\t\tconst uniforms = uniformsGroup.uniforms;\n\t\tconst cache = uniformsGroup.__cache;\n\n\t\tgl.bindBuffer( gl.UNIFORM_BUFFER, buffer );\n\n\t\tfor ( let i = 0, il = uniforms.length; i < il; i ++ ) {\n\n\t\t\tconst uniform = uniforms[ i ];\n\n\t\t\t// partly update the buffer if necessary\n\n\t\t\tif ( hasUniformChanged( uniform, i, cache ) === true ) {\n\n\t\t\t\tconst offset = uniform.__offset;\n\n\t\t\t\tconst values = Array.isArray( uniform.value ) ? uniform.value : [ uniform.value ];\n\n\t\t\t\tlet arrayOffset = 0;\n\n\t\t\t\tfor ( let i = 0; i < values.length; i ++ ) {\n\n\t\t\t\t\tconst value = values[ i ];\n\n\t\t\t\t\tconst info = getUniformSize( value );\n\n\t\t\t\t\tif ( typeof value === 'number' ) {\n\n\t\t\t\t\t\tuniform.__data[ 0 ] = value;\n\t\t\t\t\t\tgl.bufferSubData( gl.UNIFORM_BUFFER, offset + arrayOffset, uniform.__data );\n\n\t\t\t\t\t} else if ( value.isMatrix3 ) {\n\n\t\t\t\t\t\t// manually converting 3x3 to 3x4\n\n\t\t\t\t\t\tuniform.__data[ 0 ] = value.elements[ 0 ];\n\t\t\t\t\t\tuniform.__data[ 1 ] = value.elements[ 1 ];\n\t\t\t\t\t\tuniform.__data[ 2 ] = value.elements[ 2 ];\n\t\t\t\t\t\tuniform.__data[ 3 ] = value.elements[ 0 ];\n\t\t\t\t\t\tuniform.__data[ 4 ] = value.elements[ 3 ];\n\t\t\t\t\t\tuniform.__data[ 5 ] = value.elements[ 4 ];\n\t\t\t\t\t\tuniform.__data[ 6 ] = value.elements[ 5 ];\n\t\t\t\t\t\tuniform.__data[ 7 ] = value.elements[ 0 ];\n\t\t\t\t\t\tuniform.__data[ 8 ] = value.elements[ 6 ];\n\t\t\t\t\t\tuniform.__data[ 9 ] = value.elements[ 7 ];\n\t\t\t\t\t\tuniform.__data[ 10 ] = value.elements[ 8 ];\n\t\t\t\t\t\tuniform.__data[ 11 ] = value.elements[ 0 ];\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tvalue.toArray( uniform.__data, arrayOffset );\n\n\t\t\t\t\t\tarrayOffset += info.storage / Float32Array.BYTES_PER_ELEMENT;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tgl.bufferSubData( gl.UNIFORM_BUFFER, offset, uniform.__data );\n\n\t\t\t}\n\n\t\t}\n\n\t\tgl.bindBuffer( gl.UNIFORM_BUFFER, null );\n\n\t}\n\n\tfunction hasUniformChanged( uniform, index, cache ) {\n\n\t\tconst value = uniform.value;\n\n\t\tif ( cache[ index ] === undefined ) {\n\n\t\t\t// cache entry does not exist so far\n\n\t\t\tif ( typeof value === 'number' ) {\n\n\t\t\t\tcache[ index ] = value;\n\n\t\t\t} else {\n\n\t\t\t\tconst values = Array.isArray( value ) ? value : [ value ];\n\n\t\t\t\tconst tempValues = [];\n\n\t\t\t\tfor ( let i = 0; i < values.length; i ++ ) {\n\n\t\t\t\t\ttempValues.push( values[ i ].clone() );\n\n\t\t\t\t}\n\n\t\t\t\tcache[ index ] = tempValues;\n\n\t\t\t}\n\n\t\t\treturn true;\n\n\t\t} else {\n\n\t\t\t// compare current value with cached entry\n\n\t\t\tif ( typeof value === 'number' ) {\n\n\t\t\t\tif ( cache[ index ] !== value ) {\n\n\t\t\t\t\tcache[ index ] = value;\n\t\t\t\t\treturn true;\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tconst cachedObjects = Array.isArray( cache[ index ] ) ? cache[ index ] : [ cache[ index ] ];\n\t\t\t\tconst values = Array.isArray( value ) ? value : [ value ];\n\n\t\t\t\tfor ( let i = 0; i < cachedObjects.length; i ++ ) {\n\n\t\t\t\t\tconst cachedObject = cachedObjects[ i ];\n\n\t\t\t\t\tif ( cachedObject.equals( values[ i ] ) === false ) {\n\n\t\t\t\t\t\tcachedObject.copy( values[ i ] );\n\t\t\t\t\t\treturn true;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn false;\n\n\t}\n\n\tfunction prepareUniformsGroup( uniformsGroup ) {\n\n\t\t// determine total buffer size according to the STD140 layout\n\t\t// Hint: STD140 is the only supported layout in WebGL 2\n\n\t\tconst uniforms = uniformsGroup.uniforms;\n\n\t\tlet offset = 0; // global buffer offset in bytes\n\t\tconst chunkSize = 16; // size of a chunk in bytes\n\t\tlet chunkOffset = 0; // offset within a single chunk in bytes\n\n\t\tfor ( let i = 0, l = uniforms.length; i < l; i ++ ) {\n\n\t\t\tconst uniform = uniforms[ i ];\n\n\t\t\tconst infos = {\n\t\t\t\tboundary: 0, // bytes\n\t\t\t\tstorage: 0 // bytes\n\t\t\t};\n\n\t\t\tconst values = Array.isArray( uniform.value ) ? uniform.value : [ uniform.value ];\n\n\t\t\tfor ( let j = 0, jl = values.length; j < jl; j ++ ) {\n\n\t\t\t\tconst value = values[ j ];\n\n\t\t\t\tconst info = getUniformSize( value );\n\n\t\t\t\tinfos.boundary += info.boundary;\n\t\t\t\tinfos.storage += info.storage;\n\n\t\t\t}\n\n\t\t\t// the following two properties will be used for partial buffer updates\n\n\t\t\tuniform.__data = new Float32Array( infos.storage / Float32Array.BYTES_PER_ELEMENT );\n\t\t\tuniform.__offset = offset;\n\n\t\t\t//\n\n\t\t\tif ( i > 0 ) {\n\n\t\t\t\tchunkOffset = offset % chunkSize;\n\n\t\t\t\tconst remainingSizeInChunk = chunkSize - chunkOffset;\n\n\t\t\t\t// check for chunk overflow\n\n\t\t\t\tif ( chunkOffset !== 0 && ( remainingSizeInChunk - infos.boundary ) < 0 ) {\n\n\t\t\t\t\t// add padding and adjust offset\n\n\t\t\t\t\toffset += ( chunkSize - chunkOffset );\n\t\t\t\t\tuniform.__offset = offset;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\toffset += infos.storage;\n\n\t\t}\n\n\t\t// ensure correct final padding\n\n\t\tchunkOffset = offset % chunkSize;\n\n\t\tif ( chunkOffset > 0 ) offset += ( chunkSize - chunkOffset );\n\n\t\t//\n\n\t\tuniformsGroup.__size = offset;\n\t\tuniformsGroup.__cache = {};\n\n\t\treturn this;\n\n\t}\n\n\tfunction getUniformSize( value ) {\n\n\t\tconst info = {\n\t\t\tboundary: 0, // bytes\n\t\t\tstorage: 0 // bytes\n\t\t};\n\n\t\t// determine sizes according to STD140\n\n\t\tif ( typeof value === 'number' ) {\n\n\t\t\t// float/int\n\n\t\t\tinfo.boundary = 4;\n\t\t\tinfo.storage = 4;\n\n\t\t} else if ( value.isVector2 ) {\n\n\t\t\t// vec2\n\n\t\t\tinfo.boundary = 8;\n\t\t\tinfo.storage = 8;\n\n\t\t} else if ( value.isVector3 || value.isColor ) {\n\n\t\t\t// vec3\n\n\t\t\tinfo.boundary = 16;\n\t\t\tinfo.storage = 12; // evil: vec3 must start on a 16-byte boundary but it only consumes 12 bytes\n\n\t\t} else if ( value.isVector4 ) {\n\n\t\t\t// vec4\n\n\t\t\tinfo.boundary = 16;\n\t\t\tinfo.storage = 16;\n\n\t\t} else if ( value.isMatrix3 ) {\n\n\t\t\t// mat3 (in STD140 a 3x3 matrix is represented as 3x4)\n\n\t\t\tinfo.boundary = 48;\n\t\t\tinfo.storage = 48;\n\n\t\t} else if ( value.isMatrix4 ) {\n\n\t\t\t// mat4\n\n\t\t\tinfo.boundary = 64;\n\t\t\tinfo.storage = 64;\n\n\t\t} else if ( value.isTexture ) {\n\n\t\t\tconsole.warn( 'THREE.WebGLRenderer: Texture samplers can not be part of an uniforms group.' );\n\n\t\t} else {\n\n\t\t\tconsole.warn( 'THREE.WebGLRenderer: Unsupported uniform value type.', value );\n\n\t\t}\n\n\t\treturn info;\n\n\t}\n\n\tfunction onUniformsGroupsDispose( event ) {\n\n\t\tconst uniformsGroup = event.target;\n\n\t\tuniformsGroup.removeEventListener( 'dispose', onUniformsGroupsDispose );\n\n\t\tconst index = allocatedBindingPoints.indexOf( uniformsGroup.__bindingPointIndex );\n\t\tallocatedBindingPoints.splice( index, 1 );\n\n\t\tgl.deleteBuffer( buffers[ uniformsGroup.id ] );\n\n\t\tdelete buffers[ uniformsGroup.id ];\n\t\tdelete updateList[ uniformsGroup.id ];\n\n\t}\n\n\tfunction dispose() {\n\n\t\tfor ( const id in buffers ) {\n\n\t\t\tgl.deleteBuffer( buffers[ id ] );\n\n\t\t}\n\n\t\tallocatedBindingPoints = [];\n\t\tbuffers = {};\n\t\tupdateList = {};\n\n\t}\n\n\treturn {\n\n\t\tbind: bind,\n\t\tupdate: update,\n\n\t\tdispose: dispose\n\n\t};\n\n}\n\n\nexport { WebGLUniformsGroups };\n","import {\n\tREVISION,\n\tBackSide,\n\tFrontSide,\n\tDoubleSide,\n\tRGBAFormat,\n\tHalfFloatType,\n\tFloatType,\n\tUnsignedByteType,\n\tNoToneMapping,\n\tLinearMipmapLinearFilter,\n\tSRGBColorSpace,\n\tLinearSRGBColorSpace,\n\tsRGBEncoding,\n\tLinearEncoding,\n\tRGBAIntegerFormat,\n\tRGIntegerFormat,\n\tRedIntegerFormat,\n\tUnsignedIntType,\n\tUnsignedShortType,\n\tUnsignedInt248Type,\n\tUnsignedShort4444Type,\n\tUnsignedShort5551Type,\n\tWebGLCoordinateSystem,\n\tDisplayP3ColorSpace,\n\tLinearDisplayP3ColorSpace\n} from '../constants.js';\nimport { Color } from '../math/Color.js';\nimport { Frustum } from '../math/Frustum.js';\nimport { Matrix4 } from '../math/Matrix4.js';\nimport { Vector2 } from '../math/Vector2.js';\nimport { Vector3 } from '../math/Vector3.js';\nimport { Vector4 } from '../math/Vector4.js';\nimport { floorPowerOfTwo } from '../math/MathUtils.js';\nimport { WebGLAnimation } from './webgl/WebGLAnimation.js';\nimport { WebGLAttributes } from './webgl/WebGLAttributes.js';\nimport { WebGLBackground } from './webgl/WebGLBackground.js';\nimport { WebGLBindingStates } from './webgl/WebGLBindingStates.js';\nimport { WebGLBufferRenderer } from './webgl/WebGLBufferRenderer.js';\nimport { WebGLCapabilities } from './webgl/WebGLCapabilities.js';\nimport { WebGLClipping } from './webgl/WebGLClipping.js';\nimport { WebGLCubeMaps } from './webgl/WebGLCubeMaps.js';\nimport { WebGLCubeUVMaps } from './webgl/WebGLCubeUVMaps.js';\nimport { WebGLExtensions } from './webgl/WebGLExtensions.js';\nimport { WebGLGeometries } from './webgl/WebGLGeometries.js';\nimport { WebGLIndexedBufferRenderer } from './webgl/WebGLIndexedBufferRenderer.js';\nimport { WebGLInfo } from './webgl/WebGLInfo.js';\nimport { WebGLMorphtargets } from './webgl/WebGLMorphtargets.js';\nimport { WebGLObjects } from './webgl/WebGLObjects.js';\nimport { WebGLPrograms } from './webgl/WebGLPrograms.js';\nimport { WebGLProperties } from './webgl/WebGLProperties.js';\nimport { WebGLRenderLists } from './webgl/WebGLRenderLists.js';\nimport { WebGLRenderStates } from './webgl/WebGLRenderStates.js';\nimport { WebGLRenderTarget } from './WebGLRenderTarget.js';\nimport { WebGLShadowMap } from './webgl/WebGLShadowMap.js';\nimport { WebGLState } from './webgl/WebGLState.js';\nimport { WebGLTextures } from './webgl/WebGLTextures.js';\nimport { WebGLUniforms } from './webgl/WebGLUniforms.js';\nimport { WebGLUtils } from './webgl/WebGLUtils.js';\nimport { WebXRManager } from './webxr/WebXRManager.js';\nimport { WebGLMaterials } from './webgl/WebGLMaterials.js';\nimport { WebGLUniformsGroups } from './webgl/WebGLUniformsGroups.js';\nimport { createCanvasElement } from '../utils.js';\nimport { ColorManagement } from '../math/ColorManagement.js';\n\nclass WebGLRenderer {\n\n\tconstructor( parameters = {} ) {\n\n\t\tconst {\n\t\t\tcanvas = createCanvasElement(),\n\t\t\tcontext = null,\n\t\t\tdepth = true,\n\t\t\tstencil = true,\n\t\t\talpha = false,\n\t\t\tantialias = false,\n\t\t\tpremultipliedAlpha = true,\n\t\t\tpreserveDrawingBuffer = false,\n\t\t\tpowerPreference = 'default',\n\t\t\tfailIfMajorPerformanceCaveat = false,\n\t\t} = parameters;\n\n\t\tthis.isWebGLRenderer = true;\n\n\t\tlet _alpha;\n\n\t\tif ( context !== null ) {\n\n\t\t\t_alpha = context.getContextAttributes().alpha;\n\n\t\t} else {\n\n\t\t\t_alpha = alpha;\n\n\t\t}\n\n\t\tconst uintClearColor = new Uint32Array( 4 );\n\t\tconst intClearColor = new Int32Array( 4 );\n\n\t\tlet currentRenderList = null;\n\t\tlet currentRenderState = null;\n\n\t\t// render() can be called from within a callback triggered by another render.\n\t\t// We track this so that the nested render call gets its list and state isolated from the parent render call.\n\n\t\tconst renderListStack = [];\n\t\tconst renderStateStack = [];\n\n\t\t// public properties\n\n\t\tthis.domElement = canvas;\n\n\t\t// Debug configuration container\n\t\tthis.debug = {\n\n\t\t\t/**\n\t\t\t * Enables error checking and reporting when shader programs are being compiled\n\t\t\t * @type {boolean}\n\t\t\t */\n\t\t\tcheckShaderErrors: true,\n\t\t\t/**\n\t\t\t * Callback for custom error reporting.\n\t\t\t * @type {?Function}\n\t\t\t */\n\t\t\tonShaderError: null\n\t\t};\n\n\t\t// clearing\n\n\t\tthis.autoClear = true;\n\t\tthis.autoClearColor = true;\n\t\tthis.autoClearDepth = true;\n\t\tthis.autoClearStencil = true;\n\n\t\t// scene graph\n\n\t\tthis.sortObjects = true;\n\n\t\t// user-defined clipping\n\n\t\tthis.clippingPlanes = [];\n\t\tthis.localClippingEnabled = false;\n\n\t\t// physically based shading\n\n\t\tthis._outputColorSpace = SRGBColorSpace;\n\n\t\t// physical lights\n\n\t\tthis._useLegacyLights = false;\n\n\t\t// tone mapping\n\n\t\tthis.toneMapping = NoToneMapping;\n\t\tthis.toneMappingExposure = 1.0;\n\n\t\t// internal properties\n\n\t\tconst _this = this;\n\n\t\tlet _isContextLost = false;\n\n\t\t// internal state cache\n\n\t\tlet _currentActiveCubeFace = 0;\n\t\tlet _currentActiveMipmapLevel = 0;\n\t\tlet _currentRenderTarget = null;\n\t\tlet _currentMaterialId = - 1;\n\n\t\tlet _currentCamera = null;\n\n\t\tconst _currentViewport = new Vector4();\n\t\tconst _currentScissor = new Vector4();\n\t\tlet _currentScissorTest = null;\n\n\t\tconst _currentClearColor = new Color( 0x000000 );\n\t\tlet _currentClearAlpha = 0;\n\n\t\t//\n\n\t\tlet _width = canvas.width;\n\t\tlet _height = canvas.height;\n\n\t\tlet _pixelRatio = 1;\n\t\tlet _opaqueSort = null;\n\t\tlet _transparentSort = null;\n\n\t\tconst _viewport = new Vector4( 0, 0, _width, _height );\n\t\tconst _scissor = new Vector4( 0, 0, _width, _height );\n\t\tlet _scissorTest = false;\n\n\t\t// frustum\n\n\t\tconst _frustum = new Frustum();\n\n\t\t// clipping\n\n\t\tlet _clippingEnabled = false;\n\t\tlet _localClippingEnabled = false;\n\n\t\t// transmission\n\n\t\tlet _transmissionRenderTarget = null;\n\n\t\t// camera matrices cache\n\n\t\tconst _projScreenMatrix = new Matrix4();\n\n\t\tconst _vector2 = new Vector2();\n\t\tconst _vector3 = new Vector3();\n\n\t\tconst _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true };\n\n\t\tfunction getTargetPixelRatio() {\n\n\t\t\treturn _currentRenderTarget === null ? _pixelRatio : 1;\n\n\t\t}\n\n\t\t// initialize\n\n\t\tlet _gl = context;\n\n\t\tfunction getContext( contextNames, contextAttributes ) {\n\n\t\t\tfor ( let i = 0; i < contextNames.length; i ++ ) {\n\n\t\t\t\tconst contextName = contextNames[ i ];\n\t\t\t\tconst context = canvas.getContext( contextName, contextAttributes );\n\t\t\t\tif ( context !== null ) return context;\n\n\t\t\t}\n\n\t\t\treturn null;\n\n\t\t}\n\n\t\ttry {\n\n\t\t\tconst contextAttributes = {\n\t\t\t\talpha: true,\n\t\t\t\tdepth,\n\t\t\t\tstencil,\n\t\t\t\tantialias,\n\t\t\t\tpremultipliedAlpha,\n\t\t\t\tpreserveDrawingBuffer,\n\t\t\t\tpowerPreference,\n\t\t\t\tfailIfMajorPerformanceCaveat,\n\t\t\t};\n\n\t\t\t// OffscreenCanvas does not have setAttribute, see #22811\n\t\t\tif ( 'setAttribute' in canvas ) canvas.setAttribute( 'data-engine', `three.js r${REVISION}` );\n\n\t\t\t// event listeners must be registered before WebGL context is created, see #12753\n\t\t\tcanvas.addEventListener( 'webglcontextlost', onContextLost, false );\n\t\t\tcanvas.addEventListener( 'webglcontextrestored', onContextRestore, false );\n\t\t\tcanvas.addEventListener( 'webglcontextcreationerror', onContextCreationError, false );\n\n\t\t\tif ( _gl === null ) {\n\n\t\t\t\tconst contextNames = [ 'webgl2', 'webgl', 'experimental-webgl' ];\n\n\t\t\t\tif ( _this.isWebGL1Renderer === true ) {\n\n\t\t\t\t\tcontextNames.shift();\n\n\t\t\t\t}\n\n\t\t\t\t_gl = getContext( contextNames, contextAttributes );\n\n\t\t\t\tif ( _gl === null ) {\n\n\t\t\t\t\tif ( getContext( contextNames ) ) {\n\n\t\t\t\t\t\tthrow new Error( 'Error creating WebGL context with your selected attributes.' );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tthrow new Error( 'Error creating WebGL context.' );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( typeof WebGLRenderingContext !== 'undefined' && _gl instanceof WebGLRenderingContext ) { // @deprecated, r153\n\n\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: WebGL 1 support was deprecated in r153 and will be removed in r163.' );\n\n\t\t\t}\n\n\t\t\t// Some experimental-webgl implementations do not have getShaderPrecisionFormat\n\n\t\t\tif ( _gl.getShaderPrecisionFormat === undefined ) {\n\n\t\t\t\t_gl.getShaderPrecisionFormat = function () {\n\n\t\t\t\t\treturn { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 };\n\n\t\t\t\t};\n\n\t\t\t}\n\n\t\t} catch ( error ) {\n\n\t\t\tconsole.error( 'THREE.WebGLRenderer: ' + error.message );\n\t\t\tthrow error;\n\n\t\t}\n\n\t\tlet extensions, capabilities, state, info;\n\t\tlet properties, textures, cubemaps, cubeuvmaps, attributes, geometries, objects;\n\t\tlet programCache, materials, renderLists, renderStates, clipping, shadowMap;\n\n\t\tlet background, morphtargets, bufferRenderer, indexedBufferRenderer;\n\n\t\tlet utils, bindingStates, uniformsGroups;\n\n\t\tfunction initGLContext() {\n\n\t\t\textensions = new WebGLExtensions( _gl );\n\n\t\t\tcapabilities = new WebGLCapabilities( _gl, extensions, parameters );\n\n\t\t\textensions.init( capabilities );\n\n\t\t\tutils = new WebGLUtils( _gl, extensions, capabilities );\n\n\t\t\tstate = new WebGLState( _gl, extensions, capabilities );\n\n\t\t\tinfo = new WebGLInfo( _gl );\n\t\t\tproperties = new WebGLProperties();\n\t\t\ttextures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info );\n\t\t\tcubemaps = new WebGLCubeMaps( _this );\n\t\t\tcubeuvmaps = new WebGLCubeUVMaps( _this );\n\t\t\tattributes = new WebGLAttributes( _gl, capabilities );\n\t\t\tbindingStates = new WebGLBindingStates( _gl, extensions, attributes, capabilities );\n\t\t\tgeometries = new WebGLGeometries( _gl, attributes, info, bindingStates );\n\t\t\tobjects = new WebGLObjects( _gl, geometries, attributes, info );\n\t\t\tmorphtargets = new WebGLMorphtargets( _gl, capabilities, textures );\n\t\t\tclipping = new WebGLClipping( properties );\n\t\t\tprogramCache = new WebGLPrograms( _this, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping );\n\t\t\tmaterials = new WebGLMaterials( _this, properties );\n\t\t\trenderLists = new WebGLRenderLists();\n\t\t\trenderStates = new WebGLRenderStates( extensions, capabilities );\n\t\t\tbackground = new WebGLBackground( _this, cubemaps, cubeuvmaps, state, objects, _alpha, premultipliedAlpha );\n\t\t\tshadowMap = new WebGLShadowMap( _this, objects, capabilities );\n\t\t\tuniformsGroups = new WebGLUniformsGroups( _gl, info, capabilities, state );\n\n\t\t\tbufferRenderer = new WebGLBufferRenderer( _gl, extensions, info, capabilities );\n\t\t\tindexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, capabilities );\n\n\t\t\tinfo.programs = programCache.programs;\n\n\t\t\t_this.capabilities = capabilities;\n\t\t\t_this.extensions = extensions;\n\t\t\t_this.properties = properties;\n\t\t\t_this.renderLists = renderLists;\n\t\t\t_this.shadowMap = shadowMap;\n\t\t\t_this.state = state;\n\t\t\t_this.info = info;\n\n\t\t}\n\n\t\tinitGLContext();\n\n\t\t// xr\n\n\t\tconst xr = new WebXRManager( _this, _gl );\n\n\t\tthis.xr = xr;\n\n\t\t// API\n\n\t\tthis.getContext = function () {\n\n\t\t\treturn _gl;\n\n\t\t};\n\n\t\tthis.getContextAttributes = function () {\n\n\t\t\treturn _gl.getContextAttributes();\n\n\t\t};\n\n\t\tthis.forceContextLoss = function () {\n\n\t\t\tconst extension = extensions.get( 'WEBGL_lose_context' );\n\t\t\tif ( extension ) extension.loseContext();\n\n\t\t};\n\n\t\tthis.forceContextRestore = function () {\n\n\t\t\tconst extension = extensions.get( 'WEBGL_lose_context' );\n\t\t\tif ( extension ) extension.restoreContext();\n\n\t\t};\n\n\t\tthis.getPixelRatio = function () {\n\n\t\t\treturn _pixelRatio;\n\n\t\t};\n\n\t\tthis.setPixelRatio = function ( value ) {\n\n\t\t\tif ( value === undefined ) return;\n\n\t\t\t_pixelRatio = value;\n\n\t\t\tthis.setSize( _width, _height, false );\n\n\t\t};\n\n\t\tthis.getSize = function ( target ) {\n\n\t\t\treturn target.set( _width, _height );\n\n\t\t};\n\n\t\tthis.setSize = function ( width, height, updateStyle = true ) {\n\n\t\t\tif ( xr.isPresenting ) {\n\n\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Can\\'t change size while VR device is presenting.' );\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\t_width = width;\n\t\t\t_height = height;\n\n\t\t\tcanvas.width = Math.floor( width * _pixelRatio );\n\t\t\tcanvas.height = Math.floor( height * _pixelRatio );\n\n\t\t\tif ( updateStyle === true ) {\n\n\t\t\t\tcanvas.style.width = width + 'px';\n\t\t\t\tcanvas.style.height = height + 'px';\n\n\t\t\t}\n\n\t\t\tthis.setViewport( 0, 0, width, height );\n\n\t\t};\n\n\t\tthis.getDrawingBufferSize = function ( target ) {\n\n\t\t\treturn target.set( _width * _pixelRatio, _height * _pixelRatio ).floor();\n\n\t\t};\n\n\t\tthis.setDrawingBufferSize = function ( width, height, pixelRatio ) {\n\n\t\t\t_width = width;\n\t\t\t_height = height;\n\n\t\t\t_pixelRatio = pixelRatio;\n\n\t\t\tcanvas.width = Math.floor( width * pixelRatio );\n\t\t\tcanvas.height = Math.floor( height * pixelRatio );\n\n\t\t\tthis.setViewport( 0, 0, width, height );\n\n\t\t};\n\n\t\tthis.getCurrentViewport = function ( target ) {\n\n\t\t\treturn target.copy( _currentViewport );\n\n\t\t};\n\n\t\tthis.getViewport = function ( target ) {\n\n\t\t\treturn target.copy( _viewport );\n\n\t\t};\n\n\t\tthis.setViewport = function ( x, y, width, height ) {\n\n\t\t\tif ( x.isVector4 ) {\n\n\t\t\t\t_viewport.set( x.x, x.y, x.z, x.w );\n\n\t\t\t} else {\n\n\t\t\t\t_viewport.set( x, y, width, height );\n\n\t\t\t}\n\n\t\t\tstate.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() );\n\n\t\t};\n\n\t\tthis.getScissor = function ( target ) {\n\n\t\t\treturn target.copy( _scissor );\n\n\t\t};\n\n\t\tthis.setScissor = function ( x, y, width, height ) {\n\n\t\t\tif ( x.isVector4 ) {\n\n\t\t\t\t_scissor.set( x.x, x.y, x.z, x.w );\n\n\t\t\t} else {\n\n\t\t\t\t_scissor.set( x, y, width, height );\n\n\t\t\t}\n\n\t\t\tstate.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() );\n\n\t\t};\n\n\t\tthis.getScissorTest = function () {\n\n\t\t\treturn _scissorTest;\n\n\t\t};\n\n\t\tthis.setScissorTest = function ( boolean ) {\n\n\t\t\tstate.setScissorTest( _scissorTest = boolean );\n\n\t\t};\n\n\t\tthis.setOpaqueSort = function ( method ) {\n\n\t\t\t_opaqueSort = method;\n\n\t\t};\n\n\t\tthis.setTransparentSort = function ( method ) {\n\n\t\t\t_transparentSort = method;\n\n\t\t};\n\n\t\t// Clearing\n\n\t\tthis.getClearColor = function ( target ) {\n\n\t\t\treturn target.copy( background.getClearColor() );\n\n\t\t};\n\n\t\tthis.setClearColor = function () {\n\n\t\t\tbackground.setClearColor.apply( background, arguments );\n\n\t\t};\n\n\t\tthis.getClearAlpha = function () {\n\n\t\t\treturn background.getClearAlpha();\n\n\t\t};\n\n\t\tthis.setClearAlpha = function () {\n\n\t\t\tbackground.setClearAlpha.apply( background, arguments );\n\n\t\t};\n\n\t\tthis.clear = function ( color = true, depth = true, stencil = true ) {\n\n\t\t\tlet bits = 0;\n\n\t\t\tif ( color ) {\n\n\t\t\t\t// check if we're trying to clear an integer target\n\t\t\t\tlet isIntegerFormat = false;\n\t\t\t\tif ( _currentRenderTarget !== null ) {\n\n\t\t\t\t\tconst targetFormat = _currentRenderTarget.texture.format;\n\t\t\t\t\tisIntegerFormat = targetFormat === RGBAIntegerFormat ||\n\t\t\t\t\t\ttargetFormat === RGIntegerFormat ||\n\t\t\t\t\t\ttargetFormat === RedIntegerFormat;\n\n\t\t\t\t}\n\n\t\t\t\t// use the appropriate clear functions to clear the target if it's a signed\n\t\t\t\t// or unsigned integer target\n\t\t\t\tif ( isIntegerFormat ) {\n\n\t\t\t\t\tconst targetType = _currentRenderTarget.texture.type;\n\t\t\t\t\tconst isUnsignedType = targetType === UnsignedByteType ||\n\t\t\t\t\t\ttargetType === UnsignedIntType ||\n\t\t\t\t\t\ttargetType === UnsignedShortType ||\n\t\t\t\t\t\ttargetType === UnsignedInt248Type ||\n\t\t\t\t\t\ttargetType === UnsignedShort4444Type ||\n\t\t\t\t\t\ttargetType === UnsignedShort5551Type;\n\n\t\t\t\t\tconst clearColor = background.getClearColor();\n\t\t\t\t\tconst a = background.getClearAlpha();\n\t\t\t\t\tconst r = clearColor.r;\n\t\t\t\t\tconst g = clearColor.g;\n\t\t\t\t\tconst b = clearColor.b;\n\n\t\t\t\t\tif ( isUnsignedType ) {\n\n\t\t\t\t\t\tuintClearColor[ 0 ] = r;\n\t\t\t\t\t\tuintClearColor[ 1 ] = g;\n\t\t\t\t\t\tuintClearColor[ 2 ] = b;\n\t\t\t\t\t\tuintClearColor[ 3 ] = a;\n\t\t\t\t\t\t_gl.clearBufferuiv( _gl.COLOR, 0, uintClearColor );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tintClearColor[ 0 ] = r;\n\t\t\t\t\t\tintClearColor[ 1 ] = g;\n\t\t\t\t\t\tintClearColor[ 2 ] = b;\n\t\t\t\t\t\tintClearColor[ 3 ] = a;\n\t\t\t\t\t\t_gl.clearBufferiv( _gl.COLOR, 0, intClearColor );\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\tbits |= _gl.COLOR_BUFFER_BIT;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( depth ) bits |= _gl.DEPTH_BUFFER_BIT;\n\t\t\tif ( stencil ) {\n\n\t\t\t\tbits |= _gl.STENCIL_BUFFER_BIT;\n\t\t\t\tthis.state.buffers.stencil.setMask( 0xffffffff );\n\n\t\t\t}\n\n\t\t\t_gl.clear( bits );\n\n\t\t};\n\n\t\tthis.clearColor = function () {\n\n\t\t\tthis.clear( true, false, false );\n\n\t\t};\n\n\t\tthis.clearDepth = function () {\n\n\t\t\tthis.clear( false, true, false );\n\n\t\t};\n\n\t\tthis.clearStencil = function () {\n\n\t\t\tthis.clear( false, false, true );\n\n\t\t};\n\n\t\t//\n\n\t\tthis.dispose = function () {\n\n\t\t\tcanvas.removeEventListener( 'webglcontextlost', onContextLost, false );\n\t\t\tcanvas.removeEventListener( 'webglcontextrestored', onContextRestore, false );\n\t\t\tcanvas.removeEventListener( 'webglcontextcreationerror', onContextCreationError, false );\n\n\t\t\trenderLists.dispose();\n\t\t\trenderStates.dispose();\n\t\t\tproperties.dispose();\n\t\t\tcubemaps.dispose();\n\t\t\tcubeuvmaps.dispose();\n\t\t\tobjects.dispose();\n\t\t\tbindingStates.dispose();\n\t\t\tuniformsGroups.dispose();\n\t\t\tprogramCache.dispose();\n\n\t\t\txr.dispose();\n\n\t\t\txr.removeEventListener( 'sessionstart', onXRSessionStart );\n\t\t\txr.removeEventListener( 'sessionend', onXRSessionEnd );\n\n\t\t\tif ( _transmissionRenderTarget ) {\n\n\t\t\t\t_transmissionRenderTarget.dispose();\n\t\t\t\t_transmissionRenderTarget = null;\n\n\t\t\t}\n\n\t\t\tanimation.stop();\n\n\t\t};\n\n\t\t// Events\n\n\t\tfunction onContextLost( event ) {\n\n\t\t\tevent.preventDefault();\n\n\t\t\tconsole.log( 'THREE.WebGLRenderer: Context Lost.' );\n\n\t\t\t_isContextLost = true;\n\n\t\t}\n\n\t\tfunction onContextRestore( /* event */ ) {\n\n\t\t\tconsole.log( 'THREE.WebGLRenderer: Context Restored.' );\n\n\t\t\t_isContextLost = false;\n\n\t\t\tconst infoAutoReset = info.autoReset;\n\t\t\tconst shadowMapEnabled = shadowMap.enabled;\n\t\t\tconst shadowMapAutoUpdate = shadowMap.autoUpdate;\n\t\t\tconst shadowMapNeedsUpdate = shadowMap.needsUpdate;\n\t\t\tconst shadowMapType = shadowMap.type;\n\n\t\t\tinitGLContext();\n\n\t\t\tinfo.autoReset = infoAutoReset;\n\t\t\tshadowMap.enabled = shadowMapEnabled;\n\t\t\tshadowMap.autoUpdate = shadowMapAutoUpdate;\n\t\t\tshadowMap.needsUpdate = shadowMapNeedsUpdate;\n\t\t\tshadowMap.type = shadowMapType;\n\n\t\t}\n\n\t\tfunction onContextCreationError( event ) {\n\n\t\t\tconsole.error( 'THREE.WebGLRenderer: A WebGL context could not be created. Reason: ', event.statusMessage );\n\n\t\t}\n\n\t\tfunction onMaterialDispose( event ) {\n\n\t\t\tconst material = event.target;\n\n\t\t\tmaterial.removeEventListener( 'dispose', onMaterialDispose );\n\n\t\t\tdeallocateMaterial( material );\n\n\t\t}\n\n\t\t// Buffer deallocation\n\n\t\tfunction deallocateMaterial( material ) {\n\n\t\t\treleaseMaterialProgramReferences( material );\n\n\t\t\tproperties.remove( material );\n\n\t\t}\n\n\n\t\tfunction releaseMaterialProgramReferences( material ) {\n\n\t\t\tconst programs = properties.get( material ).programs;\n\n\t\t\tif ( programs !== undefined ) {\n\n\t\t\t\tprograms.forEach( function ( program ) {\n\n\t\t\t\t\tprogramCache.releaseProgram( program );\n\n\t\t\t\t} );\n\n\t\t\t\tif ( material.isShaderMaterial ) {\n\n\t\t\t\t\tprogramCache.releaseShaderCache( material );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\t// Buffer rendering\n\n\t\tthis.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) {\n\n\t\t\tif ( scene === null ) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null)\n\n\t\t\tconst frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );\n\n\t\t\tconst program = setProgram( camera, scene, geometry, material, object );\n\n\t\t\tstate.setMaterial( material, frontFaceCW );\n\n\t\t\t//\n\n\t\t\tlet index = geometry.index;\n\t\t\tlet rangeFactor = 1;\n\n\t\t\tif ( material.wireframe === true ) {\n\n\t\t\t\tindex = geometries.getWireframeAttribute( geometry );\n\n\t\t\t\tif ( index === undefined ) return;\n\n\t\t\t\trangeFactor = 2;\n\n\t\t\t}\n\n\t\t\t//\n\n\t\t\tconst drawRange = geometry.drawRange;\n\t\t\tconst position = geometry.attributes.position;\n\n\t\t\tlet drawStart = drawRange.start * rangeFactor;\n\t\t\tlet drawEnd = ( drawRange.start + drawRange.count ) * rangeFactor;\n\n\t\t\tif ( group !== null ) {\n\n\t\t\t\tdrawStart = Math.max( drawStart, group.start * rangeFactor );\n\t\t\t\tdrawEnd = Math.min( drawEnd, ( group.start + group.count ) * rangeFactor );\n\n\t\t\t}\n\n\t\t\tif ( index !== null ) {\n\n\t\t\t\tdrawStart = Math.max( drawStart, 0 );\n\t\t\t\tdrawEnd = Math.min( drawEnd, index.count );\n\n\t\t\t} else if ( position !== undefined && position !== null ) {\n\n\t\t\t\tdrawStart = Math.max( drawStart, 0 );\n\t\t\t\tdrawEnd = Math.min( drawEnd, position.count );\n\n\t\t\t}\n\n\t\t\tconst drawCount = drawEnd - drawStart;\n\n\t\t\tif ( drawCount < 0 || drawCount === Infinity ) return;\n\n\t\t\t//\n\n\t\t\tbindingStates.setup( object, material, program, geometry, index );\n\n\t\t\tlet attribute;\n\t\t\tlet renderer = bufferRenderer;\n\n\t\t\tif ( index !== null ) {\n\n\t\t\t\tattribute = attributes.get( index );\n\n\t\t\t\trenderer = indexedBufferRenderer;\n\t\t\t\trenderer.setIndex( attribute );\n\n\t\t\t}\n\n\t\t\t//\n\n\t\t\tif ( object.isMesh ) {\n\n\t\t\t\tif ( material.wireframe === true ) {\n\n\t\t\t\t\tstate.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() );\n\t\t\t\t\trenderer.setMode( _gl.LINES );\n\n\t\t\t\t} else {\n\n\t\t\t\t\trenderer.setMode( _gl.TRIANGLES );\n\n\t\t\t\t}\n\n\t\t\t} else if ( object.isLine ) {\n\n\t\t\t\tlet lineWidth = material.linewidth;\n\n\t\t\t\tif ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material\n\n\t\t\t\tstate.setLineWidth( lineWidth * getTargetPixelRatio() );\n\n\t\t\t\tif ( object.isLineSegments ) {\n\n\t\t\t\t\trenderer.setMode( _gl.LINES );\n\n\t\t\t\t} else if ( object.isLineLoop ) {\n\n\t\t\t\t\trenderer.setMode( _gl.LINE_LOOP );\n\n\t\t\t\t} else {\n\n\t\t\t\t\trenderer.setMode( _gl.LINE_STRIP );\n\n\t\t\t\t}\n\n\t\t\t} else if ( object.isPoints ) {\n\n\t\t\t\trenderer.setMode( _gl.POINTS );\n\n\t\t\t} else if ( object.isSprite ) {\n\n\t\t\t\trenderer.setMode( _gl.TRIANGLES );\n\n\t\t\t}\n\n\t\t\tif ( object.isInstancedMesh ) {\n\n\t\t\t\trenderer.renderInstances( drawStart, drawCount, object.count );\n\n\t\t\t} else if ( geometry.isInstancedBufferGeometry ) {\n\n\t\t\t\tconst maxInstanceCount = geometry._maxInstanceCount !== undefined ? geometry._maxInstanceCount : Infinity;\n\t\t\t\tconst instanceCount = Math.min( geometry.instanceCount, maxInstanceCount );\n\n\t\t\t\trenderer.renderInstances( drawStart, drawCount, instanceCount );\n\n\t\t\t} else {\n\n\t\t\t\trenderer.render( drawStart, drawCount );\n\n\t\t\t}\n\n\t\t};\n\n\t\t// Compile\n\n\t\tfunction prepareMaterial( material, scene, object ) {\n\n\t\t\tif ( material.transparent === true && material.side === DoubleSide && material.forceSinglePass === false ) {\n\n\t\t\t\tmaterial.side = BackSide;\n\t\t\t\tmaterial.needsUpdate = true;\n\t\t\t\tgetProgram( material, scene, object );\n\n\t\t\t\tmaterial.side = FrontSide;\n\t\t\t\tmaterial.needsUpdate = true;\n\t\t\t\tgetProgram( material, scene, object );\n\n\t\t\t\tmaterial.side = DoubleSide;\n\n\t\t\t} else {\n\n\t\t\t\tgetProgram( material, scene, object );\n\n\t\t\t}\n\n\t\t}\n\n\t\tthis.compile = function ( scene, camera, targetScene = null ) {\n\n\t\t\tif ( targetScene === null ) targetScene = scene;\n\n\t\t\tcurrentRenderState = renderStates.get( targetScene );\n\t\t\tcurrentRenderState.init();\n\n\t\t\trenderStateStack.push( currentRenderState );\n\n\t\t\t// gather lights from both the target scene and the new object that will be added to the scene.\n\n\t\t\ttargetScene.traverseVisible( function ( object ) {\n\n\t\t\t\tif ( object.isLight && object.layers.test( camera.layers ) ) {\n\n\t\t\t\t\tcurrentRenderState.pushLight( object );\n\n\t\t\t\t\tif ( object.castShadow ) {\n\n\t\t\t\t\t\tcurrentRenderState.pushShadow( object );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} );\n\n\t\t\tif ( scene !== targetScene ) {\n\n\t\t\t\tscene.traverseVisible( function ( object ) {\n\n\t\t\t\t\tif ( object.isLight && object.layers.test( camera.layers ) ) {\n\n\t\t\t\t\t\tcurrentRenderState.pushLight( object );\n\n\t\t\t\t\t\tif ( object.castShadow ) {\n\n\t\t\t\t\t\t\tcurrentRenderState.pushShadow( object );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t} );\n\n\t\t\t}\n\n\t\t\tcurrentRenderState.setupLights( _this._useLegacyLights );\n\n\t\t\t// Only initialize materials in the new scene, not the targetScene.\n\n\t\t\tconst materials = new Set();\n\n\t\t\tscene.traverse( function ( object ) {\n\n\t\t\t\tconst material = object.material;\n\n\t\t\t\tif ( material ) {\n\n\t\t\t\t\tif ( Array.isArray( material ) ) {\n\n\t\t\t\t\t\tfor ( let i = 0; i < material.length; i ++ ) {\n\n\t\t\t\t\t\t\tconst material2 = material[ i ];\n\n\t\t\t\t\t\t\tprepareMaterial( material2, targetScene, object );\n\t\t\t\t\t\t\tmaterials.add( material2 );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tprepareMaterial( material, targetScene, object );\n\t\t\t\t\t\tmaterials.add( material );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} );\n\n\t\t\trenderStateStack.pop();\n\t\t\tcurrentRenderState = null;\n\n\t\t\treturn materials;\n\n\t\t};\n\n\t\t// compileAsync\n\n\t\tthis.compileAsync = function ( scene, camera, targetScene = null ) {\n\n\t\t\tconst materials = this.compile( scene, camera, targetScene );\n\n\t\t\t// Wait for all the materials in the new object to indicate that they're\n\t\t\t// ready to be used before resolving the promise.\n\n\t\t\treturn new Promise( ( resolve ) => {\n\n\t\t\t\tfunction checkMaterialsReady() {\n\n\t\t\t\t\tmaterials.forEach( function ( material ) {\n\n\t\t\t\t\t\tconst materialProperties = properties.get( material );\n\t\t\t\t\t\tconst program = materialProperties.currentProgram;\n\n\t\t\t\t\t\tif ( program.isReady() ) {\n\n\t\t\t\t\t\t\t// remove any programs that report they're ready to use from the list\n\t\t\t\t\t\t\tmaterials.delete( material );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} );\n\n\t\t\t\t\t// once the list of compiling materials is empty, call the callback\n\n\t\t\t\t\tif ( materials.size === 0 ) {\n\n\t\t\t\t\t\tresolve( scene );\n\t\t\t\t\t\treturn;\n\n\t\t\t\t\t}\n\n\t\t\t\t\t// if some materials are still not ready, wait a bit and check again\n\n\t\t\t\t\tsetTimeout( checkMaterialsReady, 10 );\n\n\t\t\t\t}\n\n\t\t\t\tif ( extensions.get( 'KHR_parallel_shader_compile' ) !== null ) {\n\n\t\t\t\t\t// If we can check the compilation status of the materials without\n\t\t\t\t\t// blocking then do so right away.\n\n\t\t\t\t\tcheckMaterialsReady();\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// Otherwise start by waiting a bit to give the materials we just\n\t\t\t\t\t// initialized a chance to finish.\n\n\t\t\t\t\tsetTimeout( checkMaterialsReady, 10 );\n\n\t\t\t\t}\n\n\t\t\t} );\n\n\t\t};\n\n\t\t// Animation Loop\n\n\t\tlet onAnimationFrameCallback = null;\n\n\t\tfunction onAnimationFrame( time ) {\n\n\t\t\tif ( onAnimationFrameCallback ) onAnimationFrameCallback( time );\n\n\t\t}\n\n\t\tfunction onXRSessionStart() {\n\n\t\t\tanimation.stop();\n\n\t\t}\n\n\t\tfunction onXRSessionEnd() {\n\n\t\t\tanimation.start();\n\n\t\t}\n\n\t\tconst animation = new WebGLAnimation();\n\t\tanimation.setAnimationLoop( onAnimationFrame );\n\n\t\tif ( typeof self !== 'undefined' ) animation.setContext( self );\n\n\t\tthis.setAnimationLoop = function ( callback ) {\n\n\t\t\tonAnimationFrameCallback = callback;\n\t\t\txr.setAnimationLoop( callback );\n\n\t\t\t( callback === null ) ? animation.stop() : animation.start();\n\n\t\t};\n\n\t\txr.addEventListener( 'sessionstart', onXRSessionStart );\n\t\txr.addEventListener( 'sessionend', onXRSessionEnd );\n\n\t\t// Rendering\n\n\t\tthis.render = function ( scene, camera ) {\n\n\t\t\tif ( camera !== undefined && camera.isCamera !== true ) {\n\n\t\t\t\tconsole.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' );\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\tif ( _isContextLost === true ) return;\n\n\t\t\t// update scene graph\n\n\t\t\tif ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();\n\n\t\t\t// update camera matrices and frustum\n\n\t\t\tif ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();\n\n\t\t\tif ( xr.enabled === true && xr.isPresenting === true ) {\n\n\t\t\t\tif ( xr.cameraAutoUpdate === true ) xr.updateCamera( camera );\n\n\t\t\t\tcamera = xr.getCamera(); // use XR camera for rendering\n\n\t\t\t}\n\n\t\t\t//\n\t\t\tif ( scene.isScene === true ) scene.onBeforeRender( _this, scene, camera, _currentRenderTarget );\n\n\t\t\tcurrentRenderState = renderStates.get( scene, renderStateStack.length );\n\t\t\tcurrentRenderState.init();\n\n\t\t\trenderStateStack.push( currentRenderState );\n\n\t\t\t_projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );\n\t\t\t_frustum.setFromProjectionMatrix( _projScreenMatrix );\n\n\t\t\t_localClippingEnabled = this.localClippingEnabled;\n\t\t\t_clippingEnabled = clipping.init( this.clippingPlanes, _localClippingEnabled );\n\n\t\t\tcurrentRenderList = renderLists.get( scene, renderListStack.length );\n\t\t\tcurrentRenderList.init();\n\n\t\t\trenderListStack.push( currentRenderList );\n\n\t\t\tprojectObject( scene, camera, 0, _this.sortObjects );\n\n\t\t\tcurrentRenderList.finish();\n\n\t\t\tif ( _this.sortObjects === true ) {\n\n\t\t\t\tcurrentRenderList.sort( _opaqueSort, _transparentSort );\n\n\t\t\t}\n\n\t\t\t//\n\n\t\t\tthis.info.render.frame ++;\n\n\t\t\tif ( _clippingEnabled === true ) clipping.beginShadows();\n\n\t\t\tconst shadowsArray = currentRenderState.state.shadowsArray;\n\n\t\t\tshadowMap.render( shadowsArray, scene, camera );\n\n\t\t\tif ( _clippingEnabled === true ) clipping.endShadows();\n\n\t\t\t//\n\n\t\t\tif ( this.info.autoReset === true ) this.info.reset();\n\n\n\t\t\t//\n\n\t\t\tbackground.render( currentRenderList, scene );\n\n\t\t\t// render scene\n\n\t\t\tcurrentRenderState.setupLights( _this._useLegacyLights );\n\n\t\t\tif ( camera.isArrayCamera ) {\n\n\t\t\t\tconst cameras = camera.cameras;\n\n\t\t\t\tfor ( let i = 0, l = cameras.length; i < l; i ++ ) {\n\n\t\t\t\t\tconst camera2 = cameras[ i ];\n\n\t\t\t\t\trenderScene( currentRenderList, scene, camera2, camera2.viewport );\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\trenderScene( currentRenderList, scene, camera );\n\n\t\t\t}\n\n\t\t\t//\n\n\t\t\tif ( _currentRenderTarget !== null ) {\n\n\t\t\t\t// resolve multisample renderbuffers to a single-sample texture if necessary\n\n\t\t\t\ttextures.updateMultisampleRenderTarget( _currentRenderTarget );\n\n\t\t\t\t// Generate mipmap if we're using any kind of mipmap filtering\n\n\t\t\t\ttextures.updateRenderTargetMipmap( _currentRenderTarget );\n\n\t\t\t}\n\n\t\t\t//\n\n\t\t\tif ( scene.isScene === true ) scene.onAfterRender( _this, scene, camera );\n\n\t\t\t// _gl.finish();\n\n\t\t\tbindingStates.resetDefaultState();\n\t\t\t_currentMaterialId = - 1;\n\t\t\t_currentCamera = null;\n\n\t\t\trenderStateStack.pop();\n\n\t\t\tif ( renderStateStack.length > 0 ) {\n\n\t\t\t\tcurrentRenderState = renderStateStack[ renderStateStack.length - 1 ];\n\n\t\t\t} else {\n\n\t\t\t\tcurrentRenderState = null;\n\n\t\t\t}\n\n\t\t\trenderListStack.pop();\n\n\t\t\tif ( renderListStack.length > 0 ) {\n\n\t\t\t\tcurrentRenderList = renderListStack[ renderListStack.length - 1 ];\n\n\t\t\t} else {\n\n\t\t\t\tcurrentRenderList = null;\n\n\t\t\t}\n\n\t\t};\n\n\t\tfunction projectObject( object, camera, groupOrder, sortObjects ) {\n\n\t\t\tif ( object.visible === false ) return;\n\n\t\t\tconst visible = object.layers.test( camera.layers );\n\n\t\t\tif ( visible ) {\n\n\t\t\t\tif ( object.isGroup ) {\n\n\t\t\t\t\tgroupOrder = object.renderOrder;\n\n\t\t\t\t} else if ( object.isLOD ) {\n\n\t\t\t\t\tif ( object.autoUpdate === true ) object.update( camera );\n\n\t\t\t\t} else if ( object.isLight ) {\n\n\t\t\t\t\tcurrentRenderState.pushLight( object );\n\n\t\t\t\t\tif ( object.castShadow ) {\n\n\t\t\t\t\t\tcurrentRenderState.pushShadow( object );\n\n\t\t\t\t\t}\n\n\t\t\t\t} else if ( object.isSprite ) {\n\n\t\t\t\t\tif ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) {\n\n\t\t\t\t\t\tif ( sortObjects ) {\n\n\t\t\t\t\t\t\t_vector3.setFromMatrixPosition( object.matrixWorld )\n\t\t\t\t\t\t\t\t.applyMatrix4( _projScreenMatrix );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst geometry = objects.update( object );\n\t\t\t\t\t\tconst material = object.material;\n\n\t\t\t\t\t\tif ( material.visible ) {\n\n\t\t\t\t\t\t\tcurrentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t} else if ( object.isMesh || object.isLine || object.isPoints ) {\n\n\t\t\t\t\tif ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) {\n\n\t\t\t\t\t\tconst geometry = objects.update( object );\n\t\t\t\t\t\tconst material = object.material;\n\n\t\t\t\t\t\tif ( sortObjects ) {\n\n\t\t\t\t\t\t\tif ( object.boundingSphere !== undefined ) {\n\n\t\t\t\t\t\t\t\tif ( object.boundingSphere === null ) object.computeBoundingSphere();\n\t\t\t\t\t\t\t\t_vector3.copy( object.boundingSphere.center );\n\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\tif ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();\n\t\t\t\t\t\t\t\t_vector3.copy( geometry.boundingSphere.center );\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t_vector3\n\t\t\t\t\t\t\t\t.applyMatrix4( object.matrixWorld )\n\t\t\t\t\t\t\t\t.applyMatrix4( _projScreenMatrix );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ( Array.isArray( material ) ) {\n\n\t\t\t\t\t\t\tconst groups = geometry.groups;\n\n\t\t\t\t\t\t\tfor ( let i = 0, l = groups.length; i < l; i ++ ) {\n\n\t\t\t\t\t\t\t\tconst group = groups[ i ];\n\t\t\t\t\t\t\t\tconst groupMaterial = material[ group.materialIndex ];\n\n\t\t\t\t\t\t\t\tif ( groupMaterial && groupMaterial.visible ) {\n\n\t\t\t\t\t\t\t\t\tcurrentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group );\n\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t} else if ( material.visible ) {\n\n\t\t\t\t\t\t\tcurrentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tconst children = object.children;\n\n\t\t\tfor ( let i = 0, l = children.length; i < l; i ++ ) {\n\n\t\t\t\tprojectObject( children[ i ], camera, groupOrder, sortObjects );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction renderScene( currentRenderList, scene, camera, viewport ) {\n\n\t\t\tconst opaqueObjects = currentRenderList.opaque;\n\t\t\tconst transmissiveObjects = currentRenderList.transmissive;\n\t\t\tconst transparentObjects = currentRenderList.transparent;\n\n\t\t\tcurrentRenderState.setupLightsView( camera );\n\n\t\t\tif ( _clippingEnabled === true ) clipping.setGlobalState( _this.clippingPlanes, camera );\n\n\t\t\tif ( transmissiveObjects.length > 0 ) renderTransmissionPass( opaqueObjects, transmissiveObjects, scene, camera );\n\n\t\t\tif ( viewport ) state.viewport( _currentViewport.copy( viewport ) );\n\n\t\t\tif ( opaqueObjects.length > 0 ) renderObjects( opaqueObjects, scene, camera );\n\t\t\tif ( transmissiveObjects.length > 0 ) renderObjects( transmissiveObjects, scene, camera );\n\t\t\tif ( transparentObjects.length > 0 ) renderObjects( transparentObjects, scene, camera );\n\n\t\t\t// Ensure depth buffer writing is enabled so it can be cleared on next render\n\n\t\t\tstate.buffers.depth.setTest( true );\n\t\t\tstate.buffers.depth.setMask( true );\n\t\t\tstate.buffers.color.setMask( true );\n\n\t\t\tstate.setPolygonOffset( false );\n\n\t\t}\n\n\t\tfunction renderTransmissionPass( opaqueObjects, transmissiveObjects, scene, camera ) {\n\n\t\t\tconst overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null;\n\n\t\t\tif ( overrideMaterial !== null ) {\n\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\tconst isWebGL2 = capabilities.isWebGL2;\n\n\t\t\tif ( _transmissionRenderTarget === null ) {\n\n\t\t\t\t_transmissionRenderTarget = new WebGLRenderTarget( 1, 1, {\n\t\t\t\t\tgenerateMipmaps: true,\n\t\t\t\t\ttype: extensions.has( 'EXT_color_buffer_half_float' ) ? HalfFloatType : UnsignedByteType,\n\t\t\t\t\tminFilter: LinearMipmapLinearFilter,\n\t\t\t\t\tsamples: ( isWebGL2 ) ? 4 : 0\n\t\t\t\t} );\n\n\t\t\t\t// debug\n\n\t\t\t\t/*\n\t\t\t\tconst geometry = new PlaneGeometry();\n\t\t\t\tconst material = new MeshBasicMaterial( { map: _transmissionRenderTarget.texture } );\n\n\t\t\t\tconst mesh = new Mesh( geometry, material );\n\t\t\t\tscene.add( mesh );\n\t\t\t\t*/\n\n\t\t\t}\n\n\t\t\t_this.getDrawingBufferSize( _vector2 );\n\n\t\t\tif ( isWebGL2 ) {\n\n\t\t\t\t_transmissionRenderTarget.setSize( _vector2.x, _vector2.y );\n\n\t\t\t} else {\n\n\t\t\t\t_transmissionRenderTarget.setSize( floorPowerOfTwo( _vector2.x ), floorPowerOfTwo( _vector2.y ) );\n\n\t\t\t}\n\n\t\t\t//\n\n\t\t\tconst currentRenderTarget = _this.getRenderTarget();\n\t\t\t_this.setRenderTarget( _transmissionRenderTarget );\n\n\t\t\t_this.getClearColor( _currentClearColor );\n\t\t\t_currentClearAlpha = _this.getClearAlpha();\n\t\t\tif ( _currentClearAlpha < 1 ) _this.setClearColor( 0xffffff, 0.5 );\n\n\t\t\t_this.clear();\n\n\t\t\t// Turn off the features which can affect the frag color for opaque objects pass.\n\t\t\t// Otherwise they are applied twice in opaque objects pass and transmission objects pass.\n\t\t\tconst currentToneMapping = _this.toneMapping;\n\t\t\t_this.toneMapping = NoToneMapping;\n\n\t\t\trenderObjects( opaqueObjects, scene, camera );\n\n\t\t\ttextures.updateMultisampleRenderTarget( _transmissionRenderTarget );\n\t\t\ttextures.updateRenderTargetMipmap( _transmissionRenderTarget );\n\n\t\t\tlet renderTargetNeedsUpdate = false;\n\n\t\t\tfor ( let i = 0, l = transmissiveObjects.length; i < l; i ++ ) {\n\n\t\t\t\tconst renderItem = transmissiveObjects[ i ];\n\n\t\t\t\tconst object = renderItem.object;\n\t\t\t\tconst geometry = renderItem.geometry;\n\t\t\t\tconst material = renderItem.material;\n\t\t\t\tconst group = renderItem.group;\n\n\t\t\t\tif ( material.side === DoubleSide && object.layers.test( camera.layers ) ) {\n\n\t\t\t\t\tconst currentSide = material.side;\n\n\t\t\t\t\tmaterial.side = BackSide;\n\t\t\t\t\tmaterial.needsUpdate = true;\n\n\t\t\t\t\trenderObject( object, scene, camera, geometry, material, group );\n\n\t\t\t\t\tmaterial.side = currentSide;\n\t\t\t\t\tmaterial.needsUpdate = true;\n\n\t\t\t\t\trenderTargetNeedsUpdate = true;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif ( renderTargetNeedsUpdate === true ) {\n\n\t\t\t\ttextures.updateMultisampleRenderTarget( _transmissionRenderTarget );\n\t\t\t\ttextures.updateRenderTargetMipmap( _transmissionRenderTarget );\n\n\t\t\t}\n\n\t\t\t_this.setRenderTarget( currentRenderTarget );\n\n\t\t\t_this.setClearColor( _currentClearColor, _currentClearAlpha );\n\n\t\t\t_this.toneMapping = currentToneMapping;\n\n\t\t}\n\n\t\tfunction renderObjects( renderList, scene, camera ) {\n\n\t\t\tconst overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null;\n\n\t\t\tfor ( let i = 0, l = renderList.length; i < l; i ++ ) {\n\n\t\t\t\tconst renderItem = renderList[ i ];\n\n\t\t\t\tconst object = renderItem.object;\n\t\t\t\tconst geometry = renderItem.geometry;\n\t\t\t\tconst material = overrideMaterial === null ? renderItem.material : overrideMaterial;\n\t\t\t\tconst group = renderItem.group;\n\n\t\t\t\tif ( object.layers.test( camera.layers ) ) {\n\n\t\t\t\t\trenderObject( object, scene, camera, geometry, material, group );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction renderObject( object, scene, camera, geometry, material, group ) {\n\n\t\t\tobject.onBeforeRender( _this, scene, camera, geometry, material, group );\n\n\t\t\tobject.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );\n\t\t\tobject.normalMatrix.getNormalMatrix( object.modelViewMatrix );\n\n\t\t\tmaterial.onBeforeRender( _this, scene, camera, geometry, object, group );\n\n\t\t\tif ( material.transparent === true && material.side === DoubleSide && material.forceSinglePass === false ) {\n\n\t\t\t\tmaterial.side = BackSide;\n\t\t\t\tmaterial.needsUpdate = true;\n\t\t\t\t_this.renderBufferDirect( camera, scene, geometry, material, object, group );\n\n\t\t\t\tmaterial.side = FrontSide;\n\t\t\t\tmaterial.needsUpdate = true;\n\t\t\t\t_this.renderBufferDirect( camera, scene, geometry, material, object, group );\n\n\t\t\t\tmaterial.side = DoubleSide;\n\n\t\t\t} else {\n\n\t\t\t\t_this.renderBufferDirect( camera, scene, geometry, material, object, group );\n\n\t\t\t}\n\n\t\t\tobject.onAfterRender( _this, scene, camera, geometry, material, group );\n\n\t\t}\n\n\t\tfunction getProgram( material, scene, object ) {\n\n\t\t\tif ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ...\n\n\t\t\tconst materialProperties = properties.get( material );\n\n\t\t\tconst lights = currentRenderState.state.lights;\n\t\t\tconst shadowsArray = currentRenderState.state.shadowsArray;\n\n\t\t\tconst lightsStateVersion = lights.state.version;\n\n\t\t\tconst parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, object );\n\t\t\tconst programCacheKey = programCache.getProgramCacheKey( parameters );\n\n\t\t\tlet programs = materialProperties.programs;\n\n\t\t\t// always update environment and fog - changing these trigger an getProgram call, but it's possible that the program doesn't change\n\n\t\t\tmaterialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null;\n\t\t\tmaterialProperties.fog = scene.fog;\n\t\t\tmaterialProperties.envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || materialProperties.environment );\n\n\t\t\tif ( programs === undefined ) {\n\n\t\t\t\t// new material\n\n\t\t\t\tmaterial.addEventListener( 'dispose', onMaterialDispose );\n\n\t\t\t\tprograms = new Map();\n\t\t\t\tmaterialProperties.programs = programs;\n\n\t\t\t}\n\n\t\t\tlet program = programs.get( programCacheKey );\n\n\t\t\tif ( program !== undefined ) {\n\n\t\t\t\t// early out if program and light state is identical\n\n\t\t\t\tif ( materialProperties.currentProgram === program && materialProperties.lightsStateVersion === lightsStateVersion ) {\n\n\t\t\t\t\tupdateCommonMaterialProperties( material, parameters );\n\n\t\t\t\t\treturn program;\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tparameters.uniforms = programCache.getUniforms( material );\n\n\t\t\t\tmaterial.onBuild( object, parameters, _this );\n\n\t\t\t\tmaterial.onBeforeCompile( parameters, _this );\n\n\t\t\t\tprogram = programCache.acquireProgram( parameters, programCacheKey );\n\t\t\t\tprograms.set( programCacheKey, program );\n\n\t\t\t\tmaterialProperties.uniforms = parameters.uniforms;\n\n\t\t\t}\n\n\t\t\tconst uniforms = materialProperties.uniforms;\n\n\t\t\tif ( ( ! material.isShaderMaterial && ! material.isRawShaderMaterial ) || material.clipping === true ) {\n\n\t\t\t\tuniforms.clippingPlanes = clipping.uniform;\n\n\t\t\t}\n\n\t\t\tupdateCommonMaterialProperties( material, parameters );\n\n\t\t\t// store the light setup it was created for\n\n\t\t\tmaterialProperties.needsLights = materialNeedsLights( material );\n\t\t\tmaterialProperties.lightsStateVersion = lightsStateVersion;\n\n\t\t\tif ( materialProperties.needsLights ) {\n\n\t\t\t\t// wire up the material to this renderer's lighting state\n\n\t\t\t\tuniforms.ambientLightColor.value = lights.state.ambient;\n\t\t\t\tuniforms.lightProbe.value = lights.state.probe;\n\t\t\t\tuniforms.directionalLights.value = lights.state.directional;\n\t\t\t\tuniforms.directionalLightShadows.value = lights.state.directionalShadow;\n\t\t\t\tuniforms.spotLights.value = lights.state.spot;\n\t\t\t\tuniforms.spotLightShadows.value = lights.state.spotShadow;\n\t\t\t\tuniforms.rectAreaLights.value = lights.state.rectArea;\n\t\t\t\tuniforms.ltc_1.value = lights.state.rectAreaLTC1;\n\t\t\t\tuniforms.ltc_2.value = lights.state.rectAreaLTC2;\n\t\t\t\tuniforms.pointLights.value = lights.state.point;\n\t\t\t\tuniforms.pointLightShadows.value = lights.state.pointShadow;\n\t\t\t\tuniforms.hemisphereLights.value = lights.state.hemi;\n\n\t\t\t\tuniforms.directionalShadowMap.value = lights.state.directionalShadowMap;\n\t\t\t\tuniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix;\n\t\t\t\tuniforms.spotShadowMap.value = lights.state.spotShadowMap;\n\t\t\t\tuniforms.spotLightMatrix.value = lights.state.spotLightMatrix;\n\t\t\t\tuniforms.spotLightMap.value = lights.state.spotLightMap;\n\t\t\t\tuniforms.pointShadowMap.value = lights.state.pointShadowMap;\n\t\t\t\tuniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix;\n\t\t\t\t// TODO (abelnation): add area lights shadow info to uniforms\n\n\t\t\t}\n\n\t\t\tmaterialProperties.currentProgram = program;\n\t\t\tmaterialProperties.uniformsList = null;\n\n\t\t\treturn program;\n\n\t\t}\n\n\t\tfunction getUniformList( materialProperties ) {\n\n\t\t\tif ( materialProperties.uniformsList === null ) {\n\n\t\t\t\tconst progUniforms = materialProperties.currentProgram.getUniforms();\n\t\t\t\tmaterialProperties.uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, materialProperties.uniforms );\n\n\t\t\t}\n\n\t\t\treturn materialProperties.uniformsList;\n\n\t\t}\n\n\t\tfunction updateCommonMaterialProperties( material, parameters ) {\n\n\t\t\tconst materialProperties = properties.get( material );\n\n\t\t\tmaterialProperties.outputColorSpace = parameters.outputColorSpace;\n\t\t\tmaterialProperties.instancing = parameters.instancing;\n\t\t\tmaterialProperties.instancingColor = parameters.instancingColor;\n\t\t\tmaterialProperties.skinning = parameters.skinning;\n\t\t\tmaterialProperties.morphTargets = parameters.morphTargets;\n\t\t\tmaterialProperties.morphNormals = parameters.morphNormals;\n\t\t\tmaterialProperties.morphColors = parameters.morphColors;\n\t\t\tmaterialProperties.morphTargetsCount = parameters.morphTargetsCount;\n\t\t\tmaterialProperties.numClippingPlanes = parameters.numClippingPlanes;\n\t\t\tmaterialProperties.numIntersection = parameters.numClipIntersection;\n\t\t\tmaterialProperties.vertexAlphas = parameters.vertexAlphas;\n\t\t\tmaterialProperties.vertexTangents = parameters.vertexTangents;\n\t\t\tmaterialProperties.toneMapping = parameters.toneMapping;\n\n\t\t}\n\n\t\tfunction setProgram( camera, scene, geometry, material, object ) {\n\n\t\t\tif ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ...\n\n\t\t\ttextures.resetTextureUnits();\n\n\t\t\tconst fog = scene.fog;\n\t\t\tconst environment = material.isMeshStandardMaterial ? scene.environment : null;\n\t\t\tconst colorSpace = ( _currentRenderTarget === null ) ? _this.outputColorSpace : ( _currentRenderTarget.isXRRenderTarget === true ? _currentRenderTarget.texture.colorSpace : LinearSRGBColorSpace );\n\t\t\tconst envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment );\n\t\t\tconst vertexAlphas = material.vertexColors === true && !! geometry.attributes.color && geometry.attributes.color.itemSize === 4;\n\t\t\tconst vertexTangents = !! geometry.attributes.tangent && ( !! material.normalMap || material.anisotropy > 0 );\n\t\t\tconst morphTargets = !! geometry.morphAttributes.position;\n\t\t\tconst morphNormals = !! geometry.morphAttributes.normal;\n\t\t\tconst morphColors = !! geometry.morphAttributes.color;\n\n\t\t\tlet toneMapping = NoToneMapping;\n\n\t\t\tif ( material.toneMapped ) {\n\n\t\t\t\tif ( _currentRenderTarget === null || _currentRenderTarget.isXRRenderTarget === true ) {\n\n\t\t\t\t\ttoneMapping = _this.toneMapping;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tconst morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color;\n\t\t\tconst morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0;\n\n\t\t\tconst materialProperties = properties.get( material );\n\t\t\tconst lights = currentRenderState.state.lights;\n\n\t\t\tif ( _clippingEnabled === true ) {\n\n\t\t\t\tif ( _localClippingEnabled === true || camera !== _currentCamera ) {\n\n\t\t\t\t\tconst useCache =\n\t\t\t\t\t\tcamera === _currentCamera &&\n\t\t\t\t\t\tmaterial.id === _currentMaterialId;\n\n\t\t\t\t\t// we might want to call this function with some ClippingGroup\n\t\t\t\t\t// object instead of the material, once it becomes feasible\n\t\t\t\t\t// (#8465, #8379)\n\t\t\t\t\tclipping.setState( material, camera, useCache );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t//\n\n\t\t\tlet needsProgramChange = false;\n\n\t\t\tif ( material.version === materialProperties.__version ) {\n\n\t\t\t\tif ( materialProperties.needsLights && ( materialProperties.lightsStateVersion !== lights.state.version ) ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( materialProperties.outputColorSpace !== colorSpace ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( object.isInstancedMesh && materialProperties.instancing === false ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( ! object.isInstancedMesh && materialProperties.instancing === true ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( object.isSkinnedMesh && materialProperties.skinning === false ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( ! object.isSkinnedMesh && materialProperties.skinning === true ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( object.isInstancedMesh && materialProperties.instancingColor === true && object.instanceColor === null ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( object.isInstancedMesh && materialProperties.instancingColor === false && object.instanceColor !== null ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( materialProperties.envMap !== envMap ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( material.fog === true && materialProperties.fog !== fog ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( materialProperties.numClippingPlanes !== undefined &&\n\t\t\t\t\t( materialProperties.numClippingPlanes !== clipping.numPlanes ||\n\t\t\t\t\tmaterialProperties.numIntersection !== clipping.numIntersection ) ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( materialProperties.vertexAlphas !== vertexAlphas ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( materialProperties.vertexTangents !== vertexTangents ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( materialProperties.morphTargets !== morphTargets ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( materialProperties.morphNormals !== morphNormals ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( materialProperties.morphColors !== morphColors ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( materialProperties.toneMapping !== toneMapping ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t} else if ( capabilities.isWebGL2 === true && materialProperties.morphTargetsCount !== morphTargetsCount ) {\n\n\t\t\t\t\tneedsProgramChange = true;\n\n\t\t\t\t}\n\n\t\t\t} else {\n\n\t\t\t\tneedsProgramChange = true;\n\t\t\t\tmaterialProperties.__version = material.version;\n\n\t\t\t}\n\n\t\t\t//\n\n\t\t\tlet program = materialProperties.currentProgram;\n\n\t\t\tif ( needsProgramChange === true ) {\n\n\t\t\t\tprogram = getProgram( material, scene, object );\n\n\t\t\t}\n\n\t\t\tlet refreshProgram = false;\n\t\t\tlet refreshMaterial = false;\n\t\t\tlet refreshLights = false;\n\n\t\t\tconst p_uniforms = program.getUniforms(),\n\t\t\t\tm_uniforms = materialProperties.uniforms;\n\n\t\t\tif ( state.useProgram( program.program ) ) {\n\n\t\t\t\trefreshProgram = true;\n\t\t\t\trefreshMaterial = true;\n\t\t\t\trefreshLights = true;\n\n\t\t\t}\n\n\t\t\tif ( material.id !== _currentMaterialId ) {\n\n\t\t\t\t_currentMaterialId = material.id;\n\n\t\t\t\trefreshMaterial = true;\n\n\t\t\t}\n\n\t\t\tif ( refreshProgram || _currentCamera !== camera ) {\n\n\t\t\t\t// common camera uniforms\n\n\t\t\t\tp_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix );\n\t\t\t\tp_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse );\n\n\t\t\t\tconst uCamPos = p_uniforms.map.cameraPosition;\n\n\t\t\t\tif ( uCamPos !== undefined ) {\n\n\t\t\t\t\tuCamPos.setValue( _gl, _vector3.setFromMatrixPosition( camera.matrixWorld ) );\n\n\t\t\t\t}\n\n\t\t\t\tif ( capabilities.logarithmicDepthBuffer ) {\n\n\t\t\t\t\tp_uniforms.setValue( _gl, 'logDepthBufFC',\n\t\t\t\t\t\t2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) );\n\n\t\t\t\t}\n\n\t\t\t\t// consider moving isOrthographic to UniformLib and WebGLMaterials, see https://github.com/mrdoob/three.js/pull/26467#issuecomment-1645185067\n\n\t\t\t\tif ( material.isMeshPhongMaterial ||\n\t\t\t\t\tmaterial.isMeshToonMaterial ||\n\t\t\t\t\tmaterial.isMeshLambertMaterial ||\n\t\t\t\t\tmaterial.isMeshBasicMaterial ||\n\t\t\t\t\tmaterial.isMeshStandardMaterial ||\n\t\t\t\t\tmaterial.isShaderMaterial ) {\n\n\t\t\t\t\tp_uniforms.setValue( _gl, 'isOrthographic', camera.isOrthographicCamera === true );\n\n\t\t\t\t}\n\n\t\t\t\tif ( _currentCamera !== camera ) {\n\n\t\t\t\t\t_currentCamera = camera;\n\n\t\t\t\t\t// lighting uniforms depend on the camera so enforce an update\n\t\t\t\t\t// now, in case this material supports lights - or later, when\n\t\t\t\t\t// the next material that does gets activated:\n\n\t\t\t\t\trefreshMaterial = true;\t\t// set to true on material change\n\t\t\t\t\trefreshLights = true;\t\t// remains set until update done\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// skinning and morph target uniforms must be set even if material didn't change\n\t\t\t// auto-setting of texture unit for bone and morph texture must go before other textures\n\t\t\t// otherwise textures used for skinning and morphing can take over texture units reserved for other material textures\n\n\t\t\tif ( object.isSkinnedMesh ) {\n\n\t\t\t\tp_uniforms.setOptional( _gl, object, 'bindMatrix' );\n\t\t\t\tp_uniforms.setOptional( _gl, object, 'bindMatrixInverse' );\n\n\t\t\t\tconst skeleton = object.skeleton;\n\n\t\t\t\tif ( skeleton ) {\n\n\t\t\t\t\tif ( capabilities.floatVertexTextures ) {\n\n\t\t\t\t\t\tif ( skeleton.boneTexture === null ) skeleton.computeBoneTexture();\n\n\t\t\t\t\t\tp_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture, textures );\n\t\t\t\t\t\tp_uniforms.setValue( _gl, 'boneTextureSize', skeleton.boneTextureSize );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: SkinnedMesh can only be used with WebGL 2. With WebGL 1 OES_texture_float and vertex textures support is required.' );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tconst morphAttributes = geometry.morphAttributes;\n\n\t\t\tif ( morphAttributes.position !== undefined || morphAttributes.normal !== undefined || ( morphAttributes.color !== undefined && capabilities.isWebGL2 === true ) ) {\n\n\t\t\t\tmorphtargets.update( object, geometry, program );\n\n\t\t\t}\n\n\t\t\tif ( refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow ) {\n\n\t\t\t\tmaterialProperties.receiveShadow = object.receiveShadow;\n\t\t\t\tp_uniforms.setValue( _gl, 'receiveShadow', object.receiveShadow );\n\n\t\t\t}\n\n\t\t\t// https://github.com/mrdoob/three.js/pull/24467#issuecomment-1209031512\n\n\t\t\tif ( material.isMeshGouraudMaterial && material.envMap !== null ) {\n\n\t\t\t\tm_uniforms.envMap.value = envMap;\n\n\t\t\t\tm_uniforms.flipEnvMap.value = ( envMap.isCubeTexture && envMap.isRenderTargetTexture === false ) ? - 1 : 1;\n\n\t\t\t}\n\n\t\t\tif ( refreshMaterial ) {\n\n\t\t\t\tp_uniforms.setValue( _gl, 'toneMappingExposure', _this.toneMappingExposure );\n\n\t\t\t\tif ( materialProperties.needsLights ) {\n\n\t\t\t\t\t// the current material requires lighting info\n\n\t\t\t\t\t// note: all lighting uniforms are always set correctly\n\t\t\t\t\t// they simply reference the renderer's state for their\n\t\t\t\t\t// values\n\t\t\t\t\t//\n\t\t\t\t\t// use the current material's .needsUpdate flags to set\n\t\t\t\t\t// the GL state when required\n\n\t\t\t\t\tmarkUniformsLightsNeedsUpdate( m_uniforms, refreshLights );\n\n\t\t\t\t}\n\n\t\t\t\t// refresh uniforms common to several materials\n\n\t\t\t\tif ( fog && material.fog === true ) {\n\n\t\t\t\t\tmaterials.refreshFogUniforms( m_uniforms, fog );\n\n\t\t\t\t}\n\n\t\t\t\tmaterials.refreshMaterialUniforms( m_uniforms, material, _pixelRatio, _height, _transmissionRenderTarget );\n\n\t\t\t\tWebGLUniforms.upload( _gl, getUniformList( materialProperties ), m_uniforms, textures );\n\n\t\t\t}\n\n\t\t\tif ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) {\n\n\t\t\t\tWebGLUniforms.upload( _gl, getUniformList( materialProperties ), m_uniforms, textures );\n\t\t\t\tmaterial.uniformsNeedUpdate = false;\n\n\t\t\t}\n\n\t\t\tif ( material.isSpriteMaterial ) {\n\n\t\t\t\tp_uniforms.setValue( _gl, 'center', object.center );\n\n\t\t\t}\n\n\t\t\t// common matrices\n\n\t\t\tp_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix );\n\t\t\tp_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix );\n\t\t\tp_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld );\n\n\t\t\t// UBOs\n\n\t\t\tif ( material.isShaderMaterial || material.isRawShaderMaterial ) {\n\n\t\t\t\tconst groups = material.uniformsGroups;\n\n\t\t\t\tfor ( let i = 0, l = groups.length; i < l; i ++ ) {\n\n\t\t\t\t\tif ( capabilities.isWebGL2 ) {\n\n\t\t\t\t\t\tconst group = groups[ i ];\n\n\t\t\t\t\t\tuniformsGroups.update( group, program );\n\t\t\t\t\t\tuniformsGroups.bind( group, program );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Uniform Buffer Objects can only be used with WebGL 2.' );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn program;\n\n\t\t}\n\n\t\t// If uniforms are marked as clean, they don't need to be loaded to the GPU.\n\n\t\tfunction markUniformsLightsNeedsUpdate( uniforms, value ) {\n\n\t\t\tuniforms.ambientLightColor.needsUpdate = value;\n\t\t\tuniforms.lightProbe.needsUpdate = value;\n\n\t\t\tuniforms.directionalLights.needsUpdate = value;\n\t\t\tuniforms.directionalLightShadows.needsUpdate = value;\n\t\t\tuniforms.pointLights.needsUpdate = value;\n\t\t\tuniforms.pointLightShadows.needsUpdate = value;\n\t\t\tuniforms.spotLights.needsUpdate = value;\n\t\t\tuniforms.spotLightShadows.needsUpdate = value;\n\t\t\tuniforms.rectAreaLights.needsUpdate = value;\n\t\t\tuniforms.hemisphereLights.needsUpdate = value;\n\n\t\t}\n\n\t\tfunction materialNeedsLights( material ) {\n\n\t\t\treturn material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial ||\n\t\t\t\tmaterial.isMeshStandardMaterial || material.isShadowMaterial ||\n\t\t\t\t( material.isShaderMaterial && material.lights === true );\n\n\t\t}\n\n\t\tthis.getActiveCubeFace = function () {\n\n\t\t\treturn _currentActiveCubeFace;\n\n\t\t};\n\n\t\tthis.getActiveMipmapLevel = function () {\n\n\t\t\treturn _currentActiveMipmapLevel;\n\n\t\t};\n\n\t\tthis.getRenderTarget = function () {\n\n\t\t\treturn _currentRenderTarget;\n\n\t\t};\n\n\t\tthis.setRenderTargetTextures = function ( renderTarget, colorTexture, depthTexture ) {\n\n\t\t\tproperties.get( renderTarget.texture ).__webglTexture = colorTexture;\n\t\t\tproperties.get( renderTarget.depthTexture ).__webglTexture = depthTexture;\n\n\t\t\tconst renderTargetProperties = properties.get( renderTarget );\n\t\t\trenderTargetProperties.__hasExternalTextures = true;\n\n\t\t\tif ( renderTargetProperties.__hasExternalTextures ) {\n\n\t\t\t\trenderTargetProperties.__autoAllocateDepthBuffer = depthTexture === undefined;\n\n\t\t\t\tif ( ! renderTargetProperties.__autoAllocateDepthBuffer ) {\n\n\t\t\t\t\t// The multisample_render_to_texture extension doesn't work properly if there\n\t\t\t\t\t// are midframe flushes and an external depth buffer. Disable use of the extension.\n\t\t\t\t\tif ( extensions.has( 'WEBGL_multisampled_render_to_texture' ) === true ) {\n\n\t\t\t\t\t\tconsole.warn( 'THREE.WebGLRenderer: Render-to-texture extension was disabled because an external texture was provided' );\n\t\t\t\t\t\trenderTargetProperties.__useRenderToTexture = false;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t};\n\n\t\tthis.setRenderTargetFramebuffer = function ( renderTarget, defaultFramebuffer ) {\n\n\t\t\tconst renderTargetProperties = properties.get( renderTarget );\n\t\t\trenderTargetProperties.__webglFramebuffer = defaultFramebuffer;\n\t\t\trenderTargetProperties.__useDefaultFramebuffer = defaultFramebuffer === undefined;\n\n\t\t};\n\n\t\tthis.setRenderTarget = function ( renderTarget, activeCubeFace = 0, activeMipmapLevel = 0 ) {\n\n\t\t\t_currentRenderTarget = renderTarget;\n\t\t\t_currentActiveCubeFace = activeCubeFace;\n\t\t\t_currentActiveMipmapLevel = activeMipmapLevel;\n\n\t\t\tlet useDefaultFramebuffer = true;\n\t\t\tlet framebuffer = null;\n\t\t\tlet isCube = false;\n\t\t\tlet isRenderTarget3D = false;\n\n\t\t\tif ( renderTarget ) {\n\n\t\t\t\tconst renderTargetProperties = properties.get( renderTarget );\n\n\t\t\t\tif ( renderTargetProperties.__useDefaultFramebuffer !== undefined ) {\n\n\t\t\t\t\t// We need to make sure to rebind the framebuffer.\n\t\t\t\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, null );\n\t\t\t\t\tuseDefaultFramebuffer = false;\n\n\t\t\t\t} else if ( renderTargetProperties.__webglFramebuffer === undefined ) {\n\n\t\t\t\t\ttextures.setupRenderTarget( renderTarget );\n\n\t\t\t\t} else if ( renderTargetProperties.__hasExternalTextures ) {\n\n\t\t\t\t\t// Color and depth texture must be rebound in order for the swapchain to update.\n\t\t\t\t\ttextures.rebindTextures( renderTarget, properties.get( renderTarget.texture ).__webglTexture, properties.get( renderTarget.depthTexture ).__webglTexture );\n\n\t\t\t\t}\n\n\t\t\t\tconst texture = renderTarget.texture;\n\n\t\t\t\tif ( texture.isData3DTexture || texture.isDataArrayTexture || texture.isCompressedArrayTexture ) {\n\n\t\t\t\t\tisRenderTarget3D = true;\n\n\t\t\t\t}\n\n\t\t\t\tconst __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer;\n\n\t\t\t\tif ( renderTarget.isWebGLCubeRenderTarget ) {\n\n\t\t\t\t\tif ( Array.isArray( __webglFramebuffer[ activeCubeFace ] ) ) {\n\n\t\t\t\t\t\tframebuffer = __webglFramebuffer[ activeCubeFace ][ activeMipmapLevel ];\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tframebuffer = __webglFramebuffer[ activeCubeFace ];\n\n\t\t\t\t\t}\n\n\t\t\t\t\tisCube = true;\n\n\t\t\t\t} else if ( ( capabilities.isWebGL2 && renderTarget.samples > 0 ) && textures.useMultisampledRTT( renderTarget ) === false ) {\n\n\t\t\t\t\tframebuffer = properties.get( renderTarget ).__webglMultisampledFramebuffer;\n\n\t\t\t\t} else {\n\n\t\t\t\t\tif ( Array.isArray( __webglFramebuffer ) ) {\n\n\t\t\t\t\t\tframebuffer = __webglFramebuffer[ activeMipmapLevel ];\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tframebuffer = __webglFramebuffer;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\t_currentViewport.copy( renderTarget.viewport );\n\t\t\t\t_currentScissor.copy( renderTarget.scissor );\n\t\t\t\t_currentScissorTest = renderTarget.scissorTest;\n\n\t\t\t} else {\n\n\t\t\t\t_currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor();\n\t\t\t\t_currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor();\n\t\t\t\t_currentScissorTest = _scissorTest;\n\n\t\t\t}\n\n\t\t\tconst framebufferBound = state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );\n\n\t\t\tif ( framebufferBound && capabilities.drawBuffers && useDefaultFramebuffer ) {\n\n\t\t\t\tstate.drawBuffers( renderTarget, framebuffer );\n\n\t\t\t}\n\n\t\t\tstate.viewport( _currentViewport );\n\t\t\tstate.scissor( _currentScissor );\n\t\t\tstate.setScissorTest( _currentScissorTest );\n\n\t\t\tif ( isCube ) {\n\n\t\t\t\tconst textureProperties = properties.get( renderTarget.texture );\n\t\t\t\t_gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel );\n\n\t\t\t} else if ( isRenderTarget3D ) {\n\n\t\t\t\tconst textureProperties = properties.get( renderTarget.texture );\n\t\t\t\tconst layer = activeCubeFace || 0;\n\t\t\t\t_gl.framebufferTextureLayer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureProperties.__webglTexture, activeMipmapLevel || 0, layer );\n\n\t\t\t}\n\n\t\t\t_currentMaterialId = - 1; // reset current material to ensure correct uniform bindings\n\n\t\t};\n\n\t\tthis.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex ) {\n\n\t\t\tif ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) {\n\n\t\t\t\tconsole.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' );\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\tlet framebuffer = properties.get( renderTarget ).__webglFramebuffer;\n\n\t\t\tif ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) {\n\n\t\t\t\tframebuffer = framebuffer[ activeCubeFaceIndex ];\n\n\t\t\t}\n\n\t\t\tif ( framebuffer ) {\n\n\t\t\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );\n\n\t\t\t\ttry {\n\n\t\t\t\t\tconst texture = renderTarget.texture;\n\t\t\t\t\tconst textureFormat = texture.format;\n\t\t\t\t\tconst textureType = texture.type;\n\n\t\t\t\t\tif ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) {\n\n\t\t\t\t\t\tconsole.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' );\n\t\t\t\t\t\treturn;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tconst halfFloatSupportedByExt = ( textureType === HalfFloatType ) && ( extensions.has( 'EXT_color_buffer_half_float' ) || ( capabilities.isWebGL2 && extensions.has( 'EXT_color_buffer_float' ) ) );\n\n\t\t\t\t\tif ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) && // Edge and Chrome Mac < 52 (#9513)\n\t\t\t\t\t\t! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.has( 'OES_texture_float' ) || extensions.has( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox\n\t\t\t\t\t\t! halfFloatSupportedByExt ) {\n\n\t\t\t\t\t\tconsole.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' );\n\t\t\t\t\t\treturn;\n\n\t\t\t\t\t}\n\n\t\t\t\t\t// the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604)\n\n\t\t\t\t\tif ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) {\n\n\t\t\t\t\t\t_gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer );\n\n\t\t\t\t\t}\n\n\t\t\t\t} finally {\n\n\t\t\t\t\t// restore framebuffer of current render target if necessary\n\n\t\t\t\t\tconst framebuffer = ( _currentRenderTarget !== null ) ? properties.get( _currentRenderTarget ).__webglFramebuffer : null;\n\t\t\t\t\tstate.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t};\n\n\t\tthis.copyFramebufferToTexture = function ( position, texture, level = 0 ) {\n\n\t\t\tconst levelScale = Math.pow( 2, - level );\n\t\t\tconst width = Math.floor( texture.image.width * levelScale );\n\t\t\tconst height = Math.floor( texture.image.height * levelScale );\n\n\t\t\ttextures.setTexture2D( texture, 0 );\n\n\t\t\t_gl.copyTexSubImage2D( _gl.TEXTURE_2D, level, 0, 0, position.x, position.y, width, height );\n\n\t\t\tstate.unbindTexture();\n\n\t\t};\n\n\t\tthis.copyTextureToTexture = function ( position, srcTexture, dstTexture, level = 0 ) {\n\n\t\t\tconst width = srcTexture.image.width;\n\t\t\tconst height = srcTexture.image.height;\n\t\t\tconst glFormat = utils.convert( dstTexture.format );\n\t\t\tconst glType = utils.convert( dstTexture.type );\n\n\t\t\ttextures.setTexture2D( dstTexture, 0 );\n\n\t\t\t// As another texture upload may have changed pixelStorei\n\t\t\t// parameters, make sure they are correct for the dstTexture\n\t\t\t_gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment );\n\n\t\t\tif ( srcTexture.isDataTexture ) {\n\n\t\t\t\t_gl.texSubImage2D( _gl.TEXTURE_2D, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data );\n\n\t\t\t} else {\n\n\t\t\t\tif ( srcTexture.isCompressedTexture ) {\n\n\t\t\t\t\t_gl.compressedTexSubImage2D( _gl.TEXTURE_2D, level, position.x, position.y, srcTexture.mipmaps[ 0 ].width, srcTexture.mipmaps[ 0 ].height, glFormat, srcTexture.mipmaps[ 0 ].data );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t_gl.texSubImage2D( _gl.TEXTURE_2D, level, position.x, position.y, glFormat, glType, srcTexture.image );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// Generate mipmaps only when copying level 0\n\t\t\tif ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( _gl.TEXTURE_2D );\n\n\t\t\tstate.unbindTexture();\n\n\t\t};\n\n\t\tthis.copyTextureToTexture3D = function ( sourceBox, position, srcTexture, dstTexture, level = 0 ) {\n\n\t\t\tif ( _this.isWebGL1Renderer ) {\n\n\t\t\t\tconsole.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2.' );\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\tconst width = sourceBox.max.x - sourceBox.min.x + 1;\n\t\t\tconst height = sourceBox.max.y - sourceBox.min.y + 1;\n\t\t\tconst depth = sourceBox.max.z - sourceBox.min.z + 1;\n\t\t\tconst glFormat = utils.convert( dstTexture.format );\n\t\t\tconst glType = utils.convert( dstTexture.type );\n\t\t\tlet glTarget;\n\n\t\t\tif ( dstTexture.isData3DTexture ) {\n\n\t\t\t\ttextures.setTexture3D( dstTexture, 0 );\n\t\t\t\tglTarget = _gl.TEXTURE_3D;\n\n\t\t\t} else if ( dstTexture.isDataArrayTexture ) {\n\n\t\t\t\ttextures.setTexture2DArray( dstTexture, 0 );\n\t\t\t\tglTarget = _gl.TEXTURE_2D_ARRAY;\n\n\t\t\t} else {\n\n\t\t\t\tconsole.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray.' );\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\t_gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment );\n\n\t\t\tconst unpackRowLen = _gl.getParameter( _gl.UNPACK_ROW_LENGTH );\n\t\t\tconst unpackImageHeight = _gl.getParameter( _gl.UNPACK_IMAGE_HEIGHT );\n\t\t\tconst unpackSkipPixels = _gl.getParameter( _gl.UNPACK_SKIP_PIXELS );\n\t\t\tconst unpackSkipRows = _gl.getParameter( _gl.UNPACK_SKIP_ROWS );\n\t\t\tconst unpackSkipImages = _gl.getParameter( _gl.UNPACK_SKIP_IMAGES );\n\n\t\t\tconst image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[ 0 ] : srcTexture.image;\n\n\t\t\t_gl.pixelStorei( _gl.UNPACK_ROW_LENGTH, image.width );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_IMAGE_HEIGHT, image.height );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_SKIP_PIXELS, sourceBox.min.x );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_SKIP_ROWS, sourceBox.min.y );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_SKIP_IMAGES, sourceBox.min.z );\n\n\t\t\tif ( srcTexture.isDataTexture || srcTexture.isData3DTexture ) {\n\n\t\t\t\t_gl.texSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image.data );\n\n\t\t\t} else {\n\n\t\t\t\tif ( srcTexture.isCompressedArrayTexture ) {\n\n\t\t\t\t\tconsole.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: untested support for compressed srcTexture.' );\n\t\t\t\t\t_gl.compressedTexSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, image.data );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t_gl.texSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t_gl.pixelStorei( _gl.UNPACK_ROW_LENGTH, unpackRowLen );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_IMAGE_HEIGHT, unpackImageHeight );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_SKIP_PIXELS, unpackSkipPixels );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_SKIP_ROWS, unpackSkipRows );\n\t\t\t_gl.pixelStorei( _gl.UNPACK_SKIP_IMAGES, unpackSkipImages );\n\n\t\t\t// Generate mipmaps only when copying level 0\n\t\t\tif ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( glTarget );\n\n\t\t\tstate.unbindTexture();\n\n\t\t};\n\n\t\tthis.initTexture = function ( texture ) {\n\n\t\t\tif ( texture.isCubeTexture ) {\n\n\t\t\t\ttextures.setTextureCube( texture, 0 );\n\n\t\t\t} else if ( texture.isData3DTexture ) {\n\n\t\t\t\ttextures.setTexture3D( texture, 0 );\n\n\t\t\t} else if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) {\n\n\t\t\t\ttextures.setTexture2DArray( texture, 0 );\n\n\t\t\t} else {\n\n\t\t\t\ttextures.setTexture2D( texture, 0 );\n\n\t\t\t}\n\n\t\t\tstate.unbindTexture();\n\n\t\t};\n\n\t\tthis.resetState = function () {\n\n\t\t\t_currentActiveCubeFace = 0;\n\t\t\t_currentActiveMipmapLevel = 0;\n\t\t\t_currentRenderTarget = null;\n\n\t\t\tstate.reset();\n\t\t\tbindingStates.reset();\n\n\t\t};\n\n\t\tif ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) {\n\n\t\t\t__THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) );\n\n\t\t}\n\n\t}\n\n\tget coordinateSystem() {\n\n\t\treturn WebGLCoordinateSystem;\n\n\t}\n\n\tget outputColorSpace() {\n\n\t\treturn this._outputColorSpace;\n\n\t}\n\n\tset outputColorSpace( colorSpace ) {\n\n\t\tthis._outputColorSpace = colorSpace;\n\n\t\tconst gl = this.getContext();\n\t\tgl.drawingBufferColorSpace = colorSpace === DisplayP3ColorSpace ? 'display-p3' : 'srgb';\n\t\tgl.unpackColorSpace = ColorManagement.workingColorSpace === LinearDisplayP3ColorSpace ? 'display-p3' : 'srgb';\n\n\t}\n\n\tget physicallyCorrectLights() { // @deprecated, r150\n\n\t\tconsole.warn( 'THREE.WebGLRenderer: The property .physicallyCorrectLights has been removed. Set renderer.useLegacyLights instead.' );\n\t\treturn ! this.useLegacyLights;\n\n\t}\n\n\tset physicallyCorrectLights( value ) { // @deprecated, r150\n\n\t\tconsole.warn( 'THREE.WebGLRenderer: The property .physicallyCorrectLights has been removed. Set renderer.useLegacyLights instead.' );\n\t\tthis.useLegacyLights = ! value;\n\n\t}\n\n\tget outputEncoding() { // @deprecated, r152\n\n\t\tconsole.warn( 'THREE.WebGLRenderer: Property .outputEncoding has been removed. Use .outputColorSpace instead.' );\n\t\treturn this.outputColorSpace === SRGBColorSpace ? sRGBEncoding : LinearEncoding;\n\n\t}\n\n\tset outputEncoding( encoding ) { // @deprecated, r152\n\n\t\tconsole.warn( 'THREE.WebGLRenderer: Property .outputEncoding has been removed. Use .outputColorSpace instead.' );\n\t\tthis.outputColorSpace = encoding === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;\n\n\t}\n\n\tget useLegacyLights() { // @deprecated, r155\n\n\t\tconsole.warn( 'THREE.WebGLRenderer: The property .useLegacyLights has been deprecated. Migrate your lighting according to the following guide: https://discourse.threejs.org/t/updates-to-lighting-in-three-js-r155/53733.' );\n\t\treturn this._useLegacyLights;\n\n\t}\n\n\tset useLegacyLights( value ) { // @deprecated, r155\n\n\t\tconsole.warn( 'THREE.WebGLRenderer: The property .useLegacyLights has been deprecated. Migrate your lighting according to the following guide: https://discourse.threejs.org/t/updates-to-lighting-in-three-js-r155/53733.' );\n\t\tthis._useLegacyLights = value;\n\n\t}\n\n}\n\n\nexport { WebGLRenderer };\n","import { Color } from '../math/Color.js';\n\nclass Fog {\n\n\tconstructor( color, near = 1, far = 1000 ) {\n\n\t\tthis.isFog = true;\n\n\t\tthis.name = '';\n\n\t\tthis.color = new Color( color );\n\n\t\tthis.near = near;\n\t\tthis.far = far;\n\n\t}\n\n\tclone() {\n\n\t\treturn new Fog( this.color, this.near, this.far );\n\n\t}\n\n\ttoJSON( /* meta */ ) {\n\n\t\treturn {\n\t\t\ttype: 'Fog',\n\t\t\tname: this.name,\n\t\t\tcolor: this.color.getHex(),\n\t\t\tnear: this.near,\n\t\t\tfar: this.far\n\t\t};\n\n\t}\n\n}\n\nexport { Fog };\n","import { Object3D } from '../core/Object3D.js';\n\nclass Scene extends Object3D {\n\n\tconstructor() {\n\n\t\tsuper();\n\n\t\tthis.isScene = true;\n\n\t\tthis.type = 'Scene';\n\n\t\tthis.background = null;\n\t\tthis.environment = null;\n\t\tthis.fog = null;\n\n\t\tthis.backgroundBlurriness = 0;\n\t\tthis.backgroundIntensity = 1;\n\n\t\tthis.overrideMaterial = null;\n\n\t\tif ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) {\n\n\t\t\t__THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) );\n\n\t\t}\n\n\t}\n\n\tcopy( source, recursive ) {\n\n\t\tsuper.copy( source, recursive );\n\n\t\tif ( source.background !== null ) this.background = source.background.clone();\n\t\tif ( source.environment !== null ) this.environment = source.environment.clone();\n\t\tif ( source.fog !== null ) this.fog = source.fog.clone();\n\n\t\tthis.backgroundBlurriness = source.backgroundBlurriness;\n\t\tthis.backgroundIntensity = source.backgroundIntensity;\n\n\t\tif ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone();\n\n\t\tthis.matrixAutoUpdate = source.matrixAutoUpdate;\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON( meta ) {\n\n\t\tconst data = super.toJSON( meta );\n\n\t\tif ( this.fog !== null ) data.object.fog = this.fog.toJSON();\n\t\tif ( this.backgroundBlurriness > 0 ) data.object.backgroundBlurriness = this.backgroundBlurriness;\n\t\tif ( this.backgroundIntensity !== 1 ) data.object.backgroundIntensity = this.backgroundIntensity;\n\n\t\treturn data;\n\n\t}\n\n}\n\nexport { Scene };\n","import { Texture } from './Texture.js';\nimport { NearestFilter } from '../constants.js';\n\nclass DataTexture extends Texture {\n\n\tconstructor( data = null, width = 1, height = 1, format, type, mapping, wrapS, wrapT, magFilter = NearestFilter, minFilter = NearestFilter, anisotropy, colorSpace ) {\n\n\t\tsuper( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace );\n\n\t\tthis.isDataTexture = true;\n\n\t\tthis.image = { data: data, width: width, height: height };\n\n\t\tthis.generateMipmaps = false;\n\t\tthis.flipY = false;\n\t\tthis.unpackAlignment = 1;\n\n\t}\n\n}\n\nexport { DataTexture };\n","import { Material } from './Material.js';\nimport { Color } from '../math/Color.js';\n\nclass LineBasicMaterial extends Material {\n\n\tconstructor( parameters ) {\n\n\t\tsuper();\n\n\t\tthis.isLineBasicMaterial = true;\n\n\t\tthis.type = 'LineBasicMaterial';\n\n\t\tthis.color = new Color( 0xffffff );\n\n\t\tthis.map = null;\n\n\t\tthis.linewidth = 1;\n\t\tthis.linecap = 'round';\n\t\tthis.linejoin = 'round';\n\n\t\tthis.fog = true;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.color.copy( source.color );\n\n\t\tthis.map = source.map;\n\n\t\tthis.linewidth = source.linewidth;\n\t\tthis.linecap = source.linecap;\n\t\tthis.linejoin = source.linejoin;\n\n\t\tthis.fog = source.fog;\n\n\t\treturn this;\n\n\t}\n\n}\n\nexport { LineBasicMaterial };\n","import { Sphere } from '../math/Sphere.js';\nimport { Ray } from '../math/Ray.js';\nimport { Matrix4 } from '../math/Matrix4.js';\nimport { Object3D } from '../core/Object3D.js';\nimport { Vector3 } from '../math/Vector3.js';\nimport { LineBasicMaterial } from '../materials/LineBasicMaterial.js';\nimport { BufferGeometry } from '../core/BufferGeometry.js';\nimport { Float32BufferAttribute } from '../core/BufferAttribute.js';\n\nconst _start = /*@__PURE__*/ new Vector3();\nconst _end = /*@__PURE__*/ new Vector3();\nconst _inverseMatrix = /*@__PURE__*/ new Matrix4();\nconst _ray = /*@__PURE__*/ new Ray();\nconst _sphere = /*@__PURE__*/ new Sphere();\n\nclass Line extends Object3D {\n\n\tconstructor( geometry = new BufferGeometry(), material = new LineBasicMaterial() ) {\n\n\t\tsuper();\n\n\t\tthis.isLine = true;\n\n\t\tthis.type = 'Line';\n\n\t\tthis.geometry = geometry;\n\t\tthis.material = material;\n\n\t\tthis.updateMorphTargets();\n\n\t}\n\n\tcopy( source, recursive ) {\n\n\t\tsuper.copy( source, recursive );\n\n\t\tthis.material = Array.isArray( source.material ) ? source.material.slice() : source.material;\n\t\tthis.geometry = source.geometry;\n\n\t\treturn this;\n\n\t}\n\n\tcomputeLineDistances() {\n\n\t\tconst geometry = this.geometry;\n\n\t\t// we assume non-indexed geometry\n\n\t\tif ( geometry.index === null ) {\n\n\t\t\tconst positionAttribute = geometry.attributes.position;\n\t\t\tconst lineDistances = [ 0 ];\n\n\t\t\tfor ( let i = 1, l = positionAttribute.count; i < l; i ++ ) {\n\n\t\t\t\t_start.fromBufferAttribute( positionAttribute, i - 1 );\n\t\t\t\t_end.fromBufferAttribute( positionAttribute, i );\n\n\t\t\t\tlineDistances[ i ] = lineDistances[ i - 1 ];\n\t\t\t\tlineDistances[ i ] += _start.distanceTo( _end );\n\n\t\t\t}\n\n\t\t\tgeometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) );\n\n\t\t} else {\n\n\t\t\tconsole.warn( 'THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n\traycast( raycaster, intersects ) {\n\n\t\tconst geometry = this.geometry;\n\t\tconst matrixWorld = this.matrixWorld;\n\t\tconst threshold = raycaster.params.Line.threshold;\n\t\tconst drawRange = geometry.drawRange;\n\n\t\t// Checking boundingSphere distance to ray\n\n\t\tif ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();\n\n\t\t_sphere.copy( geometry.boundingSphere );\n\t\t_sphere.applyMatrix4( matrixWorld );\n\t\t_sphere.radius += threshold;\n\n\t\tif ( raycaster.ray.intersectsSphere( _sphere ) === false ) return;\n\n\t\t//\n\n\t\t_inverseMatrix.copy( matrixWorld ).invert();\n\t\t_ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix );\n\n\t\tconst localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 );\n\t\tconst localThresholdSq = localThreshold * localThreshold;\n\n\t\tconst vStart = new Vector3();\n\t\tconst vEnd = new Vector3();\n\t\tconst interSegment = new Vector3();\n\t\tconst interRay = new Vector3();\n\t\tconst step = this.isLineSegments ? 2 : 1;\n\n\t\tconst index = geometry.index;\n\t\tconst attributes = geometry.attributes;\n\t\tconst positionAttribute = attributes.position;\n\n\t\tif ( index !== null ) {\n\n\t\t\tconst start = Math.max( 0, drawRange.start );\n\t\t\tconst end = Math.min( index.count, ( drawRange.start + drawRange.count ) );\n\n\t\t\tfor ( let i = start, l = end - 1; i < l; i += step ) {\n\n\t\t\t\tconst a = index.getX( i );\n\t\t\t\tconst b = index.getX( i + 1 );\n\n\t\t\t\tvStart.fromBufferAttribute( positionAttribute, a );\n\t\t\t\tvEnd.fromBufferAttribute( positionAttribute, b );\n\n\t\t\t\tconst distSq = _ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment );\n\n\t\t\t\tif ( distSq > localThresholdSq ) continue;\n\n\t\t\t\tinterRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation\n\n\t\t\t\tconst distance = raycaster.ray.origin.distanceTo( interRay );\n\n\t\t\t\tif ( distance < raycaster.near || distance > raycaster.far ) continue;\n\n\t\t\t\tintersects.push( {\n\n\t\t\t\t\tdistance: distance,\n\t\t\t\t\t// What do we want? intersection point on the ray or on the segment??\n\t\t\t\t\t// point: raycaster.ray.at( distance ),\n\t\t\t\t\tpoint: interSegment.clone().applyMatrix4( this.matrixWorld ),\n\t\t\t\t\tindex: i,\n\t\t\t\t\tface: null,\n\t\t\t\t\tfaceIndex: null,\n\t\t\t\t\tobject: this\n\n\t\t\t\t} );\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\tconst start = Math.max( 0, drawRange.start );\n\t\t\tconst end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) );\n\n\t\t\tfor ( let i = start, l = end - 1; i < l; i += step ) {\n\n\t\t\t\tvStart.fromBufferAttribute( positionAttribute, i );\n\t\t\t\tvEnd.fromBufferAttribute( positionAttribute, i + 1 );\n\n\t\t\t\tconst distSq = _ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment );\n\n\t\t\t\tif ( distSq > localThresholdSq ) continue;\n\n\t\t\t\tinterRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation\n\n\t\t\t\tconst distance = raycaster.ray.origin.distanceTo( interRay );\n\n\t\t\t\tif ( distance < raycaster.near || distance > raycaster.far ) continue;\n\n\t\t\t\tintersects.push( {\n\n\t\t\t\t\tdistance: distance,\n\t\t\t\t\t// What do we want? intersection point on the ray or on the segment??\n\t\t\t\t\t// point: raycaster.ray.at( distance ),\n\t\t\t\t\tpoint: interSegment.clone().applyMatrix4( this.matrixWorld ),\n\t\t\t\t\tindex: i,\n\t\t\t\t\tface: null,\n\t\t\t\t\tfaceIndex: null,\n\t\t\t\t\tobject: this\n\n\t\t\t\t} );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tupdateMorphTargets() {\n\n\t\tconst geometry = this.geometry;\n\n\t\tconst morphAttributes = geometry.morphAttributes;\n\t\tconst keys = Object.keys( morphAttributes );\n\n\t\tif ( keys.length > 0 ) {\n\n\t\t\tconst morphAttribute = morphAttributes[ keys[ 0 ] ];\n\n\t\t\tif ( morphAttribute !== undefined ) {\n\n\t\t\t\tthis.morphTargetInfluences = [];\n\t\t\t\tthis.morphTargetDictionary = {};\n\n\t\t\t\tfor ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) {\n\n\t\t\t\t\tconst name = morphAttribute[ m ].name || String( m );\n\n\t\t\t\t\tthis.morphTargetInfluences.push( 0 );\n\t\t\t\t\tthis.morphTargetDictionary[ name ] = m;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n\nexport { Line };\n","import { Line } from './Line.js';\nimport { Vector3 } from '../math/Vector3.js';\nimport { Float32BufferAttribute } from '../core/BufferAttribute.js';\n\nconst _start = /*@__PURE__*/ new Vector3();\nconst _end = /*@__PURE__*/ new Vector3();\n\nclass LineSegments extends Line {\n\n\tconstructor( geometry, material ) {\n\n\t\tsuper( geometry, material );\n\n\t\tthis.isLineSegments = true;\n\n\t\tthis.type = 'LineSegments';\n\n\t}\n\n\tcomputeLineDistances() {\n\n\t\tconst geometry = this.geometry;\n\n\t\t// we assume non-indexed geometry\n\n\t\tif ( geometry.index === null ) {\n\n\t\t\tconst positionAttribute = geometry.attributes.position;\n\t\t\tconst lineDistances = [];\n\n\t\t\tfor ( let i = 0, l = positionAttribute.count; i < l; i += 2 ) {\n\n\t\t\t\t_start.fromBufferAttribute( positionAttribute, i );\n\t\t\t\t_end.fromBufferAttribute( positionAttribute, i + 1 );\n\n\t\t\t\tlineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ];\n\t\t\t\tlineDistances[ i + 1 ] = lineDistances[ i ] + _start.distanceTo( _end );\n\n\t\t\t}\n\n\t\t\tgeometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) );\n\n\t\t} else {\n\n\t\t\tconsole.warn( 'THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' );\n\n\t\t}\n\n\t\treturn this;\n\n\t}\n\n}\n\nexport { LineSegments };\n","import { Material } from './Material.js';\nimport { Color } from '../math/Color.js';\n\nclass PointsMaterial extends Material {\n\n\tconstructor( parameters ) {\n\n\t\tsuper();\n\n\t\tthis.isPointsMaterial = true;\n\n\t\tthis.type = 'PointsMaterial';\n\n\t\tthis.color = new Color( 0xffffff );\n\n\t\tthis.map = null;\n\n\t\tthis.alphaMap = null;\n\n\t\tthis.size = 1;\n\t\tthis.sizeAttenuation = true;\n\n\t\tthis.fog = true;\n\n\t\tthis.setValues( parameters );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.color.copy( source.color );\n\n\t\tthis.map = source.map;\n\n\t\tthis.alphaMap = source.alphaMap;\n\n\t\tthis.size = source.size;\n\t\tthis.sizeAttenuation = source.sizeAttenuation;\n\n\t\tthis.fog = source.fog;\n\n\t\treturn this;\n\n\t}\n\n}\n\nexport { PointsMaterial };\n","import { Sphere } from '../math/Sphere.js';\nimport { Ray } from '../math/Ray.js';\nimport { Matrix4 } from '../math/Matrix4.js';\nimport { Object3D } from '../core/Object3D.js';\nimport { Vector3 } from '../math/Vector3.js';\nimport { PointsMaterial } from '../materials/PointsMaterial.js';\nimport { BufferGeometry } from '../core/BufferGeometry.js';\n\nconst _inverseMatrix = /*@__PURE__*/ new Matrix4();\nconst _ray = /*@__PURE__*/ new Ray();\nconst _sphere = /*@__PURE__*/ new Sphere();\nconst _position = /*@__PURE__*/ new Vector3();\n\nclass Points extends Object3D {\n\n\tconstructor( geometry = new BufferGeometry(), material = new PointsMaterial() ) {\n\n\t\tsuper();\n\n\t\tthis.isPoints = true;\n\n\t\tthis.type = 'Points';\n\n\t\tthis.geometry = geometry;\n\t\tthis.material = material;\n\n\t\tthis.updateMorphTargets();\n\n\t}\n\n\tcopy( source, recursive ) {\n\n\t\tsuper.copy( source, recursive );\n\n\t\tthis.material = Array.isArray( source.material ) ? source.material.slice() : source.material;\n\t\tthis.geometry = source.geometry;\n\n\t\treturn this;\n\n\t}\n\n\traycast( raycaster, intersects ) {\n\n\t\tconst geometry = this.geometry;\n\t\tconst matrixWorld = this.matrixWorld;\n\t\tconst threshold = raycaster.params.Points.threshold;\n\t\tconst drawRange = geometry.drawRange;\n\n\t\t// Checking boundingSphere distance to ray\n\n\t\tif ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();\n\n\t\t_sphere.copy( geometry.boundingSphere );\n\t\t_sphere.applyMatrix4( matrixWorld );\n\t\t_sphere.radius += threshold;\n\n\t\tif ( raycaster.ray.intersectsSphere( _sphere ) === false ) return;\n\n\t\t//\n\n\t\t_inverseMatrix.copy( matrixWorld ).invert();\n\t\t_ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix );\n\n\t\tconst localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 );\n\t\tconst localThresholdSq = localThreshold * localThreshold;\n\n\t\tconst index = geometry.index;\n\t\tconst attributes = geometry.attributes;\n\t\tconst positionAttribute = attributes.position;\n\n\t\tif ( index !== null ) {\n\n\t\t\tconst start = Math.max( 0, drawRange.start );\n\t\t\tconst end = Math.min( index.count, ( drawRange.start + drawRange.count ) );\n\n\t\t\tfor ( let i = start, il = end; i < il; i ++ ) {\n\n\t\t\t\tconst a = index.getX( i );\n\n\t\t\t\t_position.fromBufferAttribute( positionAttribute, a );\n\n\t\t\t\ttestPoint( _position, a, localThresholdSq, matrixWorld, raycaster, intersects, this );\n\n\t\t\t}\n\n\t\t} else {\n\n\t\t\tconst start = Math.max( 0, drawRange.start );\n\t\t\tconst end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) );\n\n\t\t\tfor ( let i = start, l = end; i < l; i ++ ) {\n\n\t\t\t\t_position.fromBufferAttribute( positionAttribute, i );\n\n\t\t\t\ttestPoint( _position, i, localThresholdSq, matrixWorld, raycaster, intersects, this );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tupdateMorphTargets() {\n\n\t\tconst geometry = this.geometry;\n\n\t\tconst morphAttributes = geometry.morphAttributes;\n\t\tconst keys = Object.keys( morphAttributes );\n\n\t\tif ( keys.length > 0 ) {\n\n\t\t\tconst morphAttribute = morphAttributes[ keys[ 0 ] ];\n\n\t\t\tif ( morphAttribute !== undefined ) {\n\n\t\t\t\tthis.morphTargetInfluences = [];\n\t\t\t\tthis.morphTargetDictionary = {};\n\n\t\t\t\tfor ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) {\n\n\t\t\t\t\tconst name = morphAttribute[ m ].name || String( m );\n\n\t\t\t\t\tthis.morphTargetInfluences.push( 0 );\n\t\t\t\t\tthis.morphTargetDictionary[ name ] = m;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n\nfunction testPoint( point, index, localThresholdSq, matrixWorld, raycaster, intersects, object ) {\n\n\tconst rayPointDistanceSq = _ray.distanceSqToPoint( point );\n\n\tif ( rayPointDistanceSq < localThresholdSq ) {\n\n\t\tconst intersectPoint = new Vector3();\n\n\t\t_ray.closestPointToPoint( point, intersectPoint );\n\t\tintersectPoint.applyMatrix4( matrixWorld );\n\n\t\tconst distance = raycaster.ray.origin.distanceTo( intersectPoint );\n\n\t\tif ( distance < raycaster.near || distance > raycaster.far ) return;\n\n\t\tintersects.push( {\n\n\t\t\tdistance: distance,\n\t\t\tdistanceToRay: Math.sqrt( rayPointDistanceSq ),\n\t\t\tpoint: intersectPoint,\n\t\t\tindex: index,\n\t\t\tface: null,\n\t\t\tobject: object\n\n\t\t} );\n\n\t}\n\n}\n\nexport { Points };\n","import { Texture } from './Texture.js';\n\nclass CanvasTexture extends Texture {\n\n\tconstructor( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {\n\n\t\tsuper( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );\n\n\t\tthis.isCanvasTexture = true;\n\n\t\tthis.needsUpdate = true;\n\n\t}\n\n}\n\nexport { CanvasTexture };\n","import { BufferGeometry } from '../core/BufferGeometry.js';\nimport { Float32BufferAttribute } from '../core/BufferAttribute.js';\nimport { Vector3 } from '../math/Vector3.js';\nimport { Vector2 } from '../math/Vector2.js';\n\nclass CylinderGeometry extends BufferGeometry {\n\n\tconstructor( radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2 ) {\n\n\t\tsuper();\n\n\t\tthis.type = 'CylinderGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradiusTop: radiusTop,\n\t\t\tradiusBottom: radiusBottom,\n\t\t\theight: height,\n\t\t\tradialSegments: radialSegments,\n\t\t\theightSegments: heightSegments,\n\t\t\topenEnded: openEnded,\n\t\t\tthetaStart: thetaStart,\n\t\t\tthetaLength: thetaLength\n\t\t};\n\n\t\tconst scope = this;\n\n\t\tradialSegments = Math.floor( radialSegments );\n\t\theightSegments = Math.floor( heightSegments );\n\n\t\t// buffers\n\n\t\tconst indices = [];\n\t\tconst vertices = [];\n\t\tconst normals = [];\n\t\tconst uvs = [];\n\n\t\t// helper variables\n\n\t\tlet index = 0;\n\t\tconst indexArray = [];\n\t\tconst halfHeight = height / 2;\n\t\tlet groupStart = 0;\n\n\t\t// generate geometry\n\n\t\tgenerateTorso();\n\n\t\tif ( openEnded === false ) {\n\n\t\t\tif ( radiusTop > 0 ) generateCap( true );\n\t\t\tif ( radiusBottom > 0 ) generateCap( false );\n\n\t\t}\n\n\t\t// build geometry\n\n\t\tthis.setIndex( indices );\n\t\tthis.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n\t\tthis.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n\t\tthis.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n\t\tfunction generateTorso() {\n\n\t\t\tconst normal = new Vector3();\n\t\t\tconst vertex = new Vector3();\n\n\t\t\tlet groupCount = 0;\n\n\t\t\t// this will be used to calculate the normal\n\t\t\tconst slope = ( radiusBottom - radiusTop ) / height;\n\n\t\t\t// generate vertices, normals and uvs\n\n\t\t\tfor ( let y = 0; y <= heightSegments; y ++ ) {\n\n\t\t\t\tconst indexRow = [];\n\n\t\t\t\tconst v = y / heightSegments;\n\n\t\t\t\t// calculate the radius of the current row\n\n\t\t\t\tconst radius = v * ( radiusBottom - radiusTop ) + radiusTop;\n\n\t\t\t\tfor ( let x = 0; x <= radialSegments; x ++ ) {\n\n\t\t\t\t\tconst u = x / radialSegments;\n\n\t\t\t\t\tconst theta = u * thetaLength + thetaStart;\n\n\t\t\t\t\tconst sinTheta = Math.sin( theta );\n\t\t\t\t\tconst cosTheta = Math.cos( theta );\n\n\t\t\t\t\t// vertex\n\n\t\t\t\t\tvertex.x = radius * sinTheta;\n\t\t\t\t\tvertex.y = - v * height + halfHeight;\n\t\t\t\t\tvertex.z = radius * cosTheta;\n\t\t\t\t\tvertices.push( vertex.x, vertex.y, vertex.z );\n\n\t\t\t\t\t// normal\n\n\t\t\t\t\tnormal.set( sinTheta, slope, cosTheta ).normalize();\n\t\t\t\t\tnormals.push( normal.x, normal.y, normal.z );\n\n\t\t\t\t\t// uv\n\n\t\t\t\t\tuvs.push( u, 1 - v );\n\n\t\t\t\t\t// save index of vertex in respective row\n\n\t\t\t\t\tindexRow.push( index ++ );\n\n\t\t\t\t}\n\n\t\t\t\t// now save vertices of the row in our index array\n\n\t\t\t\tindexArray.push( indexRow );\n\n\t\t\t}\n\n\t\t\t// generate indices\n\n\t\t\tfor ( let x = 0; x < radialSegments; x ++ ) {\n\n\t\t\t\tfor ( let y = 0; y < heightSegments; y ++ ) {\n\n\t\t\t\t\t// we use the index array to access the correct indices\n\n\t\t\t\t\tconst a = indexArray[ y ][ x ];\n\t\t\t\t\tconst b = indexArray[ y + 1 ][ x ];\n\t\t\t\t\tconst c = indexArray[ y + 1 ][ x + 1 ];\n\t\t\t\t\tconst d = indexArray[ y ][ x + 1 ];\n\n\t\t\t\t\t// faces\n\n\t\t\t\t\tindices.push( a, b, d );\n\t\t\t\t\tindices.push( b, c, d );\n\n\t\t\t\t\t// update group counter\n\n\t\t\t\t\tgroupCount += 6;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// add a group to the geometry. this will ensure multi material support\n\n\t\t\tscope.addGroup( groupStart, groupCount, 0 );\n\n\t\t\t// calculate new start value for groups\n\n\t\t\tgroupStart += groupCount;\n\n\t\t}\n\n\t\tfunction generateCap( top ) {\n\n\t\t\t// save the index of the first center vertex\n\t\t\tconst centerIndexStart = index;\n\n\t\t\tconst uv = new Vector2();\n\t\t\tconst vertex = new Vector3();\n\n\t\t\tlet groupCount = 0;\n\n\t\t\tconst radius = ( top === true ) ? radiusTop : radiusBottom;\n\t\t\tconst sign = ( top === true ) ? 1 : - 1;\n\n\t\t\t// first we generate the center vertex data of the cap.\n\t\t\t// because the geometry needs one set of uvs per face,\n\t\t\t// we must generate a center vertex per face/segment\n\n\t\t\tfor ( let x = 1; x <= radialSegments; x ++ ) {\n\n\t\t\t\t// vertex\n\n\t\t\t\tvertices.push( 0, halfHeight * sign, 0 );\n\n\t\t\t\t// normal\n\n\t\t\t\tnormals.push( 0, sign, 0 );\n\n\t\t\t\t// uv\n\n\t\t\t\tuvs.push( 0.5, 0.5 );\n\n\t\t\t\t// increase index\n\n\t\t\t\tindex ++;\n\n\t\t\t}\n\n\t\t\t// save the index of the last center vertex\n\t\t\tconst centerIndexEnd = index;\n\n\t\t\t// now we generate the surrounding vertices, normals and uvs\n\n\t\t\tfor ( let x = 0; x <= radialSegments; x ++ ) {\n\n\t\t\t\tconst u = x / radialSegments;\n\t\t\t\tconst theta = u * thetaLength + thetaStart;\n\n\t\t\t\tconst cosTheta = Math.cos( theta );\n\t\t\t\tconst sinTheta = Math.sin( theta );\n\n\t\t\t\t// vertex\n\n\t\t\t\tvertex.x = radius * sinTheta;\n\t\t\t\tvertex.y = halfHeight * sign;\n\t\t\t\tvertex.z = radius * cosTheta;\n\t\t\t\tvertices.push( vertex.x, vertex.y, vertex.z );\n\n\t\t\t\t// normal\n\n\t\t\t\tnormals.push( 0, sign, 0 );\n\n\t\t\t\t// uv\n\n\t\t\t\tuv.x = ( cosTheta * 0.5 ) + 0.5;\n\t\t\t\tuv.y = ( sinTheta * 0.5 * sign ) + 0.5;\n\t\t\t\tuvs.push( uv.x, uv.y );\n\n\t\t\t\t// increase index\n\n\t\t\t\tindex ++;\n\n\t\t\t}\n\n\t\t\t// generate indices\n\n\t\t\tfor ( let x = 0; x < radialSegments; x ++ ) {\n\n\t\t\t\tconst c = centerIndexStart + x;\n\t\t\t\tconst i = centerIndexEnd + x;\n\n\t\t\t\tif ( top === true ) {\n\n\t\t\t\t\t// face top\n\n\t\t\t\t\tindices.push( i, i + 1, c );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// face bottom\n\n\t\t\t\t\tindices.push( i + 1, i, c );\n\n\t\t\t\t}\n\n\t\t\t\tgroupCount += 3;\n\n\t\t\t}\n\n\t\t\t// add a group to the geometry. this will ensure multi material support\n\n\t\t\tscope.addGroup( groupStart, groupCount, top === true ? 1 : 2 );\n\n\t\t\t// calculate new start value for groups\n\n\t\t\tgroupStart += groupCount;\n\n\t\t}\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.parameters = Object.assign( {}, source.parameters );\n\n\t\treturn this;\n\n\t}\n\n\tstatic fromJSON( data ) {\n\n\t\treturn new CylinderGeometry( data.radiusTop, data.radiusBottom, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength );\n\n\t}\n\n}\n\n\nexport { CylinderGeometry };\n","import { CylinderGeometry } from './CylinderGeometry.js';\n\nclass ConeGeometry extends CylinderGeometry {\n\n\tconstructor( radius = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2 ) {\n\n\t\tsuper( 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength );\n\n\t\tthis.type = 'ConeGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\theight: height,\n\t\t\tradialSegments: radialSegments,\n\t\t\theightSegments: heightSegments,\n\t\t\topenEnded: openEnded,\n\t\t\tthetaStart: thetaStart,\n\t\t\tthetaLength: thetaLength\n\t\t};\n\n\t}\n\n\tstatic fromJSON( data ) {\n\n\t\treturn new ConeGeometry( data.radius, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength );\n\n\t}\n\n}\n\nexport { ConeGeometry };\n","import { BufferGeometry } from '../core/BufferGeometry.js';\nimport { Float32BufferAttribute } from '../core/BufferAttribute.js';\nimport { Vector3 } from '../math/Vector3.js';\nimport { Vector2 } from '../math/Vector2.js';\n\nclass PolyhedronGeometry extends BufferGeometry {\n\n\tconstructor( vertices = [], indices = [], radius = 1, detail = 0 ) {\n\n\t\tsuper();\n\n\t\tthis.type = 'PolyhedronGeometry';\n\n\t\tthis.parameters = {\n\t\t\tvertices: vertices,\n\t\t\tindices: indices,\n\t\t\tradius: radius,\n\t\t\tdetail: detail\n\t\t};\n\n\t\t// default buffer data\n\n\t\tconst vertexBuffer = [];\n\t\tconst uvBuffer = [];\n\n\t\t// the subdivision creates the vertex buffer data\n\n\t\tsubdivide( detail );\n\n\t\t// all vertices should lie on a conceptual sphere with a given radius\n\n\t\tapplyRadius( radius );\n\n\t\t// finally, create the uv data\n\n\t\tgenerateUVs();\n\n\t\t// build non-indexed geometry\n\n\t\tthis.setAttribute( 'position', new Float32BufferAttribute( vertexBuffer, 3 ) );\n\t\tthis.setAttribute( 'normal', new Float32BufferAttribute( vertexBuffer.slice(), 3 ) );\n\t\tthis.setAttribute( 'uv', new Float32BufferAttribute( uvBuffer, 2 ) );\n\n\t\tif ( detail === 0 ) {\n\n\t\t\tthis.computeVertexNormals(); // flat normals\n\n\t\t} else {\n\n\t\t\tthis.normalizeNormals(); // smooth normals\n\n\t\t}\n\n\t\t// helper functions\n\n\t\tfunction subdivide( detail ) {\n\n\t\t\tconst a = new Vector3();\n\t\t\tconst b = new Vector3();\n\t\t\tconst c = new Vector3();\n\n\t\t\t// iterate over all faces and apply a subdivision with the given detail value\n\n\t\t\tfor ( let i = 0; i < indices.length; i += 3 ) {\n\n\t\t\t\t// get the vertices of the face\n\n\t\t\t\tgetVertexByIndex( indices[ i + 0 ], a );\n\t\t\t\tgetVertexByIndex( indices[ i + 1 ], b );\n\t\t\t\tgetVertexByIndex( indices[ i + 2 ], c );\n\n\t\t\t\t// perform subdivision\n\n\t\t\t\tsubdivideFace( a, b, c, detail );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction subdivideFace( a, b, c, detail ) {\n\n\t\t\tconst cols = detail + 1;\n\n\t\t\t// we use this multidimensional array as a data structure for creating the subdivision\n\n\t\t\tconst v = [];\n\n\t\t\t// construct all of the vertices for this subdivision\n\n\t\t\tfor ( let i = 0; i <= cols; i ++ ) {\n\n\t\t\t\tv[ i ] = [];\n\n\t\t\t\tconst aj = a.clone().lerp( c, i / cols );\n\t\t\t\tconst bj = b.clone().lerp( c, i / cols );\n\n\t\t\t\tconst rows = cols - i;\n\n\t\t\t\tfor ( let j = 0; j <= rows; j ++ ) {\n\n\t\t\t\t\tif ( j === 0 && i === cols ) {\n\n\t\t\t\t\t\tv[ i ][ j ] = aj;\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tv[ i ][ j ] = aj.clone().lerp( bj, j / rows );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// construct all of the faces\n\n\t\t\tfor ( let i = 0; i < cols; i ++ ) {\n\n\t\t\t\tfor ( let j = 0; j < 2 * ( cols - i ) - 1; j ++ ) {\n\n\t\t\t\t\tconst k = Math.floor( j / 2 );\n\n\t\t\t\t\tif ( j % 2 === 0 ) {\n\n\t\t\t\t\t\tpushVertex( v[ i ][ k + 1 ] );\n\t\t\t\t\t\tpushVertex( v[ i + 1 ][ k ] );\n\t\t\t\t\t\tpushVertex( v[ i ][ k ] );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\tpushVertex( v[ i ][ k + 1 ] );\n\t\t\t\t\t\tpushVertex( v[ i + 1 ][ k + 1 ] );\n\t\t\t\t\t\tpushVertex( v[ i + 1 ][ k ] );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction applyRadius( radius ) {\n\n\t\t\tconst vertex = new Vector3();\n\n\t\t\t// iterate over the entire buffer and apply the radius to each vertex\n\n\t\t\tfor ( let i = 0; i < vertexBuffer.length; i += 3 ) {\n\n\t\t\t\tvertex.x = vertexBuffer[ i + 0 ];\n\t\t\t\tvertex.y = vertexBuffer[ i + 1 ];\n\t\t\t\tvertex.z = vertexBuffer[ i + 2 ];\n\n\t\t\t\tvertex.normalize().multiplyScalar( radius );\n\n\t\t\t\tvertexBuffer[ i + 0 ] = vertex.x;\n\t\t\t\tvertexBuffer[ i + 1 ] = vertex.y;\n\t\t\t\tvertexBuffer[ i + 2 ] = vertex.z;\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction generateUVs() {\n\n\t\t\tconst vertex = new Vector3();\n\n\t\t\tfor ( let i = 0; i < vertexBuffer.length; i += 3 ) {\n\n\t\t\t\tvertex.x = vertexBuffer[ i + 0 ];\n\t\t\t\tvertex.y = vertexBuffer[ i + 1 ];\n\t\t\t\tvertex.z = vertexBuffer[ i + 2 ];\n\n\t\t\t\tconst u = azimuth( vertex ) / 2 / Math.PI + 0.5;\n\t\t\t\tconst v = inclination( vertex ) / Math.PI + 0.5;\n\t\t\t\tuvBuffer.push( u, 1 - v );\n\n\t\t\t}\n\n\t\t\tcorrectUVs();\n\n\t\t\tcorrectSeam();\n\n\t\t}\n\n\t\tfunction correctSeam() {\n\n\t\t\t// handle case when face straddles the seam, see #3269\n\n\t\t\tfor ( let i = 0; i < uvBuffer.length; i += 6 ) {\n\n\t\t\t\t// uv data of a single face\n\n\t\t\t\tconst x0 = uvBuffer[ i + 0 ];\n\t\t\t\tconst x1 = uvBuffer[ i + 2 ];\n\t\t\t\tconst x2 = uvBuffer[ i + 4 ];\n\n\t\t\t\tconst max = Math.max( x0, x1, x2 );\n\t\t\t\tconst min = Math.min( x0, x1, x2 );\n\n\t\t\t\t// 0.9 is somewhat arbitrary\n\n\t\t\t\tif ( max > 0.9 && min < 0.1 ) {\n\n\t\t\t\t\tif ( x0 < 0.2 ) uvBuffer[ i + 0 ] += 1;\n\t\t\t\t\tif ( x1 < 0.2 ) uvBuffer[ i + 2 ] += 1;\n\t\t\t\t\tif ( x2 < 0.2 ) uvBuffer[ i + 4 ] += 1;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction pushVertex( vertex ) {\n\n\t\t\tvertexBuffer.push( vertex.x, vertex.y, vertex.z );\n\n\t\t}\n\n\t\tfunction getVertexByIndex( index, vertex ) {\n\n\t\t\tconst stride = index * 3;\n\n\t\t\tvertex.x = vertices[ stride + 0 ];\n\t\t\tvertex.y = vertices[ stride + 1 ];\n\t\t\tvertex.z = vertices[ stride + 2 ];\n\n\t\t}\n\n\t\tfunction correctUVs() {\n\n\t\t\tconst a = new Vector3();\n\t\t\tconst b = new Vector3();\n\t\t\tconst c = new Vector3();\n\n\t\t\tconst centroid = new Vector3();\n\n\t\t\tconst uvA = new Vector2();\n\t\t\tconst uvB = new Vector2();\n\t\t\tconst uvC = new Vector2();\n\n\t\t\tfor ( let i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6 ) {\n\n\t\t\t\ta.set( vertexBuffer[ i + 0 ], vertexBuffer[ i + 1 ], vertexBuffer[ i + 2 ] );\n\t\t\t\tb.set( vertexBuffer[ i + 3 ], vertexBuffer[ i + 4 ], vertexBuffer[ i + 5 ] );\n\t\t\t\tc.set( vertexBuffer[ i + 6 ], vertexBuffer[ i + 7 ], vertexBuffer[ i + 8 ] );\n\n\t\t\t\tuvA.set( uvBuffer[ j + 0 ], uvBuffer[ j + 1 ] );\n\t\t\t\tuvB.set( uvBuffer[ j + 2 ], uvBuffer[ j + 3 ] );\n\t\t\t\tuvC.set( uvBuffer[ j + 4 ], uvBuffer[ j + 5 ] );\n\n\t\t\t\tcentroid.copy( a ).add( b ).add( c ).divideScalar( 3 );\n\n\t\t\t\tconst azi = azimuth( centroid );\n\n\t\t\t\tcorrectUV( uvA, j + 0, a, azi );\n\t\t\t\tcorrectUV( uvB, j + 2, b, azi );\n\t\t\t\tcorrectUV( uvC, j + 4, c, azi );\n\n\t\t\t}\n\n\t\t}\n\n\t\tfunction correctUV( uv, stride, vector, azimuth ) {\n\n\t\t\tif ( ( azimuth < 0 ) && ( uv.x === 1 ) ) {\n\n\t\t\t\tuvBuffer[ stride ] = uv.x - 1;\n\n\t\t\t}\n\n\t\t\tif ( ( vector.x === 0 ) && ( vector.z === 0 ) ) {\n\n\t\t\t\tuvBuffer[ stride ] = azimuth / 2 / Math.PI + 0.5;\n\n\t\t\t}\n\n\t\t}\n\n\t\t// Angle around the Y axis, counter-clockwise when looking from above.\n\n\t\tfunction azimuth( vector ) {\n\n\t\t\treturn Math.atan2( vector.z, - vector.x );\n\n\t\t}\n\n\n\t\t// Angle above the XZ plane.\n\n\t\tfunction inclination( vector ) {\n\n\t\t\treturn Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) );\n\n\t\t}\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.parameters = Object.assign( {}, source.parameters );\n\n\t\treturn this;\n\n\t}\n\n\tstatic fromJSON( data ) {\n\n\t\treturn new PolyhedronGeometry( data.vertices, data.indices, data.radius, data.details );\n\n\t}\n\n}\n\nexport { PolyhedronGeometry };\n","import { PolyhedronGeometry } from './PolyhedronGeometry.js';\n\nclass IcosahedronGeometry extends PolyhedronGeometry {\n\n\tconstructor( radius = 1, detail = 0 ) {\n\n\t\tconst t = ( 1 + Math.sqrt( 5 ) ) / 2;\n\n\t\tconst vertices = [\n\t\t\t- 1, t, 0, \t1, t, 0, \t- 1, - t, 0, \t1, - t, 0,\n\t\t\t0, - 1, t, \t0, 1, t,\t0, - 1, - t, \t0, 1, - t,\n\t\t\tt, 0, - 1, \tt, 0, 1, \t- t, 0, - 1, \t- t, 0, 1\n\t\t];\n\n\t\tconst indices = [\n\t\t\t0, 11, 5, \t0, 5, 1, \t0, 1, 7, \t0, 7, 10, \t0, 10, 11,\n\t\t\t1, 5, 9, \t5, 11, 4,\t11, 10, 2,\t10, 7, 6,\t7, 1, 8,\n\t\t\t3, 9, 4, \t3, 4, 2,\t3, 2, 6,\t3, 6, 8,\t3, 8, 9,\n\t\t\t4, 9, 5, \t2, 4, 11,\t6, 2, 10,\t8, 6, 7,\t9, 8, 1\n\t\t];\n\n\t\tsuper( vertices, indices, radius, detail );\n\n\t\tthis.type = 'IcosahedronGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\tdetail: detail\n\t\t};\n\n\t}\n\n\tstatic fromJSON( data ) {\n\n\t\treturn new IcosahedronGeometry( data.radius, data.detail );\n\n\t}\n\n}\n\n\nexport { IcosahedronGeometry };\n","import { PolyhedronGeometry } from './PolyhedronGeometry.js';\n\nclass OctahedronGeometry extends PolyhedronGeometry {\n\n\tconstructor( radius = 1, detail = 0 ) {\n\n\t\tconst vertices = [\n\t\t\t1, 0, 0, \t- 1, 0, 0,\t0, 1, 0,\n\t\t\t0, - 1, 0, \t0, 0, 1,\t0, 0, - 1\n\t\t];\n\n\t\tconst indices = [\n\t\t\t0, 2, 4,\t0, 4, 3,\t0, 3, 5,\n\t\t\t0, 5, 2,\t1, 2, 5,\t1, 5, 3,\n\t\t\t1, 3, 4,\t1, 4, 2\n\t\t];\n\n\t\tsuper( vertices, indices, radius, detail );\n\n\t\tthis.type = 'OctahedronGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\tdetail: detail\n\t\t};\n\n\t}\n\n\tstatic fromJSON( data ) {\n\n\t\treturn new OctahedronGeometry( data.radius, data.detail );\n\n\t}\n\n}\n\nexport { OctahedronGeometry };\n","import { PolyhedronGeometry } from './PolyhedronGeometry.js';\n\nclass TetrahedronGeometry extends PolyhedronGeometry {\n\n\tconstructor( radius = 1, detail = 0 ) {\n\n\t\tconst vertices = [\n\t\t\t1, 1, 1, \t- 1, - 1, 1, \t- 1, 1, - 1, \t1, - 1, - 1\n\t\t];\n\n\t\tconst indices = [\n\t\t\t2, 1, 0, \t0, 3, 2,\t1, 3, 0,\t2, 3, 1\n\t\t];\n\n\t\tsuper( vertices, indices, radius, detail );\n\n\t\tthis.type = 'TetrahedronGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\tdetail: detail\n\t\t};\n\n\t}\n\n\tstatic fromJSON( data ) {\n\n\t\treturn new TetrahedronGeometry( data.radius, data.detail );\n\n\t}\n\n}\n\nexport { TetrahedronGeometry };\n","import { BufferGeometry } from '../core/BufferGeometry.js';\nimport { Float32BufferAttribute } from '../core/BufferAttribute.js';\nimport { Vector3 } from '../math/Vector3.js';\n\nclass TorusGeometry extends BufferGeometry {\n\n\tconstructor( radius = 1, tube = 0.4, radialSegments = 12, tubularSegments = 48, arc = Math.PI * 2 ) {\n\n\t\tsuper();\n\n\t\tthis.type = 'TorusGeometry';\n\n\t\tthis.parameters = {\n\t\t\tradius: radius,\n\t\t\ttube: tube,\n\t\t\tradialSegments: radialSegments,\n\t\t\ttubularSegments: tubularSegments,\n\t\t\tarc: arc\n\t\t};\n\n\t\tradialSegments = Math.floor( radialSegments );\n\t\ttubularSegments = Math.floor( tubularSegments );\n\n\t\t// buffers\n\n\t\tconst indices = [];\n\t\tconst vertices = [];\n\t\tconst normals = [];\n\t\tconst uvs = [];\n\n\t\t// helper variables\n\n\t\tconst center = new Vector3();\n\t\tconst vertex = new Vector3();\n\t\tconst normal = new Vector3();\n\n\t\t// generate vertices, normals and uvs\n\n\t\tfor ( let j = 0; j <= radialSegments; j ++ ) {\n\n\t\t\tfor ( let i = 0; i <= tubularSegments; i ++ ) {\n\n\t\t\t\tconst u = i / tubularSegments * arc;\n\t\t\t\tconst v = j / radialSegments * Math.PI * 2;\n\n\t\t\t\t// vertex\n\n\t\t\t\tvertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u );\n\t\t\t\tvertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u );\n\t\t\t\tvertex.z = tube * Math.sin( v );\n\n\t\t\t\tvertices.push( vertex.x, vertex.y, vertex.z );\n\n\t\t\t\t// normal\n\n\t\t\t\tcenter.x = radius * Math.cos( u );\n\t\t\t\tcenter.y = radius * Math.sin( u );\n\t\t\t\tnormal.subVectors( vertex, center ).normalize();\n\n\t\t\t\tnormals.push( normal.x, normal.y, normal.z );\n\n\t\t\t\t// uv\n\n\t\t\t\tuvs.push( i / tubularSegments );\n\t\t\t\tuvs.push( j / radialSegments );\n\n\t\t\t}\n\n\t\t}\n\n\t\t// generate indices\n\n\t\tfor ( let j = 1; j <= radialSegments; j ++ ) {\n\n\t\t\tfor ( let i = 1; i <= tubularSegments; i ++ ) {\n\n\t\t\t\t// indices\n\n\t\t\t\tconst a = ( tubularSegments + 1 ) * j + i - 1;\n\t\t\t\tconst b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1;\n\t\t\t\tconst c = ( tubularSegments + 1 ) * ( j - 1 ) + i;\n\t\t\t\tconst d = ( tubularSegments + 1 ) * j + i;\n\n\t\t\t\t// faces\n\n\t\t\t\tindices.push( a, b, d );\n\t\t\t\tindices.push( b, c, d );\n\n\t\t\t}\n\n\t\t}\n\n\t\t// build geometry\n\n\t\tthis.setIndex( indices );\n\t\tthis.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n\t\tthis.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n\t\tthis.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.parameters = Object.assign( {}, source.parameters );\n\n\t\treturn this;\n\n\t}\n\n\tstatic fromJSON( data ) {\n\n\t\treturn new TorusGeometry( data.radius, data.tube, data.radialSegments, data.tubularSegments, data.arc );\n\n\t}\n\n}\n\nexport { TorusGeometry };\n","import { Object3D } from '../core/Object3D.js';\nimport { Color } from '../math/Color.js';\n\nclass Light extends Object3D {\n\n\tconstructor( color, intensity = 1 ) {\n\n\t\tsuper();\n\n\t\tthis.isLight = true;\n\n\t\tthis.type = 'Light';\n\n\t\tthis.color = new Color( color );\n\t\tthis.intensity = intensity;\n\n\t}\n\n\tdispose() {\n\n\t\t// Empty here in base class; some subclasses override.\n\n\t}\n\n\tcopy( source, recursive ) {\n\n\t\tsuper.copy( source, recursive );\n\n\t\tthis.color.copy( source.color );\n\t\tthis.intensity = source.intensity;\n\n\t\treturn this;\n\n\t}\n\n\ttoJSON( meta ) {\n\n\t\tconst data = super.toJSON( meta );\n\n\t\tdata.object.color = this.color.getHex();\n\t\tdata.object.intensity = this.intensity;\n\n\t\tif ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex();\n\n\t\tif ( this.distance !== undefined ) data.object.distance = this.distance;\n\t\tif ( this.angle !== undefined ) data.object.angle = this.angle;\n\t\tif ( this.decay !== undefined ) data.object.decay = this.decay;\n\t\tif ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra;\n\n\t\tif ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON();\n\n\t\treturn data;\n\n\t}\n\n}\n\nexport { Light };\n","import { Matrix4 } from '../math/Matrix4.js';\nimport { Vector2 } from '../math/Vector2.js';\nimport { Vector3 } from '../math/Vector3.js';\nimport { Vector4 } from '../math/Vector4.js';\nimport { Frustum } from '../math/Frustum.js';\n\nconst _projScreenMatrix = /*@__PURE__*/ new Matrix4();\nconst _lightPositionWorld = /*@__PURE__*/ new Vector3();\nconst _lookTarget = /*@__PURE__*/ new Vector3();\n\nclass LightShadow {\n\n\tconstructor( camera ) {\n\n\t\tthis.camera = camera;\n\n\t\tthis.bias = 0;\n\t\tthis.normalBias = 0;\n\t\tthis.radius = 1;\n\t\tthis.blurSamples = 8;\n\n\t\tthis.mapSize = new Vector2( 512, 512 );\n\n\t\tthis.map = null;\n\t\tthis.mapPass = null;\n\t\tthis.matrix = new Matrix4();\n\n\t\tthis.autoUpdate = true;\n\t\tthis.needsUpdate = false;\n\n\t\tthis._frustum = new Frustum();\n\t\tthis._frameExtents = new Vector2( 1, 1 );\n\n\t\tthis._viewportCount = 1;\n\n\t\tthis._viewports = [\n\n\t\t\tnew Vector4( 0, 0, 1, 1 )\n\n\t\t];\n\n\t}\n\n\tgetViewportCount() {\n\n\t\treturn this._viewportCount;\n\n\t}\n\n\tgetFrustum() {\n\n\t\treturn this._frustum;\n\n\t}\n\n\tupdateMatrices( light ) {\n\n\t\tconst shadowCamera = this.camera;\n\t\tconst shadowMatrix = this.matrix;\n\n\t\t_lightPositionWorld.setFromMatrixPosition( light.matrixWorld );\n\t\tshadowCamera.position.copy( _lightPositionWorld );\n\n\t\t_lookTarget.setFromMatrixPosition( light.target.matrixWorld );\n\t\tshadowCamera.lookAt( _lookTarget );\n\t\tshadowCamera.updateMatrixWorld();\n\n\t\t_projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse );\n\t\tthis._frustum.setFromProjectionMatrix( _projScreenMatrix );\n\n\t\tshadowMatrix.set(\n\t\t\t0.5, 0.0, 0.0, 0.5,\n\t\t\t0.0, 0.5, 0.0, 0.5,\n\t\t\t0.0, 0.0, 0.5, 0.5,\n\t\t\t0.0, 0.0, 0.0, 1.0\n\t\t);\n\n\t\tshadowMatrix.multiply( _projScreenMatrix );\n\n\t}\n\n\tgetViewport( viewportIndex ) {\n\n\t\treturn this._viewports[ viewportIndex ];\n\n\t}\n\n\tgetFrameExtents() {\n\n\t\treturn this._frameExtents;\n\n\t}\n\n\tdispose() {\n\n\t\tif ( this.map ) {\n\n\t\t\tthis.map.dispose();\n\n\t\t}\n\n\t\tif ( this.mapPass ) {\n\n\t\t\tthis.mapPass.dispose();\n\n\t\t}\n\n\t}\n\n\tcopy( source ) {\n\n\t\tthis.camera = source.camera.clone();\n\n\t\tthis.bias = source.bias;\n\t\tthis.radius = source.radius;\n\n\t\tthis.mapSize.copy( source.mapSize );\n\n\t\treturn this;\n\n\t}\n\n\tclone() {\n\n\t\treturn new this.constructor().copy( this );\n\n\t}\n\n\ttoJSON() {\n\n\t\tconst object = {};\n\n\t\tif ( this.bias !== 0 ) object.bias = this.bias;\n\t\tif ( this.normalBias !== 0 ) object.normalBias = this.normalBias;\n\t\tif ( this.radius !== 1 ) object.radius = this.radius;\n\t\tif ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray();\n\n\t\tobject.camera = this.camera.toJSON( false ).object;\n\t\tdelete object.camera.matrix;\n\n\t\treturn object;\n\n\t}\n\n}\n\nexport { LightShadow };\n","import { LightShadow } from './LightShadow.js';\nimport { OrthographicCamera } from '../cameras/OrthographicCamera.js';\n\nclass DirectionalLightShadow extends LightShadow {\n\n\tconstructor() {\n\n\t\tsuper( new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) );\n\n\t\tthis.isDirectionalLightShadow = true;\n\n\t}\n\n}\n\nexport { DirectionalLightShadow };\n","import { Light } from './Light.js';\nimport { DirectionalLightShadow } from './DirectionalLightShadow.js';\nimport { Object3D } from '../core/Object3D.js';\n\nclass DirectionalLight extends Light {\n\n\tconstructor( color, intensity ) {\n\n\t\tsuper( color, intensity );\n\n\t\tthis.isDirectionalLight = true;\n\n\t\tthis.type = 'DirectionalLight';\n\n\t\tthis.position.copy( Object3D.DEFAULT_UP );\n\t\tthis.updateMatrix();\n\n\t\tthis.target = new Object3D();\n\n\t\tthis.shadow = new DirectionalLightShadow();\n\n\t}\n\n\tdispose() {\n\n\t\tthis.shadow.dispose();\n\n\t}\n\n\tcopy( source ) {\n\n\t\tsuper.copy( source );\n\n\t\tthis.target = source.target.clone();\n\t\tthis.shadow = source.shadow.clone();\n\n\t\treturn this;\n\n\t}\n\n}\n\nexport { DirectionalLight };\n","import { Light } from './Light.js';\n\nclass AmbientLight extends Light {\n\n\tconstructor( color, intensity ) {\n\n\t\tsuper( color, intensity );\n\n\t\tthis.isAmbientLight = true;\n\n\t\tthis.type = 'AmbientLight';\n\n\t}\n\n}\n\nexport { AmbientLight };\n","import { Matrix4 } from '../math/Matrix4.js';\nimport * as MathUtils from '../math/MathUtils.js';\nimport { PerspectiveCamera } from './PerspectiveCamera.js';\n\nconst _eyeRight = /*@__PURE__*/ new Matrix4();\nconst _eyeLeft = /*@__PURE__*/ new Matrix4();\nconst _projectionMatrix = /*@__PURE__*/ new Matrix4();\n\nclass StereoCamera {\n\n\tconstructor() {\n\n\t\tthis.type = 'StereoCamera';\n\n\t\tthis.aspect = 1;\n\n\t\tthis.eyeSep = 0.064;\n\n\t\tthis.cameraL = new PerspectiveCamera();\n\t\tthis.cameraL.layers.enable( 1 );\n\t\tthis.cameraL.matrixAutoUpdate = false;\n\n\t\tthis.cameraR = new PerspectiveCamera();\n\t\tthis.cameraR.layers.enable( 2 );\n\t\tthis.cameraR.matrixAutoUpdate = false;\n\n\t\tthis._cache = {\n\t\t\tfocus: null,\n\t\t\tfov: null,\n\t\t\taspect: null,\n\t\t\tnear: null,\n\t\t\tfar: null,\n\t\t\tzoom: null,\n\t\t\teyeSep: null\n\t\t};\n\n\t}\n\n\tupdate( camera ) {\n\n\t\tconst cache = this._cache;\n\n\t\tconst needsUpdate = cache.focus !== camera.focus || cache.fov !== camera.fov ||\n\t\t\tcache.aspect !== camera.aspect * this.aspect || cache.near !== camera.near ||\n\t\t\tcache.far !== camera.far || cache.zoom !== camera.zoom || cache.eyeSep !== this.eyeSep;\n\n\t\tif ( needsUpdate ) {\n\n\t\t\tcache.focus = camera.focus;\n\t\t\tcache.fov = camera.fov;\n\t\t\tcache.aspect = camera.aspect * this.aspect;\n\t\t\tcache.near = camera.near;\n\t\t\tcache.far = camera.far;\n\t\t\tcache.zoom = camera.zoom;\n\t\t\tcache.eyeSep = this.eyeSep;\n\n\t\t\t// Off-axis stereoscopic effect based on\n\t\t\t// http://paulbourke.net/stereographics/stereorender/\n\n\t\t\t_projectionMatrix.copy( camera.projectionMatrix );\n\t\t\tconst eyeSepHalf = cache.eyeSep / 2;\n\t\t\tconst eyeSepOnProjection = eyeSepHalf * cache.near / cache.focus;\n\t\t\tconst ymax = ( cache.near * Math.tan( MathUtils.DEG2RAD * cache.fov * 0.5 ) ) / cache.zoom;\n\t\t\tlet xmin, xmax;\n\n\t\t\t// translate xOffset\n\n\t\t\t_eyeLeft.elements[ 12 ] = - eyeSepHalf;\n\t\t\t_eyeRight.elements[ 12 ] = eyeSepHalf;\n\n\t\t\t// for left eye\n\n\t\t\txmin = - ymax * cache.aspect + eyeSepOnProjection;\n\t\t\txmax = ymax * cache.aspect + eyeSepOnProjection;\n\n\t\t\t_projectionMatrix.elements[ 0 ] = 2 * cache.near / ( xmax - xmin );\n\t\t\t_projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin );\n\n\t\t\tthis.cameraL.projectionMatrix.copy( _projectionMatrix );\n\n\t\t\t// for right eye\n\n\t\t\txmin = - ymax * cache.aspect - eyeSepOnProjection;\n\t\t\txmax = ymax * cache.aspect - eyeSepOnProjection;\n\n\t\t\t_projectionMatrix.elements[ 0 ] = 2 * cache.near / ( xmax - xmin );\n\t\t\t_projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin );\n\n\t\t\tthis.cameraR.projectionMatrix.copy( _projectionMatrix );\n\n\t\t}\n\n\t\tthis.cameraL.matrixWorld.copy( camera.matrixWorld ).multiply( _eyeLeft );\n\t\tthis.cameraR.matrixWorld.copy( camera.matrixWorld ).multiply( _eyeRight );\n\n\t}\n\n}\n\nexport { StereoCamera };\n","class Uniform {\n\n\tconstructor( value ) {\n\n\t\tthis.value = value;\n\n\t}\n\n\tclone() {\n\n\t\treturn new Uniform( this.value.clone === undefined ? this.value : this.value.clone() );\n\n\t}\n\n}\n\nexport { Uniform };\n","/**\n * @file Utils\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector2, Vector3, Matrix4, Quaternion } from 'three'\n\nexport function getQuery (id: string) {\n if (typeof window === 'undefined') return undefined\n\n const a = new RegExp(`${id}=([^&#=]*)`)\n const m = a.exec(window.location.search)\n\n if (m) {\n return decodeURIComponent(m[1])\n } else {\n return undefined\n }\n}\n\nexport function boolean (value: any) {\n if (!value) {\n return false\n }\n\n if (typeof value === 'string') {\n return /^1|true|t|yes|y$/i.test(value)\n }\n\n return true\n}\n\nexport function defaults (value: any, defaultValue: any) {\n return value !== undefined ? value : defaultValue\n}\n\nexport function createParams (params: {[k in keyof T]?: any}, defaultParams: T) {\n const o: any = Object.assign({}, params)\n for (const k in defaultParams) {\n const value = params[k]\n if (value === undefined) o[k] = defaultParams[k]\n }\n return o as T\n}\n\nexport function updateParams (params: T, newParams: {[k in keyof T]?: any}) {\n for (const k in newParams) {\n const value = newParams[k]\n if (value !== undefined) params[k] = value\n }\n return params as T\n}\n\nexport function pick (object: { [index: string]: any }) {\n const properties = [].slice.call(arguments, 1)\n return properties.reduce((a: { [index: string]: any }, e: any) => {\n a[ e ] = object[ e ]\n return a\n }, {})\n}\n\nexport function flatten (array: any[], ret: any[]) {\n ret = defaults(ret, [])\n for (let i = 0; i < array.length; i++) {\n if (Array.isArray(array[i])) {\n flatten(array[i], ret)\n } else {\n ret.push(array[i])\n }\n }\n return ret\n}\n\nexport function getProtocol () {\n const protocol = window.location.protocol\n return protocol.match(/http(s)?:/gi) === null ? 'http:' : protocol\n}\n\nexport function getBrowser () {\n if (typeof window === 'undefined') return false\n\n const ua = window.navigator.userAgent\n\n if (/Opera|OPR/.test(ua)) {\n return 'Opera'\n } else if (/Chrome/i.test(ua)) {\n return 'Chrome'\n } else if (/Firefox/i.test(ua)) {\n return 'Firefox'\n } else if (/Mobile(\\/.*)? Safari/i.test(ua)) {\n return 'Mobile Safari'\n } else if (/MSIE/i.test(ua)) {\n return 'Internet Explorer'\n } else if (/Safari/i.test(ua)) {\n return 'Safari'\n }\n\n return false\n}\n\nexport function getAbsolutePath (relativePath: string) {\n const loc = window.location\n const pn = loc.pathname\n const basePath = pn.substring(0, pn.lastIndexOf('/') + 1)\n\n return loc.origin + basePath + relativePath\n}\n\nexport function deepCopy (src: any) {\n if (typeof src !== 'object') {\n return src\n }\n\n const dst: { [index: string]: any } = Array.isArray(src) ? [] : {}\n\n for (let key in src) {\n dst[ key ] = deepCopy(src[ key ])\n }\n\n return dst\n}\n\nexport function deepEqual(a: any, b: any) {\n // from https://github.com/epoberezkin/fast-deep-equal MIT\n if (a === b) return true;\n\n const arrA = Array.isArray(a)\n const arrB = Array.isArray(b)\n\n if (arrA && arrB) {\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i++) {\n if (!deepEqual(a[i], b[i])) return false\n }\n return true\n }\n\n if (arrA !== arrB) return false\n\n if (a && b && typeof a === 'object' && typeof b === 'object') {\n const keys = Object.keys(a)\n if (keys.length !== Object.keys(b).length) return false;\n\n const dateA = a instanceof Date\n const dateB = b instanceof Date\n if (dateA && dateB) return a.getTime() === b.getTime()\n if (dateA !== dateB) return false\n\n const regexpA = a instanceof RegExp\n const regexpB = b instanceof RegExp\n if (regexpA && regexpB) return a.toString() === b.toString()\n if (regexpA !== regexpB) return false\n\n for (let i = 0; i < keys.length; i++) {\n if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false\n }\n\n for (let i = 0; i < keys.length; i++) {\n if(!deepEqual(a[keys[i]], b[keys[i]])) return false\n }\n\n return true\n }\n\n return false\n}\n\nfunction openUrl (url: string) {\n const opened = window.open(url, '_blank')\n if (!opened) {\n window.location.href = url\n }\n}\n\nexport function download (data: Blob|string, downloadName = 'download') {\n // using ideas from https://github.com/eligrey/FileSaver.js/blob/master/FileSaver.js\n\n if (!data) return\n\n const isSafari = getBrowser() === 'Safari'\n const isChromeIos = /CriOS\\/[\\d]+/.test(window.navigator.userAgent)\n\n const a = document.createElement('a')\n\n function open (str: string) {\n openUrl(isChromeIos ? str : str.replace(/^data:[^;]*;/, 'data:attachment/file;'))\n }\n\n if (typeof navigator !== 'undefined' && (navigator as any).msSaveOrOpenBlob) {\n // native saveAs in IE 10+\n (navigator as any).msSaveOrOpenBlob(data, downloadName)\n } else if ((isSafari || isChromeIos) && FileReader) {\n if (data instanceof Blob) {\n // no downloading of blob urls in Safari\n var reader = new FileReader()\n reader.onloadend = function () {\n open(reader.result as string)\n }\n reader.readAsDataURL(data)\n } else {\n open(data)\n }\n } else {\n let objectUrlCreated = false\n if (data instanceof Blob) {\n data = URL.createObjectURL(data)\n objectUrlCreated = true\n }\n\n if ('download' in a) {\n // download link available\n a.style.display = 'hidden'\n document.body.appendChild(a)\n a.href = data\n a.download = downloadName\n a.target = '_blank'\n a.click()\n document.body.removeChild(a)\n } else {\n openUrl(data)\n }\n\n if (objectUrlCreated) {\n window.URL.revokeObjectURL(data)\n }\n }\n}\n\nexport function submit (url: string, data: FormData, callback: Function, onerror: Function) {\n const xhr = new XMLHttpRequest()\n xhr.open('POST', url)\n\n xhr.addEventListener('load', function () {\n if (xhr.status === 200 || xhr.status === 304) {\n callback(xhr.response)\n } else {\n if (typeof onerror === 'function') {\n onerror(xhr.status)\n }\n }\n }, false)\n\n xhr.send(data)\n}\n\ninterface HTMLInputEvent extends Event {\n target: HTMLInputElement & EventTarget\n}\n\nexport function open (callback: Function, extensionList = ['*']) {\n const fileInput = document.createElement('input')\n fileInput.type = 'file'\n fileInput.multiple = true\n fileInput.style.display = 'hidden'\n document.body.appendChild(fileInput)\n fileInput.accept = '.' + extensionList.join(',.')\n fileInput.addEventListener('change', function (e: HTMLInputEvent) {\n callback(e.target.files)\n }, false)\n\n fileInput.click()\n}\n\nexport function throttle (func: Function, wait: number, options: { leading?: boolean, trailing?: boolean }) {\n // from http://underscorejs.org/docs/underscore.html\n\n let context: any\n let args: any\n let result: any\n let timeout: any = null\n let previous = 0\n\n if (!options) options = {}\n\n function later () {\n previous = options.leading === false ? 0 : Date.now()\n timeout = null\n result = func.apply(context, args)\n if (!timeout) context = args = null\n }\n\n return function throttle (this: any) {\n var now = Date.now()\n if (!previous && options.leading === false) previous = now\n var remaining = wait - (now - previous)\n context = this\n args = arguments\n if (remaining <= 0 || remaining > wait) {\n if (timeout) {\n clearTimeout(timeout)\n timeout = null\n }\n previous = now\n result = func.apply(context, args)\n if (!timeout) context = args = null\n } else if (!timeout && options.trailing !== false) {\n timeout = setTimeout(later, remaining)\n }\n\n return result\n }\n}\n\nexport function lexicographicCompare (elm1: T, elm2: T) {\n if (elm1 < elm2) return -1\n if (elm1 > elm2) return 1\n return 0\n}\n\n/**\n * Does a binary search to get the index of an element in the input array\n * @function\n * @example\n * var array = [ 1, 2, 3, 4, 5, 6 ];\n * var element = 4;\n * binarySearchIndexOf( array, element ); // returns 3\n *\n * @param {Array} array - sorted array\n * @param {Anything} element - element to search for in the array\n * @param {Function} [compareFunction] - compare function\n * @return {Number} the index of the element or -1 if not in the array\n */\nexport function binarySearchIndexOf (array: T[], element: T, compareFunction = lexicographicCompare) {\n let low = 0\n let high = array.length - 1\n while (low <= high) {\n const mid = (low + high) >> 1\n const cmp = compareFunction(element, array[ mid ])\n if (cmp > 0) {\n low = mid + 1\n } else if (cmp < 0) {\n high = mid - 1\n } else {\n return mid\n }\n }\n return -low - 1\n}\n\nexport function binarySearchForLeftRange (array: number[], leftRange: number) {\n let high = array.length - 1\n if (array[ high ] < leftRange) return -1\n let low = 0\n while (low <= high) {\n const mid = (low + high) >> 1\n if (array[ mid ] >= leftRange) {\n high = mid - 1\n } else {\n low = mid + 1\n }\n }\n return high + 1\n}\n\nexport function binarySearchForRightRange (array: number[], rightRange: number) {\n if (array[ 0 ] > rightRange) return -1\n let low = 0\n let high = array.length - 1\n while (low <= high) {\n const mid = (low + high) >> 1\n if (array[ mid ] > rightRange) {\n high = mid - 1\n } else {\n low = mid + 1\n }\n }\n return low - 1\n}\n\nexport function rangeInSortedArray (array: number[], min: number, max: number) {\n const indexLeft = binarySearchForLeftRange(array, min)\n const indexRight = binarySearchForRightRange(array, max)\n if (indexLeft === -1 || indexRight === -1 || indexLeft > indexRight) {\n return 0\n } else {\n return indexRight - indexLeft + 1\n }\n}\n\nexport function dataURItoImage (dataURI: string) {\n const img = document.createElement('img')\n img.src = dataURI\n return img\n}\n\nexport function uniqueArray (array: any[]) {\n return array.sort().filter(function (value, index, sorted) {\n return (index === 0) || (value !== sorted[ index - 1 ])\n })\n}\n\n// String/arraybuffer conversion\n\nexport function uint8ToString (u8a: Uint8Array) {\n const chunkSize = 0x7000\n\n if (u8a.length > chunkSize) {\n const c = []\n\n for (let i = 0; i < u8a.length; i += chunkSize) {\n c.push(String.fromCharCode.apply(\n null, u8a.subarray(i, i + chunkSize)\n ))\n }\n\n return c.join('')\n } else {\n return String.fromCharCode.apply(null, u8a)\n }\n}\n\nexport function uint8ToLines (u8a: Uint8Array, chunkSize = 1024 * 1024 * 10, newline = '\\n') {\n let partialLine = ''\n let lines: string[] = []\n\n for (let i = 0; i < u8a.length; i += chunkSize) {\n const str = uint8ToString(u8a.subarray(i, i + chunkSize))\n const idx = str.lastIndexOf(newline)\n\n if (idx === -1) {\n partialLine += str\n } else {\n const str2 = partialLine + str.substr(0, idx)\n lines = lines.concat(str2.split(newline))\n\n if (idx === str.length - newline.length) {\n partialLine = ''\n } else {\n partialLine = str.substr(idx + newline.length)\n }\n }\n }\n\n if (partialLine !== '') {\n lines.push(partialLine)\n }\n\n return lines\n}\n\nexport type TypedArrayString = 'int8'|'int16'|'int32'|'uint8'|'uint16'|'uint32'|'float32'\nexport function getTypedArray (arrayType: TypedArrayString, arraySize: number) {\n switch (arrayType) {\n case 'int8':\n return new Int8Array(arraySize)\n case 'int16':\n return new Int16Array(arraySize)\n case 'int32':\n return new Int32Array(arraySize)\n case 'uint8':\n return new Uint8Array(arraySize)\n case 'uint16':\n return new Uint16Array(arraySize)\n case 'uint32':\n return new Uint32Array(arraySize)\n case 'float32':\n return new Float32Array(arraySize)\n default:\n throw new Error('arrayType unknown: ' + arrayType)\n }\n}\n\nexport function getUintArray (sizeOrArray: any, maxUint: number) { // TODO\n const TypedArray = maxUint > 65535 ? Uint32Array : Uint16Array\n return new TypedArray(sizeOrArray)\n}\n\nexport function ensureArray (value: any) {\n return Array.isArray(value) ? value : [value]\n}\n\nexport function ensureBuffer (a: any) { // TODO\n return (a.buffer && a.buffer instanceof ArrayBuffer) ? a.buffer : a\n}\n\nfunction _ensureClassFromArg (arg: any, constructor: { new (arg: any): any }) {\n return arg instanceof constructor ? arg : new constructor(arg)\n}\n\nfunction _ensureClassFromArray (array: any, constructor: { new (): any }) {\n if (array === undefined) {\n array = new constructor()\n } else if (Array.isArray(array)) {\n array = new constructor().fromArray(array)\n }\n return array\n}\n\nexport function ensureVector2 (v?: number[]|Vector2) {\n return _ensureClassFromArray(v, Vector2)\n}\n\nexport function ensureVector3 (v?: number[]|Vector3) {\n return _ensureClassFromArray(v, Vector3)\n}\n\nexport function ensureMatrix4 (m?: number[]|Matrix4) {\n return _ensureClassFromArray(m, Matrix4)\n}\n\nexport function ensureQuaternion (q?: number[]|Quaternion) {\n return _ensureClassFromArray(q, Quaternion)\n}\n\nexport function ensureFloat32Array (a?: number[]|Float32Array) {\n return _ensureClassFromArg(a, Float32Array)\n}\n\nexport interface RingBuffer {\n has: (value: T) => boolean\n get: (value: number) => T\n push: (value: T) => void\n count: number\n data: T[]\n clear: () => void\n}\n\nexport function createRingBuffer (length: number): RingBuffer {\n let pointer = 0\n let count = 0\n const buffer: T[] = []\n\n return {\n has: function (value: any) { return buffer.indexOf(value) !== -1 },\n get: function (idx: number) { return buffer[idx] },\n push: function (item: any) {\n buffer[pointer] = item\n pointer = (length + pointer + 1) % length\n ++count\n },\n get count () { return count },\n get data () { return buffer.slice(0, Math.min(count, length)) },\n clear: function () {\n count = 0\n pointer = 0\n buffer.length = 0\n }\n }\n}\n\nexport interface SimpleDict {\n has: (k: K) => boolean\n add: (k: K, v: V) => void\n del: (k: K) => void\n values: V[]\n}\n\nexport function createSimpleDict (): SimpleDict {\n const set: { [k: string]: V } = {}\n\n return {\n has: function (k: K) { return set[JSON.stringify(k)] !== undefined },\n add: function (k: K, v: V) { set[JSON.stringify(k)] = v },\n del: function (k: K) { delete set[JSON.stringify(k)] },\n get values () { return Object.keys(set).map(k => set[k]) }\n }\n}\n\nexport interface SimpleSet {\n has: (value: T) => boolean\n add: (value: T) => void\n del: (value: T) => void\n list: T[]\n}\n\nexport function createSimpleSet (): SimpleSet {\n const set: { [k: string]: T } = {}\n\n return {\n has: function (v: T) { return set[JSON.stringify(v)] !== undefined },\n add: function (v: T) { set[JSON.stringify(v)] = v },\n del: function (v: T) { delete set[JSON.stringify(v)] },\n get list () { return Object.keys(set).map(k => set[k]) },\n }\n}\n","/**\n * @file Registry\n * @author Alexander Rose \n * @private\n */\n\nimport { defaults } from '../utils'\n\nfunction toLowerCaseString (value: string) {\n return defaults(value, '').toString().toLowerCase()\n}\n\nexport default class Registry {\n name: string\n private _dict: {[k: string]: any}\n\n constructor (name: string) {\n this.name = name\n this._dict = {}\n }\n\n add (key: string, value: any) {\n this._dict[ toLowerCaseString(key) ] = value\n }\n\n get (key: string) {\n return this._dict[ toLowerCaseString(key) ]\n }\n\n get names () {\n return Object.keys(this._dict)\n }\n}","/**\n * @file Math Utils\n * @author Alexander Rose \n * @private\n */\n\nexport function degToRad (deg: number) {\n return deg * 0.01745 // deg * Math.PI / 180\n}\n\nexport function radToDeg (rad: number) {\n return rad * 57.29578 // rad * 180 / Math.PI\n}\n\n// http://www.broofa.com/Tools/Math.uuid.htm\nconst chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('')\nconst uuid = new Array(36)\n\nexport function generateUUID () {\n let rnd = 0\n let r\n\n for (let i = 0; i < 36; i++) {\n if (i === 8 || i === 13 || i === 18 || i === 23) {\n uuid[ i ] = '-'\n } else if (i === 14) {\n uuid[ i ] = '4'\n } else {\n if (rnd <= 0x02) rnd = 0x2000000 + (Math.random() * 0x1000000) | 0\n r = rnd & 0xf\n rnd = rnd >> 4\n uuid[ i ] = chars[ (i === 19) ? (r & 0x3) | 0x8 : r ]\n }\n }\n\n return uuid.join('')\n}\n\nexport function countSetBits (i: number) {\n i = i - ((i >> 1) & 0x55555555)\n i = (i & 0x33333333) + ((i >> 2) & 0x33333333)\n return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24\n}\n\nexport function normalize (value: number, min: number, max: number) {\n return (value - min) / (max - min)\n}\n\nexport function clamp (value: number, min: number, max: number) {\n return Math.max(min, Math.min(max, value))\n}\n\nexport function pclamp (value: number) {\n return clamp(value, 0, 100)\n}\n\nexport function saturate (value: number) {\n return clamp(value, 0, 1)\n}\n\nexport function lerp (start: number, stop: number, alpha: number) {\n return start + (stop - start) * alpha\n}\n\nexport function spline (p0: number, p1: number, p2: number, p3: number, t: number, tension: number) {\n const v0 = (p2 - p0) * tension\n const v1 = (p3 - p1) * tension\n const t2 = t * t\n const t3 = t * t2\n return (2 * p1 - 2 * p2 + v0 + v1) * t3 +\n (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 +\n v0 * t + p1\n}\n\nexport function smoothstep (min: number, max: number, x: number) {\n x = saturate(normalize(x, min, max))\n return x * x * (3 - 2 * x)\n}\n\nexport function smootherstep (min: number, max: number, x: number) {\n x = saturate(normalize(x, min, max))\n return x * x * x * (x * (x * 6 - 15) + 10)\n}\n\nexport function smootheststep (min: number, max: number, x: number) {\n x = saturate(normalize(x, min, max))\n return (\n -20 * Math.pow(x, 7) +\n 70 * Math.pow(x, 6) -\n 84 * Math.pow(x, 5) +\n 35 * Math.pow(x, 4)\n )\n}\n\nexport function almostIdentity (value: number, start: number, stop: number) {\n if (value > start) return value\n const a = 2 * stop - start\n const b = 2 * start - 3 * stop\n const t = value / start\n return (a * t + b) * t * t + stop\n}","\n/**\n * @license\n *\n * chroma.js - JavaScript library for color conversions\n * \n * Copyright (c) 2011-2017, Gregor Aisch\n * All rights reserved.\n * \n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n * \n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n * \n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n * \n * 3. The name Gregor Aisch may not be used to endorse or promote products\n * derived from this software without specific prior written permission.\n * \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL GREGOR AISCH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,\n * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\n * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n */\n\n(function() {\n var Color, DEG2RAD, LAB_CONSTANTS, PI, PITHIRD, RAD2DEG, TWOPI, _average_lrgb, _guess_formats, _guess_formats_sorted, _input, _interpolators, abs, atan2, bezier, blend, blend_f, brewer, burn, chroma, clip_rgb, cmyk2rgb, colors, cos, css2rgb, darken, dodge, each, floor, hcg2rgb, hex2rgb, hsi2rgb, hsl2css, hsl2rgb, hsv2rgb, interpolate, interpolate_hsx, interpolate_lab, interpolate_lrgb, interpolate_num, interpolate_rgb, lab2lch, lab2rgb, lab_xyz, lch2lab, lch2rgb, lighten, limit, log, luminance_x, m, max, multiply, normal, num2rgb, overlay, pow, rgb2cmyk, rgb2css, rgb2hcg, rgb2hex, rgb2hsi, rgb2hsl, rgb2hsv, rgb2lab, rgb2lch, rgb2luminance, rgb2num, rgb2temperature, rgb2xyz, rgb_xyz, rnd, root, round, screen, sin, sqrt, temperature2rgb, type, unpack, w3cx11, xyz_lab, xyz_rgb,\n slice = [].slice;\n\n type = (function() {\n\n /*\n for browser-safe type checking+\n ported from jQuery's $.type\n */\n var classToType, len, name, o, ref;\n classToType = {};\n ref = \"Boolean Number String Function Array Date RegExp Undefined Null\".split(\" \");\n for (o = 0, len = ref.length; o < len; o++) {\n name = ref[o];\n classToType[\"[object \" + name + \"]\"] = name.toLowerCase();\n }\n return function(obj) {\n var strType;\n strType = Object.prototype.toString.call(obj);\n return classToType[strType] || \"object\";\n };\n })();\n\n limit = function(x, min, max) {\n if (min == null) {\n min = 0;\n }\n if (max == null) {\n max = 1;\n }\n if (x < min) {\n x = min;\n }\n if (x > max) {\n x = max;\n }\n return x;\n };\n\n unpack = function(args) {\n if (args.length >= 3) {\n return Array.prototype.slice.call(args);\n } else {\n return args[0];\n }\n };\n\n clip_rgb = function(rgb) {\n var i, o;\n rgb._clipped = false;\n rgb._unclipped = rgb.slice(0);\n for (i = o = 0; o < 3; i = ++o) {\n if (i < 3) {\n if (rgb[i] < 0 || rgb[i] > 255) {\n rgb._clipped = true;\n }\n if (rgb[i] < 0) {\n rgb[i] = 0;\n }\n if (rgb[i] > 255) {\n rgb[i] = 255;\n }\n } else if (i === 3) {\n if (rgb[i] < 0) {\n rgb[i] = 0;\n }\n if (rgb[i] > 1) {\n rgb[i] = 1;\n }\n }\n }\n if (!rgb._clipped) {\n delete rgb._unclipped;\n }\n return rgb;\n };\n\n PI = Math.PI, round = Math.round, cos = Math.cos, floor = Math.floor, pow = Math.pow, log = Math.log, sin = Math.sin, sqrt = Math.sqrt, atan2 = Math.atan2, max = Math.max, abs = Math.abs;\n\n TWOPI = PI * 2;\n\n PITHIRD = PI / 3;\n\n DEG2RAD = PI / 180;\n\n RAD2DEG = 180 / PI;\n\n chroma = function() {\n if (arguments[0] instanceof Color) {\n return arguments[0];\n }\n return (function(func, args, ctor) {\n ctor.prototype = func.prototype;\n var child = new ctor, result = func.apply(child, args);\n return Object(result) === result ? result : child;\n })(Color, arguments, function(){});\n };\n\n chroma[\"default\"] = chroma;\n\n _interpolators = [];\n\n if ((typeof module !== \"undefined\" && module !== null) && (module.exports != null)) {\n module.exports = chroma;\n }\n\n if (typeof define === 'function' && define.amd) {\n define([], function() {\n return chroma;\n });\n } else {\n root = typeof exports !== \"undefined\" && exports !== null ? exports : this;\n root.chroma = chroma;\n }\n\n chroma.version = '1.4.1';\n\n _input = {};\n\n _guess_formats = [];\n\n _guess_formats_sorted = false;\n\n Color = (function() {\n function Color() {\n var arg, args, chk, len, len1, me, mode, o, w;\n me = this;\n args = [];\n for (o = 0, len = arguments.length; o < len; o++) {\n arg = arguments[o];\n if (arg != null) {\n args.push(arg);\n }\n }\n if (args.length > 1) {\n mode = args[args.length - 1];\n }\n if (_input[mode] != null) {\n me._rgb = clip_rgb(_input[mode](unpack(args.slice(0, -1))));\n } else {\n if (!_guess_formats_sorted) {\n _guess_formats = _guess_formats.sort(function(a, b) {\n return b.p - a.p;\n });\n _guess_formats_sorted = true;\n }\n for (w = 0, len1 = _guess_formats.length; w < len1; w++) {\n chk = _guess_formats[w];\n mode = chk.test.apply(chk, args);\n if (mode) {\n break;\n }\n }\n if (mode) {\n me._rgb = clip_rgb(_input[mode].apply(_input, args));\n }\n }\n if (me._rgb == null) {\n console.warn('unknown format: ' + args);\n }\n if (me._rgb == null) {\n me._rgb = [0, 0, 0];\n }\n if (me._rgb.length === 3) {\n me._rgb.push(1);\n }\n }\n\n Color.prototype.toString = function() {\n return this.hex();\n };\n\n return Color;\n\n })();\n\n chroma._input = _input;\n\n\n /**\n \tColorBrewer colors for chroma.js\n \n \tCopyright (c) 2002 Cynthia Brewer, Mark Harrower, and The \n \tPennsylvania State University.\n \n \tLicensed under the Apache License, Version 2.0 (the \"License\"); \n \tyou may not use this file except in compliance with the License.\n \tYou may obtain a copy of the License at\t\n \thttp://www.apache.org/licenses/LICENSE-2.0\n \n \tUnless required by applicable law or agreed to in writing, software distributed\n \tunder the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR\n \tCONDITIONS OF ANY KIND, either express or implied. See the License for the\n \tspecific language governing permissions and limitations under the License.\n \n @preserve\n */\n\n chroma.brewer = brewer = {\n OrRd: ['#fff7ec', '#fee8c8', '#fdd49e', '#fdbb84', '#fc8d59', '#ef6548', '#d7301f', '#b30000', '#7f0000'],\n PuBu: ['#fff7fb', '#ece7f2', '#d0d1e6', '#a6bddb', '#74a9cf', '#3690c0', '#0570b0', '#045a8d', '#023858'],\n BuPu: ['#f7fcfd', '#e0ecf4', '#bfd3e6', '#9ebcda', '#8c96c6', '#8c6bb1', '#88419d', '#810f7c', '#4d004b'],\n Oranges: ['#fff5eb', '#fee6ce', '#fdd0a2', '#fdae6b', '#fd8d3c', '#f16913', '#d94801', '#a63603', '#7f2704'],\n BuGn: ['#f7fcfd', '#e5f5f9', '#ccece6', '#99d8c9', '#66c2a4', '#41ae76', '#238b45', '#006d2c', '#00441b'],\n YlOrBr: ['#ffffe5', '#fff7bc', '#fee391', '#fec44f', '#fe9929', '#ec7014', '#cc4c02', '#993404', '#662506'],\n YlGn: ['#ffffe5', '#f7fcb9', '#d9f0a3', '#addd8e', '#78c679', '#41ab5d', '#238443', '#006837', '#004529'],\n Reds: ['#fff5f0', '#fee0d2', '#fcbba1', '#fc9272', '#fb6a4a', '#ef3b2c', '#cb181d', '#a50f15', '#67000d'],\n RdPu: ['#fff7f3', '#fde0dd', '#fcc5c0', '#fa9fb5', '#f768a1', '#dd3497', '#ae017e', '#7a0177', '#49006a'],\n Greens: ['#f7fcf5', '#e5f5e0', '#c7e9c0', '#a1d99b', '#74c476', '#41ab5d', '#238b45', '#006d2c', '#00441b'],\n YlGnBu: ['#ffffd9', '#edf8b1', '#c7e9b4', '#7fcdbb', '#41b6c4', '#1d91c0', '#225ea8', '#253494', '#081d58'],\n Purples: ['#fcfbfd', '#efedf5', '#dadaeb', '#bcbddc', '#9e9ac8', '#807dba', '#6a51a3', '#54278f', '#3f007d'],\n GnBu: ['#f7fcf0', '#e0f3db', '#ccebc5', '#a8ddb5', '#7bccc4', '#4eb3d3', '#2b8cbe', '#0868ac', '#084081'],\n Greys: ['#ffffff', '#f0f0f0', '#d9d9d9', '#bdbdbd', '#969696', '#737373', '#525252', '#252525', '#000000'],\n YlOrRd: ['#ffffcc', '#ffeda0', '#fed976', '#feb24c', '#fd8d3c', '#fc4e2a', '#e31a1c', '#bd0026', '#800026'],\n PuRd: ['#f7f4f9', '#e7e1ef', '#d4b9da', '#c994c7', '#df65b0', '#e7298a', '#ce1256', '#980043', '#67001f'],\n Blues: ['#f7fbff', '#deebf7', '#c6dbef', '#9ecae1', '#6baed6', '#4292c6', '#2171b5', '#08519c', '#08306b'],\n PuBuGn: ['#fff7fb', '#ece2f0', '#d0d1e6', '#a6bddb', '#67a9cf', '#3690c0', '#02818a', '#016c59', '#014636'],\n Viridis: ['#440154', '#482777', '#3f4a8a', '#31678e', '#26838f', '#1f9d8a', '#6cce5a', '#b6de2b', '#fee825'],\n Spectral: ['#9e0142', '#d53e4f', '#f46d43', '#fdae61', '#fee08b', '#ffffbf', '#e6f598', '#abdda4', '#66c2a5', '#3288bd', '#5e4fa2'],\n RdYlGn: ['#a50026', '#d73027', '#f46d43', '#fdae61', '#fee08b', '#ffffbf', '#d9ef8b', '#a6d96a', '#66bd63', '#1a9850', '#006837'],\n RdBu: ['#67001f', '#b2182b', '#d6604d', '#f4a582', '#fddbc7', '#f7f7f7', '#d1e5f0', '#92c5de', '#4393c3', '#2166ac', '#053061'],\n PiYG: ['#8e0152', '#c51b7d', '#de77ae', '#f1b6da', '#fde0ef', '#f7f7f7', '#e6f5d0', '#b8e186', '#7fbc41', '#4d9221', '#276419'],\n PRGn: ['#40004b', '#762a83', '#9970ab', '#c2a5cf', '#e7d4e8', '#f7f7f7', '#d9f0d3', '#a6dba0', '#5aae61', '#1b7837', '#00441b'],\n RdYlBu: ['#a50026', '#d73027', '#f46d43', '#fdae61', '#fee090', '#ffffbf', '#e0f3f8', '#abd9e9', '#74add1', '#4575b4', '#313695'],\n BrBG: ['#543005', '#8c510a', '#bf812d', '#dfc27d', '#f6e8c3', '#f5f5f5', '#c7eae5', '#80cdc1', '#35978f', '#01665e', '#003c30'],\n RdGy: ['#67001f', '#b2182b', '#d6604d', '#f4a582', '#fddbc7', '#ffffff', '#e0e0e0', '#bababa', '#878787', '#4d4d4d', '#1a1a1a'],\n PuOr: ['#7f3b08', '#b35806', '#e08214', '#fdb863', '#fee0b6', '#f7f7f7', '#d8daeb', '#b2abd2', '#8073ac', '#542788', '#2d004b'],\n Set2: ['#66c2a5', '#fc8d62', '#8da0cb', '#e78ac3', '#a6d854', '#ffd92f', '#e5c494', '#b3b3b3'],\n Accent: ['#7fc97f', '#beaed4', '#fdc086', '#ffff99', '#386cb0', '#f0027f', '#bf5b17', '#666666'],\n Set1: ['#e41a1c', '#377eb8', '#4daf4a', '#984ea3', '#ff7f00', '#ffff33', '#a65628', '#f781bf', '#999999'],\n Set3: ['#8dd3c7', '#ffffb3', '#bebada', '#fb8072', '#80b1d3', '#fdb462', '#b3de69', '#fccde5', '#d9d9d9', '#bc80bd', '#ccebc5', '#ffed6f'],\n Dark2: ['#1b9e77', '#d95f02', '#7570b3', '#e7298a', '#66a61e', '#e6ab02', '#a6761d', '#666666'],\n Paired: ['#a6cee3', '#1f78b4', '#b2df8a', '#33a02c', '#fb9a99', '#e31a1c', '#fdbf6f', '#ff7f00', '#cab2d6', '#6a3d9a', '#ffff99', '#b15928'],\n Pastel2: ['#b3e2cd', '#fdcdac', '#cbd5e8', '#f4cae4', '#e6f5c9', '#fff2ae', '#f1e2cc', '#cccccc'],\n Pastel1: ['#fbb4ae', '#b3cde3', '#ccebc5', '#decbe4', '#fed9a6', '#ffffcc', '#e5d8bd', '#fddaec', '#f2f2f2']\n };\n\n (function() {\n var key, results;\n results = [];\n for (key in brewer) {\n results.push(brewer[key.toLowerCase()] = brewer[key]);\n }\n return results;\n })();\n\n\n /**\n \tX11 color names\n \n \thttp://www.w3.org/TR/css3-color/#svg-color\n */\n\n w3cx11 = {\n aliceblue: '#f0f8ff',\n antiquewhite: '#faebd7',\n aqua: '#00ffff',\n aquamarine: '#7fffd4',\n azure: '#f0ffff',\n beige: '#f5f5dc',\n bisque: '#ffe4c4',\n black: '#000000',\n blanchedalmond: '#ffebcd',\n blue: '#0000ff',\n blueviolet: '#8a2be2',\n brown: '#a52a2a',\n burlywood: '#deb887',\n cadetblue: '#5f9ea0',\n chartreuse: '#7fff00',\n chocolate: '#d2691e',\n coral: '#ff7f50',\n cornflower: '#6495ed',\n cornflowerblue: '#6495ed',\n cornsilk: '#fff8dc',\n crimson: '#dc143c',\n cyan: '#00ffff',\n darkblue: '#00008b',\n darkcyan: '#008b8b',\n darkgoldenrod: '#b8860b',\n darkgray: '#a9a9a9',\n darkgreen: '#006400',\n darkgrey: '#a9a9a9',\n darkkhaki: '#bdb76b',\n darkmagenta: '#8b008b',\n darkolivegreen: '#556b2f',\n darkorange: '#ff8c00',\n darkorchid: '#9932cc',\n darkred: '#8b0000',\n darksalmon: '#e9967a',\n darkseagreen: '#8fbc8f',\n darkslateblue: '#483d8b',\n darkslategray: '#2f4f4f',\n darkslategrey: '#2f4f4f',\n darkturquoise: '#00ced1',\n darkviolet: '#9400d3',\n deeppink: '#ff1493',\n deepskyblue: '#00bfff',\n dimgray: '#696969',\n dimgrey: '#696969',\n dodgerblue: '#1e90ff',\n firebrick: '#b22222',\n floralwhite: '#fffaf0',\n forestgreen: '#228b22',\n fuchsia: '#ff00ff',\n gainsboro: '#dcdcdc',\n ghostwhite: '#f8f8ff',\n gold: '#ffd700',\n goldenrod: '#daa520',\n gray: '#808080',\n green: '#008000',\n greenyellow: '#adff2f',\n grey: '#808080',\n honeydew: '#f0fff0',\n hotpink: '#ff69b4',\n indianred: '#cd5c5c',\n indigo: '#4b0082',\n ivory: '#fffff0',\n khaki: '#f0e68c',\n laserlemon: '#ffff54',\n lavender: '#e6e6fa',\n lavenderblush: '#fff0f5',\n lawngreen: '#7cfc00',\n lemonchiffon: '#fffacd',\n lightblue: '#add8e6',\n lightcoral: '#f08080',\n lightcyan: '#e0ffff',\n lightgoldenrod: '#fafad2',\n lightgoldenrodyellow: '#fafad2',\n lightgray: '#d3d3d3',\n lightgreen: '#90ee90',\n lightgrey: '#d3d3d3',\n lightpink: '#ffb6c1',\n lightsalmon: '#ffa07a',\n lightseagreen: '#20b2aa',\n lightskyblue: '#87cefa',\n lightslategray: '#778899',\n lightslategrey: '#778899',\n lightsteelblue: '#b0c4de',\n lightyellow: '#ffffe0',\n lime: '#00ff00',\n limegreen: '#32cd32',\n linen: '#faf0e6',\n magenta: '#ff00ff',\n maroon: '#800000',\n maroon2: '#7f0000',\n maroon3: '#b03060',\n mediumaquamarine: '#66cdaa',\n mediumblue: '#0000cd',\n mediumorchid: '#ba55d3',\n mediumpurple: '#9370db',\n mediumseagreen: '#3cb371',\n mediumslateblue: '#7b68ee',\n mediumspringgreen: '#00fa9a',\n mediumturquoise: '#48d1cc',\n mediumvioletred: '#c71585',\n midnightblue: '#191970',\n mintcream: '#f5fffa',\n mistyrose: '#ffe4e1',\n moccasin: '#ffe4b5',\n navajowhite: '#ffdead',\n navy: '#000080',\n oldlace: '#fdf5e6',\n olive: '#808000',\n olivedrab: '#6b8e23',\n orange: '#ffa500',\n orangered: '#ff4500',\n orchid: '#da70d6',\n palegoldenrod: '#eee8aa',\n palegreen: '#98fb98',\n paleturquoise: '#afeeee',\n palevioletred: '#db7093',\n papayawhip: '#ffefd5',\n peachpuff: '#ffdab9',\n peru: '#cd853f',\n pink: '#ffc0cb',\n plum: '#dda0dd',\n powderblue: '#b0e0e6',\n purple: '#800080',\n purple2: '#7f007f',\n purple3: '#a020f0',\n rebeccapurple: '#663399',\n red: '#ff0000',\n rosybrown: '#bc8f8f',\n royalblue: '#4169e1',\n saddlebrown: '#8b4513',\n salmon: '#fa8072',\n sandybrown: '#f4a460',\n seagreen: '#2e8b57',\n seashell: '#fff5ee',\n sienna: '#a0522d',\n silver: '#c0c0c0',\n skyblue: '#87ceeb',\n slateblue: '#6a5acd',\n slategray: '#708090',\n slategrey: '#708090',\n snow: '#fffafa',\n springgreen: '#00ff7f',\n steelblue: '#4682b4',\n tan: '#d2b48c',\n teal: '#008080',\n thistle: '#d8bfd8',\n tomato: '#ff6347',\n turquoise: '#40e0d0',\n violet: '#ee82ee',\n wheat: '#f5deb3',\n white: '#ffffff',\n whitesmoke: '#f5f5f5',\n yellow: '#ffff00',\n yellowgreen: '#9acd32'\n };\n\n chroma.colors = colors = w3cx11;\n\n lab2rgb = function() {\n var a, args, b, g, l, r, x, y, z;\n args = unpack(arguments);\n l = args[0], a = args[1], b = args[2];\n y = (l + 16) / 116;\n x = isNaN(a) ? y : y + a / 500;\n z = isNaN(b) ? y : y - b / 200;\n y = LAB_CONSTANTS.Yn * lab_xyz(y);\n x = LAB_CONSTANTS.Xn * lab_xyz(x);\n z = LAB_CONSTANTS.Zn * lab_xyz(z);\n r = xyz_rgb(3.2404542 * x - 1.5371385 * y - 0.4985314 * z);\n g = xyz_rgb(-0.9692660 * x + 1.8760108 * y + 0.0415560 * z);\n b = xyz_rgb(0.0556434 * x - 0.2040259 * y + 1.0572252 * z);\n return [r, g, b, args.length > 3 ? args[3] : 1];\n };\n\n xyz_rgb = function(r) {\n return 255 * (r <= 0.00304 ? 12.92 * r : 1.055 * pow(r, 1 / 2.4) - 0.055);\n };\n\n lab_xyz = function(t) {\n if (t > LAB_CONSTANTS.t1) {\n return t * t * t;\n } else {\n return LAB_CONSTANTS.t2 * (t - LAB_CONSTANTS.t0);\n }\n };\n\n LAB_CONSTANTS = {\n Kn: 18,\n Xn: 0.950470,\n Yn: 1,\n Zn: 1.088830,\n t0: 0.137931034,\n t1: 0.206896552,\n t2: 0.12841855,\n t3: 0.008856452\n };\n\n rgb2lab = function() {\n var b, g, r, ref, ref1, x, y, z;\n ref = unpack(arguments), r = ref[0], g = ref[1], b = ref[2];\n ref1 = rgb2xyz(r, g, b), x = ref1[0], y = ref1[1], z = ref1[2];\n return [116 * y - 16, 500 * (x - y), 200 * (y - z)];\n };\n\n rgb_xyz = function(r) {\n if ((r /= 255) <= 0.04045) {\n return r / 12.92;\n } else {\n return pow((r + 0.055) / 1.055, 2.4);\n }\n };\n\n xyz_lab = function(t) {\n if (t > LAB_CONSTANTS.t3) {\n return pow(t, 1 / 3);\n } else {\n return t / LAB_CONSTANTS.t2 + LAB_CONSTANTS.t0;\n }\n };\n\n rgb2xyz = function() {\n var b, g, r, ref, x, y, z;\n ref = unpack(arguments), r = ref[0], g = ref[1], b = ref[2];\n r = rgb_xyz(r);\n g = rgb_xyz(g);\n b = rgb_xyz(b);\n x = xyz_lab((0.4124564 * r + 0.3575761 * g + 0.1804375 * b) / LAB_CONSTANTS.Xn);\n y = xyz_lab((0.2126729 * r + 0.7151522 * g + 0.0721750 * b) / LAB_CONSTANTS.Yn);\n z = xyz_lab((0.0193339 * r + 0.1191920 * g + 0.9503041 * b) / LAB_CONSTANTS.Zn);\n return [x, y, z];\n };\n\n chroma.lab = function() {\n return (function(func, args, ctor) {\n ctor.prototype = func.prototype;\n var child = new ctor, result = func.apply(child, args);\n return Object(result) === result ? result : child;\n })(Color, slice.call(arguments).concat(['lab']), function(){});\n };\n\n _input.lab = lab2rgb;\n\n Color.prototype.lab = function() {\n return rgb2lab(this._rgb);\n };\n\n bezier = function(colors) {\n var I, I0, I1, c, lab0, lab1, lab2, lab3, ref, ref1, ref2;\n colors = (function() {\n var len, o, results;\n results = [];\n for (o = 0, len = colors.length; o < len; o++) {\n c = colors[o];\n results.push(chroma(c));\n }\n return results;\n })();\n if (colors.length === 2) {\n ref = (function() {\n var len, o, results;\n results = [];\n for (o = 0, len = colors.length; o < len; o++) {\n c = colors[o];\n results.push(c.lab());\n }\n return results;\n })(), lab0 = ref[0], lab1 = ref[1];\n I = function(t) {\n var i, lab;\n lab = (function() {\n var o, results;\n results = [];\n for (i = o = 0; o <= 2; i = ++o) {\n results.push(lab0[i] + t * (lab1[i] - lab0[i]));\n }\n return results;\n })();\n return chroma.lab.apply(chroma, lab);\n };\n } else if (colors.length === 3) {\n ref1 = (function() {\n var len, o, results;\n results = [];\n for (o = 0, len = colors.length; o < len; o++) {\n c = colors[o];\n results.push(c.lab());\n }\n return results;\n })(), lab0 = ref1[0], lab1 = ref1[1], lab2 = ref1[2];\n I = function(t) {\n var i, lab;\n lab = (function() {\n var o, results;\n results = [];\n for (i = o = 0; o <= 2; i = ++o) {\n results.push((1 - t) * (1 - t) * lab0[i] + 2 * (1 - t) * t * lab1[i] + t * t * lab2[i]);\n }\n return results;\n })();\n return chroma.lab.apply(chroma, lab);\n };\n } else if (colors.length === 4) {\n ref2 = (function() {\n var len, o, results;\n results = [];\n for (o = 0, len = colors.length; o < len; o++) {\n c = colors[o];\n results.push(c.lab());\n }\n return results;\n })(), lab0 = ref2[0], lab1 = ref2[1], lab2 = ref2[2], lab3 = ref2[3];\n I = function(t) {\n var i, lab;\n lab = (function() {\n var o, results;\n results = [];\n for (i = o = 0; o <= 2; i = ++o) {\n results.push((1 - t) * (1 - t) * (1 - t) * lab0[i] + 3 * (1 - t) * (1 - t) * t * lab1[i] + 3 * (1 - t) * t * t * lab2[i] + t * t * t * lab3[i]);\n }\n return results;\n })();\n return chroma.lab.apply(chroma, lab);\n };\n } else if (colors.length === 5) {\n I0 = bezier(colors.slice(0, 3));\n I1 = bezier(colors.slice(2, 5));\n I = function(t) {\n if (t < 0.5) {\n return I0(t * 2);\n } else {\n return I1((t - 0.5) * 2);\n }\n };\n }\n return I;\n };\n\n chroma.bezier = function(colors) {\n var f;\n f = bezier(colors);\n f.scale = function() {\n return chroma.scale(f);\n };\n return f;\n };\n\n chroma.cubehelix = function(start, rotations, hue, gamma, lightness) {\n var dh, dl, f;\n if (start == null) {\n start = 300;\n }\n if (rotations == null) {\n rotations = -1.5;\n }\n if (hue == null) {\n hue = 1;\n }\n if (gamma == null) {\n gamma = 1;\n }\n if (lightness == null) {\n lightness = [0, 1];\n }\n dh = 0;\n if (type(lightness) === 'array') {\n dl = lightness[1] - lightness[0];\n } else {\n dl = 0;\n lightness = [lightness, lightness];\n }\n f = function(fract) {\n var a, amp, b, cos_a, g, h, l, r, sin_a;\n a = TWOPI * ((start + 120) / 360 + rotations * fract);\n l = pow(lightness[0] + dl * fract, gamma);\n h = dh !== 0 ? hue[0] + fract * dh : hue;\n amp = h * l * (1 - l) / 2;\n cos_a = cos(a);\n sin_a = sin(a);\n r = l + amp * (-0.14861 * cos_a + 1.78277 * sin_a);\n g = l + amp * (-0.29227 * cos_a - 0.90649 * sin_a);\n b = l + amp * (+1.97294 * cos_a);\n return chroma(clip_rgb([r * 255, g * 255, b * 255, 1]));\n };\n f.start = function(s) {\n if (s == null) {\n return start;\n }\n start = s;\n return f;\n };\n f.rotations = function(r) {\n if (r == null) {\n return rotations;\n }\n rotations = r;\n return f;\n };\n f.gamma = function(g) {\n if (g == null) {\n return gamma;\n }\n gamma = g;\n return f;\n };\n f.hue = function(h) {\n if (h == null) {\n return hue;\n }\n hue = h;\n if (type(hue) === 'array') {\n dh = hue[1] - hue[0];\n if (dh === 0) {\n hue = hue[1];\n }\n } else {\n dh = 0;\n }\n return f;\n };\n f.lightness = function(h) {\n if (h == null) {\n return lightness;\n }\n if (type(h) === 'array') {\n lightness = h;\n dl = h[1] - h[0];\n } else {\n lightness = [h, h];\n dl = 0;\n }\n return f;\n };\n f.scale = function() {\n return chroma.scale(f);\n };\n f.hue(hue);\n return f;\n };\n\n chroma.random = function() {\n var code, digits, i, o;\n digits = '0123456789abcdef';\n code = '#';\n for (i = o = 0; o < 6; i = ++o) {\n code += digits.charAt(floor(Math.random() * 16));\n }\n return new Color(code);\n };\n\n _interpolators = [];\n\n interpolate = function(col1, col2, f, m) {\n var interpol, len, o, res;\n if (f == null) {\n f = 0.5;\n }\n if (m == null) {\n m = 'rgb';\n }\n\n /*\n interpolates between colors\n f = 0 --> me\n f = 1 --> col\n */\n if (type(col1) !== 'object') {\n col1 = chroma(col1);\n }\n if (type(col2) !== 'object') {\n col2 = chroma(col2);\n }\n for (o = 0, len = _interpolators.length; o < len; o++) {\n interpol = _interpolators[o];\n if (m === interpol[0]) {\n res = interpol[1](col1, col2, f, m);\n break;\n }\n }\n if (res == null) {\n throw \"color mode \" + m + \" is not supported\";\n }\n return res.alpha(col1.alpha() + f * (col2.alpha() - col1.alpha()));\n };\n\n chroma.interpolate = interpolate;\n\n Color.prototype.interpolate = function(col2, f, m) {\n return interpolate(this, col2, f, m);\n };\n\n chroma.mix = interpolate;\n\n Color.prototype.mix = Color.prototype.interpolate;\n\n _input.rgb = function() {\n var k, ref, results, v;\n ref = unpack(arguments);\n results = [];\n for (k in ref) {\n v = ref[k];\n results.push(v);\n }\n return results;\n };\n\n chroma.rgb = function() {\n return (function(func, args, ctor) {\n ctor.prototype = func.prototype;\n var child = new ctor, result = func.apply(child, args);\n return Object(result) === result ? result : child;\n })(Color, slice.call(arguments).concat(['rgb']), function(){});\n };\n\n Color.prototype.rgb = function(round) {\n if (round == null) {\n round = true;\n }\n if (round) {\n return this._rgb.map(Math.round).slice(0, 3);\n } else {\n return this._rgb.slice(0, 3);\n }\n };\n\n Color.prototype.rgba = function(round) {\n if (round == null) {\n round = true;\n }\n if (!round) {\n return this._rgb.slice(0);\n }\n return [Math.round(this._rgb[0]), Math.round(this._rgb[1]), Math.round(this._rgb[2]), this._rgb[3]];\n };\n\n _guess_formats.push({\n p: 3,\n test: function(n) {\n var a;\n a = unpack(arguments);\n if (type(a) === 'array' && a.length === 3) {\n return 'rgb';\n }\n if (a.length === 4 && type(a[3]) === \"number\" && a[3] >= 0 && a[3] <= 1) {\n return 'rgb';\n }\n }\n });\n\n _input.lrgb = _input.rgb;\n\n interpolate_lrgb = function(col1, col2, f, m) {\n var xyz0, xyz1;\n xyz0 = col1._rgb;\n xyz1 = col2._rgb;\n return new Color(sqrt(pow(xyz0[0], 2) * (1 - f) + pow(xyz1[0], 2) * f), sqrt(pow(xyz0[1], 2) * (1 - f) + pow(xyz1[1], 2) * f), sqrt(pow(xyz0[2], 2) * (1 - f) + pow(xyz1[2], 2) * f), m);\n };\n\n _average_lrgb = function(colors) {\n var col, f, len, o, rgb, xyz;\n f = 1 / colors.length;\n xyz = [0, 0, 0, 0];\n for (o = 0, len = colors.length; o < len; o++) {\n col = colors[o];\n rgb = col._rgb;\n xyz[0] += pow(rgb[0], 2) * f;\n xyz[1] += pow(rgb[1], 2) * f;\n xyz[2] += pow(rgb[2], 2) * f;\n xyz[3] += rgb[3] * f;\n }\n xyz[0] = sqrt(xyz[0]);\n xyz[1] = sqrt(xyz[1]);\n xyz[2] = sqrt(xyz[2]);\n if (xyz[3] > 1) {\n xyz[3] = 1;\n }\n return new Color(clip_rgb(xyz));\n };\n\n _interpolators.push(['lrgb', interpolate_lrgb]);\n\n chroma.average = function(colors, mode) {\n var A, alpha, c, cnt, dx, dy, first, i, l, len, o, xyz, xyz2;\n if (mode == null) {\n mode = 'rgb';\n }\n l = colors.length;\n colors = colors.map(function(c) {\n return chroma(c);\n });\n first = colors.splice(0, 1)[0];\n if (mode === 'lrgb') {\n return _average_lrgb(colors);\n }\n xyz = first.get(mode);\n cnt = [];\n dx = 0;\n dy = 0;\n for (i in xyz) {\n xyz[i] = xyz[i] || 0;\n cnt.push(isNaN(xyz[i]) ? 0 : 1);\n if (mode.charAt(i) === 'h' && !isNaN(xyz[i])) {\n A = xyz[i] / 180 * PI;\n dx += cos(A);\n dy += sin(A);\n }\n }\n alpha = first.alpha();\n for (o = 0, len = colors.length; o < len; o++) {\n c = colors[o];\n xyz2 = c.get(mode);\n alpha += c.alpha();\n for (i in xyz) {\n if (!isNaN(xyz2[i])) {\n cnt[i] += 1;\n if (mode.charAt(i) === 'h') {\n A = xyz2[i] / 180 * PI;\n dx += cos(A);\n dy += sin(A);\n } else {\n xyz[i] += xyz2[i];\n }\n }\n }\n }\n for (i in xyz) {\n if (mode.charAt(i) === 'h') {\n A = atan2(dy / cnt[i], dx / cnt[i]) / PI * 180;\n while (A < 0) {\n A += 360;\n }\n while (A >= 360) {\n A -= 360;\n }\n xyz[i] = A;\n } else {\n xyz[i] = xyz[i] / cnt[i];\n }\n }\n return chroma(xyz, mode).alpha(alpha / l);\n };\n\n hex2rgb = function(hex) {\n var a, b, g, r, rgb, u;\n if (hex.match(/^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/)) {\n if (hex.length === 4 || hex.length === 7) {\n hex = hex.substr(1);\n }\n if (hex.length === 3) {\n hex = hex.split(\"\");\n hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];\n }\n u = parseInt(hex, 16);\n r = u >> 16;\n g = u >> 8 & 0xFF;\n b = u & 0xFF;\n return [r, g, b, 1];\n }\n if (hex.match(/^#?([A-Fa-f0-9]{8})$/)) {\n if (hex.length === 9) {\n hex = hex.substr(1);\n }\n u = parseInt(hex, 16);\n r = u >> 24 & 0xFF;\n g = u >> 16 & 0xFF;\n b = u >> 8 & 0xFF;\n a = round((u & 0xFF) / 0xFF * 100) / 100;\n return [r, g, b, a];\n }\n if ((_input.css != null) && (rgb = _input.css(hex))) {\n return rgb;\n }\n throw \"unknown color: \" + hex;\n };\n\n rgb2hex = function(channels, mode) {\n var a, b, g, hxa, r, str, u;\n if (mode == null) {\n mode = 'auto';\n }\n r = channels[0], g = channels[1], b = channels[2], a = channels[3];\n if (mode === 'auto') {\n mode = a < 1 ? 'rgba' : 'rgb';\n }\n r = Math.round(r);\n g = Math.round(g);\n b = Math.round(b);\n u = r << 16 | g << 8 | b;\n str = \"000000\" + u.toString(16);\n str = str.substr(str.length - 6);\n hxa = '0' + round(a * 255).toString(16);\n hxa = hxa.substr(hxa.length - 2);\n return \"#\" + (function() {\n switch (mode.toLowerCase()) {\n case 'rgba':\n return str + hxa;\n case 'argb':\n return hxa + str;\n default:\n return str;\n }\n })();\n };\n\n _input.hex = function(h) {\n return hex2rgb(h);\n };\n\n chroma.hex = function() {\n return (function(func, args, ctor) {\n ctor.prototype = func.prototype;\n var child = new ctor, result = func.apply(child, args);\n return Object(result) === result ? result : child;\n })(Color, slice.call(arguments).concat(['hex']), function(){});\n };\n\n Color.prototype.hex = function(mode) {\n if (mode == null) {\n mode = 'auto';\n }\n return rgb2hex(this._rgb, mode);\n };\n\n _guess_formats.push({\n p: 4,\n test: function(n) {\n if (arguments.length === 1 && type(n) === \"string\") {\n return 'hex';\n }\n }\n });\n\n hsl2rgb = function() {\n var args, b, c, g, h, i, l, o, r, ref, s, t1, t2, t3;\n args = unpack(arguments);\n h = args[0], s = args[1], l = args[2];\n if (s === 0) {\n r = g = b = l * 255;\n } else {\n t3 = [0, 0, 0];\n c = [0, 0, 0];\n t2 = l < 0.5 ? l * (1 + s) : l + s - l * s;\n t1 = 2 * l - t2;\n h /= 360;\n t3[0] = h + 1 / 3;\n t3[1] = h;\n t3[2] = h - 1 / 3;\n for (i = o = 0; o <= 2; i = ++o) {\n if (t3[i] < 0) {\n t3[i] += 1;\n }\n if (t3[i] > 1) {\n t3[i] -= 1;\n }\n if (6 * t3[i] < 1) {\n c[i] = t1 + (t2 - t1) * 6 * t3[i];\n } else if (2 * t3[i] < 1) {\n c[i] = t2;\n } else if (3 * t3[i] < 2) {\n c[i] = t1 + (t2 - t1) * ((2 / 3) - t3[i]) * 6;\n } else {\n c[i] = t1;\n }\n }\n ref = [round(c[0] * 255), round(c[1] * 255), round(c[2] * 255)], r = ref[0], g = ref[1], b = ref[2];\n }\n if (args.length > 3) {\n return [r, g, b, args[3]];\n } else {\n return [r, g, b];\n }\n };\n\n rgb2hsl = function(r, g, b) {\n var h, l, min, ref, s;\n if (r !== void 0 && r.length >= 3) {\n ref = r, r = ref[0], g = ref[1], b = ref[2];\n }\n r /= 255;\n g /= 255;\n b /= 255;\n min = Math.min(r, g, b);\n max = Math.max(r, g, b);\n l = (max + min) / 2;\n if (max === min) {\n s = 0;\n h = Number.NaN;\n } else {\n s = l < 0.5 ? (max - min) / (max + min) : (max - min) / (2 - max - min);\n }\n if (r === max) {\n h = (g - b) / (max - min);\n } else if (g === max) {\n h = 2 + (b - r) / (max - min);\n } else if (b === max) {\n h = 4 + (r - g) / (max - min);\n }\n h *= 60;\n if (h < 0) {\n h += 360;\n }\n return [h, s, l];\n };\n\n chroma.hsl = function() {\n return (function(func, args, ctor) {\n ctor.prototype = func.prototype;\n var child = new ctor, result = func.apply(child, args);\n return Object(result) === result ? result : child;\n })(Color, slice.call(arguments).concat(['hsl']), function(){});\n };\n\n _input.hsl = hsl2rgb;\n\n Color.prototype.hsl = function() {\n return rgb2hsl(this._rgb);\n };\n\n hsv2rgb = function() {\n var args, b, f, g, h, i, p, q, r, ref, ref1, ref2, ref3, ref4, ref5, s, t, v;\n args = unpack(arguments);\n h = args[0], s = args[1], v = args[2];\n v *= 255;\n if (s === 0) {\n r = g = b = v;\n } else {\n if (h === 360) {\n h = 0;\n }\n if (h > 360) {\n h -= 360;\n }\n if (h < 0) {\n h += 360;\n }\n h /= 60;\n i = floor(h);\n f = h - i;\n p = v * (1 - s);\n q = v * (1 - s * f);\n t = v * (1 - s * (1 - f));\n switch (i) {\n case 0:\n ref = [v, t, p], r = ref[0], g = ref[1], b = ref[2];\n break;\n case 1:\n ref1 = [q, v, p], r = ref1[0], g = ref1[1], b = ref1[2];\n break;\n case 2:\n ref2 = [p, v, t], r = ref2[0], g = ref2[1], b = ref2[2];\n break;\n case 3:\n ref3 = [p, q, v], r = ref3[0], g = ref3[1], b = ref3[2];\n break;\n case 4:\n ref4 = [t, p, v], r = ref4[0], g = ref4[1], b = ref4[2];\n break;\n case 5:\n ref5 = [v, p, q], r = ref5[0], g = ref5[1], b = ref5[2];\n }\n }\n return [r, g, b, args.length > 3 ? args[3] : 1];\n };\n\n rgb2hsv = function() {\n var b, delta, g, h, min, r, ref, s, v;\n ref = unpack(arguments), r = ref[0], g = ref[1], b = ref[2];\n min = Math.min(r, g, b);\n max = Math.max(r, g, b);\n delta = max - min;\n v = max / 255.0;\n if (max === 0) {\n h = Number.NaN;\n s = 0;\n } else {\n s = delta / max;\n if (r === max) {\n h = (g - b) / delta;\n }\n if (g === max) {\n h = 2 + (b - r) / delta;\n }\n if (b === max) {\n h = 4 + (r - g) / delta;\n }\n h *= 60;\n if (h < 0) {\n h += 360;\n }\n }\n return [h, s, v];\n };\n\n chroma.hsv = function() {\n return (function(func, args, ctor) {\n ctor.prototype = func.prototype;\n var child = new ctor, result = func.apply(child, args);\n return Object(result) === result ? result : child;\n })(Color, slice.call(arguments).concat(['hsv']), function(){});\n };\n\n _input.hsv = hsv2rgb;\n\n Color.prototype.hsv = function() {\n return rgb2hsv(this._rgb);\n };\n\n num2rgb = function(num) {\n var b, g, r;\n if (type(num) === \"number\" && num >= 0 && num <= 0xFFFFFF) {\n r = num >> 16;\n g = (num >> 8) & 0xFF;\n b = num & 0xFF;\n return [r, g, b, 1];\n }\n console.warn(\"unknown num color: \" + num);\n return [0, 0, 0, 1];\n };\n\n rgb2num = function() {\n var b, g, r, ref;\n ref = unpack(arguments), r = ref[0], g = ref[1], b = ref[2];\n return (r << 16) + (g << 8) + b;\n };\n\n chroma.num = function(num) {\n return new Color(num, 'num');\n };\n\n Color.prototype.num = function(mode) {\n if (mode == null) {\n mode = 'rgb';\n }\n return rgb2num(this._rgb, mode);\n };\n\n _input.num = num2rgb;\n\n _guess_formats.push({\n p: 1,\n test: function(n) {\n if (arguments.length === 1 && type(n) === \"number\" && n >= 0 && n <= 0xFFFFFF) {\n return 'num';\n }\n }\n });\n\n hcg2rgb = function() {\n var _c, _g, args, b, c, f, g, h, i, p, q, r, ref, ref1, ref2, ref3, ref4, ref5, t, v;\n args = unpack(arguments);\n h = args[0], c = args[1], _g = args[2];\n c = c / 100;\n g = g / 100 * 255;\n _c = c * 255;\n if (c === 0) {\n r = g = b = _g;\n } else {\n if (h === 360) {\n h = 0;\n }\n if (h > 360) {\n h -= 360;\n }\n if (h < 0) {\n h += 360;\n }\n h /= 60;\n i = floor(h);\n f = h - i;\n p = _g * (1 - c);\n q = p + _c * (1 - f);\n t = p + _c * f;\n v = p + _c;\n switch (i) {\n case 0:\n ref = [v, t, p], r = ref[0], g = ref[1], b = ref[2];\n break;\n case 1:\n ref1 = [q, v, p], r = ref1[0], g = ref1[1], b = ref1[2];\n break;\n case 2:\n ref2 = [p, v, t], r = ref2[0], g = ref2[1], b = ref2[2];\n break;\n case 3:\n ref3 = [p, q, v], r = ref3[0], g = ref3[1], b = ref3[2];\n break;\n case 4:\n ref4 = [t, p, v], r = ref4[0], g = ref4[1], b = ref4[2];\n break;\n case 5:\n ref5 = [v, p, q], r = ref5[0], g = ref5[1], b = ref5[2];\n }\n }\n return [r, g, b, args.length > 3 ? args[3] : 1];\n };\n\n rgb2hcg = function() {\n var _g, b, c, delta, g, h, min, r, ref;\n ref = unpack(arguments), r = ref[0], g = ref[1], b = ref[2];\n min = Math.min(r, g, b);\n max = Math.max(r, g, b);\n delta = max - min;\n c = delta * 100 / 255;\n _g = min / (255 - delta) * 100;\n if (delta === 0) {\n h = Number.NaN;\n } else {\n if (r === max) {\n h = (g - b) / delta;\n }\n if (g === max) {\n h = 2 + (b - r) / delta;\n }\n if (b === max) {\n h = 4 + (r - g) / delta;\n }\n h *= 60;\n if (h < 0) {\n h += 360;\n }\n }\n return [h, c, _g];\n };\n\n chroma.hcg = function() {\n return (function(func, args, ctor) {\n ctor.prototype = func.prototype;\n var child = new ctor, result = func.apply(child, args);\n return Object(result) === result ? result : child;\n })(Color, slice.call(arguments).concat(['hcg']), function(){});\n };\n\n _input.hcg = hcg2rgb;\n\n Color.prototype.hcg = function() {\n return rgb2hcg(this._rgb);\n };\n\n css2rgb = function(css) {\n var aa, ab, hsl, i, m, o, rgb, w;\n css = css.toLowerCase();\n if ((chroma.colors != null) && chroma.colors[css]) {\n return hex2rgb(chroma.colors[css]);\n }\n if (m = css.match(/rgb\\(\\s*(\\-?\\d+),\\s*(\\-?\\d+)\\s*,\\s*(\\-?\\d+)\\s*\\)/)) {\n rgb = m.slice(1, 4);\n for (i = o = 0; o <= 2; i = ++o) {\n rgb[i] = +rgb[i];\n }\n rgb[3] = 1;\n } else if (m = css.match(/rgba\\(\\s*(\\-?\\d+),\\s*(\\-?\\d+)\\s*,\\s*(\\-?\\d+)\\s*,\\s*([01]|[01]?\\.\\d+)\\)/)) {\n rgb = m.slice(1, 5);\n for (i = w = 0; w <= 3; i = ++w) {\n rgb[i] = +rgb[i];\n }\n } else if (m = css.match(/rgb\\(\\s*(\\-?\\d+(?:\\.\\d+)?)%,\\s*(\\-?\\d+(?:\\.\\d+)?)%\\s*,\\s*(\\-?\\d+(?:\\.\\d+)?)%\\s*\\)/)) {\n rgb = m.slice(1, 4);\n for (i = aa = 0; aa <= 2; i = ++aa) {\n rgb[i] = round(rgb[i] * 2.55);\n }\n rgb[3] = 1;\n } else if (m = css.match(/rgba\\(\\s*(\\-?\\d+(?:\\.\\d+)?)%,\\s*(\\-?\\d+(?:\\.\\d+)?)%\\s*,\\s*(\\-?\\d+(?:\\.\\d+)?)%\\s*,\\s*([01]|[01]?\\.\\d+)\\)/)) {\n rgb = m.slice(1, 5);\n for (i = ab = 0; ab <= 2; i = ++ab) {\n rgb[i] = round(rgb[i] * 2.55);\n }\n rgb[3] = +rgb[3];\n } else if (m = css.match(/hsl\\(\\s*(\\-?\\d+(?:\\.\\d+)?),\\s*(\\-?\\d+(?:\\.\\d+)?)%\\s*,\\s*(\\-?\\d+(?:\\.\\d+)?)%\\s*\\)/)) {\n hsl = m.slice(1, 4);\n hsl[1] *= 0.01;\n hsl[2] *= 0.01;\n rgb = hsl2rgb(hsl);\n rgb[3] = 1;\n } else if (m = css.match(/hsla\\(\\s*(\\-?\\d+(?:\\.\\d+)?),\\s*(\\-?\\d+(?:\\.\\d+)?)%\\s*,\\s*(\\-?\\d+(?:\\.\\d+)?)%\\s*,\\s*([01]|[01]?\\.\\d+)\\)/)) {\n hsl = m.slice(1, 4);\n hsl[1] *= 0.01;\n hsl[2] *= 0.01;\n rgb = hsl2rgb(hsl);\n rgb[3] = +m[4];\n }\n return rgb;\n };\n\n rgb2css = function(rgba) {\n var mode;\n mode = rgba[3] < 1 ? 'rgba' : 'rgb';\n if (mode === 'rgb') {\n return mode + '(' + rgba.slice(0, 3).map(round).join(',') + ')';\n } else if (mode === 'rgba') {\n return mode + '(' + rgba.slice(0, 3).map(round).join(',') + ',' + rgba[3] + ')';\n } else {\n\n }\n };\n\n rnd = function(a) {\n return round(a * 100) / 100;\n };\n\n hsl2css = function(hsl, alpha) {\n var mode;\n mode = alpha < 1 ? 'hsla' : 'hsl';\n hsl[0] = rnd(hsl[0] || 0);\n hsl[1] = rnd(hsl[1] * 100) + '%';\n hsl[2] = rnd(hsl[2] * 100) + '%';\n if (mode === 'hsla') {\n hsl[3] = alpha;\n }\n return mode + '(' + hsl.join(',') + ')';\n };\n\n _input.css = function(h) {\n return css2rgb(h);\n };\n\n chroma.css = function() {\n return (function(func, args, ctor) {\n ctor.prototype = func.prototype;\n var child = new ctor, result = func.apply(child, args);\n return Object(result) === result ? result : child;\n })(Color, slice.call(arguments).concat(['css']), function(){});\n };\n\n Color.prototype.css = function(mode) {\n if (mode == null) {\n mode = 'rgb';\n }\n if (mode.slice(0, 3) === 'rgb') {\n return rgb2css(this._rgb);\n } else if (mode.slice(0, 3) === 'hsl') {\n return hsl2css(this.hsl(), this.alpha());\n }\n };\n\n _input.named = function(name) {\n return hex2rgb(w3cx11[name]);\n };\n\n _guess_formats.push({\n p: 5,\n test: function(n) {\n if (arguments.length === 1 && (w3cx11[n] != null)) {\n return 'named';\n }\n }\n });\n\n Color.prototype.name = function(n) {\n var h, k;\n if (arguments.length) {\n if (w3cx11[n]) {\n this._rgb = hex2rgb(w3cx11[n]);\n }\n this._rgb[3] = 1;\n this;\n }\n h = this.hex('rgb');\n for (k in w3cx11) {\n if (h === w3cx11[k]) {\n return k;\n }\n }\n return h;\n };\n\n lch2lab = function() {\n\n /*\n Convert from a qualitative parameter h and a quantitative parameter l to a 24-bit pixel.\n These formulas were invented by David Dalrymple to obtain maximum contrast without going\n out of gamut if the parameters are in the range 0-1.\n \n A saturation multiplier was added by Gregor Aisch\n */\n var c, h, l, ref;\n ref = unpack(arguments), l = ref[0], c = ref[1], h = ref[2];\n h = h * DEG2RAD;\n return [l, cos(h) * c, sin(h) * c];\n };\n\n lch2rgb = function() {\n var L, a, args, b, c, g, h, l, r, ref, ref1;\n args = unpack(arguments);\n l = args[0], c = args[1], h = args[2];\n ref = lch2lab(l, c, h), L = ref[0], a = ref[1], b = ref[2];\n ref1 = lab2rgb(L, a, b), r = ref1[0], g = ref1[1], b = ref1[2];\n return [r, g, b, args.length > 3 ? args[3] : 1];\n };\n\n lab2lch = function() {\n var a, b, c, h, l, ref;\n ref = unpack(arguments), l = ref[0], a = ref[1], b = ref[2];\n c = sqrt(a * a + b * b);\n h = (atan2(b, a) * RAD2DEG + 360) % 360;\n if (round(c * 10000) === 0) {\n h = Number.NaN;\n }\n return [l, c, h];\n };\n\n rgb2lch = function() {\n var a, b, g, l, r, ref, ref1;\n ref = unpack(arguments), r = ref[0], g = ref[1], b = ref[2];\n ref1 = rgb2lab(r, g, b), l = ref1[0], a = ref1[1], b = ref1[2];\n return lab2lch(l, a, b);\n };\n\n chroma.lch = function() {\n var args;\n args = unpack(arguments);\n return new Color(args, 'lch');\n };\n\n chroma.hcl = function() {\n var args;\n args = unpack(arguments);\n return new Color(args, 'hcl');\n };\n\n _input.lch = lch2rgb;\n\n _input.hcl = function() {\n var c, h, l, ref;\n ref = unpack(arguments), h = ref[0], c = ref[1], l = ref[2];\n return lch2rgb([l, c, h]);\n };\n\n Color.prototype.lch = function() {\n return rgb2lch(this._rgb);\n };\n\n Color.prototype.hcl = function() {\n return rgb2lch(this._rgb).reverse();\n };\n\n rgb2cmyk = function(mode) {\n var b, c, f, g, k, m, r, ref, y;\n if (mode == null) {\n mode = 'rgb';\n }\n ref = unpack(arguments), r = ref[0], g = ref[1], b = ref[2];\n r = r / 255;\n g = g / 255;\n b = b / 255;\n k = 1 - Math.max(r, Math.max(g, b));\n f = k < 1 ? 1 / (1 - k) : 0;\n c = (1 - r - k) * f;\n m = (1 - g - k) * f;\n y = (1 - b - k) * f;\n return [c, m, y, k];\n };\n\n cmyk2rgb = function() {\n var alpha, args, b, c, g, k, m, r, y;\n args = unpack(arguments);\n c = args[0], m = args[1], y = args[2], k = args[3];\n alpha = args.length > 4 ? args[4] : 1;\n if (k === 1) {\n return [0, 0, 0, alpha];\n }\n r = c >= 1 ? 0 : 255 * (1 - c) * (1 - k);\n g = m >= 1 ? 0 : 255 * (1 - m) * (1 - k);\n b = y >= 1 ? 0 : 255 * (1 - y) * (1 - k);\n return [r, g, b, alpha];\n };\n\n _input.cmyk = function() {\n return cmyk2rgb(unpack(arguments));\n };\n\n chroma.cmyk = function() {\n return (function(func, args, ctor) {\n ctor.prototype = func.prototype;\n var child = new ctor, result = func.apply(child, args);\n return Object(result) === result ? result : child;\n })(Color, slice.call(arguments).concat(['cmyk']), function(){});\n };\n\n Color.prototype.cmyk = function() {\n return rgb2cmyk(this._rgb);\n };\n\n _input.gl = function() {\n var i, k, o, rgb, v;\n rgb = (function() {\n var ref, results;\n ref = unpack(arguments);\n results = [];\n for (k in ref) {\n v = ref[k];\n results.push(v);\n }\n return results;\n }).apply(this, arguments);\n for (i = o = 0; o <= 2; i = ++o) {\n rgb[i] *= 255;\n }\n return rgb;\n };\n\n chroma.gl = function() {\n return (function(func, args, ctor) {\n ctor.prototype = func.prototype;\n var child = new ctor, result = func.apply(child, args);\n return Object(result) === result ? result : child;\n })(Color, slice.call(arguments).concat(['gl']), function(){});\n };\n\n Color.prototype.gl = function() {\n var rgb;\n rgb = this._rgb;\n return [rgb[0] / 255, rgb[1] / 255, rgb[2] / 255, rgb[3]];\n };\n\n rgb2luminance = function(r, g, b) {\n var ref;\n ref = unpack(arguments), r = ref[0], g = ref[1], b = ref[2];\n r = luminance_x(r);\n g = luminance_x(g);\n b = luminance_x(b);\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n };\n\n luminance_x = function(x) {\n x /= 255;\n if (x <= 0.03928) {\n return x / 12.92;\n } else {\n return pow((x + 0.055) / 1.055, 2.4);\n }\n };\n\n interpolate_rgb = function(col1, col2, f, m) {\n var xyz0, xyz1;\n xyz0 = col1._rgb;\n xyz1 = col2._rgb;\n return new Color(xyz0[0] + f * (xyz1[0] - xyz0[0]), xyz0[1] + f * (xyz1[1] - xyz0[1]), xyz0[2] + f * (xyz1[2] - xyz0[2]), m);\n };\n\n _interpolators.push(['rgb', interpolate_rgb]);\n\n Color.prototype.luminance = function(lum, mode) {\n var cur_lum, eps, max_iter, rgba, test;\n if (mode == null) {\n mode = 'rgb';\n }\n if (!arguments.length) {\n return rgb2luminance(this._rgb);\n }\n rgba = this._rgb;\n if (lum === 0) {\n rgba = [0, 0, 0, this._rgb[3]];\n } else if (lum === 1) {\n rgba = [255, 255, 255, this[3]];\n } else {\n cur_lum = rgb2luminance(this._rgb);\n eps = 1e-7;\n max_iter = 20;\n test = function(l, h) {\n var lm, m;\n m = l.interpolate(h, 0.5, mode);\n lm = m.luminance();\n if (Math.abs(lum - lm) < eps || !max_iter--) {\n return m;\n }\n if (lm > lum) {\n return test(l, m);\n }\n return test(m, h);\n };\n if (cur_lum > lum) {\n rgba = test(chroma('black'), this).rgba();\n } else {\n rgba = test(this, chroma('white')).rgba();\n }\n }\n return chroma(rgba).alpha(this.alpha());\n };\n\n temperature2rgb = function(kelvin) {\n var b, g, r, temp;\n temp = kelvin / 100;\n if (temp < 66) {\n r = 255;\n g = -155.25485562709179 - 0.44596950469579133 * (g = temp - 2) + 104.49216199393888 * log(g);\n b = temp < 20 ? 0 : -254.76935184120902 + 0.8274096064007395 * (b = temp - 10) + 115.67994401066147 * log(b);\n } else {\n r = 351.97690566805693 + 0.114206453784165 * (r = temp - 55) - 40.25366309332127 * log(r);\n g = 325.4494125711974 + 0.07943456536662342 * (g = temp - 50) - 28.0852963507957 * log(g);\n b = 255;\n }\n return [r, g, b];\n };\n\n rgb2temperature = function() {\n var b, eps, g, maxTemp, minTemp, r, ref, rgb, temp;\n ref = unpack(arguments), r = ref[0], g = ref[1], b = ref[2];\n minTemp = 1000;\n maxTemp = 40000;\n eps = 0.4;\n while (maxTemp - minTemp > eps) {\n temp = (maxTemp + minTemp) * 0.5;\n rgb = temperature2rgb(temp);\n if ((rgb[2] / rgb[0]) >= (b / r)) {\n maxTemp = temp;\n } else {\n minTemp = temp;\n }\n }\n return round(temp);\n };\n\n chroma.temperature = chroma.kelvin = function() {\n return (function(func, args, ctor) {\n ctor.prototype = func.prototype;\n var child = new ctor, result = func.apply(child, args);\n return Object(result) === result ? result : child;\n })(Color, slice.call(arguments).concat(['temperature']), function(){});\n };\n\n _input.temperature = _input.kelvin = _input.K = temperature2rgb;\n\n Color.prototype.temperature = function() {\n return rgb2temperature(this._rgb);\n };\n\n Color.prototype.kelvin = Color.prototype.temperature;\n\n chroma.contrast = function(a, b) {\n var l1, l2, ref, ref1;\n if ((ref = type(a)) === 'string' || ref === 'number') {\n a = new Color(a);\n }\n if ((ref1 = type(b)) === 'string' || ref1 === 'number') {\n b = new Color(b);\n }\n l1 = a.luminance();\n l2 = b.luminance();\n if (l1 > l2) {\n return (l1 + 0.05) / (l2 + 0.05);\n } else {\n return (l2 + 0.05) / (l1 + 0.05);\n }\n };\n\n chroma.distance = function(a, b, mode) {\n var d, i, l1, l2, ref, ref1, sum_sq;\n if (mode == null) {\n mode = 'lab';\n }\n if ((ref = type(a)) === 'string' || ref === 'number') {\n a = new Color(a);\n }\n if ((ref1 = type(b)) === 'string' || ref1 === 'number') {\n b = new Color(b);\n }\n l1 = a.get(mode);\n l2 = b.get(mode);\n sum_sq = 0;\n for (i in l1) {\n d = (l1[i] || 0) - (l2[i] || 0);\n sum_sq += d * d;\n }\n return Math.sqrt(sum_sq);\n };\n\n chroma.deltaE = function(a, b, L, C) {\n var L1, L2, a1, a2, b1, b2, c1, c2, c4, dH2, delA, delB, delC, delL, f, h1, ref, ref1, ref2, ref3, sc, sh, sl, t, v1, v2, v3;\n if (L == null) {\n L = 1;\n }\n if (C == null) {\n C = 1;\n }\n if ((ref = type(a)) === 'string' || ref === 'number') {\n a = new Color(a);\n }\n if ((ref1 = type(b)) === 'string' || ref1 === 'number') {\n b = new Color(b);\n }\n ref2 = a.lab(), L1 = ref2[0], a1 = ref2[1], b1 = ref2[2];\n ref3 = b.lab(), L2 = ref3[0], a2 = ref3[1], b2 = ref3[2];\n c1 = sqrt(a1 * a1 + b1 * b1);\n c2 = sqrt(a2 * a2 + b2 * b2);\n sl = L1 < 16.0 ? 0.511 : (0.040975 * L1) / (1.0 + 0.01765 * L1);\n sc = (0.0638 * c1) / (1.0 + 0.0131 * c1) + 0.638;\n h1 = c1 < 0.000001 ? 0.0 : (atan2(b1, a1) * 180.0) / PI;\n while (h1 < 0) {\n h1 += 360;\n }\n while (h1 >= 360) {\n h1 -= 360;\n }\n t = (h1 >= 164.0) && (h1 <= 345.0) ? 0.56 + abs(0.2 * cos((PI * (h1 + 168.0)) / 180.0)) : 0.36 + abs(0.4 * cos((PI * (h1 + 35.0)) / 180.0));\n c4 = c1 * c1 * c1 * c1;\n f = sqrt(c4 / (c4 + 1900.0));\n sh = sc * (f * t + 1.0 - f);\n delL = L1 - L2;\n delC = c1 - c2;\n delA = a1 - a2;\n delB = b1 - b2;\n dH2 = delA * delA + delB * delB - delC * delC;\n v1 = delL / (L * sl);\n v2 = delC / (C * sc);\n v3 = sh;\n return sqrt(v1 * v1 + v2 * v2 + (dH2 / (v3 * v3)));\n };\n\n Color.prototype.get = function(modechan) {\n var channel, i, me, mode, ref, src;\n me = this;\n ref = modechan.split('.'), mode = ref[0], channel = ref[1];\n src = me[mode]();\n if (channel) {\n i = mode.indexOf(channel);\n if (i > -1) {\n return src[i];\n } else {\n return console.warn('unknown channel ' + channel + ' in mode ' + mode);\n }\n } else {\n return src;\n }\n };\n\n Color.prototype.set = function(modechan, value) {\n var channel, i, me, mode, ref, src;\n me = this;\n ref = modechan.split('.'), mode = ref[0], channel = ref[1];\n if (channel) {\n src = me[mode]();\n i = mode.indexOf(channel);\n if (i > -1) {\n if (type(value) === 'string') {\n switch (value.charAt(0)) {\n case '+':\n src[i] += +value;\n break;\n case '-':\n src[i] += +value;\n break;\n case '*':\n src[i] *= +(value.substr(1));\n break;\n case '/':\n src[i] /= +(value.substr(1));\n break;\n default:\n src[i] = +value;\n }\n } else {\n src[i] = value;\n }\n } else {\n console.warn('unknown channel ' + channel + ' in mode ' + mode);\n }\n } else {\n src = value;\n }\n return chroma(src, mode).alpha(me.alpha());\n };\n\n Color.prototype.clipped = function() {\n return this._rgb._clipped || false;\n };\n\n Color.prototype.alpha = function(a) {\n if (arguments.length) {\n return chroma.rgb([this._rgb[0], this._rgb[1], this._rgb[2], a]);\n }\n return this._rgb[3];\n };\n\n Color.prototype.darken = function(amount) {\n var lab, me;\n if (amount == null) {\n amount = 1;\n }\n me = this;\n lab = me.lab();\n lab[0] -= LAB_CONSTANTS.Kn * amount;\n return chroma.lab(lab).alpha(me.alpha());\n };\n\n Color.prototype.brighten = function(amount) {\n if (amount == null) {\n amount = 1;\n }\n return this.darken(-amount);\n };\n\n Color.prototype.darker = Color.prototype.darken;\n\n Color.prototype.brighter = Color.prototype.brighten;\n\n Color.prototype.saturate = function(amount) {\n var lch, me;\n if (amount == null) {\n amount = 1;\n }\n me = this;\n lch = me.lch();\n lch[1] += amount * LAB_CONSTANTS.Kn;\n if (lch[1] < 0) {\n lch[1] = 0;\n }\n return chroma.lch(lch).alpha(me.alpha());\n };\n\n Color.prototype.desaturate = function(amount) {\n if (amount == null) {\n amount = 1;\n }\n return this.saturate(-amount);\n };\n\n Color.prototype.premultiply = function() {\n var a, rgb;\n rgb = this.rgb();\n a = this.alpha();\n return chroma(rgb[0] * a, rgb[1] * a, rgb[2] * a, a);\n };\n\n blend = function(bottom, top, mode) {\n if (!blend[mode]) {\n throw 'unknown blend mode ' + mode;\n }\n return blend[mode](bottom, top);\n };\n\n blend_f = function(f) {\n return function(bottom, top) {\n var c0, c1;\n c0 = chroma(top).rgb();\n c1 = chroma(bottom).rgb();\n return chroma(f(c0, c1), 'rgb');\n };\n };\n\n each = function(f) {\n return function(c0, c1) {\n var i, o, out;\n out = [];\n for (i = o = 0; o <= 3; i = ++o) {\n out[i] = f(c0[i], c1[i]);\n }\n return out;\n };\n };\n\n normal = function(a, b) {\n return a;\n };\n\n multiply = function(a, b) {\n return a * b / 255;\n };\n\n darken = function(a, b) {\n if (a > b) {\n return b;\n } else {\n return a;\n }\n };\n\n lighten = function(a, b) {\n if (a > b) {\n return a;\n } else {\n return b;\n }\n };\n\n screen = function(a, b) {\n return 255 * (1 - (1 - a / 255) * (1 - b / 255));\n };\n\n overlay = function(a, b) {\n if (b < 128) {\n return 2 * a * b / 255;\n } else {\n return 255 * (1 - 2 * (1 - a / 255) * (1 - b / 255));\n }\n };\n\n burn = function(a, b) {\n return 255 * (1 - (1 - b / 255) / (a / 255));\n };\n\n dodge = function(a, b) {\n if (a === 255) {\n return 255;\n }\n a = 255 * (b / 255) / (1 - a / 255);\n if (a > 255) {\n return 255;\n } else {\n return a;\n }\n };\n\n blend.normal = blend_f(each(normal));\n\n blend.multiply = blend_f(each(multiply));\n\n blend.screen = blend_f(each(screen));\n\n blend.overlay = blend_f(each(overlay));\n\n blend.darken = blend_f(each(darken));\n\n blend.lighten = blend_f(each(lighten));\n\n blend.dodge = blend_f(each(dodge));\n\n blend.burn = blend_f(each(burn));\n\n chroma.blend = blend;\n\n chroma.analyze = function(data) {\n var len, o, r, val;\n r = {\n min: Number.MAX_VALUE,\n max: Number.MAX_VALUE * -1,\n sum: 0,\n values: [],\n count: 0\n };\n for (o = 0, len = data.length; o < len; o++) {\n val = data[o];\n if ((val != null) && !isNaN(val)) {\n r.values.push(val);\n r.sum += val;\n if (val < r.min) {\n r.min = val;\n }\n if (val > r.max) {\n r.max = val;\n }\n r.count += 1;\n }\n }\n r.domain = [r.min, r.max];\n r.limits = function(mode, num) {\n return chroma.limits(r, mode, num);\n };\n return r;\n };\n\n chroma.scale = function(colors, positions) {\n var _classes, _colorCache, _colors, _correctLightness, _domain, _fixed, _gamma, _max, _min, _mode, _nacol, _out, _padding, _pos, _spread, _useCache, classifyValue, f, getClass, getColor, resetCache, setColors, tmap;\n _mode = 'rgb';\n _nacol = chroma('#ccc');\n _spread = 0;\n _fixed = false;\n _domain = [0, 1];\n _pos = [];\n _padding = [0, 0];\n _classes = false;\n _colors = [];\n _out = false;\n _min = 0;\n _max = 1;\n _correctLightness = false;\n _colorCache = {};\n _useCache = true;\n _gamma = 1;\n setColors = function(colors) {\n var c, col, o, ref, ref1, w;\n if (colors == null) {\n colors = ['#fff', '#000'];\n }\n if ((colors != null) && type(colors) === 'string' && (chroma.brewer != null)) {\n colors = chroma.brewer[colors] || chroma.brewer[colors.toLowerCase()] || colors;\n }\n if (type(colors) === 'array') {\n if (colors.length === 1) {\n colors = [colors[0], colors[0]];\n }\n colors = colors.slice(0);\n for (c = o = 0, ref = colors.length - 1; 0 <= ref ? o <= ref : o >= ref; c = 0 <= ref ? ++o : --o) {\n col = colors[c];\n if (type(col) === \"string\") {\n colors[c] = chroma(col);\n }\n }\n _pos.length = 0;\n for (c = w = 0, ref1 = colors.length - 1; 0 <= ref1 ? w <= ref1 : w >= ref1; c = 0 <= ref1 ? ++w : --w) {\n _pos.push(c / (colors.length - 1));\n }\n }\n resetCache();\n return _colors = colors;\n };\n getClass = function(value) {\n var i, n;\n if (_classes != null) {\n n = _classes.length - 1;\n i = 0;\n while (i < n && value >= _classes[i]) {\n i++;\n }\n return i - 1;\n }\n return 0;\n };\n tmap = function(t) {\n return t;\n };\n classifyValue = function(value) {\n var i, maxc, minc, n, val;\n val = value;\n if (_classes.length > 2) {\n n = _classes.length - 1;\n i = getClass(value);\n minc = _classes[0] + (_classes[1] - _classes[0]) * (0 + _spread * 0.5);\n maxc = _classes[n - 1] + (_classes[n] - _classes[n - 1]) * (1 - _spread * 0.5);\n val = _min + ((_classes[i] + (_classes[i + 1] - _classes[i]) * 0.5 - minc) / (maxc - minc)) * (_max - _min);\n }\n return val;\n };\n getColor = function(val, bypassMap) {\n var c, col, i, k, o, p, ref, t;\n if (bypassMap == null) {\n bypassMap = false;\n }\n if (isNaN(val) || val === null) {\n return _nacol;\n }\n if (!bypassMap) {\n if (_classes && _classes.length > 2) {\n c = getClass(val);\n t = c / (_classes.length - 2);\n } else if (_max !== _min) {\n t = (val - _min) / (_max - _min);\n } else {\n t = 1;\n }\n } else {\n t = val;\n }\n if (!bypassMap) {\n t = tmap(t);\n }\n if (_gamma !== 1) {\n t = pow(t, _gamma);\n }\n t = _padding[0] + (t * (1 - _padding[0] - _padding[1]));\n t = Math.min(1, Math.max(0, t));\n k = Math.floor(t * 10000);\n if (_useCache && _colorCache[k]) {\n col = _colorCache[k];\n } else {\n if (type(_colors) === 'array') {\n for (i = o = 0, ref = _pos.length - 1; 0 <= ref ? o <= ref : o >= ref; i = 0 <= ref ? ++o : --o) {\n p = _pos[i];\n if (t <= p) {\n col = _colors[i];\n break;\n }\n if (t >= p && i === _pos.length - 1) {\n col = _colors[i];\n break;\n }\n if (t > p && t < _pos[i + 1]) {\n t = (t - p) / (_pos[i + 1] - p);\n col = chroma.interpolate(_colors[i], _colors[i + 1], t, _mode);\n break;\n }\n }\n } else if (type(_colors) === 'function') {\n col = _colors(t);\n }\n if (_useCache) {\n _colorCache[k] = col;\n }\n }\n return col;\n };\n resetCache = function() {\n return _colorCache = {};\n };\n setColors(colors);\n f = function(v) {\n var c;\n c = chroma(getColor(v));\n if (_out && c[_out]) {\n return c[_out]();\n } else {\n return c;\n }\n };\n f.classes = function(classes) {\n var d;\n if (classes != null) {\n if (type(classes) === 'array') {\n _classes = classes;\n _domain = [classes[0], classes[classes.length - 1]];\n } else {\n d = chroma.analyze(_domain);\n if (classes === 0) {\n _classes = [d.min, d.max];\n } else {\n _classes = chroma.limits(d, 'e', classes);\n }\n }\n return f;\n }\n return _classes;\n };\n f.domain = function(domain) {\n var c, d, k, len, o, ref, w;\n if (!arguments.length) {\n return _domain;\n }\n _min = domain[0];\n _max = domain[domain.length - 1];\n _pos = [];\n k = _colors.length;\n if (domain.length === k && _min !== _max) {\n for (o = 0, len = domain.length; o < len; o++) {\n d = domain[o];\n _pos.push((d - _min) / (_max - _min));\n }\n } else {\n for (c = w = 0, ref = k - 1; 0 <= ref ? w <= ref : w >= ref; c = 0 <= ref ? ++w : --w) {\n _pos.push(c / (k - 1));\n }\n }\n _domain = [_min, _max];\n return f;\n };\n f.mode = function(_m) {\n if (!arguments.length) {\n return _mode;\n }\n _mode = _m;\n resetCache();\n return f;\n };\n f.range = function(colors, _pos) {\n setColors(colors, _pos);\n return f;\n };\n f.out = function(_o) {\n _out = _o;\n return f;\n };\n f.spread = function(val) {\n if (!arguments.length) {\n return _spread;\n }\n _spread = val;\n return f;\n };\n f.correctLightness = function(v) {\n if (v == null) {\n v = true;\n }\n _correctLightness = v;\n resetCache();\n if (_correctLightness) {\n tmap = function(t) {\n var L0, L1, L_actual, L_diff, L_ideal, max_iter, pol, t0, t1;\n L0 = getColor(0, true).lab()[0];\n L1 = getColor(1, true).lab()[0];\n pol = L0 > L1;\n L_actual = getColor(t, true).lab()[0];\n L_ideal = L0 + (L1 - L0) * t;\n L_diff = L_actual - L_ideal;\n t0 = 0;\n t1 = 1;\n max_iter = 20;\n while (Math.abs(L_diff) > 1e-2 && max_iter-- > 0) {\n (function() {\n if (pol) {\n L_diff *= -1;\n }\n if (L_diff < 0) {\n t0 = t;\n t += (t1 - t) * 0.5;\n } else {\n t1 = t;\n t += (t0 - t) * 0.5;\n }\n L_actual = getColor(t, true).lab()[0];\n return L_diff = L_actual - L_ideal;\n })();\n }\n return t;\n };\n } else {\n tmap = function(t) {\n return t;\n };\n }\n return f;\n };\n f.padding = function(p) {\n if (p != null) {\n if (type(p) === 'number') {\n p = [p, p];\n }\n _padding = p;\n return f;\n } else {\n return _padding;\n }\n };\n f.colors = function(numColors, out) {\n var dd, dm, i, o, ref, result, results, samples, w;\n if (arguments.length < 2) {\n out = 'hex';\n }\n result = [];\n if (arguments.length === 0) {\n result = _colors.slice(0);\n } else if (numColors === 1) {\n result = [f(0.5)];\n } else if (numColors > 1) {\n dm = _domain[0];\n dd = _domain[1] - dm;\n result = (function() {\n results = [];\n for (var o = 0; 0 <= numColors ? o < numColors : o > numColors; 0 <= numColors ? o++ : o--){ results.push(o); }\n return results;\n }).apply(this).map(function(i) {\n return f(dm + i / (numColors - 1) * dd);\n });\n } else {\n colors = [];\n samples = [];\n if (_classes && _classes.length > 2) {\n for (i = w = 1, ref = _classes.length; 1 <= ref ? w < ref : w > ref; i = 1 <= ref ? ++w : --w) {\n samples.push((_classes[i - 1] + _classes[i]) * 0.5);\n }\n } else {\n samples = _domain;\n }\n result = samples.map(function(v) {\n return f(v);\n });\n }\n if (chroma[out]) {\n result = result.map(function(c) {\n return c[out]();\n });\n }\n return result;\n };\n f.cache = function(c) {\n if (c != null) {\n _useCache = c;\n return f;\n } else {\n return _useCache;\n }\n };\n f.gamma = function(g) {\n if (g != null) {\n _gamma = g;\n return f;\n } else {\n return _gamma;\n }\n };\n f.nodata = function(d) {\n if (d != null) {\n _nacol = chroma(d);\n return f;\n } else {\n return _nacol;\n }\n };\n return f;\n };\n\n if (chroma.scales == null) {\n chroma.scales = {};\n }\n\n chroma.scales.cool = function() {\n return chroma.scale([chroma.hsl(180, 1, .9), chroma.hsl(250, .7, .4)]);\n };\n\n chroma.scales.hot = function() {\n return chroma.scale(['#000', '#f00', '#ff0', '#fff'], [0, .25, .75, 1]).mode('rgb');\n };\n\n chroma.analyze = function(data, key, filter) {\n var add, k, len, o, r, val, visit;\n r = {\n min: Number.MAX_VALUE,\n max: Number.MAX_VALUE * -1,\n sum: 0,\n values: [],\n count: 0\n };\n if (filter == null) {\n filter = function() {\n return true;\n };\n }\n add = function(val) {\n if ((val != null) && !isNaN(val)) {\n r.values.push(val);\n r.sum += val;\n if (val < r.min) {\n r.min = val;\n }\n if (val > r.max) {\n r.max = val;\n }\n r.count += 1;\n }\n };\n visit = function(val, k) {\n if (filter(val, k)) {\n if ((key != null) && type(key) === 'function') {\n return add(key(val));\n } else if ((key != null) && type(key) === 'string' || type(key) === 'number') {\n return add(val[key]);\n } else {\n return add(val);\n }\n }\n };\n if (type(data) === 'array') {\n for (o = 0, len = data.length; o < len; o++) {\n val = data[o];\n visit(val);\n }\n } else {\n for (k in data) {\n val = data[k];\n visit(val, k);\n }\n }\n r.domain = [r.min, r.max];\n r.limits = function(mode, num) {\n return chroma.limits(r, mode, num);\n };\n return r;\n };\n\n chroma.limits = function(data, mode, num) {\n var aa, ab, ac, ad, ae, af, ag, ah, ai, aj, ak, al, am, assignments, best, centroids, cluster, clusterSizes, dist, i, j, kClusters, limits, max_log, min, min_log, mindist, n, nb_iters, newCentroids, o, p, pb, pr, ref, ref1, ref10, ref11, ref12, ref13, ref14, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, repeat, sum, tmpKMeansBreaks, v, value, values, w;\n if (mode == null) {\n mode = 'equal';\n }\n if (num == null) {\n num = 7;\n }\n if (type(data) === 'array') {\n data = chroma.analyze(data);\n }\n min = data.min;\n max = data.max;\n sum = data.sum;\n values = data.values.sort(function(a, b) {\n return a - b;\n });\n if (num === 1) {\n return [min, max];\n }\n limits = [];\n if (mode.substr(0, 1) === 'c') {\n limits.push(min);\n limits.push(max);\n }\n if (mode.substr(0, 1) === 'e') {\n limits.push(min);\n for (i = o = 1, ref = num - 1; 1 <= ref ? o <= ref : o >= ref; i = 1 <= ref ? ++o : --o) {\n limits.push(min + (i / num) * (max - min));\n }\n limits.push(max);\n } else if (mode.substr(0, 1) === 'l') {\n if (min <= 0) {\n throw 'Logarithmic scales are only possible for values > 0';\n }\n min_log = Math.LOG10E * log(min);\n max_log = Math.LOG10E * log(max);\n limits.push(min);\n for (i = w = 1, ref1 = num - 1; 1 <= ref1 ? w <= ref1 : w >= ref1; i = 1 <= ref1 ? ++w : --w) {\n limits.push(pow(10, min_log + (i / num) * (max_log - min_log)));\n }\n limits.push(max);\n } else if (mode.substr(0, 1) === 'q') {\n limits.push(min);\n for (i = aa = 1, ref2 = num - 1; 1 <= ref2 ? aa <= ref2 : aa >= ref2; i = 1 <= ref2 ? ++aa : --aa) {\n p = (values.length - 1) * i / num;\n pb = floor(p);\n if (pb === p) {\n limits.push(values[pb]);\n } else {\n pr = p - pb;\n limits.push(values[pb] * (1 - pr) + values[pb + 1] * pr);\n }\n }\n limits.push(max);\n } else if (mode.substr(0, 1) === 'k') {\n\n /*\n implementation based on\n http://code.google.com/p/figue/source/browse/trunk/figue.js#336\n simplified for 1-d input values\n */\n n = values.length;\n assignments = new Array(n);\n clusterSizes = new Array(num);\n repeat = true;\n nb_iters = 0;\n centroids = null;\n centroids = [];\n centroids.push(min);\n for (i = ab = 1, ref3 = num - 1; 1 <= ref3 ? ab <= ref3 : ab >= ref3; i = 1 <= ref3 ? ++ab : --ab) {\n centroids.push(min + (i / num) * (max - min));\n }\n centroids.push(max);\n while (repeat) {\n for (j = ac = 0, ref4 = num - 1; 0 <= ref4 ? ac <= ref4 : ac >= ref4; j = 0 <= ref4 ? ++ac : --ac) {\n clusterSizes[j] = 0;\n }\n for (i = ad = 0, ref5 = n - 1; 0 <= ref5 ? ad <= ref5 : ad >= ref5; i = 0 <= ref5 ? ++ad : --ad) {\n value = values[i];\n mindist = Number.MAX_VALUE;\n for (j = ae = 0, ref6 = num - 1; 0 <= ref6 ? ae <= ref6 : ae >= ref6; j = 0 <= ref6 ? ++ae : --ae) {\n dist = abs(centroids[j] - value);\n if (dist < mindist) {\n mindist = dist;\n best = j;\n }\n }\n clusterSizes[best]++;\n assignments[i] = best;\n }\n newCentroids = new Array(num);\n for (j = af = 0, ref7 = num - 1; 0 <= ref7 ? af <= ref7 : af >= ref7; j = 0 <= ref7 ? ++af : --af) {\n newCentroids[j] = null;\n }\n for (i = ag = 0, ref8 = n - 1; 0 <= ref8 ? ag <= ref8 : ag >= ref8; i = 0 <= ref8 ? ++ag : --ag) {\n cluster = assignments[i];\n if (newCentroids[cluster] === null) {\n newCentroids[cluster] = values[i];\n } else {\n newCentroids[cluster] += values[i];\n }\n }\n for (j = ah = 0, ref9 = num - 1; 0 <= ref9 ? ah <= ref9 : ah >= ref9; j = 0 <= ref9 ? ++ah : --ah) {\n newCentroids[j] *= 1 / clusterSizes[j];\n }\n repeat = false;\n for (j = ai = 0, ref10 = num - 1; 0 <= ref10 ? ai <= ref10 : ai >= ref10; j = 0 <= ref10 ? ++ai : --ai) {\n if (newCentroids[j] !== centroids[i]) {\n repeat = true;\n break;\n }\n }\n centroids = newCentroids;\n nb_iters++;\n if (nb_iters > 200) {\n repeat = false;\n }\n }\n kClusters = {};\n for (j = aj = 0, ref11 = num - 1; 0 <= ref11 ? aj <= ref11 : aj >= ref11; j = 0 <= ref11 ? ++aj : --aj) {\n kClusters[j] = [];\n }\n for (i = ak = 0, ref12 = n - 1; 0 <= ref12 ? ak <= ref12 : ak >= ref12; i = 0 <= ref12 ? ++ak : --ak) {\n cluster = assignments[i];\n kClusters[cluster].push(values[i]);\n }\n tmpKMeansBreaks = [];\n for (j = al = 0, ref13 = num - 1; 0 <= ref13 ? al <= ref13 : al >= ref13; j = 0 <= ref13 ? ++al : --al) {\n tmpKMeansBreaks.push(kClusters[j][0]);\n tmpKMeansBreaks.push(kClusters[j][kClusters[j].length - 1]);\n }\n tmpKMeansBreaks = tmpKMeansBreaks.sort(function(a, b) {\n return a - b;\n });\n limits.push(tmpKMeansBreaks[0]);\n for (i = am = 1, ref14 = tmpKMeansBreaks.length - 1; am <= ref14; i = am += 2) {\n v = tmpKMeansBreaks[i];\n if (!isNaN(v) && limits.indexOf(v) === -1) {\n limits.push(v);\n }\n }\n }\n return limits;\n };\n\n hsi2rgb = function(h, s, i) {\n\n /*\n borrowed from here:\n http://hummer.stanford.edu/museinfo/doc/examples/humdrum/keyscape2/hsi2rgb.cpp\n */\n var args, b, g, r;\n args = unpack(arguments);\n h = args[0], s = args[1], i = args[2];\n if (isNaN(h)) {\n h = 0;\n }\n h /= 360;\n if (h < 1 / 3) {\n b = (1 - s) / 3;\n r = (1 + s * cos(TWOPI * h) / cos(PITHIRD - TWOPI * h)) / 3;\n g = 1 - (b + r);\n } else if (h < 2 / 3) {\n h -= 1 / 3;\n r = (1 - s) / 3;\n g = (1 + s * cos(TWOPI * h) / cos(PITHIRD - TWOPI * h)) / 3;\n b = 1 - (r + g);\n } else {\n h -= 2 / 3;\n g = (1 - s) / 3;\n b = (1 + s * cos(TWOPI * h) / cos(PITHIRD - TWOPI * h)) / 3;\n r = 1 - (g + b);\n }\n r = limit(i * r * 3);\n g = limit(i * g * 3);\n b = limit(i * b * 3);\n return [r * 255, g * 255, b * 255, args.length > 3 ? args[3] : 1];\n };\n\n rgb2hsi = function() {\n\n /*\n borrowed from here:\n http://hummer.stanford.edu/museinfo/doc/examples/humdrum/keyscape2/rgb2hsi.cpp\n */\n var b, g, h, i, min, r, ref, s;\n ref = unpack(arguments), r = ref[0], g = ref[1], b = ref[2];\n TWOPI = Math.PI * 2;\n r /= 255;\n g /= 255;\n b /= 255;\n min = Math.min(r, g, b);\n i = (r + g + b) / 3;\n s = 1 - min / i;\n if (s === 0) {\n h = 0;\n } else {\n h = ((r - g) + (r - b)) / 2;\n h /= Math.sqrt((r - g) * (r - g) + (r - b) * (g - b));\n h = Math.acos(h);\n if (b > g) {\n h = TWOPI - h;\n }\n h /= TWOPI;\n }\n return [h * 360, s, i];\n };\n\n chroma.hsi = function() {\n return (function(func, args, ctor) {\n ctor.prototype = func.prototype;\n var child = new ctor, result = func.apply(child, args);\n return Object(result) === result ? result : child;\n })(Color, slice.call(arguments).concat(['hsi']), function(){});\n };\n\n _input.hsi = hsi2rgb;\n\n Color.prototype.hsi = function() {\n return rgb2hsi(this._rgb);\n };\n\n interpolate_hsx = function(col1, col2, f, m) {\n var dh, hue, hue0, hue1, lbv, lbv0, lbv1, res, sat, sat0, sat1, xyz0, xyz1;\n if (m === 'hsl') {\n xyz0 = col1.hsl();\n xyz1 = col2.hsl();\n } else if (m === 'hsv') {\n xyz0 = col1.hsv();\n xyz1 = col2.hsv();\n } else if (m === 'hcg') {\n xyz0 = col1.hcg();\n xyz1 = col2.hcg();\n } else if (m === 'hsi') {\n xyz0 = col1.hsi();\n xyz1 = col2.hsi();\n } else if (m === 'lch' || m === 'hcl') {\n m = 'hcl';\n xyz0 = col1.hcl();\n xyz1 = col2.hcl();\n }\n if (m.substr(0, 1) === 'h') {\n hue0 = xyz0[0], sat0 = xyz0[1], lbv0 = xyz0[2];\n hue1 = xyz1[0], sat1 = xyz1[1], lbv1 = xyz1[2];\n }\n if (!isNaN(hue0) && !isNaN(hue1)) {\n if (hue1 > hue0 && hue1 - hue0 > 180) {\n dh = hue1 - (hue0 + 360);\n } else if (hue1 < hue0 && hue0 - hue1 > 180) {\n dh = hue1 + 360 - hue0;\n } else {\n dh = hue1 - hue0;\n }\n hue = hue0 + f * dh;\n } else if (!isNaN(hue0)) {\n hue = hue0;\n if ((lbv1 === 1 || lbv1 === 0) && m !== 'hsv') {\n sat = sat0;\n }\n } else if (!isNaN(hue1)) {\n hue = hue1;\n if ((lbv0 === 1 || lbv0 === 0) && m !== 'hsv') {\n sat = sat1;\n }\n } else {\n hue = Number.NaN;\n }\n if (sat == null) {\n sat = sat0 + f * (sat1 - sat0);\n }\n lbv = lbv0 + f * (lbv1 - lbv0);\n return res = chroma[m](hue, sat, lbv);\n };\n\n _interpolators = _interpolators.concat((function() {\n var len, o, ref, results;\n ref = ['hsv', 'hsl', 'hsi', 'hcl', 'lch', 'hcg'];\n results = [];\n for (o = 0, len = ref.length; o < len; o++) {\n m = ref[o];\n results.push([m, interpolate_hsx]);\n }\n return results;\n })());\n\n interpolate_num = function(col1, col2, f, m) {\n var n1, n2;\n n1 = col1.num();\n n2 = col2.num();\n return chroma.num(n1 + (n2 - n1) * f, 'num');\n };\n\n _interpolators.push(['num', interpolate_num]);\n\n interpolate_lab = function(col1, col2, f, m) {\n var res, xyz0, xyz1;\n xyz0 = col1.lab();\n xyz1 = col2.lab();\n return res = new Color(xyz0[0] + f * (xyz1[0] - xyz0[0]), xyz0[1] + f * (xyz1[1] - xyz0[1]), xyz0[2] + f * (xyz1[2] - xyz0[2]), m);\n };\n\n _interpolators.push(['lab', interpolate_lab]);\n\n}).call(this);\n","/**\n * @file Colormaker\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3, Color } from 'three'\nimport * as chroma from 'chroma-js'\n\nimport { createParams } from '../utils'\nimport { NumberArray } from '../types'\nimport Structure from '../structure/structure'\nimport Surface from '../surface/surface'\nimport Volume from '../surface/volume'\nimport AtomProxy from '../proxy/atom-proxy'\nimport BondProxy from '../proxy/bond-proxy'\n\nexport type ColorMode = 'rgb'|'hsv'|'hsl'|'hsi'|'lab'|'hcl'\nexport type ColorSpace = 'sRGB' | 'linear'\n\n/**\n * Internal color space for all colors (global).\n * Colors are always specified as sRGB; if this is set to\n * 'linear' then colors get linearized when used internally\n * as vertex or texture colors.\n * @see setColorSpace/getColorSpace.\n */\nvar colorSpace: ColorSpace = 'sRGB' // default: don't linearize\n\n/** Set the global internal color space for colormakers */\nexport function setColorSpace(space: ColorSpace) {\n colorSpace = space\n}\n\n/** Get the global internal color space for colormakers */\nexport function getColorSpace() {\n return colorSpace\n}\n\nexport const ScaleDefaultParameters = {\n scale: 'uniform' as string|string[],\n mode: 'hcl' as ColorMode,\n domain: [ 0, 1 ] as number[],\n value: 0xFFFFFF,\n reverse: false,\n}\nexport type ScaleParameters = typeof ScaleDefaultParameters\n\nexport interface ColorData {\n atomData?: number[],\n bondData?: number[]\n}\n\nexport interface ColormakerParameters extends ScaleParameters {\n structure?: Structure\n volume?: Volume\n surface?: Surface\n data?: ColorData\n}\n\nexport type StuctureColormakerParams = { structure: Structure } & Partial\nexport type VolumeColormakerParams = { volume: Volume } & Partial\nexport type ColormakerScale = (v: number) => number\nexport type ColormakerConstructor = new (...p: ConstructorParameters) => Colormaker\n\nconst tmpColor = new Color()\n\n/** Decorator for optionally linearizing a numeric color */\ntype colorFuncType = (value: any, fromTo?: boolean) => number // decorator applies to functions with this shape\nexport function manageColor\n (_target: Object,\n _name: string | symbol,\n descriptor: TypedPropertyDescriptor): PropertyDescriptor {\n const originalMethod = descriptor.value\n const linearize: colorFuncType = function (this: T, value: any, fromTo?: boolean) {\n let result = originalMethod!.bind(this, value, fromTo)()\n if (colorSpace == 'linear') {\n tmpColor.set(result)\n tmpColor.convertSRGBToLinear()\n return tmpColor.getHex()\n } else {\n return result\n }\n }\n descriptor.value = linearize\n return descriptor\n }\n\n/**\n * Class for making colors.\n * @interface\n */\nabstract class Colormaker {\n parameters: ColormakerParameters\n atomProxy?: AtomProxy\n\n /**\n * Create a colormaker instance\n * @param {ColormakerParameters} params - colormaker parameter\n */\n constructor (params: Partial = {}) {\n this.parameters = createParams(params, ScaleDefaultParameters)\n\n if (typeof this.parameters.value === 'string') {\n this.parameters.value = tmpColor.set(this.parameters.value).getHex()\n }\n\n if (this.parameters.structure) {\n this.atomProxy = this.parameters.structure.getAtomProxy()\n }\n }\n\n getScale (params: Partial = {}) {\n const p = createParams(params, this.parameters)\n\n if (p.scale === 'rainbow') {\n p.scale = [ 'red', 'orange', 'yellow', 'green', 'blue' ]\n } else if (p.scale === 'rwb') {\n p.scale = [ 'red', 'white', 'blue' ]\n }\n\n if (p.reverse) {\n p.domain = p.domain.slice().reverse()\n }\n return chroma\n .scale(p.scale as any) // TODO\n .mode(p.mode)\n .domain(p.domain)\n .out('num' as any) // returns RGB color as numeric (not string \"#ffffff\")\n }\n\n /**\n * save a color to an array\n * @param {Integer} color - hex color value\n * @param {Array|TypedArray} array - destination\n * @param {Integer} offset - index into the array\n * @return {Array} the destination array\n */\n colorToArray (color: number, array: NumberArray = [], offset = 0) {\n array[ offset ] = (color >> 16 & 255) / 255\n array[ offset + 1 ] = (color >> 8 & 255) / 255\n array[ offset + 2 ] = (color & 255) / 255\n\n return array\n }\n\n atomColor? (atom: AtomProxy): number\n\n /**\n * save an atom color to an array\n * @param {AtomProxy} atom - atom to get color for\n * @param {Array|TypedArray} array - destination\n * @param {Integer} offset - index into the array\n * @return {Array} the destination array\n */\n atomColorToArray (atom: AtomProxy, array: NumberArray, offset: number) {\n return this.colorToArray(\n this.atomColor ? this.atomColor(atom) : 0x000000, array, offset\n )\n }\n\n /**\n * return the color for an bond\n * @param {BondProxy} bond - bond to get color for\n * @param {Boolean} fromTo - whether to use the first or second atom of the bond\n * @return {Integer} hex bond color\n */\n bondColor (bond: BondProxy, fromTo: boolean) {\n if (this.atomProxy && this.atomColor) {\n this.atomProxy.index = fromTo ? bond.atomIndex1 : bond.atomIndex2\n return this.atomColor(this.atomProxy)\n } else {\n return 0x000000\n }\n }\n\n /**\n * safe a bond color to an array\n * @param {BondProxy} bond - bond to get color for\n * @param {Boolean} fromTo - whether to use the first or second atom of the bond\n * @param {Array|TypedArray} array - destination\n * @param {Integer} offset - index into the array\n * @return {Array} the destination array\n */\n bondColorToArray (bond: BondProxy, fromTo: boolean, array: NumberArray, offset: number) {\n return this.colorToArray(\n this.bondColor(bond, fromTo), array, offset\n )\n }\n\n volumeColor? (index: number): number\n\n /**\n * safe a volume cell color to an array\n * @param {Integer} index - volume cell index\n * @param {Array|TypedArray} array - destination\n * @param {Integer} offset - index into the array\n * @return {Array} the destination array\n */\n volumeColorToArray (index: number, array: NumberArray, offset: number) {\n return this.colorToArray(\n this.volumeColor ? this.volumeColor(index) : 0x000000, array, offset\n )\n }\n\n positionColor? (position: Vector3): number\n\n /**\n * safe a color for coordinates in space to an array\n * @param {Vector3} coords - xyz coordinates\n * @param {Array|TypedArray} array - destination\n * @param {Integer} offset - index into the array\n * @return {Array} the destination array\n */\n positionColorToArray (coords: Vector3, array: NumberArray, offset: number) {\n return this.colorToArray(\n this.positionColor ? this.positionColor(coords) : 0x000000, array, offset\n )\n }\n}\n\nexport default Colormaker\n","/**\n * @file Selection Constants\n * @author Alexander Rose \n * @private\n */\n\nexport enum kwd {\n PROTEIN = 1,\n NUCLEIC = 2,\n RNA = 3,\n DNA = 4,\n POLYMER = 5,\n WATER = 6,\n HELIX = 7,\n SHEET = 8,\n TURN = 9,\n BACKBONE = 10,\n SIDECHAIN = 11,\n ALL = 12,\n HETERO = 13,\n ION = 14,\n SACCHARIDE = 15,\n SUGAR = 15,\n BONDED = 16,\n RING = 17,\n AROMATICRING = 18,\n METAL = 19,\n POLARH = 20,\n NONE = 21\n}\n\nexport const SelectAllKeyword = [ '*', '', 'ALL' ]\nexport const SelectNoneKeyword = [ 'NONE' ]\n\nexport const AtomOnlyKeywords = [\n kwd.BACKBONE, kwd.SIDECHAIN, kwd.BONDED, kwd.RING, kwd.AROMATICRING, kwd.METAL, kwd.POLARH\n]\n\nexport const ChainKeywords = [\n kwd.POLYMER, kwd.WATER\n]\n\nexport const SmallResname = [ 'ALA', 'GLY', 'SER' ]\nexport const NucleophilicResname = [ 'CYS', 'SER', 'THR' ]\nexport const HydrophobicResname = [ 'ALA', 'ILE', 'LEU', 'MET', 'PHE', 'PRO', 'TRP', 'VAL' ]\nexport const AromaticResname = [ 'PHE', 'TRP', 'TYR', 'HIS' ]\nexport const AmideResname = [ 'ASN', 'GLN' ]\nexport const AcidicResname = [ 'ASP', 'GLU' ]\nexport const BasicResname = [ 'ARG', 'HIS', 'LYS' ]\nexport const ChargedResname = [ 'ARG', 'ASP', 'GLU', 'HIS', 'LYS' ]\nexport const PolarResname = [ 'ASN', 'ARG', 'ASP', 'CYS', 'GLY', 'GLN', 'GLU', 'HIS', 'LYS', 'SER', 'THR', 'TYR' ]\nexport const NonpolarResname = [ 'ALA', 'ILE', 'LEU', 'MET', 'PHE', 'PRO', 'TRP', 'VAL' ]\nexport const CyclicResname = [ 'HIS', 'PHE', 'PRO', 'TRP', 'TYR' ]\nexport const AliphaticResname = [ 'ALA', 'GLY', 'ILE', 'LEU', 'VAL' ]\n","/*jslint onevar:true, undef:true, newcap:true, regexp:true, bitwise:true, maxerr:50, indent:4, white:false, nomen:false, plusplus:false */\n/*global define:false, require:false, exports:false, module:false, signals:false */\n\n/** @license\n * JS Signals \n * Released under the MIT license\n * Author: Miller Medeiros\n * Version: 1.0.0 - Build: 268 (2012/11/29 05:48 PM)\n */\n\n(function(global){\n\n // SignalBinding -------------------------------------------------\n //================================================================\n\n /**\n * Object that represents a binding between a Signal and a listener function.\n *
- This is an internal constructor and shouldn't be called by regular users.\n *
- inspired by Joa Ebert AS3 SignalBinding and Robert Penner's Slot classes.\n * @author Miller Medeiros\n * @constructor\n * @internal\n * @name SignalBinding\n * @param {Signal} signal Reference to Signal object that listener is currently bound to.\n * @param {Function} listener Handler function bound to the signal.\n * @param {boolean} isOnce If binding should be executed just once.\n * @param {Object} [listenerContext] Context on which listener will be executed (object that should represent the `this` variable inside listener function).\n * @param {Number} [priority] The priority level of the event listener. (default = 0).\n */\n function SignalBinding(signal, listener, isOnce, listenerContext, priority) {\n\n /**\n * Handler function bound to the signal.\n * @type Function\n * @private\n */\n this._listener = listener;\n\n /**\n * If binding should be executed just once.\n * @type boolean\n * @private\n */\n this._isOnce = isOnce;\n\n /**\n * Context on which listener will be executed (object that should represent the `this` variable inside listener function).\n * @memberOf SignalBinding.prototype\n * @name context\n * @type Object|undefined|null\n */\n this.context = listenerContext;\n\n /**\n * Reference to Signal object that listener is currently bound to.\n * @type Signal\n * @private\n */\n this._signal = signal;\n\n /**\n * Listener priority\n * @type Number\n * @private\n */\n this._priority = priority || 0;\n }\n\n SignalBinding.prototype = {\n\n /**\n * If binding is active and should be executed.\n * @type boolean\n */\n active : true,\n\n /**\n * Default parameters passed to listener during `Signal.dispatch` and `SignalBinding.execute`. (curried parameters)\n * @type Array|null\n */\n params : null,\n\n /**\n * Call listener passing arbitrary parameters.\n *

If binding was added using `Signal.addOnce()` it will be automatically removed from signal dispatch queue, this method is used internally for the signal dispatch.

\n * @param {Array} [paramsArr] Array of parameters that should be passed to the listener\n * @return {*} Value returned by the listener.\n */\n execute : function (paramsArr) {\n var handlerReturn, params;\n if (this.active && !!this._listener) {\n params = this.params? this.params.concat(paramsArr) : paramsArr;\n handlerReturn = this._listener.apply(this.context, params);\n if (this._isOnce) {\n this.detach();\n }\n }\n return handlerReturn;\n },\n\n /**\n * Detach binding from signal.\n * - alias to: mySignal.remove(myBinding.getListener());\n * @return {Function|null} Handler function bound to the signal or `null` if binding was previously detached.\n */\n detach : function () {\n return this.isBound()? this._signal.remove(this._listener, this.context) : null;\n },\n\n /**\n * @return {Boolean} `true` if binding is still bound to the signal and have a listener.\n */\n isBound : function () {\n return (!!this._signal && !!this._listener);\n },\n\n /**\n * @return {boolean} If SignalBinding will only be executed once.\n */\n isOnce : function () {\n return this._isOnce;\n },\n\n /**\n * @return {Function} Handler function bound to the signal.\n */\n getListener : function () {\n return this._listener;\n },\n\n /**\n * @return {Signal} Signal that listener is currently bound to.\n */\n getSignal : function () {\n return this._signal;\n },\n\n /**\n * Delete instance properties\n * @private\n */\n _destroy : function () {\n delete this._signal;\n delete this._listener;\n delete this.context;\n },\n\n /**\n * @return {string} String representation of the object.\n */\n toString : function () {\n return '[SignalBinding isOnce:' + this._isOnce +', isBound:'+ this.isBound() +', active:' + this.active + ']';\n }\n\n };\n\n\n/*global SignalBinding:false*/\n\n // Signal --------------------------------------------------------\n //================================================================\n\n function validateListener(listener, fnName) {\n if (typeof listener !== 'function') {\n throw new Error( 'listener is a required param of {fn}() and should be a Function.'.replace('{fn}', fnName) );\n }\n }\n\n /**\n * Custom event broadcaster\n *
- inspired by Robert Penner's AS3 Signals.\n * @name Signal\n * @author Miller Medeiros\n * @constructor\n */\n function Signal() {\n /**\n * @type Array.\n * @private\n */\n this._bindings = [];\n this._prevParams = null;\n\n // enforce dispatch to aways work on same context (#47)\n var self = this;\n this.dispatch = function(){\n Signal.prototype.dispatch.apply(self, arguments);\n };\n }\n\n Signal.prototype = {\n\n /**\n * Signals Version Number\n * @type String\n * @const\n */\n VERSION : '1.0.0',\n\n /**\n * If Signal should keep record of previously dispatched parameters and\n * automatically execute listener during `add()`/`addOnce()` if Signal was\n * already dispatched before.\n * @type boolean\n */\n memorize : false,\n\n /**\n * @type boolean\n * @private\n */\n _shouldPropagate : true,\n\n /**\n * If Signal is active and should broadcast events.\n *

IMPORTANT: Setting this property during a dispatch will only affect the next dispatch, if you want to stop the propagation of a signal use `halt()` instead.

\n * @type boolean\n */\n active : true,\n\n /**\n * @param {Function} listener\n * @param {boolean} isOnce\n * @param {Object} [listenerContext]\n * @param {Number} [priority]\n * @return {SignalBinding}\n * @private\n */\n _registerListener : function (listener, isOnce, listenerContext, priority) {\n\n var prevIndex = this._indexOfListener(listener, listenerContext),\n binding;\n\n if (prevIndex !== -1) {\n binding = this._bindings[prevIndex];\n if (binding.isOnce() !== isOnce) {\n throw new Error('You cannot add'+ (isOnce? '' : 'Once') +'() then add'+ (!isOnce? '' : 'Once') +'() the same listener without removing the relationship first.');\n }\n } else {\n binding = new SignalBinding(this, listener, isOnce, listenerContext, priority);\n this._addBinding(binding);\n }\n\n if(this.memorize && this._prevParams){\n binding.execute(this._prevParams);\n }\n\n return binding;\n },\n\n /**\n * @param {SignalBinding} binding\n * @private\n */\n _addBinding : function (binding) {\n //simplified insertion sort\n var n = this._bindings.length;\n do { --n; } while (this._bindings[n] && binding._priority <= this._bindings[n]._priority);\n this._bindings.splice(n + 1, 0, binding);\n },\n\n /**\n * @param {Function} listener\n * @return {number}\n * @private\n */\n _indexOfListener : function (listener, context) {\n var n = this._bindings.length,\n cur;\n while (n--) {\n cur = this._bindings[n];\n if (cur._listener === listener && cur.context === context) {\n return n;\n }\n }\n return -1;\n },\n\n /**\n * Check if listener was attached to Signal.\n * @param {Function} listener\n * @param {Object} [context]\n * @return {boolean} if Signal has the specified listener.\n */\n has : function (listener, context) {\n return this._indexOfListener(listener, context) !== -1;\n },\n\n /**\n * Add a listener to the signal.\n * @param {Function} listener Signal handler function.\n * @param {Object} [listenerContext] Context on which listener will be executed (object that should represent the `this` variable inside listener function).\n * @param {Number} [priority] The priority level of the event listener. Listeners with higher priority will be executed before listeners with lower priority. Listeners with same priority level will be executed at the same order as they were added. (default = 0)\n * @return {SignalBinding} An Object representing the binding between the Signal and listener.\n */\n add : function (listener, listenerContext, priority) {\n validateListener(listener, 'add');\n return this._registerListener(listener, false, listenerContext, priority);\n },\n\n /**\n * Add listener to the signal that should be removed after first execution (will be executed only once).\n * @param {Function} listener Signal handler function.\n * @param {Object} [listenerContext] Context on which listener will be executed (object that should represent the `this` variable inside listener function).\n * @param {Number} [priority] The priority level of the event listener. Listeners with higher priority will be executed before listeners with lower priority. Listeners with same priority level will be executed at the same order as they were added. (default = 0)\n * @return {SignalBinding} An Object representing the binding between the Signal and listener.\n */\n addOnce : function (listener, listenerContext, priority) {\n validateListener(listener, 'addOnce');\n return this._registerListener(listener, true, listenerContext, priority);\n },\n\n /**\n * Remove a single listener from the dispatch queue.\n * @param {Function} listener Handler function that should be removed.\n * @param {Object} [context] Execution context (since you can add the same handler multiple times if executing in a different context).\n * @return {Function} Listener handler function.\n */\n remove : function (listener, context) {\n validateListener(listener, 'remove');\n\n var i = this._indexOfListener(listener, context);\n if (i !== -1) {\n this._bindings[i]._destroy(); //no reason to a SignalBinding exist if it isn't attached to a signal\n this._bindings.splice(i, 1);\n }\n return listener;\n },\n\n /**\n * Remove all listeners from the Signal.\n */\n removeAll : function () {\n var n = this._bindings.length;\n while (n--) {\n this._bindings[n]._destroy();\n }\n this._bindings.length = 0;\n },\n\n /**\n * @return {number} Number of listeners attached to the Signal.\n */\n getNumListeners : function () {\n return this._bindings.length;\n },\n\n /**\n * Stop propagation of the event, blocking the dispatch to next listeners on the queue.\n *

IMPORTANT: should be called only during signal dispatch, calling it before/after dispatch won't affect signal broadcast.

\n * @see Signal.prototype.disable\n */\n halt : function () {\n this._shouldPropagate = false;\n },\n\n /**\n * Dispatch/Broadcast Signal to all listeners added to the queue.\n * @param {...*} [params] Parameters that should be passed to each handler.\n */\n dispatch : function (params) {\n if (! this.active) {\n return;\n }\n\n var paramsArr = Array.prototype.slice.call(arguments),\n n = this._bindings.length,\n bindings;\n\n if (this.memorize) {\n this._prevParams = paramsArr;\n }\n\n if (! n) {\n //should come after memorize\n return;\n }\n\n bindings = this._bindings.slice(); //clone array in case add/remove items during dispatch\n this._shouldPropagate = true; //in case `halt` was called before dispatch or during the previous dispatch.\n\n //execute all callbacks until end of the list or until a callback returns `false` or stops propagation\n //reverse loop since listeners with higher priority will be added at the end of the list\n do { n--; } while (bindings[n] && this._shouldPropagate && bindings[n].execute(paramsArr) !== false);\n },\n\n /**\n * Forget memorized arguments.\n * @see Signal.memorize\n */\n forget : function(){\n this._prevParams = null;\n },\n\n /**\n * Remove all bindings from signal and destroy any reference to external objects (destroy Signal object).\n *

IMPORTANT: calling any method on the signal instance after calling dispose will throw errors.

\n */\n dispose : function () {\n this.removeAll();\n delete this._bindings;\n delete this._prevParams;\n },\n\n /**\n * @return {string} String representation of the object.\n */\n toString : function () {\n return '[Signal active:'+ this.active +' numListeners:'+ this.getNumListeners() +']';\n }\n\n };\n\n\n // Namespace -----------------------------------------------------\n //================================================================\n\n /**\n * Signals namespace\n * @namespace\n * @name signals\n */\n var signals = Signal;\n\n /**\n * Custom event broadcaster\n * @see Signal\n */\n // alias for backwards compatibility (see #gh-44)\n signals.Signal = Signal;\n\n\n\n //exports to multiple environments\n if(typeof define === 'function' && define.amd){ //AMD\n define(function () { return signals; });\n } else if (typeof module !== 'undefined' && module.exports){ //node\n module.exports = signals;\n } else { //browser\n //use string because of Google closure compiler ADVANCED_MODE\n /*jslint sub:true */\n global['signals'] = signals;\n }\n\n}(this));\n","/**\n * @file Selection Test\n * @author Alexander Rose \n * @private\n */\n\nimport { binarySearchIndexOf, rangeInSortedArray } from '../utils'\nimport { kwd, AtomOnlyKeywords, ChainKeywords } from './selection-constants'\n\nimport AtomProxy from '../proxy/atom-proxy'\nimport ResidueProxy from '../proxy/residue-proxy'\nimport ChainProxy from '../proxy/chain-proxy'\nimport ModelProxy from '../proxy/model-proxy'\n\nexport type ProxyEntity = AtomProxy|ResidueProxy|ChainProxy|ModelProxy\ntype TestEntityFn = (e: ProxyEntity, s: SelectionRule) => boolean|-1\ntype FilterFn = (s: SelectionRule) => boolean\nexport type SelectionTest = false|((e: ProxyEntity) => boolean|-1)\n\nexport type SelectionOperator = 'AND'|'OR'\nexport interface SelectionRule {\n keyword?: any\n atomname?: string\n element?: string\n atomindex?: number[]\n altloc?: string\n inscode?: string\n resname?: string|string[]\n sstruc?: string\n resno?: number|[number, number]\n chainname?: string\n model?: number\n\n error?: string\n rules?: SelectionRule[]\n negate?: boolean\n operator?: SelectionOperator\n}\n\nfunction atomTestFn (a: AtomProxy, s: SelectionRule) {\n // returning -1 means the rule is not applicable\n if (s.atomname === undefined && s.element === undefined &&\n s.altloc === undefined && s.atomindex === undefined &&\n s.keyword === undefined && s.inscode === undefined &&\n s.resname === undefined && s.sstruc === undefined &&\n s.resno === undefined && s.chainname === undefined &&\n s.model === undefined\n ) return -1\n\n if (s.keyword !== undefined) {\n if (s.keyword === kwd.BACKBONE && !a.isBackbone()) return false\n if (s.keyword === kwd.SIDECHAIN && !a.isSidechain()) return false\n if (s.keyword === kwd.BONDED && !a.isBonded()) return false\n if (s.keyword === kwd.RING && !a.isRing()) return false\n if (s.keyword === kwd.AROMATICRING && !a.isAromatic()) return false\n\n if (s.keyword === kwd.HETERO && !a.isHetero()) return false\n if (s.keyword === kwd.PROTEIN && !a.isProtein()) return false\n if (s.keyword === kwd.NUCLEIC && !a.isNucleic()) return false\n if (s.keyword === kwd.RNA && !a.isRna()) return false\n if (s.keyword === kwd.DNA && !a.isDna()) return false\n if (s.keyword === kwd.POLYMER && !a.isPolymer()) return false\n if (s.keyword === kwd.WATER && !a.isWater()) return false\n if (s.keyword === kwd.HELIX && !a.isHelix()) return false\n if (s.keyword === kwd.SHEET && !a.isSheet()) return false\n if (s.keyword === kwd.TURN && !a.isTurn()) return false\n if (s.keyword === kwd.ION && !a.isIon()) return false\n if (s.keyword === kwd.SACCHARIDE && !a.isSaccharide()) return false\n if (s.keyword === kwd.METAL && !a.isMetal()) return false\n if (s.keyword === kwd.POLARH && !a.isPolarHydrogen()) return false\n }\n\n if (s.atomname !== undefined && s.atomname !== a.atomname) return false\n if (s.element !== undefined && s.element !== a.element) return false\n if (s.altloc !== undefined && s.altloc !== a.altloc) return false\n\n if (s.atomindex !== undefined &&\n binarySearchIndexOf(s.atomindex, a.index) < 0\n ) return false\n\n if (s.resname !== undefined) {\n if (Array.isArray(s.resname)) {\n if (!s.resname.includes(a.resname)) return false\n } else {\n if (s.resname !== a.resname) return false\n }\n }\n if (s.sstruc !== undefined && s.sstruc !== a.sstruc) return false\n if (s.resno !== undefined) {\n if (Array.isArray(s.resno) && s.resno.length === 2) {\n if (s.resno[0] > a.resno || s.resno[1] < a.resno) return false\n } else {\n if (s.resno !== a.resno) return false\n }\n }\n if (s.inscode !== undefined && s.inscode !== a.inscode) return false\n\n if (s.chainname !== undefined && s.chainname !== a.chainname) return false\n if (s.model !== undefined && s.model !== a.modelIndex) return false\n\n return true\n}\n\nfunction residueTestFn (r: ResidueProxy, s: SelectionRule) {\n // returning -1 means the rule is not applicable\n if (s.resname === undefined && s.resno === undefined && s.inscode === undefined &&\n s.sstruc === undefined && s.model === undefined && s.chainname === undefined &&\n s.atomindex === undefined &&\n (s.keyword === undefined || AtomOnlyKeywords.includes(s.keyword))\n ) return -1\n\n if (s.keyword !== undefined) {\n if (s.keyword === kwd.HETERO && !r.isHetero()) return false\n if (s.keyword === kwd.PROTEIN && !r.isProtein()) return false\n if (s.keyword === kwd.NUCLEIC && !r.isNucleic()) return false\n if (s.keyword === kwd.RNA && !r.isRna()) return false\n if (s.keyword === kwd.DNA && !r.isDna()) return false\n if (s.keyword === kwd.POLYMER && !r.isPolymer()) return false\n if (s.keyword === kwd.WATER && !r.isWater()) return false\n if (s.keyword === kwd.HELIX && !r.isHelix()) return false\n if (s.keyword === kwd.SHEET && !r.isSheet()) return false\n if (s.keyword === kwd.TURN && !r.isTurn()) return false\n if (s.keyword === kwd.ION && !r.isIon()) return false\n if (s.keyword === kwd.SACCHARIDE && !r.isSaccharide()) return false\n }\n\n if (s.atomindex !== undefined &&\n rangeInSortedArray(s.atomindex, r.atomOffset, r.atomEnd) === 0\n ) return false\n\n if (s.resname !== undefined) {\n if (Array.isArray(s.resname)) {\n if (!s.resname.includes(r.resname)) return false\n } else {\n if (s.resname !== r.resname) return false\n }\n }\n if (s.sstruc !== undefined && s.sstruc !== r.sstruc) return false\n if (s.resno !== undefined) {\n if (Array.isArray(s.resno) && s.resno.length === 2) {\n if (s.resno[0] > r.resno || s.resno[1] < r.resno) return false\n } else {\n if (s.resno !== r.resno) return false\n }\n }\n if (s.inscode !== undefined && s.inscode !== r.inscode) return false\n\n if (s.chainname !== undefined && s.chainname !== r.chainname) return false\n if (s.model !== undefined && s.model !== r.modelIndex) return false\n\n return true\n}\n\nfunction chainTestFn (c: ChainProxy, s: SelectionRule) {\n // returning -1 means the rule is not applicable\n if (s.chainname === undefined && s.model === undefined && s.atomindex === undefined &&\n (s.keyword === undefined || !ChainKeywords.includes(s.keyword) || !c.entity)\n ) return -1\n\n if (s.keyword !== undefined) {\n if (s.keyword === kwd.POLYMER && !c.entity.isPolymer()) return false\n if (s.keyword === kwd.WATER && !c.entity.isWater()) return false\n }\n\n if (s.atomindex !== undefined &&\n rangeInSortedArray(s.atomindex, c.atomOffset, c.atomEnd) === 0\n ) return false\n\n if (s.chainname !== undefined && s.chainname !== c.chainname) return false\n\n if (s.model !== undefined && s.model !== c.modelIndex) return false\n\n return true\n}\n\nfunction modelTestFn (m: ModelProxy, s: SelectionRule) {\n // returning -1 means the rule is not applicable\n if (s.model === undefined && s.atomindex === undefined) return -1\n\n if (s.atomindex !== undefined &&\n rangeInSortedArray(s.atomindex, m.atomOffset, m.atomEnd) === 0\n ) return false\n\n if (s.model !== undefined && s.model !== m.index) return false\n\n return true\n}\n\nfunction makeTest (selection: SelectionRule|null, fn: TestEntityFn) {\n if (selection === null) return false\n if (selection.error) return false\n if (!selection.rules || selection.rules.length === 0) return false\n\n const n = selection.rules.length\n\n const t = !selection.negate\n const f = !!selection.negate\n\n const subTests: SelectionTest[] = []\n for (let i = 0; i < n; ++i) {\n const s = selection.rules[ i ]\n if (s.hasOwnProperty('operator')) {\n subTests[ i ] = makeTest(s, fn) as SelectionTest // TODO\n }\n }\n\n // ( x and y ) can short circuit on false\n // ( x or y ) can short circuit on true\n // not ( x and y )\n\n return function test (entity: ProxyEntity) {\n const and = selection.operator === 'AND'\n let na = false\n\n for (let i = 0; i < n; ++i) {\n const s = selection.rules![ i ] // TODO\n let ret\n\n if (s.hasOwnProperty('operator')) {\n const test = subTests[ i ]\n if (test !== false) {\n ret = test(entity)\n } else {\n ret = -1\n }\n\n if (ret === -1) {\n na = true\n continue\n } else if (ret === true) {\n if (and) { continue } else { return t }\n } else {\n if (and) { return f } else { continue }\n }\n } else {\n if (s.keyword === kwd.ALL) {\n if (and) { continue } else { return t }\n } else if (s.keyword === kwd.NONE) {\n if (and) { continue } else { return f }\n }\n\n ret = fn(entity, s)\n\n // console.log( entity.qualifiedName(), ret, s, selection.negate, \"t\", t, \"f\", f )\n\n if (ret === -1) {\n na = true\n continue\n } else if (ret === true) {\n if (and) { continue } else { return t }\n } else {\n if (and) { return f } else { continue }\n }\n }\n }\n\n if (na) {\n return -1\n } else {\n if (and) { return t } else { return f }\n }\n } as SelectionTest\n}\n\nfunction filter (selection: SelectionRule, fn: FilterFn) {\n if (selection.error) return selection\n if (!selection.rules || selection.rules.length === 0) return selection\n\n const n = selection.rules.length\n\n const filtered: SelectionRule = {\n operator: selection.operator,\n rules: []\n }\n if (selection.hasOwnProperty('negate')) {\n filtered.negate = selection.negate\n }\n\n for (let i = 0; i < n; ++i) {\n const s = selection.rules[ i ]\n if (s.hasOwnProperty('operator')) {\n const fs = filter(s, fn)\n if (fs !== null) filtered.rules!.push(fs) // TODO\n } else if (!fn(s)) {\n filtered.rules!.push(s) // TODO\n }\n }\n\n if (filtered.rules!.length > 0) { // TODO\n // TODO maybe the filtered rules could be returned\n // in some case, but the way how tests are applied\n // e.g. when traversing a structure would also need\n // to change\n return selection\n // return filtered;\n } else {\n return null\n }\n}\n\nfunction makeAtomTest (selection: SelectionRule, atomOnly = false) {\n let filteredSelection: SelectionRule|null = selection\n if (atomOnly) {\n filteredSelection = filter(selection, function (s) {\n if (s.keyword !== undefined && !AtomOnlyKeywords.includes(s.keyword)) return true\n if (s.model !== undefined) return true\n if (s.chainname !== undefined) return true\n if (s.resname !== undefined) return true\n if (s.resno !== undefined) return true\n if (s.sstruc !== undefined) return true\n return false\n })\n }\n return makeTest(filteredSelection, atomTestFn)\n}\n\nfunction makeResidueTest (selection: SelectionRule, residueOnly = false) {\n let filteredSelection: SelectionRule|null = selection\n if (residueOnly) {\n filteredSelection = filter(selection, function (s) {\n if (s.keyword !== undefined && AtomOnlyKeywords.includes(s.keyword)) return true\n if (s.model !== undefined) return true\n if (s.chainname !== undefined) return true\n if (s.atomname !== undefined) return true\n if (s.element !== undefined) return true\n if (s.altloc !== undefined) return true\n return false\n })\n }\n return makeTest(filteredSelection, residueTestFn)\n}\n\nfunction makeChainTest (selection: SelectionRule, chainOnly = false) {\n let filteredSelection: SelectionRule|null = selection\n if (chainOnly) {\n filteredSelection = filter(selection, function (s) {\n if (s.keyword !== undefined && !ChainKeywords.includes(s.keyword)) return true\n // if( s.model!==undefined ) return true;\n if (s.resname !== undefined) return true\n if (s.resno !== undefined) return true\n if (s.atomname !== undefined) return true\n if (s.element !== undefined) return true\n if (s.altloc !== undefined) return true\n if (s.sstruc !== undefined) return true\n if (s.inscode !== undefined) return true\n return false\n })\n }\n return makeTest(filteredSelection, chainTestFn)\n}\n\nfunction makeModelTest (selection: SelectionRule, modelOnly = false) {\n let filteredSelection: SelectionRule|null = selection\n if (modelOnly) {\n filteredSelection = filter(selection, function (s) {\n if (s.keyword !== undefined) return true\n if (s.chainname !== undefined) return true\n if (s.resname !== undefined) return true\n if (s.resno !== undefined) return true\n if (s.atomname !== undefined) return true\n if (s.element !== undefined) return true\n if (s.altloc !== undefined) return true\n if (s.sstruc !== undefined) return true\n if (s.inscode !== undefined) return true\n return false\n })\n }\n return makeTest(filteredSelection, modelTestFn)\n}\n\nexport {\n makeAtomTest,\n makeResidueTest,\n makeChainTest,\n makeModelTest\n}\n","/**\n * @file Selection\n * @author Alexander Rose \n * @private\n */\n\nimport { Signal } from 'signals'\n\nimport { parseSele } from './selection-parser'\nimport {\n SelectionTest, SelectionRule,\n makeAtomTest, makeResidueTest, makeChainTest, makeModelTest\n} from './selection-test'\nimport { SelectAllKeyword, SelectNoneKeyword } from './selection-constants'\n\nexport type SelectionSignals = {\n stringChanged: Signal\n}\n\n/**\n * Selection\n */\nclass Selection {\n signals: SelectionSignals\n string: string\n selection: SelectionRule\n\n test: SelectionTest\n residueTest: SelectionTest\n chainTest: SelectionTest\n modelTest: SelectionTest\n\n atomOnlyTest: SelectionTest\n residueOnlyTest: SelectionTest\n chainOnlyTest: SelectionTest\n modelOnlyTest: SelectionTest\n\n /**\n * Create Selection\n * @param {String} string - selection string, see {@tutorial selection-language}\n */\n constructor (string?: string) {\n this.signals = {\n stringChanged: new Signal()\n }\n\n this.setString(string)\n }\n\n get type () { return 'selection' }\n\n setString (string?: string, silent?: boolean) {\n if (string === undefined) string = this.string || ''\n if (string === this.string) return\n\n try {\n this.selection = parseSele(string)\n } catch (e) {\n // Log.error( e.stack );\n this.selection = { 'error': e.message }\n }\n const selection = this.selection\n\n this.string = string\n\n this.test = makeAtomTest(selection)\n this.residueTest = makeResidueTest(selection)\n this.chainTest = makeChainTest(selection)\n this.modelTest = makeModelTest(selection)\n\n this.atomOnlyTest = makeAtomTest(selection, true)\n this.residueOnlyTest = makeResidueTest(selection, true)\n this.chainOnlyTest = makeChainTest(selection, true)\n this.modelOnlyTest = makeModelTest(selection, true)\n\n if (!silent) {\n this.signals.stringChanged.dispatch(this.string)\n }\n }\n\n isAllSelection () {\n return SelectAllKeyword.includes(this.string.toUpperCase())\n }\n\n isNoneSelection () {\n return SelectNoneKeyword.includes(this.string.toUpperCase())\n }\n}\n\nexport default Selection\n","/**\n * @file Selection Parser\n * @author Alexander Rose \n * @private\n */\n\nimport { SelectionRule, SelectionOperator } from './selection-test'\nimport {\n kwd, SelectAllKeyword,\n SmallResname, NucleophilicResname, HydrophobicResname, AromaticResname,\n AmideResname, AcidicResname, BasicResname, ChargedResname,\n PolarResname, NonpolarResname, CyclicResname, AliphaticResname\n} from './selection-constants'\n\nfunction parseSele (string: string) {\n let retSelection: SelectionRule = {\n operator: undefined,\n rules: []\n }\n\n if (!string) {\n return retSelection\n }\n\n let selection = retSelection\n let newSelection: SelectionRule\n let oldSelection: SelectionRule\n const selectionStack: SelectionRule[] = []\n\n string = string.replace(/\\(/g, ' ( ').replace(/\\)/g, ' ) ').trim()\n if (string.charAt(0) === '(' && string.substr(-1) === ')') {\n string = string.slice(1, -1).trim()\n }\n const chunks = string.split(/\\s+/)\n\n // Log.log( string, chunks )\n\n const createNewContext = (operator?: SelectionOperator) => {\n newSelection = {\n operator,\n rules: []\n }\n if (selection === undefined) {\n selection = newSelection\n retSelection = newSelection\n } else {\n selection.rules!.push(newSelection)\n selectionStack.push(selection)\n selection = newSelection\n }\n }\n\n const getPrevContext = function (operator?: SelectionOperator) {\n oldSelection = selection\n selection = selectionStack.pop()!\n if (selection === undefined) {\n createNewContext(operator)\n pushRule(oldSelection)\n }\n }\n\n const pushRule = function (rule: SelectionRule) {\n selection.rules!.push(rule)\n }\n\n let not: false|0|1|2 = false\n\n for (let i = 0; i < chunks.length; ++i) {\n const c = chunks[ i ]\n const cu = c.toUpperCase()\n\n // handle parens\n\n if (c === '(') {\n // Log.log( \"(\" );\n not = false\n createNewContext()\n continue\n } else if (c === ')') {\n // Log.log( \")\" );\n getPrevContext()\n if (selection.negate) {\n getPrevContext()\n }\n continue\n }\n\n // leave 'not' context\n\n if (not > 0) {\n if (cu === 'NOT') {\n not = 1\n } else if (not === 1) {\n not = 2\n } else if (not === 2) {\n not = false\n getPrevContext()\n } else {\n throw new Error(\"something went wrong with 'not'\")\n }\n }\n\n // handle logic operators\n\n if (cu === 'AND') {\n // Log.log( \"AND\" );\n if (selection.operator === 'OR') {\n const lastRule = selection.rules!.pop()!\n createNewContext('AND')\n pushRule(lastRule)\n } else {\n selection.operator = 'AND'\n }\n continue\n } else if (cu === 'OR') {\n // Log.log( \"OR\" );\n if (selection.operator === 'AND') {\n getPrevContext('OR')\n } else {\n selection.operator = 'OR'\n }\n continue\n } else if (c.toUpperCase() === 'NOT') {\n // Log.log( \"NOT\", j );\n not = 1\n createNewContext()\n selection.negate = true\n continue\n } else {\n // Log.log( \"chunk\", c, j, selection );\n }\n\n // handle keyword attributes\n\n // ensure `cu` is not a number before testing if it is in the\n // kwd enum dictionary which includes the enum numbers as well...\n if (+cu !== +cu) {\n const keyword = (kwd as any)[ cu ]\n if (keyword !== undefined) {\n pushRule({ keyword })\n continue\n }\n }\n\n if (cu === 'HYDROGEN') {\n pushRule({\n operator: 'OR',\n rules: [\n { element: 'H' },\n { element: 'D' }\n ]\n })\n continue\n }\n\n if (cu === 'SMALL') {\n pushRule({ resname: SmallResname })\n continue\n }\n\n if (cu === 'NUCLEOPHILIC') {\n pushRule({ resname: NucleophilicResname })\n continue\n }\n\n if (cu === 'HYDROPHOBIC') {\n pushRule({ resname: HydrophobicResname })\n continue\n }\n\n if (cu === 'AROMATIC') {\n pushRule({ resname: AromaticResname })\n continue\n }\n\n if (cu === 'AMIDE') {\n pushRule({ resname: AmideResname })\n continue\n }\n\n if (cu === 'ACIDIC') {\n pushRule({ resname: AcidicResname })\n continue\n }\n\n if (cu === 'BASIC') {\n pushRule({ resname: BasicResname })\n continue\n }\n\n if (cu === 'CHARGED') {\n pushRule({ resname: ChargedResname })\n continue\n }\n\n if (cu === 'POLAR') {\n pushRule({ resname: PolarResname })\n continue\n }\n\n if (cu === 'NONPOLAR') {\n pushRule({ resname: NonpolarResname })\n continue\n }\n\n if (cu === 'CYCLIC') {\n pushRule({ resname: CyclicResname })\n continue\n }\n\n if (cu === 'ALIPHATIC') {\n pushRule({ resname: AliphaticResname })\n continue\n }\n\n if (cu === 'SIDECHAINATTACHED') {\n pushRule({\n operator: 'OR',\n rules: [\n { keyword: kwd.SIDECHAIN },\n {\n operator: 'AND',\n negate: false,\n rules: [\n { keyword: kwd.PROTEIN },\n {\n operator: 'OR',\n negate: false,\n rules: [\n { atomname: 'CA' },\n { atomname: 'BB' }\n ]\n }\n ]\n },\n {\n operator: 'AND',\n negate: false,\n rules: [\n { resname: 'PRO' },\n { atomname: 'N' }\n ]\n },\n {\n operator: 'AND',\n negate: false,\n rules: [\n { keyword: kwd.NUCLEIC },\n {\n operator: 'OR',\n negate: true,\n rules: [\n { atomname: 'P' },\n { atomname: 'OP1' },\n { atomname: 'OP2' },\n { atomname: \"O3'\" },\n { atomname: 'O3*' },\n { atomname: \"HO3'\"},\n { atomname: \"O5'\" },\n { atomname: 'O5*' },\n { atomname: \"HO5'\"},\n { atomname: \"C5'\" },\n { atomname: 'C5*' },\n { atomname: \"H5'\" },\n { atomname: \"H5''\"}\n ]\n }\n ]\n }\n ]\n })\n continue\n }\n\n if (cu === 'APOLARH') {\n pushRule({\n operator: 'AND',\n negate: false,\n rules: [\n { element: 'H' },\n {\n negate: true,\n operator: undefined,\n rules: [\n { keyword: kwd.POLARH }\n ]\n }\n ]\n })\n continue\n }\n\n if (cu === 'LIGAND') {\n pushRule({\n operator: 'AND',\n rules: [\n {\n operator: 'OR',\n rules: [\n {\n operator: 'AND',\n rules: [\n { keyword: kwd.HETERO },\n {\n negate: true,\n operator: undefined,\n rules: [\n { keyword: kwd.POLYMER }\n ]\n }\n ]\n },\n {\n negate: true,\n operator: undefined,\n rules: [\n { keyword: kwd.POLYMER }\n ]\n }\n ]\n },\n {\n negate: true,\n operator: undefined,\n rules: [\n {\n operator: 'OR',\n rules: [\n { keyword: kwd.WATER },\n { keyword: kwd.ION }\n ]\n }\n ]\n }\n ]\n })\n continue\n }\n\n if (SelectAllKeyword.indexOf(cu) !== -1) {\n pushRule({ keyword: kwd.ALL })\n continue\n }\n\n // handle atom expressions\n\n if (c.charAt(0) === '@') {\n const indexList = c.substr(1).split(',').map(x => parseInt(x))\n indexList.sort(function (a, b) { return a - b })\n pushRule({ atomindex: indexList })\n continue\n }\n\n if (c.charAt(0) === '#') {\n console.error('# for element selection deprecated, use _')\n pushRule({ element: cu.substr(1) })\n continue\n }\n if (c.charAt(0) === '_') {\n pushRule({ element: cu.substr(1) })\n continue\n }\n\n if (c[0] === '[' && c[c.length - 1] === ']') {\n const resnameList = cu.substr(1, c.length - 2).split(',')\n const resname = resnameList.length > 1 ? resnameList : resnameList[ 0 ]\n pushRule({ resname: resname })\n continue\n } else if (\n (c.length >= 1 && c.length <= 4) &&\n c[0] !== '^' && c[0] !== ':' && c[0] !== '.' && c[0] !== '%' && c[0] !== '/' &&\n isNaN(parseInt(c))\n ) {\n pushRule({ resname: cu })\n continue\n }\n\n // there must be only one constraint per rule\n // otherwise a test quickly becomes not applicable\n // e.g. chainTest for chainname when resno is present too\n\n const sele: SelectionRule = {\n operator: 'AND',\n rules: []\n }\n\n const model = c.split('/')\n if (model.length > 1 && model[1]) {\n if (isNaN(parseInt(model[1]))) {\n throw new Error('model must be an integer')\n }\n sele.rules!.push({\n model: parseInt(model[1])\n })\n }\n\n const altloc = model[0].split('%')\n if (altloc.length > 1) {\n sele.rules!.push({\n altloc: altloc[1]\n })\n }\n\n const atomname = altloc[0].split('.')\n if (atomname.length > 1 && atomname[1]) {\n if (atomname[1].length > 4) {\n throw new Error('atomname must be one to four characters')\n }\n sele.rules!.push({\n atomname: atomname[1].substring(0, 4).toUpperCase()\n })\n }\n\n const chain = atomname[0].split(':')\n if (chain.length > 1 && chain[1]) {\n sele.rules!.push({\n chainname: chain[1]\n })\n }\n\n const inscode = chain[0].split('^')\n if (inscode.length > 1) {\n sele.rules!.push({\n inscode: inscode[1]\n })\n }\n\n if (inscode[0]) {\n let negate, negate2\n if (inscode[0][0] === '-') {\n inscode[0] = inscode[0].substr(1)\n negate = true\n }\n if (inscode[0].includes('--')) {\n inscode[0] = inscode[0].replace('--', '-')\n negate2 = true\n }\n let resi = inscode[0].split('-')\n if (resi.length === 1) {\n let resiSingle = parseInt(resi[0])\n if (isNaN(resiSingle)) {\n throw new Error('resi must be an integer')\n }\n if (negate) resiSingle *= -1\n sele.rules!.push({\n resno: resiSingle\n })\n } else if (resi.length === 2) {\n const resiRange = resi.map(x => parseInt(x))\n if (negate) resiRange[0] *= -1\n if (negate2) resiRange[1] *= -1\n sele.rules!.push({\n resno: [resiRange[0], resiRange[1]]\n })\n } else {\n throw new Error(\"resi range must contain one '-'\")\n }\n }\n\n // round up\n\n if (sele.rules!.length === 1) {\n pushRule(sele.rules![ 0 ])\n } else if (sele.rules!.length > 1) {\n pushRule(sele)\n } else {\n throw new Error('empty selection chunk')\n }\n }\n\n // cleanup\n\n if (\n retSelection.operator === undefined &&\n retSelection.rules!.length === 1 &&\n retSelection.rules![ 0 ].hasOwnProperty('operator')\n ) {\n retSelection = retSelection.rules![ 0 ]\n }\n\n return retSelection\n}\n\nexport {\n parseSele\n}\n","/**\n * @file Selection Colormaker\n * @author Alexander Rose \n * @private\n */\n\nimport { Color } from 'three'\n\nimport { ColormakerRegistry } from '../globals'\nimport Selection from '../selection/selection'\nimport Colormaker, { ColormakerParameters } from './colormaker'\nimport AtomProxy from '../proxy/atom-proxy'\nimport Structure from '../structure/structure'\n\nexport type SelectionSchemeData = [ string, string, ColormakerParameters|undefined ]\n\n/**\n * Color based on {@link Selection}\n */\nclass SelectionColormaker extends Colormaker {\n colormakerList: any[] = [] // TODO\n selectionList: Selection[] = []\n\n constructor (params: { structure: Structure, dataList: SelectionSchemeData[] } & Partial) {\n super(params)\n\n const dataList = params.dataList || []\n\n dataList.forEach((data: SelectionSchemeData) => {\n const [ scheme, sele, params = {} ] = data\n\n if (ColormakerRegistry.hasScheme(scheme)) {\n Object.assign(params, {\n scheme: scheme,\n structure: this.parameters.structure\n })\n } else {\n Object.assign(params, {\n scheme: 'uniform',\n value: new Color(scheme).getHex()\n })\n }\n\n this.colormakerList.push(ColormakerRegistry.getScheme(params as { scheme: string } & ColormakerParameters))\n this.selectionList.push(new Selection(sele))\n })\n }\n\n // NOT NEEDED @manageColor\n atomColor (a: AtomProxy) {\n for (let i = 0, n = this.selectionList.length; i < n; ++i) {\n const test = this.selectionList[ i ].test\n if (test && test(a)) {\n return this.colormakerList[ i ].atomColor(a)\n }\n }\n\n return 0xFFFFFF\n }\n}\n\nexport default SelectionColormaker\n","/**\n * @file Colormaker Registry\n * @author Alexander Rose \n * @private\n */\n\nimport { generateUUID } from '../math/math-utils'\nimport Colormaker, { ColormakerConstructor, ColormakerParameters } from './colormaker'\nimport SelectionColormaker, { SelectionSchemeData } from './selection-colormaker'\nimport Structure from '../structure/structure'\n\ntype ColormakerDefinitionFunction = ((this: Colormaker, param?: ColormakerParameters) => void)\n\nconst ColormakerScales = {\n '': '',\n\n // Sequential\n OrRd: '[S] Orange-Red',\n PuBu: '[S] Purple-Blue',\n BuPu: '[S] Blue-Purple',\n Oranges: '[S] Oranges',\n BuGn: '[S] Blue-Green',\n YlOrBr: '[S] Yellow-Orange-Brown',\n YlGn: '[S] Yellow-Green',\n Reds: '[S] Reds',\n RdPu: '[S] Red-Purple',\n Greens: '[S] Greens',\n YlGnBu: '[S] Yellow-Green-Blue',\n Purples: '[S] Purples',\n GnBu: '[S] Green-Blue',\n Greys: '[S] Greys',\n YlOrRd: '[S] Yellow-Orange-Red',\n PuRd: '[S] Purple-Red',\n Blues: '[S] Blues',\n PuBuGn: '[S] Purple-Blue-Green',\n\n // Diverging\n Viridis: '[D] Viridis',\n Spectral: '[D] Spectral',\n RdYlGn: '[D] Red-Yellow-Green',\n RdBu: '[D] Red-Blue',\n PiYG: '[D] Pink-Yellowgreen',\n PRGn: '[D] Purplered-Green',\n RdYlBu: '[D] Red-Yellow-Blue',\n BrBG: '[D] Brown-Bluegreen',\n RdGy: '[D] Red-Grey',\n PuOr: '[D] Purple-Orange',\n\n // Qualitative\n Set1: '[Q] Set1',\n Set2: '[Q] Set2',\n Set3: '[Q] Set3',\n Dark2: '[Q] Dark2',\n Paired: '[Q] Paired',\n Pastel1: '[Q] Pastel1',\n Pastel2: '[Q] Pastel2',\n Accent: '[Q] Accent',\n\n // Other\n rainbow: '[?] Rainbow',\n rwb: '[?] Red-White-Blue'\n}\n\nconst ColormakerModes = {\n '': '',\n\n rgb: 'Red Green Blue',\n hsv: 'Hue Saturation Value',\n hsl: 'Hue Saturation Lightness',\n hsi: 'Hue Saturation Intensity',\n lab: 'CIE L*a*b*',\n hcl: 'Hue Chroma Lightness'\n}\n\n/**\n * Class for registering {@link Colormaker}s. Generally use the\n * global {@link src/globals.js~ColormakerRegistry} instance.\n */\nclass ColormakerRegistry {\n schemes: { [k: string]: ColormakerConstructor }\n userSchemes: { [k: string]: ColormakerConstructor }\n\n constructor () {\n this.schemes = {}\n this.userSchemes = {}\n }\n\n getScheme (params: Partial<{ scheme: string } & ColormakerParameters>) {\n const p = params || {}\n const id = (p.scheme || '').toLowerCase()\n\n let SchemeClass: ColormakerConstructor\n\n if (id in this.schemes) {\n SchemeClass = this.schemes[ id ]\n } else if (id in this.userSchemes) {\n SchemeClass = this.userSchemes[ id ]\n } else {\n //@ts-expect-error abstract class used as a constructor\n SchemeClass = Colormaker\n }\n\n return new SchemeClass(params)\n }\n\n /**\n * Get an description of available schemes as an\n * object with id-label as key-value pairs\n * @return {Object} available schemes\n */\n getSchemes () {\n const types: { [k: string]: string } = {}\n\n Object.keys(this.schemes).forEach(function (k) {\n types[ k ] = k\n })\n\n Object.keys(this.userSchemes).forEach(function (k) {\n types[ k ] = k.split('|')[ 1 ]\n })\n\n return types\n }\n\n /**\n * Get an description of available scales as an\n * object with id-label as key-value pairs\n * @return {Object} available scales\n */\n getScales () {\n return ColormakerScales\n }\n\n getModes () {\n return ColormakerModes\n }\n\n /**\n * Add a scheme with a hardcoded id\n * @param {String} id - the id\n * @param {Colormaker} scheme - the colormaker\n * @return {undefined}\n */\n add (id: string, scheme: ColormakerConstructor) {\n id = id.toLowerCase()\n this.schemes[ id ] = scheme\n }\n\n /**\n * Register a custom scheme\n *\n * @example\n * // Create a class with a `atomColor` method that returns a hex color.\n * var schemeId = NGL.ColormakerRegistry.addScheme( function( params ){\n * this.atomColor = function( atom ){\n * if( atom.serial < 1000 ){\n * return 0x0000FF; // blue\n * }else if( atom.serial > 2000 ){\n * return 0xFF0000; // red\n * }else{\n * return 0x00FF00; // green\n * }\n * };\n * } );\n *\n * stage.loadFile( \"rcsb://3dqb.pdb\" ).then( function( o ){\n * o.addRepresentation( \"cartoon\", { color: schemeId } ); // pass schemeId here\n * o.autoView();\n * } );\n *\n * @param {Function|Colormaker} scheme - constructor or {@link Colormaker} instance\n * @param {String} label - scheme label\n * @return {String} id to refer to the registered scheme\n */\n addScheme (scheme: ColormakerConstructor|ColormakerDefinitionFunction, label?: string) {\n if (!(isColormakerSubClass(scheme))) {\n scheme = this._createScheme(scheme)\n }\n\n return this._addUserScheme(scheme, label)\n }\n\n /**\n * Add a user-defined scheme\n * @param {Colormaker} scheme - the user-defined scheme\n * @param {String} [label] - scheme label\n * @return {String} id to refer to the registered scheme\n */\n _addUserScheme (scheme: ColormakerConstructor, label?: string) {\n label = label || ''\n const id = `${generateUUID()}|${label}`.toLowerCase()\n this.userSchemes[ id ] = scheme\n\n return id\n }\n\n /**\n * Remove the scheme with the given id\n * @param {String} id - scheme to remove\n * @return {undefined}\n */\n removeScheme (id: string) {\n id = id.toLowerCase()\n delete this.userSchemes[ id ]\n }\n\n _createScheme (constructor: ColormakerDefinitionFunction): ColormakerConstructor {\n class _Colormaker extends Colormaker {\n constructor (params: ColormakerParameters) {\n super(params)\n constructor.call(this, params)\n }\n }\n return _Colormaker\n }\n\n /**\n * Create and a selection-based coloring scheme. Supply a list with pairs\n * of colorname and selection for coloring by selections. Use the last\n * entry as a default (catch all) coloring definition.\n *\n * @example\n * var schemeId = NGL.ColormakerRegistry.addSelectionScheme( [\n * [ \"red\", \"64-74 or 134-154 or 222-254 or 310-310 or 322-326\" ],\n * [ \"green\", \"311-322\" ],\n * [ \"yellow\", \"40-63 or 75-95 or 112-133 or 155-173 or 202-221 or 255-277 or 289-309\" ],\n * [ \"blue\", \"1-39 or 96-112 or 174-201 or 278-288\" ],\n * [ \"white\", \"*\" ]\n * ], \"Transmembrane 3dqb\" );\n *\n * stage.loadFile( \"rcsb://3dqb.pdb\" ).then( function( o ){\n * o.addRepresentation( \"cartoon\", { color: schemeId } ); // pass schemeId here\n * o.autoView();\n * } );\n *\n * @param {Array} dataList - cloror-selection pairs\n * @param {String} label - scheme name\n * @return {String} id to refer to the registered scheme\n */\n addSelectionScheme (dataList: SelectionSchemeData[], label?: string) {\n class MySelectionColormaker extends SelectionColormaker {\n constructor (params: { structure: Structure } & ColormakerParameters) {\n super(Object.assign({ dataList }, params))\n }\n }\n\n return this._addUserScheme(MySelectionColormaker, label)\n }\n\n /**\n * Check if a scheme with the given id exists\n * @param {String} id - the id to check\n * @return {Boolean} flag indicating if the scheme exists\n */\n hasScheme (id: string) {\n id = id.toLowerCase()\n return id in this.schemes || id in this.userSchemes\n }\n}\n\nfunction isColormakerSubClass (\n scheme: ColormakerConstructor|((this: Colormaker, param?: ColormakerParameters) => void)\n): scheme is ColormakerConstructor {\n return (scheme instanceof Colormaker)\n}\n\nexport default ColormakerRegistry\n","/**\n * @file Worker Utils\n * @author Alexander Rose \n * @private\n */\n\nimport { uniqueArray } from '../utils'\n\nexport type FunctionWithDeps = { __deps?: Function[] } & Function\nexport interface WorkerEvent {\n data: {\n __name: string\n __postId: string\n }\n}\n\nfunction getWorkerDeps (vars: FunctionWithDeps[]) {\n const deps = vars\n vars.forEach(function (sym) {\n if (sym.__deps) {\n Array.prototype.push.apply(deps, getWorkerDeps(sym.__deps))\n }\n })\n return deps\n}\n\nfunction makeWorkerString (vars: any) {\n const deps = uniqueArray(getWorkerDeps(vars))\n return deps.map(function (sym) {\n return sym.toString()\n }).join('\\n\\n\\n')\n}\n\nfunction onmessage (e: WorkerEvent) {\n const name = e.data.__name\n const postId = e.data.__postId\n\n /* global self */\n if (name === undefined) {\n console.error('message __name undefined')\n } else if ((self as any).func === undefined) {\n console.error('worker func undefined', name)\n } else {\n const callback = function (aMessage: any, transferList: any[]) {\n aMessage = aMessage || {}\n if (postId !== undefined) aMessage.__postId = postId\n\n try {\n (self as any).postMessage(aMessage, transferList)\n } catch (error) {\n console.error('self.postMessage:', error);\n (self as any).postMessage(aMessage)\n }\n };\n (self as any).func(e, callback)\n }\n}\n\nexport function makeWorkerBlob (func: Function, deps: Function[]) {\n let str = \"'use strict';\\n\\n\" + makeWorkerString(deps)\n str += '\\n\\n\\nself.func = ' + func.toString() + ';'\n str += '\\n\\n\\nself.onmessage = ' + onmessage.toString() + ';'\n // console.log(str);\n return new Blob([ str ], { type: 'application/javascript' })\n}\n","/**\n * @file Globals\n * @author Alexander Rose \n * @private\n */\n\nimport { getBrowser, getQuery, boolean } from './utils'\nimport Registry from './utils/registry'\nimport _ColormakerRegistry from './color/colormaker-registry'\nimport _ParserRegistry from './parser/parser-registry'\nimport _WorkerRegistry from './worker/worker-registry'\nimport { MeasurementRepresentationParameters } from './representation/measurement-representation';\n\n/**\n * The browser name: \"Opera\", \"Chrome\", \"Firefox\", \"Mobile Safari\",\n * \"Internet Explorer\", \"Safari\" or false.\n */\nexport const Browser = getBrowser()\n\n/**\n * Flag indicating support for the 'passive' option for event handler\n */\nexport let SupportsPassiveEventHandler = false\ntry {\n // Test via a getter in the options object to see if the passive property is accessed\n const opts = Object.defineProperty({}, 'passive', {\n get: function () {\n SupportsPassiveEventHandler = true\n }\n })\n window.addEventListener('test', e => {}, opts)\n} catch (e) {}\n\n/**\n * Flag indicating a mobile browser\n */\nexport const Mobile = typeof window !== 'undefined' ? typeof window.orientation !== 'undefined' : false\n\nexport let SupportsReadPixelsFloat = false\nexport function setSupportsReadPixelsFloat (value: boolean) {\n SupportsReadPixelsFloat = value\n}\n\n/**\n * Flag indicating support for the `EXT_frag_depth` WebGL extension\n * (Always present in WebGL2)\n */\nexport let ExtensionFragDepth = false\nexport function setExtensionFragDepth (value: boolean) {\n ExtensionFragDepth = value\n}\n\nexport const Log = {\n log: Function.prototype.bind.call(console.log, console),\n info: Function.prototype.bind.call(console.info, console),\n warn: Function.prototype.bind.call(console.warn, console),\n error: Function.prototype.bind.call(console.error, console),\n time: Function.prototype.bind.call(console.time, console),\n timeEnd: Function.prototype.bind.call(console.timeEnd, console)\n}\n\nexport let MeasurementDefaultParams: Partial = {\n color: 'green',\n labelColor: 0x808080,\n labelAttachment: 'bottom-center',\n labelSize: 0.7,\n labelZOffset: 0.5,\n labelYOffset: 0.1,\n labelBorder: true,\n labelBorderColor: 0xd3d3d3,\n labelBorderWidth: 0.25,\n lineOpacity: 0.8,\n linewidth: 5.0,\n opacity: 0.6,\n\n labelUnit: 'angstrom',\n arcVisible: true,\n planeVisible: false\n}\nexport function setMeasurementDefaultParams (params = {}) {\n Object.assign(MeasurementDefaultParams, params)\n}\n\nexport let Debug = boolean(getQuery('debug'))\nexport function setDebug (value: boolean) {\n Debug = value\n}\n\nexport const WebglErrorMessage = '

Your browser/graphics card does not seem to support WebGL.

Find out how to get it here.

'\n\n/**\n * List of file extensions to be recognized as scripts\n */\nexport const ScriptExtensions = [ 'ngl', 'js' ]\n\nexport const WorkerRegistry = new _WorkerRegistry()\nexport const ColormakerRegistry = new _ColormakerRegistry()\nexport const DatasourceRegistry = new Registry('datasource')\nexport const RepresentationRegistry = new Registry('representatation')\nexport const ParserRegistry = new _ParserRegistry()\nexport const ShaderRegistry = new Registry('shader')\nexport const DecompressorRegistry = new Registry('decompressor')\nexport const ComponentRegistry = new Registry('component')\nexport const BufferRegistry = new Registry('buffer')\nexport const PickerRegistry = new Registry('picker')\n\nexport let ListingDatasource: any\nexport function setListingDatasource (value: any) {\n ListingDatasource = value\n}\n\nexport let TrajectoryDatasource: any // TODO should accept mdsrvDatasource\nexport function setTrajectoryDatasource (value: any) {\n TrajectoryDatasource = value\n}\n","/**\n * @file Worker Registry\n * @author Alexander Rose \n * @private\n */\n\nimport { makeWorkerBlob } from './worker-utils'\n\nclass WorkerRegistry {\n activeWorkerCount = 0\n\n private _funcDict: { [k: string]: Function } = {}\n private _depsDict: { [k: string]: Function[] } = {}\n private _blobDict: { [k: string]: Blob } = {}\n\n add (name: string, func: Function, deps: Function[]) {\n this._funcDict[ name ] = func\n this._depsDict[ name ] = deps\n }\n\n get (name: string) {\n if (!this._blobDict[ name ]) {\n this._blobDict[ name ] = makeWorkerBlob(\n this._funcDict[ name ], this._depsDict[ name ]\n )\n }\n return this._blobDict[ name ]\n }\n}\n\nexport default WorkerRegistry\n","/**\n * @file Parser Registry\n * @author Alexander Rose \n * @private\n */\n\nimport Registry from '../utils/registry'\n\nclass ParserRegistry extends Registry {\n constructor () {\n super('parser')\n }\n\n __hasObjName (key: string, objName: string) {\n const parser = this.get(key)\n return parser && parser.prototype.__objName === objName\n }\n\n isTrajectory (key: string) {\n return this.__hasObjName(key, 'frames')\n }\n\n isStructure (key: string) {\n return this.__hasObjName(key, 'structure')\n }\n\n isVolume (key: string) {\n return this.__hasObjName(key, 'volume')\n }\n\n isSurface (key: string) {\n return this.__hasObjName(key, 'surface')\n }\n\n isBinary (key: string) {\n const parser = this.get(key)\n return parser && parser.prototype.isBinary\n }\n\n isXml (key: string) {\n const parser = this.get(key)\n return parser && parser.prototype.isXml\n }\n\n isJson (key: string) {\n const parser = this.get(key)\n return parser && parser.prototype.isJson\n }\n\n getTrajectoryExtensions () {\n return this.names.filter(name => this.isTrajectory(name))\n }\n\n getStructureExtensions () {\n return this.names.filter(name => this.isStructure(name))\n }\n\n getVolumeExtensions () {\n return this.names.filter(name => this.isVolume(name))\n }\n\n getSurfaceExtensions () {\n return this.names.filter(name => this.isSurface(name))\n }\n}\n\nexport default ParserRegistry\n","/**\n * @file Streamer\n * @author Alexander Rose \n * @private\n */\n\nimport { DecompressorRegistry } from '../globals'\nimport { uint8ToString, defaults } from '../utils'\n\nexport interface StreamerParams {\n compressed?: string|false\n binary?: boolean\n json?: boolean\n xml?: boolean\n}\n\nabstract class Streamer {\n src: any\n data: any\n\n compressed: string|false\n binary: boolean\n json: boolean\n xml: boolean\n\n chunkSize = 1024 * 1024 * 10\n newline = '\\n'\n\n protected __pointer = 0\n protected __partialLine = ''\n\n constructor (src: any, params: StreamerParams = {}) {\n this.compressed = defaults(params.compressed, false)\n this.binary = defaults(params.binary, false)\n this.json = defaults(params.json, false)\n this.xml = defaults(params.xml, false)\n\n this.src = src\n }\n\n isBinary () {\n return this.binary || this.compressed\n }\n\n read () {\n return this._read().then(data => {\n const decompressFn = this.compressed ? DecompressorRegistry.get(this.compressed) : undefined\n\n if (this.compressed && decompressFn) {\n this.data = decompressFn(data)\n } else {\n if ((this.binary || this.compressed) && data instanceof ArrayBuffer) {\n data = new Uint8Array(data)\n }\n this.data = data\n }\n\n return this.data\n })\n }\n\n protected abstract _read (): Promise\n\n protected _chunk (start: number, end: number) {\n end = Math.min(this.data.length, end)\n\n if (start === 0 && this.data.length === end) {\n return this.data\n } else {\n if (this.isBinary()) {\n return this.data.subarray(start, end)\n } else {\n return this.data.substring(start, end)\n }\n }\n }\n\n chunk (start: number) {\n const end = start + this.chunkSize\n\n return this._chunk(start, end)\n }\n\n peekLines (m: number) {\n const data = this.data\n const n = data.length\n\n // FIXME does not work for multi-char newline\n const newline = this.isBinary() ? this.newline.charCodeAt(0) : this.newline\n\n let i\n let count = 0\n for (i = 0; i < n; ++i) {\n if (data[ i ] === newline) ++count\n if (count === m) break\n }\n\n const chunk = this._chunk(0, i + 1)\n const d = this.chunkToLines(chunk, '', i > n)\n\n return d.lines\n }\n\n chunkCount () {\n return Math.floor(this.data.length / this.chunkSize) + 1\n }\n\n asText () {\n return this.isBinary() ? uint8ToString(this.data) : this.data\n }\n\n chunkToLines (chunk: string|Uint8Array, partialLine: string, isLast: boolean) {\n const newline = this.newline\n\n if (!this.isBinary() && chunk.length === this.data.length) {\n return {\n lines: (chunk as string).split(newline),\n partialLine: ''\n }\n }\n\n let lines: string[] = []\n const str = this.isBinary() ? uint8ToString(chunk as Uint8Array) : chunk\n const idx = str.lastIndexOf(newline)\n\n if (idx === -1) {\n partialLine += str\n } else {\n const str2 = partialLine + str.substr(0, idx)\n lines = lines.concat(str2.split(newline))\n\n if (idx === str.length - newline.length) {\n partialLine = ''\n } else {\n partialLine = str.substr(idx + newline.length)\n }\n }\n\n if (isLast && partialLine !== '') {\n lines.push(partialLine)\n }\n\n return {\n lines: lines,\n partialLine: partialLine\n }\n }\n\n nextChunk () {\n const start = this.__pointer\n\n if (start > this.data.length) {\n return undefined\n }\n\n this.__pointer += this.chunkSize\n return this.chunk(start)\n }\n\n nextChunkOfLines () {\n const chunk = this.nextChunk()\n\n if (chunk === undefined) {\n return undefined\n }\n\n const isLast = this.__pointer > this.data.length\n const d = this.chunkToLines(chunk, this.__partialLine, isLast)\n\n this.__partialLine = d.partialLine\n\n return d.lines\n }\n\n eachChunk (callback: (chunk: string|Uint8Array, chunkNo: number, chunkCount: number) => void) {\n const chunkSize = this.chunkSize\n const n = this.data.length\n const chunkCount = this.chunkCount()\n\n for (let i = 0; i < n; i += chunkSize) {\n const chunk = this.chunk(i)\n const chunkNo = Math.round(i / chunkSize)\n\n callback(chunk, chunkNo, chunkCount)\n }\n }\n\n eachChunkOfLines (callback: (chunk: string[], chunkNo: number, chunkCount: number) => void) {\n this.eachChunk((chunk, chunkNo, chunkCount) => {\n const isLast = chunkNo === chunkCount + 1\n const d = this.chunkToLines(chunk, this.__partialLine, isLast)\n\n this.__partialLine = d.partialLine\n\n callback(d.lines, chunkNo, chunkCount)\n })\n }\n\n dispose () {\n delete this.src\n }\n}\n\nexport default Streamer\n","/**\n * @file File Streamer\n * @author Alexander Rose \n * @private\n */\n\nimport Streamer from './streamer'\n\ninterface FileReaderEventTarget extends EventTarget {\n result:string | ArrayBuffer | null\n}\n\ninterface FileReaderEvent extends ProgressEvent {\n target: FileReaderEventTarget | null;\n}\n\nclass FileStreamer extends Streamer {\n _read () {\n return new Promise((resolve, reject) => {\n const file = this.src\n const reader = new FileReader()\n\n reader.onload = (event: FileReaderEvent) => {\n if(event.target) resolve(event.target.result)\n }\n\n // if (typeof this.onprogress === 'function') {\n // reader.onprogress = event => this.onprogress(event)\n // }\n\n reader.onerror = event => reject(event)\n\n if (this.binary || this.compressed) {\n reader.readAsArrayBuffer(file)\n } else {\n reader.readAsText(file)\n }\n })\n }\n}\n\nexport default FileStreamer\n","/**\n * @file Network Streamer\n * @author Alexander Rose \n * @private\n */\n\nimport Streamer from './streamer'\n\nclass NetworkStreamer extends Streamer {\n _read () {\n return new Promise((resolve, reject) => {\n const url = this.src\n const xhr = new XMLHttpRequest()\n\n xhr.open('GET', url, true)\n\n xhr.addEventListener('load', () => {\n if (xhr.status === 200 || xhr.status === 304 ||\n // when requesting from local file system\n // the status in Google Chrome/Chromium is 0\n xhr.status === 0\n ) {\n try {\n resolve(xhr.response)\n } catch (e) {\n reject(e)\n }\n } else {\n reject(xhr.statusText)\n }\n }, false)\n\n // if (typeof this.onprogress === 'function') {\n // xhr.addEventListener('progress', event => this.onprogress(event), false);\n // }\n\n xhr.addEventListener('error', event => reject('network error'), false)\n\n if (this.isBinary()) {\n xhr.responseType = 'arraybuffer'\n } else if (this.json) {\n xhr.responseType = 'json'\n } else if (this.xml) {\n xhr.responseType = 'document'\n } else {\n xhr.responseType = 'text'\n }\n // xhr.crossOrigin = true;\n\n xhr.send()\n })\n }\n}\n\nexport default NetworkStreamer\n","/**\n * @file Loader\n * @author Alexander Rose \n * @private\n */\n\nimport { ParserRegistry } from '../globals'\nimport { createParams } from '../utils'\nimport FileStreamer from '../streamer/file-streamer'\nimport NetworkStreamer from '../streamer/network-streamer'\nimport { LoaderParameters, LoaderInput } from './loader-utils'\n\n/**\n * Loader parameter object.\n * @typedef {Object} LoaderParameters - loader parameters\n * @property {String} ext - file extension, determines file type\n * @property {Boolean} compressed - flag data as compressed\n * @property {Boolean} binary - flag data as binary\n * @property {String} name - set data name\n */\n\n/**\n * Loader base class\n */\nabstract class Loader {\n parameters: LoaderParameters\n streamer: FileStreamer | NetworkStreamer\n\n /**\n * Construct a loader object\n * @param {String|File|Blob} src - data source, string is interpreted as an URL\n * @param {LoaderParameters} params - parameters object\n */\n constructor (src: LoaderInput, params: Partial = {}) {\n this.parameters = createParams(params, {\n ext: '',\n compressed: false,\n binary: ParserRegistry.isBinary(params.ext || ''),\n name: '',\n\n dir: '',\n path: '',\n protocol: ''\n } as LoaderParameters)\n\n const streamerParams = {\n compressed: this.parameters.compressed as string|false,\n binary: this.parameters.binary,\n json: ParserRegistry.isJson(this.parameters.ext),\n xml: ParserRegistry.isXml(this.parameters.ext)\n }\n\n if ((typeof File !== 'undefined' && src instanceof File) ||\n (typeof Blob !== 'undefined' && src instanceof Blob)\n ) {\n this.streamer = new FileStreamer(src, streamerParams)\n } else {\n this.streamer = new NetworkStreamer(src, streamerParams)\n }\n }\n\n /**\n * Load data\n * @abstract\n * @return {Promise} resolves to the loaded data {@link Object}\n */\n abstract load (): Promise\n}\n\nexport default Loader\n","/**\n * @file Parser Loader\n * @author Alexander Rose \n * @private\n */\n\nimport { ParserRegistry } from '../globals'\nimport type { InferBondsOptions } from '../structure/structure-utils'\nimport Loader from './loader'\nimport { LoaderParameters, LoaderInput } from './loader-utils'\n\nexport interface ParserParams {\n voxelSize?: number\n firstModelOnly?: boolean\n asTrajectory?: boolean\n cAlphaOnly?: boolean\n name?: string\n path?: string\n delimiter?: string\n comment?: string\n columnNames?: string\n inferBonds?: InferBondsOptions\n}\n\n/**\n * Parser loader class\n * @extends Loader\n */\nclass ParserLoader extends Loader {\n parserParams: ParserParams\n\n constructor (src: LoaderInput, params: Partial & ParserParams = {}) {\n super(src, params)\n this.parserParams = {\n voxelSize: params.voxelSize,\n firstModelOnly: params.firstModelOnly,\n asTrajectory: params.asTrajectory,\n cAlphaOnly: params.cAlphaOnly,\n delimiter: params.delimiter,\n comment: params.comment,\n columnNames: params.columnNames,\n inferBonds: params.inferBonds,\n name: this.parameters.name,\n path: this.parameters.path\n }\n }\n\n /**\n * Load parsed object\n * @return {Promise} resolves to the loaded & parsed {@link Structure},\n * {@link Volume}, {@link Surface} or data object\n */\n load () {\n var ParserClass = ParserRegistry.get(this.parameters.ext)\n var parser = new ParserClass(this.streamer, this.parserParams)\n\n return parser.parse()\n }\n}\n\nexport default ParserLoader\n","/**\n * @file Script\n * @author Alexander Rose \n * @private\n */\n\nimport { Signal } from 'signals'\n\nimport { Log } from './globals'\nimport Stage from './stage/stage'\n\nexport interface ScriptSignals {\n elementAdded: Signal\n elementRemoved: Signal\n nameChanged: Signal\n}\n\n/**\n * Script class\n */\nclass Script {\n readonly signals: ScriptSignals = {\n elementAdded: new Signal(),\n elementRemoved: new Signal(),\n nameChanged: new Signal()\n }\n\n readonly dir: string\n readonly fn: Function\n\n readonly type = 'Script'\n\n /**\n * Create a script instance\n * @param {String} functionBody - the function source\n * @param {String} name - name of the script\n * @param {String} path - path of the script\n */\n constructor (functionBody: string, readonly name: string, readonly path: string) {\n this.dir = path.substring(0, path.lastIndexOf('/') + 1)\n\n try {\n /* eslint-disable no-new-func */\n this.fn = new Function('stage', '__name', '__path', '__dir', functionBody)\n } catch (e) {\n Log.error('Script compilation failed', e)\n this.fn = function () {}\n }\n }\n\n /**\n * Execute the script\n * @param {Stage} stage - the stage context\n * @return {Promise} - resolve when script finished running\n */\n run (stage: Stage): Promise {\n return new Promise((resolve, reject) => {\n try {\n this.fn.apply(null, [ stage, this.name, this.path, this.dir ])\n resolve()\n } catch (e) {\n Log.error('Script.fn', e)\n reject(e)\n }\n })\n }\n}\n\nexport default Script\n","/**\n * @file Script Loader\n * @author Alexander Rose \n * @private\n */\n\nimport Loader from './loader'\nimport Script from '../script'\n\n/**\n * Script loader class\n * @extends Loader\n */\nclass ScriptLoader extends Loader {\n /**\n * Load script\n * @return {Promise} resolves to the loaded {@link Script}\n */\n load () {\n return this.streamer.read().then(() => {\n return new Script(\n this.streamer.asText(), this.parameters.name, this.parameters.path\n )\n })\n }\n}\n\nexport default ScriptLoader\n","/**\n * @file Loader Utils\n * @author Alexander Rose \n * @private\n */\n\nimport {\n DatasourceRegistry, DecompressorRegistry, ParserRegistry, ScriptExtensions\n} from '../globals'\nimport ParserLoader, { ParserParams } from './parser-loader'\nimport ScriptLoader from './script-loader'\n\nexport interface LoaderParameters {\n ext: string // file extension, determines file type\n compressed: string|false // flag data as compressed\n binary: boolean // flag data as binary\n name: string // set data name\n\n dir: string\n path: string\n protocol: string\n}\n\nexport type LoaderInput = File|Blob|string\n\nexport function getFileInfo (file: LoaderInput) {\n const compressedExtList = DecompressorRegistry.names\n\n let path: string\n let compressed: string|false\n let protocol = ''\n\n if (file instanceof File) {\n path = file.name\n } else if (file instanceof Blob) {\n path = ''\n } else {\n path = file\n }\n const queryIndex = path.lastIndexOf('?')\n const query = queryIndex !== -1 ? path.substring(queryIndex) : ''\n path = path.substring(0, queryIndex === -1 ? path.length : queryIndex)\n\n const name = path.replace(/^.*[\\\\/]/, '')\n let base = name.substring(0, name.lastIndexOf('.'))\n\n const nameSplit = name.split('.')\n let ext = nameSplit.length > 1 ? (nameSplit.pop() || '').toLowerCase() : ''\n\n const protocolMatch = path.match(/^(.+):\\/\\/(.+)$/)\n if (protocolMatch) {\n protocol = protocolMatch[ 1 ].toLowerCase()\n path = protocolMatch[ 2 ] || ''\n }\n\n const dir = path.substring(0, path.lastIndexOf('/') + 1)\n\n if (compressedExtList.includes(ext)) {\n compressed = ext\n const n = path.length - ext.length - 1\n ext = (path.substr(0, n).split('.').pop() || '').toLowerCase()\n const m = base.length - ext.length - 1\n base = base.substr(0, m)\n } else {\n compressed = false\n }\n\n return { path, name, ext, base, dir, compressed, protocol, query, 'src': file }\n}\n\nexport function getDataInfo (src: LoaderInput) {\n let info = getFileInfo(src)\n const datasource = DatasourceRegistry.get(info.protocol)\n if (datasource) {\n info = getFileInfo(datasource.getUrl(info.src))\n if (!info.ext && datasource.getExt) {\n info.ext = datasource.getExt(src)\n }\n }\n return info\n}\n\n/**\n * Load a file\n *\n * @example\n * // load from URL\n * NGL.autoLoad( \"http://files.rcsb.org/download/5IOS.cif\" );\n *\n * @example\n * // load binary data in CCP4 format via a Blob\n * var binaryBlob = new Blob( [ ccp4Data ], { type: 'application/octet-binary'} );\n * NGL.autoLoad( binaryBlob, { ext: \"ccp4\" } );\n *\n * @example\n * // load string data in PDB format via a Blob\n * var stringBlob = new Blob( [ pdbData ], { type: 'text/plain'} );\n * NGL.autoLoad( stringBlob, { ext: \"pdb\" } );\n *\n * @example\n * // load a File object\n * NGL.autoLoad( file );\n *\n * @param {String|File|Blob} file - either a URL or an object containing the file data\n * @param {LoaderParameters} params - loading parameters\n * @return {Promise} Promise resolves to the loaded data\n */\nexport function autoLoad (file: LoaderInput, params: Partial = {}) {\n const p = Object.assign(getDataInfo(file), params)\n\n let loader\n if (ParserRegistry.names.includes(p.ext)) {\n loader = new ParserLoader(p.src, p)\n } else if (ScriptExtensions.includes(p.ext)) {\n loader = new ScriptLoader(p.src, p)\n }\n\n if (loader) {\n return loader.load()\n } else {\n return Promise.reject(new Error(`autoLoad: ext '${p.ext}' unknown`))\n }\n}\n","/* global window, exports, define */\n\n!function() {\n 'use strict'\n\n var re = {\n not_string: /[^s]/,\n not_bool: /[^t]/,\n not_type: /[^T]/,\n not_primitive: /[^v]/,\n number: /[diefg]/,\n numeric_arg: /[bcdiefguxX]/,\n json: /[j]/,\n not_json: /[^j]/,\n text: /^[^\\x25]+/,\n modulo: /^\\x25{2}/,\n placeholder: /^\\x25(?:([1-9]\\d*)\\$|\\(([^)]+)\\))?(\\+)?(0|'[^$])?(-)?(\\d+)?(?:\\.(\\d+))?([b-gijostTuvxX])/,\n key: /^([a-z_][a-z_\\d]*)/i,\n key_access: /^\\.([a-z_][a-z_\\d]*)/i,\n index_access: /^\\[(\\d+)\\]/,\n sign: /^[+-]/\n }\n\n function sprintf(key) {\n // `arguments` is not an array, but should be fine for this call\n return sprintf_format(sprintf_parse(key), arguments)\n }\n\n function vsprintf(fmt, argv) {\n return sprintf.apply(null, [fmt].concat(argv || []))\n }\n\n function sprintf_format(parse_tree, argv) {\n var cursor = 1, tree_length = parse_tree.length, arg, output = '', i, k, ph, pad, pad_character, pad_length, is_positive, sign\n for (i = 0; i < tree_length; i++) {\n if (typeof parse_tree[i] === 'string') {\n output += parse_tree[i]\n }\n else if (typeof parse_tree[i] === 'object') {\n ph = parse_tree[i] // convenience purposes only\n if (ph.keys) { // keyword argument\n arg = argv[cursor]\n for (k = 0; k < ph.keys.length; k++) {\n if (arg == undefined) {\n throw new Error(sprintf('[sprintf] Cannot access property \"%s\" of undefined value \"%s\"', ph.keys[k], ph.keys[k-1]))\n }\n arg = arg[ph.keys[k]]\n }\n }\n else if (ph.param_no) { // positional argument (explicit)\n arg = argv[ph.param_no]\n }\n else { // positional argument (implicit)\n arg = argv[cursor++]\n }\n\n if (re.not_type.test(ph.type) && re.not_primitive.test(ph.type) && arg instanceof Function) {\n arg = arg()\n }\n\n if (re.numeric_arg.test(ph.type) && (typeof arg !== 'number' && isNaN(arg))) {\n throw new TypeError(sprintf('[sprintf] expecting number but found %T', arg))\n }\n\n if (re.number.test(ph.type)) {\n is_positive = arg >= 0\n }\n\n switch (ph.type) {\n case 'b':\n arg = parseInt(arg, 10).toString(2)\n break\n case 'c':\n arg = String.fromCharCode(parseInt(arg, 10))\n break\n case 'd':\n case 'i':\n arg = parseInt(arg, 10)\n break\n case 'j':\n arg = JSON.stringify(arg, null, ph.width ? parseInt(ph.width) : 0)\n break\n case 'e':\n arg = ph.precision ? parseFloat(arg).toExponential(ph.precision) : parseFloat(arg).toExponential()\n break\n case 'f':\n arg = ph.precision ? parseFloat(arg).toFixed(ph.precision) : parseFloat(arg)\n break\n case 'g':\n arg = ph.precision ? String(Number(arg.toPrecision(ph.precision))) : parseFloat(arg)\n break\n case 'o':\n arg = (parseInt(arg, 10) >>> 0).toString(8)\n break\n case 's':\n arg = String(arg)\n arg = (ph.precision ? arg.substring(0, ph.precision) : arg)\n break\n case 't':\n arg = String(!!arg)\n arg = (ph.precision ? arg.substring(0, ph.precision) : arg)\n break\n case 'T':\n arg = Object.prototype.toString.call(arg).slice(8, -1).toLowerCase()\n arg = (ph.precision ? arg.substring(0, ph.precision) : arg)\n break\n case 'u':\n arg = parseInt(arg, 10) >>> 0\n break\n case 'v':\n arg = arg.valueOf()\n arg = (ph.precision ? arg.substring(0, ph.precision) : arg)\n break\n case 'x':\n arg = (parseInt(arg, 10) >>> 0).toString(16)\n break\n case 'X':\n arg = (parseInt(arg, 10) >>> 0).toString(16).toUpperCase()\n break\n }\n if (re.json.test(ph.type)) {\n output += arg\n }\n else {\n if (re.number.test(ph.type) && (!is_positive || ph.sign)) {\n sign = is_positive ? '+' : '-'\n arg = arg.toString().replace(re.sign, '')\n }\n else {\n sign = ''\n }\n pad_character = ph.pad_char ? ph.pad_char === '0' ? '0' : ph.pad_char.charAt(1) : ' '\n pad_length = ph.width - (sign + arg).length\n pad = ph.width ? (pad_length > 0 ? pad_character.repeat(pad_length) : '') : ''\n output += ph.align ? sign + arg + pad : (pad_character === '0' ? sign + pad + arg : pad + sign + arg)\n }\n }\n }\n return output\n }\n\n var sprintf_cache = Object.create(null)\n\n function sprintf_parse(fmt) {\n if (sprintf_cache[fmt]) {\n return sprintf_cache[fmt]\n }\n\n var _fmt = fmt, match, parse_tree = [], arg_names = 0\n while (_fmt) {\n if ((match = re.text.exec(_fmt)) !== null) {\n parse_tree.push(match[0])\n }\n else if ((match = re.modulo.exec(_fmt)) !== null) {\n parse_tree.push('%')\n }\n else if ((match = re.placeholder.exec(_fmt)) !== null) {\n if (match[2]) {\n arg_names |= 1\n var field_list = [], replacement_field = match[2], field_match = []\n if ((field_match = re.key.exec(replacement_field)) !== null) {\n field_list.push(field_match[1])\n while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {\n if ((field_match = re.key_access.exec(replacement_field)) !== null) {\n field_list.push(field_match[1])\n }\n else if ((field_match = re.index_access.exec(replacement_field)) !== null) {\n field_list.push(field_match[1])\n }\n else {\n throw new SyntaxError('[sprintf] failed to parse named argument key')\n }\n }\n }\n else {\n throw new SyntaxError('[sprintf] failed to parse named argument key')\n }\n match[2] = field_list\n }\n else {\n arg_names |= 2\n }\n if (arg_names === 3) {\n throw new Error('[sprintf] mixing positional and named placeholders is not (yet) supported')\n }\n\n parse_tree.push(\n {\n placeholder: match[0],\n param_no: match[1],\n keys: match[2],\n sign: match[3],\n pad_char: match[4],\n align: match[5],\n width: match[6],\n precision: match[7],\n type: match[8]\n }\n )\n }\n else {\n throw new SyntaxError('[sprintf] unexpected placeholder')\n }\n _fmt = _fmt.substring(match[0].length)\n }\n return sprintf_cache[fmt] = parse_tree\n }\n\n /**\n * export to either browser or node.js\n */\n /* eslint-disable quote-props */\n if (typeof exports !== 'undefined') {\n exports['sprintf'] = sprintf\n exports['vsprintf'] = vsprintf\n }\n if (typeof window !== 'undefined') {\n window['sprintf'] = sprintf\n window['vsprintf'] = vsprintf\n\n if (typeof define === 'function' && define['amd']) {\n define(function() {\n return {\n 'sprintf': sprintf,\n 'vsprintf': vsprintf\n }\n })\n }\n }\n /* eslint-enable quote-props */\n}(); // eslint-disable-line\n","/**\n * @file Writer\n * @author Alexander Rose \n * @private\n */\n\nimport { defaults, download } from '../utils'\n\n/**\n * Base class for writers\n * @interface\n */\nabstract class Writer {\n readonly mimeType: string\n readonly defaultName: string\n readonly defaultExt: string\n\n /**\n * @abstract\n * @return {Anything} the data to be written\n */\n abstract getData (): any\n\n /**\n * Get a blob with the written data\n * @return {Blob} the blob\n */\n getBlob () {\n return new Blob([ this.getData() ], { type: this.mimeType })\n }\n\n /**\n * Trigger a download of the\n * @param {[type]} name [description]\n * @param {[type]} ext [description]\n * @return {[type]} [description]\n */\n download (name?: string, ext?: string) {\n name = defaults(name, this.defaultName)\n ext = defaults(ext, this.defaultExt)\n\n download(this.getBlob(), `${name}.${ext}`)\n }\n}\n\nexport default Writer","/**\n * @file IO Buffer\n * @author Alexander Rose \n * @private\n *\n * Adapted and converted to TypeScript from https://github.com/image-js/iobuffer\n * MIT License, Copyright (c) 2015 Michaël Zasso\n */\n\nimport { TypedArray } from '../types'\n\nconst defaultByteLength = 1024 * 8\nconst charArray: string[] = []\n\nexport interface IOBufferParameters {\n offset?: number // Ignore the first n bytes of the ArrayBuffer\n}\n\n/**\n * Class for writing and reading binary data\n */\nclass IOBuffer {\n private _lastWrittenByte: number\n private _mark = 0\n private _marks: number[] = []\n private _data: DataView\n\n offset = 0 // The current offset of the buffer's pointer\n littleEndian = true\n buffer: ArrayBuffer // Reference to the internal ArrayBuffer object\n length: number // Byte length of the internal ArrayBuffer\n byteLength: number // Byte length of the internal ArrayBuffer\n byteOffset: number // Byte offset of the internal ArrayBuffer\n\n /**\n * If it's a number, it will initialize the buffer with the number as\n * the buffer's length. If it's undefined, it will initialize the buffer\n * with a default length of 8 Kb. If its an ArrayBuffer, a TypedArray,\n * it will create a view over the underlying ArrayBuffer.\n */\n constructor (data: number|ArrayBuffer|TypedArray, params: IOBufferParameters = {}) {\n let dataIsGiven = false\n if (data === undefined) {\n data = defaultByteLength\n }\n if (typeof data === 'number') {\n data = new ArrayBuffer(data)\n } else {\n dataIsGiven = true\n }\n\n const offset = params.offset ? params.offset >>> 0 : 0\n let byteLength = data.byteLength - offset\n let dvOffset = offset\n if (!(data instanceof ArrayBuffer)) {\n if (data.byteLength !== data.buffer.byteLength) {\n dvOffset = data.byteOffset + offset\n }\n data = data.buffer\n }\n if (dataIsGiven) {\n this._lastWrittenByte = byteLength\n } else {\n this._lastWrittenByte = 0\n }\n\n this.buffer = data\n this.length = byteLength\n this.byteLength = byteLength\n this.byteOffset = dvOffset\n\n this._data = new DataView(this.buffer, dvOffset, byteLength)\n }\n\n /**\n * Checks if the memory allocated to the buffer is sufficient to store more bytes after the offset\n * @param {number} [byteLength=1] The needed memory in bytes\n * @return {boolean} Returns true if there is sufficient space and false otherwise\n */\n available (byteLength: number) {\n if (byteLength === undefined) byteLength = 1\n return (this.offset + byteLength) <= this.length\n }\n\n /**\n * Check if little-endian mode is used for reading and writing multi-byte values\n * @return {boolean} Returns true if little-endian mode is used, false otherwise\n */\n isLittleEndian () {\n return this.littleEndian\n }\n\n /**\n * Set little-endian mode for reading and writing multi-byte values\n * @return {IOBuffer}\n */\n setLittleEndian () {\n this.littleEndian = true\n return this\n }\n\n /**\n * Check if big-endian mode is used for reading and writing multi-byte values\n * @return {boolean} Returns true if big-endian mode is used, false otherwise\n */\n isBigEndian () {\n return !this.littleEndian\n }\n\n /**\n * Switches to big-endian mode for reading and writing multi-byte values\n * @return {IOBuffer}\n */\n setBigEndian () {\n this.littleEndian = false\n return this\n }\n\n /**\n * Move the pointer n bytes forward\n * @param {number} n\n * @return {IOBuffer}\n */\n skip (n: number) {\n if (n === undefined) n = 1\n this.offset += n\n return this\n }\n\n /**\n * Move the pointer to the given offset\n * @param {number} offset\n * @return {IOBuffer}\n */\n seek (offset: number) {\n this.offset = offset\n return this\n }\n\n /**\n * Store the current pointer offset.\n * @see {@link IOBuffer#reset}\n * @return {IOBuffer}\n */\n mark () {\n this._mark = this.offset\n return this\n }\n\n /**\n * Move the pointer back to the last pointer offset set by mark\n * @see {@link IOBuffer#mark}\n * @return {IOBuffer}\n */\n reset () {\n this.offset = this._mark\n return this\n }\n\n /**\n * Push the current pointer offset to the mark stack\n * @see {@link IOBuffer#popMark}\n * @return {IOBuffer}\n */\n pushMark () {\n this._marks.push(this.offset)\n return this\n }\n\n /**\n * Pop the last pointer offset from the mark stack, and set the current pointer offset to the popped value\n * @see {@link IOBuffer#pushMark}\n * @return {IOBuffer}\n */\n popMark () {\n const offset = this._marks.pop()\n if (offset === undefined) throw new Error('Mark stack empty')\n this.seek(offset)\n return this\n }\n\n /**\n * Move the pointer offset back to 0\n * @return {IOBuffer}\n */\n rewind () {\n this.offset = 0\n return this\n }\n\n /**\n * Make sure the buffer has sufficient memory to write a given byteLength at the current pointer offset\n * If the buffer's memory is insufficient, this method will create a new buffer (a copy) with a length\n * that is twice (byteLength + current offset)\n * @param {number} [byteLength = 1]\n * @return {IOBuffer}\n */\n ensureAvailable (byteLength: number) {\n if (byteLength === undefined) byteLength = 1\n if (!this.available(byteLength)) {\n const lengthNeeded = this.offset + byteLength\n const newLength = lengthNeeded * 2\n const newArray = new Uint8Array(newLength)\n newArray.set(new Uint8Array(this.buffer))\n this.buffer = newArray.buffer\n this.length = this.byteLength = newLength\n this._data = new DataView(this.buffer)\n }\n return this\n }\n\n /**\n * Read a byte and return false if the byte's value is 0, or true otherwise\n * Moves pointer forward\n * @return {boolean}\n */\n readBoolean () {\n return this.readUint8() !== 0\n }\n\n /**\n * Read a signed 8-bit integer and move pointer forward\n * @return {number}\n */\n readInt8 () {\n return this._data.getInt8(this.offset++)\n }\n\n /**\n * Read an unsigned 8-bit integer and move pointer forward\n * @return {number}\n */\n readUint8 () {\n return this._data.getUint8(this.offset++)\n }\n\n /**\n * Alias for {@link IOBuffer#readUint8}\n * @return {number}\n */\n readByte () {\n return this.readUint8()\n }\n\n /**\n * Read n bytes and move pointer forward.\n * @param {number} n\n * @return {Uint8Array}\n */\n readBytes (n: number) {\n if (n === undefined) n = 1\n var bytes = new Uint8Array(n)\n for (var i = 0; i < n; i++) {\n bytes[i] = this.readByte()\n }\n return bytes\n }\n\n /**\n * Read a 16-bit signed integer and move pointer forward\n * @return {number}\n */\n readInt16 () {\n var value = this._data.getInt16(this.offset, this.littleEndian)\n this.offset += 2\n return value\n }\n\n /**\n * Read a 16-bit unsigned integer and move pointer forward\n * @return {number}\n */\n readUint16 () {\n var value = this._data.getUint16(this.offset, this.littleEndian)\n this.offset += 2\n return value\n }\n\n /**\n * Read a 32-bit signed integer and move pointer forward\n * @return {number}\n */\n readInt32 () {\n var value = this._data.getInt32(this.offset, this.littleEndian)\n this.offset += 4\n return value\n }\n\n /**\n * Read a 32-bit unsigned integer and move pointer forward\n * @return {number}\n */\n readUint32 () {\n var value = this._data.getUint32(this.offset, this.littleEndian)\n this.offset += 4\n return value\n }\n\n /**\n * Read a 32-bit floating number and move pointer forward\n * @return {number}\n */\n readFloat32 () {\n var value = this._data.getFloat32(this.offset, this.littleEndian)\n this.offset += 4\n return value\n }\n\n /**\n * Read a 64-bit floating number and move pointer forward\n * @return {number}\n */\n readFloat64 () {\n var value = this._data.getFloat64(this.offset, this.littleEndian)\n this.offset += 8\n return value\n }\n\n /**\n * Read 1-byte ascii character and move pointer forward\n * @return {string}\n */\n readChar () {\n return String.fromCharCode(this.readInt8())\n }\n\n /**\n * Read n 1-byte ascii characters and move pointer forward\n * @param {number} n\n * @return {string}\n */\n readChars (n = 1) {\n charArray.length = n\n for (var i = 0; i < n; i++) {\n charArray[i] = this.readChar()\n }\n return charArray.join('')\n }\n\n /**\n * Write 0xff if the passed value is truthy, 0x00 otherwise\n * @param {any} value\n * @return {IOBuffer}\n */\n writeBoolean (value = false) {\n this.writeUint8(value ? 0xff : 0x00)\n return this\n }\n\n /**\n * Write value as an 8-bit signed integer\n * @param {number} value\n * @return {IOBuffer}\n */\n writeInt8 (value: number) {\n this.ensureAvailable(1)\n this._data.setInt8(this.offset++, value)\n this._updateLastWrittenByte()\n return this\n }\n\n /**\n * Write value as a 8-bit unsigned integer\n * @param {number} value\n * @return {IOBuffer}\n */\n writeUint8 (value: number) {\n this.ensureAvailable(1)\n this._data.setUint8(this.offset++, value)\n this._updateLastWrittenByte()\n return this\n }\n\n /**\n * An alias for {@link IOBuffer#writeUint8}\n * @param {number} value\n * @return {IOBuffer}\n */\n writeByte (value: number) {\n return this.writeUint8(value)\n }\n\n /**\n * Write bytes\n * @param {Array|Uint8Array} bytes\n * @return {IOBuffer}\n */\n writeBytes (bytes: number[]|Uint8Array) {\n this.ensureAvailable(bytes.length)\n for (var i = 0; i < bytes.length; i++) {\n this._data.setUint8(this.offset++, bytes[i])\n }\n this._updateLastWrittenByte()\n return this\n }\n\n /**\n * Write value as an 16-bit signed integer\n * @param {number} value\n * @return {IOBuffer}\n */\n writeInt16 (value: number) {\n this.ensureAvailable(2)\n this._data.setInt16(this.offset, value, this.littleEndian)\n this.offset += 2\n this._updateLastWrittenByte()\n return this\n }\n\n /**\n * Write value as a 16-bit unsigned integer\n * @param {number} value\n * @return {IOBuffer}\n */\n writeUint16 (value: number) {\n this.ensureAvailable(2)\n this._data.setUint16(this.offset, value, this.littleEndian)\n this.offset += 2\n this._updateLastWrittenByte()\n return this\n }\n\n /**\n * Write a 32-bit signed integer at the current pointer offset\n * @param {number} value\n * @return {IOBuffer}\n */\n writeInt32 (value: number) {\n this.ensureAvailable(4)\n this._data.setInt32(this.offset, value, this.littleEndian)\n this.offset += 4\n this._updateLastWrittenByte()\n return this\n }\n\n /**\n * Write a 32-bit unsigned integer at the current pointer offset\n * @param {number} value - The value to set\n * @return {IOBuffer}\n */\n writeUint32 (value: number) {\n this.ensureAvailable(4)\n this._data.setUint32(this.offset, value, this.littleEndian)\n this.offset += 4\n this._updateLastWrittenByte()\n return this\n }\n\n /**\n * Write a 32-bit floating number at the current pointer offset\n * @param {number} value - The value to set\n * @return {IOBuffer}\n */\n writeFloat32 (value: number) {\n this.ensureAvailable(4)\n this._data.setFloat32(this.offset, value, this.littleEndian)\n this.offset += 4\n this._updateLastWrittenByte()\n return this\n }\n\n /**\n * Write a 64-bit floating number at the current pointer offset\n * @param {number} value\n * @return {IOBuffer}\n */\n writeFloat64 (value: number) {\n this.ensureAvailable(8)\n this._data.setFloat64(this.offset, value, this.littleEndian)\n this.offset += 8\n this._updateLastWrittenByte()\n return this\n }\n\n /**\n * Write the charCode of the passed string's first character to the current pointer offset\n * @param {string} str - The character to set\n * @return {IOBuffer}\n */\n writeChar (str: string) {\n return this.writeUint8(str.charCodeAt(0))\n }\n\n /**\n * Write the charCodes of the passed string's characters to the current pointer offset\n * @param {string} str\n * @return {IOBuffer}\n */\n writeChars (str: string) {\n for (var i = 0; i < str.length; i++) {\n this.writeUint8(str.charCodeAt(i))\n }\n return this\n }\n\n /**\n * Export a Uint8Array view of the internal buffer.\n * The view starts at the byte offset and its length\n * is calculated to stop at the last written byte or the original length.\n * @return {Uint8Array}\n */\n toArray () {\n return new Uint8Array(this.buffer, this.byteOffset, this._lastWrittenByte)\n }\n\n /**\n * Update the last written byte offset\n * @private\n */\n _updateLastWrittenByte () {\n if (this.offset > this._lastWrittenByte) {\n this._lastWrittenByte = this.offset\n }\n }\n}\n\nexport default IOBuffer\n","/**\n * @file Counter\n * @author Alexander Rose \n * @private\n */\n\nimport { Log } from '../globals'\n\nimport * as signalsWrapper from 'signals'\n\n/**\n * {@link Signal}, dispatched when the `count` changes\n * @example\n * counter.signals.countChanged.add( function( delta ){ ... } );\n * @event Counter#countChanged\n * @type {Integer}\n */\n\nexport interface CounterSignals {\n countChanged: signalsWrapper.Signal\n}\n\n/**\n * Counter class for keeping track of counts\n */\nclass Counter {\n count = 0\n\n signals: CounterSignals = {\n countChanged: new signalsWrapper.Signal()\n }\n\n /**\n * Set the `count` to zero\n * @return {undefined}\n */\n clear () {\n this.change(-this.count)\n }\n\n /**\n * Change the `count`\n * @fires Counter#countChanged\n * @param {Integer} delta - count change\n * @return {undefined}\n */\n change (delta: number) {\n this.count += delta\n this.signals.countChanged.dispatch(delta, this.count)\n\n if (this.count < 0) {\n Log.warn('Counter.count below zero', this.count)\n }\n }\n\n /**\n * Increments the `count` by one.\n * @return {undefined}\n */\n increment () {\n this.change(1)\n }\n\n /**\n * Decrements the `count` by one.\n * @return {undefined}\n */\n decrement () {\n this.change(-1)\n }\n\n /**\n * Listen to another counter object and change this `count` by the\n * same amount\n * @param {Counter} counter - the counter object to listen to\n * @return {undefined}\n */\n listen (counter: Counter) {\n this.change(counter.count)\n counter.signals.countChanged.add(this.change, this)\n }\n\n /**\n * Stop listening to the other counter object\n * @param {Counter} counter - the counter object to stop listening to\n * @return {undefined}\n */\n unlisten (counter: Counter) {\n const countChanged = counter.signals.countChanged\n if (countChanged.has(this.change, this)) {\n countChanged.remove(this.change, this)\n }\n }\n\n /**\n * Invole the callback function once, when the `count` becomes zero\n * @param {Function} callback - the callback function\n * @param {Object} context - the context for the callback function\n * @return {undefined}\n */\n onZeroOnce (callback: () => void, context?: any) {\n if (this.count === 0) {\n callback.call(context)\n } else {\n const fn = () => {\n if (this.count === 0) {\n this.signals.countChanged.remove(fn, this)\n callback.call(context)\n }\n }\n this.signals.countChanged.add(fn, this)\n }\n }\n\n dispose () {\n this.clear()\n this.signals.countChanged.dispose()\n }\n}\n\nexport default Counter\n","/**\n * @file Stats\n * @author Alexander Rose \n * @private\n */\n\nimport * as signalsWrapper from 'signals'\n\nexport default class Stats {\n signals = {\n updated: new signalsWrapper.Signal()\n }\n\n maxDuration = -Infinity\n minDuration = Infinity\n avgDuration = 14\n lastDuration = Infinity\n\n prevFpsTime = 0\n lastFps = Infinity\n lastFrames = 1\n frames = 0\n count = 0\n\n startTime: number\n currentTime: number\n\n constructor () {\n this.begin()\n }\n\n update () {\n this.startTime = this.end()\n this.currentTime = this.startTime\n this.signals.updated.dispatch()\n }\n\n begin () {\n this.startTime = window.performance.now()\n this.lastFrames = this.frames\n }\n\n end () {\n const time = window.performance.now()\n\n this.count += 1\n this.frames += 1\n\n this.lastDuration = time - this.startTime\n this.minDuration = Math.min(this.minDuration, this.lastDuration)\n this.maxDuration = Math.max(this.maxDuration, this.lastDuration)\n this.avgDuration -= this.avgDuration / 30\n this.avgDuration += this.lastDuration / 30\n\n if (time > this.prevFpsTime + 1000) {\n this.lastFps = this.frames\n this.prevFpsTime = time\n this.frames = 0\n }\n\n return time\n }\n}","/**\n * @file Shader Utils\n * @author Alexander Rose \n * @private\n */\n\nimport { ShaderChunk } from 'three'\n\nimport './chunk/fog_fragment.glsl'\nimport './chunk/interior_fragment.glsl'\nimport './chunk/matrix_scale.glsl'\nimport './chunk/nearclip_vertex.glsl'\nimport './chunk/nearclip_fragment.glsl'\nimport './chunk/opaque_back_fragment.glsl'\nimport './chunk/radiusclip_vertex.glsl'\nimport './chunk/radiusclip_fragment.glsl'\nimport './chunk/unpack_color.glsl'\n\nimport { ShaderRegistry } from '../globals'\n\nexport type ShaderDefine = (\n 'NEAR_CLIP'|'RADIUS_CLIP'|'PICKING'|'NOLIGHT'|'FLAT_SHADED'|'OPAQUE_BACK'|\n 'DIFFUSE_INTERIOR'|'USE_INTERIOR_COLOR'|\n 'USE_SIZEATTENUATION'|'USE_MAP'|'USE_ALPHATEST'|'SDF'|'FIXED_SIZE'|\n 'CUBIC_INTERPOLATION'|'BSPLINE_FILTER'|'CATMULROM_FILTER'|'MITCHELL_FILTER'\n)\nexport type ShaderDefines = {\n [k in ShaderDefine]?: number|string\n}\n\nfunction getDefines (defines: ShaderDefines) {\n if (defines === undefined) return ''\n\n const lines = []\n\n for (const name in defines) {\n const value = defines[ name as keyof ShaderDefines ]\n\n if (!value) continue\n\n lines.push(`#define ${name} ${value}`)\n }\n\n return lines.join('\\n') + '\\n'\n}\n\nconst reInclude = /^(?!\\/\\/)\\s*#include\\s+(\\S+)/gmi\nconst shaderCache: { [k: string]: string } = {}\n\nexport function getShader (name: string, defines: ShaderDefines = {}) {\n let hash = name + '|'\n for (const key in defines) {\n hash += key + ':' + defines[ key as keyof ShaderDefines ]\n }\n\n if (!shaderCache[ hash ]) {\n const definesText = getDefines(defines)\n\n let shaderText = ShaderRegistry.get(`shader/${name}`) as string\n if (!shaderText) {\n throw new Error(`empty shader, '${name}'`)\n }\n shaderText = shaderText.replace(reInclude, function (match, p1: keyof typeof ShaderChunk) {\n const path = `shader/chunk/${p1}.glsl`\n const chunk = ShaderRegistry.get(path) || ShaderChunk[ p1 ]\n if (!chunk) {\n throw new Error(`empty chunk, '${p1}'`)\n }\n return chunk\n })\n\n shaderCache[ hash ] = definesText + shaderText\n }\n\n return shaderCache[ hash ]\n}\n","/**\n * @file Viewer Constants\n * @author Alexander Rose \n * @private\n */\n\nimport { Debug } from '../globals'\n\nif (typeof WebGLRenderingContext !== 'undefined') {\n const wrcp = WebGLRenderingContext.prototype\n\n // wrap WebGL debug function used by three.js and\n // ignore calls to them when the debug flag is not set\n\n const _getShaderParameter = wrcp.getShaderParameter\n wrcp.getShaderParameter = function getShaderParameter (this: WebGLRenderingContext) {\n if (Debug) {\n return _getShaderParameter.apply(this, arguments)\n } else {\n return true\n }\n }\n\n const _getShaderInfoLog = wrcp.getShaderInfoLog\n wrcp.getShaderInfoLog = function getShaderInfoLog (this: WebGLRenderingContext) {\n if (Debug) {\n return _getShaderInfoLog.apply(this, arguments)\n } else {\n return ''\n }\n }\n\n const _getProgramParameter = wrcp.getProgramParameter\n wrcp.getProgramParameter = function getProgramParameter (this: WebGLRenderingContext, program, pname) {\n if (Debug || pname !== wrcp.LINK_STATUS) {\n return _getProgramParameter.apply(this, arguments)\n } else {\n return true\n }\n }\n\n const _getProgramInfoLog = wrcp.getProgramInfoLog\n wrcp.getProgramInfoLog = function getProgramInfoLog (this: WebGLRenderingContext) {\n if (Debug) {\n return _getProgramInfoLog.apply(this, arguments)\n } else {\n return ''\n }\n }\n}\n\nexport const JitterVectors = [\n [\n [ 0, 0 ]\n ],\n [\n [ 4, 4 ], [ -4, -4 ]\n ],\n [\n [ -2, -6 ], [ 6, -2 ], [ -6, 2 ], [ 2, 6 ]\n ],\n [\n [ 1, -3 ], [ -1, 3 ], [ 5, 1 ], [ -3, -5 ],\n [ -5, 5 ], [ -7, -1 ], [ 3, 7 ], [ 7, -7 ]\n ],\n [\n [ 1, 1 ], [ -1, -3 ], [ -3, 2 ], [ 4, -1 ],\n [ -5, -2 ], [ 2, 5 ], [ 5, 3 ], [ 3, -5 ],\n [ -2, 6 ], [ 0, -7 ], [ -4, -6 ], [ -6, 4 ],\n [ -8, 0 ], [ 7, -4 ], [ 6, 7 ], [ -7, -8 ]\n ],\n [\n [ -4, -7 ], [ -7, -5 ], [ -3, -5 ], [ -5, -4 ],\n [ -1, -4 ], [ -2, -2 ], [ -6, -1 ], [ -4, 0 ],\n [ -7, 1 ], [ -1, 2 ], [ -6, 3 ], [ -3, 3 ],\n [ -7, 6 ], [ -3, 6 ], [ -5, 7 ], [ -1, 7 ],\n [ 5, -7 ], [ 1, -6 ], [ 6, -5 ], [ 4, -4 ],\n [ 2, -3 ], [ 7, -2 ], [ 1, -1 ], [ 4, -1 ],\n [ 2, 1 ], [ 6, 2 ], [ 0, 4 ], [ 4, 4 ],\n [ 2, 5 ], [ 7, 5 ], [ 5, 6 ], [ 3, 7 ]\n ]\n]\n\nJitterVectors.forEach(offsetList => {\n offsetList.forEach(offset => {\n // 0.0625 = 1 / 16\n offset[ 0 ] *= 0.0625\n offset[ 1 ] *= 0.0625\n })\n})\n","/**\n * @file Tiled Renderer\n * @author Alexander Rose \n * @private\n */\n\nimport { defaults } from '../utils'\nimport { Camera, WebGLRenderer } from 'three'\nimport Viewer from './viewer'\n\nexport interface TiledRendererParams {\n factor?: number\n antialias?: boolean\n onProgress?: Function\n onFinish?: Function\n}\n\nclass TiledRenderer {\n canvas = document.createElement('canvas')\n\n private _width: number\n private _height: number\n private _n: number\n private _factor: number\n private _antialias: boolean\n private _viewerSampleLevel: number\n\n private _viewer: Viewer\n private _onProgress?: Function\n private _onFinish?: Function\n private _ctx: CanvasRenderingContext2D\n\n constructor(renderer: WebGLRenderer, camera: Camera, viewer: Viewer, params: TiledRendererParams) {\n this._viewer = viewer\n\n this._factor = defaults(params.factor, 2)\n this._antialias = defaults(params.antialias, false)\n\n this._onProgress = params.onProgress\n this._onFinish = params.onFinish\n\n if (this._antialias) this._factor *= 2\n this._n = this._factor * this._factor\n\n // canvas\n\n this._width = this._viewer.width\n this._height = this._viewer.height\n\n if (this._antialias) {\n this.canvas.width = this._width * this._factor / 2\n this.canvas.height = this._height * this._factor / 2\n } else {\n this.canvas.width = this._width * this._factor\n this.canvas.height = this._height * this._factor\n }\n\n this._ctx = this.canvas.getContext('2d')!\n\n this._viewerSampleLevel = viewer.sampleLevel\n this._viewer.setSampling(-1)\n }\n\n private _renderTile (i: number) {\n const viewer = this._viewer\n const width = this._width\n const height = this._height\n const factor = this._factor\n\n const x = i % factor\n const y = Math.floor(i / factor)\n\n const offsetX = x * width\n const offsetY = y * height\n\n viewer.camera.setViewOffset(\n width * factor,\n height * factor,\n offsetX,\n offsetY,\n width,\n height\n )\n\n viewer.render()\n\n if (this._antialias) {\n const w = Math.round((offsetX + width) / 2) - Math.round (offsetX / 2);\n const h = Math.round((offsetY + height) / 2) - Math.round (offsetY / 2);\n this._ctx.drawImage(\n viewer.renderer.domElement,\n Math.round(offsetX / 2),\n Math.round(offsetY / 2),\n w,\n h\n )\n } else {\n this._ctx.drawImage(\n viewer.renderer.domElement,\n Math.floor(offsetX),\n Math.floor(offsetY),\n Math.ceil(width),\n Math.ceil(height)\n )\n }\n\n if (typeof this._onProgress === 'function') {\n this._onProgress(i + 1, this._n, false)\n }\n }\n\n private _finalize () {\n this._viewer.setSampling(this._viewerSampleLevel)\n this._viewer.camera.view = null! // TODO\n\n if (typeof this._onFinish === 'function') {\n this._onFinish(this._n + 1, this._n, false)\n }\n }\n\n render () {\n for (let i = 0; i <= this._n; ++i) {\n if (i === this._n) {\n this._finalize()\n } else {\n this._renderTile(i)\n }\n }\n }\n\n renderAsync () {\n let count = 0\n const n = this._n\n\n const fn = () => {\n if (count === n) {\n this._finalize()\n } else {\n this._renderTile(count)\n }\n count += 1\n }\n\n for (let i = 0; i <= n; ++i) {\n setTimeout(fn, 0)\n }\n }\n}\n\nexport default TiledRenderer\n","/**\n * @file Math Constants\n * @author Alexander Rose \n * @private\n */\n\nexport const EPS = 0.0000001\nexport const TwoPI = 2 * Math.PI\n\nexport const DEG2RAD = Math.PI / 180\nexport const RAD2DEG = 180 / Math.PI\n","/**\n * @file Array Utils\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3 } from 'three'\n\nimport { NumberArray } from '../types'\nimport { TwoPI } from './math-constants'\n\nexport function circularMean (array: NumberArray, max: number, stride = 1, offset = 0, indices?: NumberArray) {\n // http://en.wikipedia.org/wiki/Center_of_mass#Systems_with_periodic_boundary_conditions\n\n // Bai, Linge; Breen, David (2008). Calculating Center of Mass in an Unbounded 2D Environment. Journal of Graphics, GPU, and Game Tools 13 (4): 53–60.\n\n // http://stackoverflow.com/questions/18166507/using-fft-to-find-the-center-of-mass-under-periodic-boundary-conditions\n\n const n = indices ? indices.length : array.length / stride\n\n let cosMean = 0\n let sinMean = 0\n\n if (indices) {\n for (let i = 0; i < n; ++i) {\n const c = (array[ indices[ i ] * stride + offset ] + max) % max\n const angle = (c / max) * TwoPI - Math.PI\n\n cosMean += Math.cos(angle)\n sinMean += Math.sin(angle)\n }\n } else {\n for (let i = offset; i < n; i += stride) {\n const c = (array[ i ] + max) % max\n const angle = (c / max) * TwoPI - Math.PI\n\n cosMean += Math.cos(angle)\n sinMean += Math.sin(angle)\n }\n }\n\n cosMean /= n\n sinMean /= n\n\n const meanAngle = Math.atan2(sinMean, cosMean)\n const mean = (meanAngle + Math.PI) / TwoPI * max\n\n return mean\n}\n\nexport function calculateCenterArray (array1: NumberArray, array2: NumberArray, center?: T, offset = 0): T {\n const n = array1.length\n const c = center || new Float32Array(n)\n\n for (let i = 0; i < n; i += 3) {\n c[ offset + i + 0 ] = (array1[ i + 0 ] + array2[ i + 0 ]) / 2.0\n c[ offset + i + 1 ] = (array1[ i + 1 ] + array2[ i + 1 ]) / 2.0\n c[ offset + i + 2 ] = (array1[ i + 2 ] + array2[ i + 2 ]) / 2.0\n }\n\n return c as T\n}\n\nexport function calculateDirectionArray (array1: NumberArray, array2: NumberArray) {\n const n = array1.length\n const direction = new Float32Array(n)\n\n for (let i = 0; i < n; i += 3) {\n direction[ i + 0 ] = array2[ i + 0 ] - array1[ i + 0 ]\n direction[ i + 1 ] = array2[ i + 1 ] - array1[ i + 1 ]\n direction[ i + 2 ] = array2[ i + 2 ] - array1[ i + 2 ]\n }\n\n return direction\n}\n\nexport function uniformArray (n: number, a: number, optionalTarget?: T): T {\n const array = optionalTarget || new Float32Array(n)\n\n for (let i = 0; i < n; ++i) {\n array[ i ] = a\n }\n\n return array as T\n}\n\nexport function uniformArray3 (n: number, a: number, b: number, c: number, optionalTarget?: NumberArray) {\n const array = optionalTarget || new Float32Array(n * 3)\n\n for (let i = 0; i < n; ++i) {\n const j = i * 3\n\n array[ j + 0 ] = a\n array[ j + 1 ] = b\n array[ j + 2 ] = c\n }\n\n return array\n}\n\nexport function centerArray3 (array: NumberArray, center = new Vector3()) {\n const n = array.length\n\n for (let i = 0; i < n; i += 3) {\n center.x += array[ i ]\n center.y += array[ i + 1 ]\n center.z += array[ i + 2 ]\n }\n\n center.divideScalar(n / 3)\n\n return center\n}\n\nexport function serialArray (n: number) {\n const array = new Float32Array(n)\n\n for (let i = 0; i < n; ++i) {\n array[ i ] = i\n }\n\n return array\n}\n\nexport function serialBlockArray (n: number, b: number, offset = 0, optionalTarget?: NumberArray) {\n const array = optionalTarget || new Float32Array(n * b)\n\n for (let i = 0; i < n; ++i) {\n const k = offset + i * b\n\n for (let j = 0; j < b; ++j) {\n array[ k + j ] = i\n }\n }\n\n return array\n}\n\nexport function randomColorArray (n: number) {\n const array = new Float32Array(n * 3)\n\n for (let i = 0; i < n; ++i) {\n const j = i * 3\n\n array[ j + 0 ] = Math.random()\n array[ j + 1 ] = Math.random()\n array[ j + 2 ] = Math.random()\n }\n\n return array\n}\n\nexport function replicateArrayEntries (array: NumberArray, m: number) {\n const n = array.length\n const repArr = new Float32Array(n * m)\n\n for (let i = 0; i < n; ++i) {\n const k = i * m\n const a = array[ i ]\n\n for (let j = 0; j < m; ++j) {\n repArr[ k + j ] = a\n }\n }\n\n return repArr\n}\n\nexport function replicateArray3Entries (array: NumberArray, m: number) {\n const n = array.length / 3\n const repArr = new Float32Array(n * m * 3)\n\n for (let i = 0; i < n; ++i) {\n const v = i * 3\n const k = i * m * 3\n\n const a = array[ v + 0 ]\n const b = array[ v + 1 ]\n const c = array[ v + 2 ]\n\n for (let j = 0; j < m; ++j) {\n const l = k + j * 3\n\n repArr[ l + 0 ] = a\n repArr[ l + 1 ] = b\n repArr[ l + 2 ] = c\n }\n }\n\n return repArr\n}\n\nexport function calculateMeanArray (array1: NumberArray, array2: NumberArray) {\n const n = array1.length\n const mean = new Float32Array(n)\n\n for (let i = 0; i < n; i++) {\n mean[ i ] = (array1[ i ] + array2[ i ]) / 2.0\n }\n\n return mean\n}\n\nexport function calculateMinArray (array1: NumberArray, array2: NumberArray) {\n const n = array1.length\n const min = new Float32Array(n)\n\n for (let i = 0; i < n; i++) {\n min[ i ] = Math.min(array1[ i ], array2[ i ])\n }\n\n return min\n}\n\nexport function copyArray (src: T, dst: T, srcOffset: number, dstOffset: number, length: number) {\n for (let i = 0; i < length; ++i) {\n dst[ dstOffset + i ] = src[ srcOffset + i ]\n }\n}\n\nexport function copyWithin (array: NumberArray|any[], srcOffset: number, dstOffset: number, length: number) {\n copyArray(array, array, srcOffset, dstOffset, length)\n}\n\nconst swap = new Float32Array(4)\nconst temp = new Float32Array(4)\n/**\n * quicksortIP\n * @function\n * @author Roman Bolzern , 2013\n * @author I4DS http://www.fhnw.ch/i4ds, 2013\n * @license MIT License \n * @description\n * In-place quicksort for typed arrays (e.g. for Float32Array)\n * provides fast sorting\n * useful e.g. for a custom shader and/or BufferGeometry\n * Complexity: http://bigocheatsheet.com/ see Quicksort\n *\n * @example\n * points: [x, y, z, x, y, z, x, y, z, ...]\n * eleSize: 3 //because of (x, y, z)\n * orderElement: 0 //order according to x\n *\n * @param {TypedArray} arr - array to be sorted\n * @param {Integer} eleSize - element size\n * @param {Integer} orderElement - index of element used for sorting, < eleSize\n * @param {Integer} [begin] - start index for range to be sorted\n * @param {Integer} [end] - end index for range to be sorted\n * @return {TypedArray} the input array\n */\nexport function quicksortIP (arr: NumberArray, eleSize: number, orderElement: number, begin = 0, end?: number) {\n end = (end || (arr.length / eleSize)) - 1\n\n const stack = []\n let sp = -1\n let left = begin\n let right = end\n let tmp = 0.0\n let x = 0\n let y = 0\n\n const swapF = function (a: number, b: number) {\n a *= eleSize; b *= eleSize\n for (y = 0; y < eleSize; y++) {\n tmp = arr[ a + y ]\n arr[ a + y ] = arr[ b + y ]\n arr[ b + y ] = tmp\n }\n }\n\n let i, j\n\n while (true) {\n if (right - left <= 25) {\n for (j = left + 1; j <= right; j++) {\n for (x = 0; x < eleSize; x++) {\n swap[ x ] = arr[ j * eleSize + x ]\n }\n\n i = j - 1\n\n while (i >= left && arr[ i * eleSize + orderElement ] > swap[ orderElement ]) {\n for (x = 0; x < eleSize; x++) {\n arr[ (i + 1) * eleSize + x ] = arr[ i * eleSize + x ]\n }\n i--\n }\n\n for (x = 0; x < eleSize; x++) {\n arr[ (i + 1) * eleSize + x ] = swap[ x ]\n }\n }\n\n if (sp === -1) break\n\n right = stack[ sp-- ] // ?\n left = stack[ sp-- ]\n } else {\n const median = (left + right) >> 1\n\n i = left + 1\n j = right\n\n swapF(median, i)\n\n if (arr[ left * eleSize + orderElement ] > arr[ right * eleSize + orderElement ]) {\n swapF(left, right)\n }\n\n if (arr[ i * eleSize + orderElement ] > arr[ right * eleSize + orderElement ]) {\n swapF(i, right)\n }\n\n if (arr[ left * eleSize + orderElement ] > arr[ i * eleSize + orderElement ]) {\n swapF(left, i)\n }\n\n for (x = 0; x < eleSize; x++) {\n temp[ x ] = arr[ i * eleSize + x ]\n }\n\n while (true) {\n do i++; while (arr[ i * eleSize + orderElement ] < temp[ orderElement ])\n do j--; while (arr[ j * eleSize + orderElement ] > temp[ orderElement ])\n if (j < i) break\n swapF(i, j)\n }\n\n for (x = 0; x < eleSize; x++) {\n arr[ (left + 1) * eleSize + x ] = arr[ j * eleSize + x ]\n arr[ j * eleSize + x ] = temp[ x ]\n }\n\n if (right - i + 1 >= j - left) {\n stack[ ++sp ] = i\n stack[ ++sp ] = right\n right = j - 1\n } else {\n stack[ ++sp ] = left\n stack[ ++sp ] = j - 1\n left = i\n }\n }\n }\n\n return arr\n}\n\nexport function quicksortCmp (arr: NumberArray|T[], cmp?: (a: number|T, b: number|T) => number, begin = 0, end?: number) {\n cmp = cmp || function cmp (a, b) {\n if (a > b) return 1\n if (a < b) return -1\n return 0\n }\n end = (end || arr.length) - 1\n\n const stack = []\n let sp = -1\n let left = begin\n let right = end\n let tmp: number|T\n\n function swap (a: number, b: number) {\n const tmp2 = arr[ a ]\n arr[ a ] = arr[ b ]\n arr[ b ] = tmp2\n }\n\n let i, j\n\n while (true) {\n if (right - left <= 25) {\n for (let k = left + 1; k <= right; ++k) {\n tmp = arr[ k ]\n i = k - 1\n\n while (i >= left && cmp(arr[ i ], tmp) > 0) {\n arr[ i + 1 ] = arr[ i ]\n --i\n }\n\n arr[ i + 1 ] = tmp\n }\n\n if (sp === -1) break\n\n right = stack[ sp-- ] // ?\n left = stack[ sp-- ]\n } else {\n const median = (left + right) >> 1\n\n i = left + 1\n j = right\n\n swap(median, i)\n\n if (cmp(arr[ left ], arr[ right ]) > 0) {\n swap(left, right)\n }\n\n if (cmp(arr[ i ], arr[ right ]) > 0) {\n swap(i, right)\n }\n\n if (cmp(arr[ left ], arr[ i ]) > 0) {\n swap(left, i)\n }\n\n tmp = arr[ i ]\n\n while (true) {\n do i++; while (cmp(arr[ i ], tmp) < 0)\n do j--; while (cmp(arr[ j ], tmp) > 0)\n if (j < i) break\n swap(i, j)\n }\n\n arr[ left + 1 ] = arr[ j ]\n arr[ j ] = tmp\n\n if (right - i + 1 >= j - left) {\n stack[ ++sp ] = i\n stack[ ++sp ] = right\n right = j - 1\n } else {\n stack[ ++sp ] = left\n stack[ ++sp ] = j - 1\n left = i\n }\n }\n }\n\n return arr\n}\n\nexport function quickselectCmp (arr: NumberArray|T[], n: number, cmp?: (a: number|T, b: number|T) => number, left = 0, right?: number) {\n cmp = cmp || function cmp (a, b) {\n if (a > b) return 1\n if (a < b) return -1\n return 0\n }\n right = (right || arr.length) - 1\n\n let pivotIndex, pivotValue, storeIndex\n\n function swap (a: number, b: number) {\n const tmp = arr[ a ]\n arr[ a ] = arr[ b ]\n arr[ b ] = tmp\n }\n\n while (true) {\n if (left === right) {\n return arr[ left ]\n }\n pivotIndex = (left + right) >> 1\n pivotValue = arr[ pivotIndex ]\n swap(pivotIndex, right)\n storeIndex = left\n for (let i = left; i < right; ++i) {\n if (cmp(arr[ i ], pivotValue) < 0) {\n swap(storeIndex, i)\n ++storeIndex\n }\n }\n swap(right, storeIndex)\n pivotIndex = storeIndex\n if (n === pivotIndex) {\n return arr[ n ]\n } else if (n < pivotIndex) {\n right = pivotIndex - 1\n } else {\n left = pivotIndex + 1\n }\n }\n}\n\nexport function arrayMax (array: NumberArray) {\n let max = -Infinity\n for (let i = 0, il = array.length; i < il; ++i) {\n if (array[ i ] > max) max = array[ i ]\n }\n return max\n}\n\nexport function arrayMin (array: NumberArray) {\n let min = Infinity\n for (let i = 0, il = array.length; i < il; ++i) {\n if (array[ i ] < min) min = array[ i ]\n }\n return min\n}\n\nexport function arraySum (array: NumberArray, stride = 1, offset = 0) {\n const n = array.length\n let sum = 0\n for (let i = offset; i < n; i += stride) {\n sum += array[ i ]\n }\n return sum\n}\n\nexport function arrayMean (array: NumberArray, stride = 1, offset = 0) {\n return arraySum(array, stride, offset) / (array.length / stride)\n}\n\nexport function arrayRms (array: NumberArray) {\n const n = array.length\n let sumSq = 0\n for (let i = 0; i < n; ++i) {\n const di = array[ i ]\n sumSq += di * di\n }\n return Math.sqrt(sumSq / n)\n}\n\nexport function arraySorted (array: NumberArray) {\n for (let i = 1, il = array.length; i < il; ++i) {\n if (array[ i - 1 ] > array[ i ]) return false\n }\n return true\n}\n\nexport function arraySortedCmp (array: NumberArray|T[], cmp: (a: number|T, b: number|T) => number) {\n for (let i = 1, il = array.length; i < il; ++i) {\n if (cmp(array[ i - 1 ], array[ i ]) > 0) return false\n }\n return true\n}\n","/**\n * @file Viewer Utils\n * @author Alexander Rose \n * @private\n */\n\nimport {\n Vector2, Vector3, Matrix4, Points, Scene, Camera,\n Object3D, WebGLRenderer, Color\n} from 'three'\n\nimport { createParams } from '../utils'\nimport TiledRenderer from './tiled-renderer'\nimport { quicksortCmp } from '../math/array-utils'\nimport Viewer from './viewer'\n\nfunction _trimCanvas (canvas: HTMLCanvasElement, r: number, g: number, b: number, a: number) {\n const canvasHeight = canvas.height\n const canvasWidth = canvas.width\n\n const ctx = canvas.getContext('2d')!\n const pixels = ctx.getImageData(0, 0, canvasWidth, canvasHeight).data\n\n let x, y, doBreak, off\n\n doBreak = false\n for (y = 0; y < canvasHeight; y++) {\n for (x = 0; x < canvasWidth; x++) {\n off = (y * canvasWidth + x) * 4\n if (pixels[ off ] !== r || pixels[ off + 1 ] !== g ||\n pixels[ off + 2 ] !== b || pixels[ off + 3 ] !== a\n ) {\n doBreak = true\n break\n }\n }\n if (doBreak) {\n break\n }\n }\n const topY = y\n\n doBreak = false\n for (x = 0; x < canvasWidth; x++) {\n for (y = 0; y < canvasHeight; y++) {\n off = (y * canvasWidth + x) * 4\n if (pixels[ off ] !== r || pixels[ off + 1 ] !== g ||\n pixels[ off + 2 ] !== b || pixels[ off + 3 ] !== a\n ) {\n doBreak = true\n break\n }\n }\n if (doBreak) {\n break\n }\n }\n const topX = x\n\n doBreak = false\n for (y = canvasHeight - 1; y >= 0; y--) {\n for (x = canvasWidth - 1; x >= 0; x--) {\n off = (y * canvasWidth + x) * 4\n if (pixels[ off ] !== r || pixels[ off + 1 ] !== g ||\n pixels[ off + 2 ] !== b || pixels[ off + 3 ] !== a\n ) {\n doBreak = true\n break\n }\n }\n if (doBreak) {\n break\n }\n }\n const bottomY = y\n\n doBreak = false\n for (x = canvasWidth - 1; x >= 0; x--) {\n for (y = canvasHeight - 1; y >= 0; y--) {\n off = (y * canvasWidth + x) * 4\n if (pixels[ off ] !== r || pixels[ off + 1 ] !== g ||\n pixels[ off + 2 ] !== b || pixels[ off + 3 ] !== a\n ) {\n doBreak = true\n break\n }\n }\n if (doBreak) {\n break\n }\n }\n const bottomX = x\n\n const trimedCanvas = document.createElement('canvas')\n trimedCanvas.width = bottomX - topX\n trimedCanvas.height = bottomY - topY\n\n const trimedCtx = trimedCanvas.getContext('2d')!\n trimedCtx.drawImage(\n canvas,\n topX, topY,\n trimedCanvas.width, trimedCanvas.height,\n 0, 0,\n trimedCanvas.width, trimedCanvas.height\n )\n\n return trimedCanvas\n}\n\n/**\n * Image parameter object.\n * @typedef {Object} ImageParameters - image generation parameters\n * @property {Boolean} trim - trim the image\n * @property {Integer} factor - scaling factor to apply to the viewer canvas\n * @property {Boolean} antialias - antialias the image\n * @property {Boolean} transparent - transparent image background\n */\n\nexport const ImageDefaultParameters = {\n trim: false,\n factor: 1,\n antialias: false,\n transparent: false,\n onProgress: undefined as Function|undefined\n}\nexport type ImageParameters = typeof ImageDefaultParameters\n\n/**\n * Make image from what is shown in a viewer canvas\n * @param {Viewer} viewer - the viewer\n * @param {ImageParameters} params - parameters object\n * @return {Promise} A Promise object that resolves to an image {@link Blob}.\n */\nexport function makeImage (viewer: Viewer, params: Partial = {}) {\n const {trim, factor, antialias, transparent} = createParams(params, ImageDefaultParameters)\n\n const renderer = viewer.renderer\n const camera = viewer.camera\n\n const originalClearAlpha = renderer.getClearAlpha()\n const backgroundColor = renderer.getClearColor(new Color())\n\n function setLineWidthAndPixelSize (invert = false) {\n let _factor = factor\n if (antialias) _factor *= 2\n if (invert) _factor = 1 / _factor\n viewer.scene.traverse(function (o: any) { // TODO\n const m = o.material\n if (m && m.linewidth) {\n m.linewidth *= _factor\n }\n if (m && m.uniforms && m.uniforms.size) {\n if (m.uniforms.size.__seen === undefined) {\n m.uniforms.size.value *= _factor\n m.uniforms.size.__seen = true\n }\n }\n if (m && m.uniforms && m.uniforms.linewidth) {\n if (m.uniforms.linewidth.__seen === undefined) {\n m.uniforms.linewidth.value *= _factor\n m.uniforms.linewidth.__seen = true\n }\n }\n })\n viewer.scene.traverse(function (o: any) { // TODO\n const m = o.material\n if (m && m.uniforms && m.uniforms.size) {\n delete m.uniforms.size.__seen\n }\n if (m && m.uniforms && m.uniforms.linewidth) {\n delete m.uniforms.linewidth.__seen\n }\n })\n }\n\n function trimCanvas (canvas: HTMLCanvasElement) {\n if (trim) {\n const bg = backgroundColor\n const r = transparent ? 0 : bg.r * 255\n const g = transparent ? 0 : bg.g * 255\n const b = transparent ? 0 : bg.b * 255\n const a = transparent ? 0 : 255\n return _trimCanvas(canvas, r, g, b, a)\n } else {\n return canvas\n }\n }\n\n function onProgress (i: number, n: number, finished: boolean) {\n if (typeof params.onProgress === 'function') {\n params.onProgress(i, n, finished)\n }\n }\n\n return new Promise(function (resolve, reject) {\n const tiledRenderer = new TiledRenderer(\n renderer, camera, viewer,\n { factor, antialias, onProgress, onFinish }\n )\n\n renderer.setClearAlpha(transparent ? 0 : 1)\n setLineWidthAndPixelSize()\n tiledRenderer.renderAsync()\n\n function onFinish (i: number, n: number) {\n const canvas = trimCanvas(tiledRenderer.canvas)\n canvas.toBlob(\n function (blob) {\n renderer.setClearAlpha(originalClearAlpha)\n setLineWidthAndPixelSize(true)\n viewer.requestRender()\n onProgress(n, n, true)\n if (blob) {\n resolve(blob)\n } else {\n reject('error creating image')\n }\n },\n 'image/png'\n )\n }\n })\n}\n\nconst vertex = new Vector3()\nconst matrix = new Matrix4()\nconst modelViewProjectionMatrix = new Matrix4()\n\nexport function sortProjectedPosition (scene: Scene, camera: Camera) {\n // console.time( \"sort\" );\n\n scene.traverseVisible(function (o) {\n if (!(o instanceof Points) || !o.userData.buffer.parameters.sortParticles) {\n return\n }\n\n const attributes = (o.geometry as any).attributes // TODO\n const n = attributes.position.count\n\n if (n === 0) return\n\n matrix.multiplyMatrices(\n camera.matrixWorldInverse, o.matrixWorld\n )\n modelViewProjectionMatrix.multiplyMatrices(\n camera.projectionMatrix, matrix\n )\n\n let sortData, sortArray, zArray: Float32Array, cmpFn\n\n if (!o.userData.sortData) {\n zArray = new Float32Array(n)\n sortArray = new Uint32Array(n)\n cmpFn = function (ai: number, bi: number) {\n const a = zArray[ ai ]\n const b = zArray[ bi ]\n if (a > b) return 1\n if (a < b) return -1\n return 0\n }\n\n sortData = {\n __zArray: zArray,\n __sortArray: sortArray,\n __cmpFn: cmpFn\n }\n\n o.userData.sortData = sortData\n } else {\n sortData = o.userData.sortData\n zArray = sortData.__zArray\n sortArray = sortData.__sortArray\n cmpFn = sortData.__cmpFn\n }\n\n for (let i = 0; i < n; ++i) {\n vertex.fromArray(attributes.position.array, i * 3)\n vertex.applyMatrix4(modelViewProjectionMatrix)\n\n // negate, so that sorting order is reversed\n zArray[ i ] = -vertex.z\n sortArray[ i ] = i\n }\n\n quicksortCmp(sortArray, cmpFn)\n\n let index, indexSrc, indexDst, tmpTab\n\n for (let name in attributes) {\n const attr = attributes[ name ]\n const array = attr.array\n const itemSize = attr.itemSize\n\n if (!sortData[ name ]) {\n sortData[ name ] = new Float32Array(itemSize * n)\n }\n\n tmpTab = sortData[ name ]\n sortData[ name ] = array\n\n for (let i = 0; i < n; ++i) {\n index = sortArray[ i ]\n\n for (let j = 0; j < itemSize; ++j) {\n indexSrc = index * itemSize + j\n indexDst = i * itemSize + j\n tmpTab[ indexDst ] = array[ indexSrc ]\n }\n }\n\n attributes[ name ].array = tmpTab\n attributes[ name ].needsUpdate = true\n }\n })\n\n // console.timeEnd( \"sort\" );\n}\n\nconst resolution = new Vector2()\nconst projectionMatrixInverse = new Matrix4()\nconst projectionMatrixTranspose = new Matrix4()\n\nexport function updateMaterialUniforms (group: Object3D, camera: Camera, renderer: WebGLRenderer, cDist: number, bRadius: number) {\n let size = new Vector2()\n renderer.getSize(size)\n const canvasHeight = size.height\n const pixelRatio = renderer.getPixelRatio()\n const ortho = camera.type === 'OrthographicCamera'\n\n resolution.set(size.width, size.height)\n projectionMatrixInverse.copy(camera.projectionMatrix).invert()\n projectionMatrixTranspose.copy(camera.projectionMatrix).transpose()\n\n group.traverse(function (o: any) {\n const m = o.material\n if (!m) return\n\n const u = m.uniforms\n if (!u) return\n\n if (m.clipNear) {\n const nearFactor = (50 - m.clipNear) / 50\n const nearClip = cDist - (bRadius * nearFactor)\n u.clipNear.value = nearClip\n }\n\n if (u.canvasHeight) {\n u.canvasHeight.value = canvasHeight\n }\n\n if (u.resolution) {\n u.resolution.value.copy(resolution)\n }\n\n if (u.pixelRatio) {\n u.pixelRatio.value = pixelRatio\n }\n\n if (u.projectionMatrixInverse) {\n u.projectionMatrixInverse.value.copy(projectionMatrixInverse)\n }\n\n if (u.projectionMatrixTranspose) {\n u.projectionMatrixTranspose.value.copy(projectionMatrixTranspose)\n }\n\n if (u.ortho) {\n u.ortho.value = ortho\n }\n })\n}\n\nexport function updateCameraUniforms (group: Object3D, camera: Camera) {\n projectionMatrixInverse.copy(camera.projectionMatrix).invert()\n projectionMatrixTranspose.copy(camera.projectionMatrix).transpose()\n\n group.traverse(function (o: any) {\n const m = o.material\n if (!m) return\n\n const u = m.uniforms\n if (!u) return\n\n if (u.projectionMatrixInverse) {\n u.projectionMatrixInverse.value.copy(projectionMatrixInverse)\n }\n\n if (u.projectionMatrixTranspose) {\n u.projectionMatrixTranspose.value.copy(projectionMatrixTranspose)\n }\n })\n}\n","/**\n * @file Viewer\n * @author Alexander Rose \n * @private\n */\n\n// adapted from https://webglfundamentals.org/webgl/resources/webgl-utils.js\n// Copyright 2012, Gregg Tavares. Modified BSD License\n\nexport function createProgram(gl: WebGLRenderingContext, shaders: WebGLShader[], attribs?: string[], locations?: number[]) {\n const program = gl.createProgram()\n if (!program) {\n console.log(`error creating WebGL program`)\n return\n }\n shaders.forEach(shader => gl.attachShader(program, shader))\n if (attribs) {\n attribs.forEach((attrib, i) => {\n gl.bindAttribLocation(program, locations ? locations[i] : i, attrib)\n })\n }\n gl.linkProgram(program);\n\n // Check the link status\n const linked = gl.getProgramParameter(program, gl.LINK_STATUS)\n if (!linked) {\n console.log(`error linking program: ${gl.getProgramInfoLog(program)}`)\n gl.deleteProgram(program)\n return null\n }\n return program\n}\n\nexport function loadShader(gl: WebGLRenderingContext, shaderSource: string, shaderType: number) {\n const shader = gl.createShader(shaderType)\n if (!shader) {\n console.log(`error creating WebGL shader ${shaderType}`)\n return // can't create shader\n }\n gl.shaderSource(shader, shaderSource)\n gl.compileShader(shader)\n\n // Check the compile status\n const compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS)\n if (!compiled) {\n console.log(`error compiling shader ${shader}: ${gl.getShaderInfoLog(shader)}`)\n gl.deleteShader(shader)\n return null\n }\n\n return shader\n}\n\n//\n\nexport function getErrorDescription(gl: WebGLRenderingContext, error: number) {\n switch (error) {\n case gl.NO_ERROR: return 'no error'\n case gl.INVALID_ENUM: return 'invalid enum'\n case gl.INVALID_VALUE: return 'invalid value'\n case gl.INVALID_OPERATION: return 'invalid operation'\n case gl.INVALID_FRAMEBUFFER_OPERATION: return 'invalid framebuffer operation'\n case gl.OUT_OF_MEMORY: return 'out of memory'\n case gl.CONTEXT_LOST_WEBGL: return 'context lost'\n }\n return 'unknown error'\n}\n\nexport function getExtension (gl: WebGLRenderingContext, name: string) {\n const ext = gl.getExtension(name)\n if (!ext) console.log(`extension '${name}' not available`)\n return ext\n}\n\nconst TextureTestVertShader = `\nattribute vec4 a_position;\n\nvoid main() {\n gl_Position = a_position;\n}`\n\nconst TextureTestFragShader = `\nprecision mediump float;\nuniform vec4 u_color;\nuniform sampler2D u_texture;\n\nvoid main() {\n gl_FragColor = texture2D(u_texture, vec2(0.5, 0.5)) * u_color;\n}`\n\nconst TextureTestTexCoords = new Float32Array([\n -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0\n])\n\nexport function testTextureSupport (type: number) {\n // adapted from\n // https://stackoverflow.com/questions/28827511/webgl-ios-render-to-floating-point-texture\n\n // Get A WebGL context\n const canvas = document.createElement('canvas')\n canvas.width = 16\n canvas.height = 16\n canvas.style.width = 16 + 'px'\n canvas.style.height = 16 + 'px'\n const gl = canvas.getContext(\"webgl\") || canvas.getContext(\"experimental-webgl\");\n if (!gl) {\n console.log(`error creating webgl context for ${type}`)\n return false\n }\n if (!(gl instanceof WebGLRenderingContext)) {\n console.log(`Got unexpected type for WebGL rendering context`)\n return false\n }\n\n getExtension(gl, 'OES_texture_float')\n getExtension(gl, 'OES_texture_half_float')\n getExtension(gl, 'WEBGL_color_buffer_float')\n\n // setup shaders\n const vertShader = loadShader(gl, TextureTestVertShader, gl.VERTEX_SHADER)\n const fragShader = loadShader(gl, TextureTestFragShader, gl.FRAGMENT_SHADER)\n if (!vertShader || !fragShader) return false\n\n // setup program\n const program = createProgram(gl, [ vertShader, fragShader ])\n if (!program) {\n console.log(`error creating WebGL program`)\n return false\n }\n gl.useProgram(program);\n\n // look up where the vertex data needs to go.\n const positionLocation = gl.getAttribLocation(program, \"a_position\");\n const colorLoc = gl.getUniformLocation(program, \"u_color\");\n if (!colorLoc) {\n console.log(`error getting 'u_color' uniform location`)\n return false\n }\n\n // provide texture coordinates for the rectangle.\n const positionBuffer = gl.createBuffer()\n gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer)\n gl.bufferData(gl.ARRAY_BUFFER, TextureTestTexCoords, gl.STATIC_DRAW)\n gl.enableVertexAttribArray(positionLocation)\n gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0)\n\n const whiteTex = gl.createTexture()\n const whiteData = new Uint8Array([255, 255, 255, 255])\n gl.bindTexture(gl.TEXTURE_2D, whiteTex)\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, whiteData)\n\n const tex = gl.createTexture()\n gl.bindTexture(gl.TEXTURE_2D, tex)\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, type, null)\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)\n\n const fb = gl.createFramebuffer()\n gl.bindFramebuffer(gl.FRAMEBUFFER, fb)\n gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0)\n const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER)\n if (status !== gl.FRAMEBUFFER_COMPLETE) {\n console.log(`error creating framebuffer for ${type}`)\n return false\n }\n\n // Draw the rectangle.\n gl.bindTexture(gl.TEXTURE_2D, whiteTex)\n gl.uniform4fv(colorLoc, [0, 10, 20, 1])\n gl.drawArrays(gl.TRIANGLES, 0, 6)\n\n gl.bindTexture(gl.TEXTURE_2D, tex)\n gl.bindFramebuffer(gl.FRAMEBUFFER, null)\n gl.clearColor(1, 0, 0, 1)\n gl.clear(gl.COLOR_BUFFER_BIT)\n gl.uniform4fv(colorLoc, [0, 1/10, 1/20, 1])\n gl.drawArrays(gl.TRIANGLES, 0, 6)\n\n // Check if rendered correctly\n const pixel = new Uint8Array(4)\n gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel)\n if (pixel[0] !== 0 || pixel[1] < 248 || pixel[2] < 248 || pixel[3] < 254) {\n console.log(`not able to actually render to ${type} texture`)\n return false\n }\n\n // Check reading from float texture\n if (type === gl.FLOAT) {\n gl.bindFramebuffer(gl.FRAMEBUFFER, fb)\n const floatPixel = new Float32Array(4)\n gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.FLOAT, floatPixel)\n const error = gl.getError()\n if (error) {\n console.log(`error reading pixels as float: '${getErrorDescription(gl, error)}'`)\n return false\n }\n }\n\n return true\n}\n","/**\n * @file Viewer\n * @author Alexander Rose \n * @private\n */\n\nimport { Signal } from 'signals'\nimport {\n PerspectiveCamera, OrthographicCamera, StereoCamera,\n Vector2, Box3, Vector3, Matrix4, Color,\n WebGLRenderer, WebGLRenderTarget,\n NearestFilter, LinearFilter, AdditiveBlending,\n RGBAFormat, FloatType, /*HalfFloatType, */UnsignedByteType,\n ShaderMaterial,\n PlaneGeometry,\n Scene, Mesh, Group, Object3D, Uniform,\n Fog, DirectionalLight, AmbientLight,\n BufferGeometry, BufferAttribute,\n LineSegments, ColorSpace,\n HalfFloatType\n} from 'three'\nimport '../shader/BasicLine.vert'\nimport '../shader/BasicLine.frag'\nimport '../shader/Quad.vert'\nimport '../shader/Quad.frag'\n\nimport {\n Debug, Log, WebglErrorMessage, Browser,\n setExtensionFragDepth, SupportsReadPixelsFloat, setSupportsReadPixelsFloat\n} from '../globals'\nimport { degToRad } from '../math/math-utils'\nimport Stats from './stats'\nimport { getShader } from '../shader/shader-utils'\nimport { setColorSpace } from '../color/colormaker'\nimport { JitterVectors } from './viewer-constants'\nimport {\n makeImage, ImageParameters,\n sortProjectedPosition, updateMaterialUniforms, updateCameraUniforms\n} from './viewer-utils'\nimport { testTextureSupport } from './gl-utils'\n\nimport Buffer from '../buffer/buffer'\n\nconst pixelBufferFloat = new Float32Array(4 * 25)\nconst pixelBufferUint = new Uint8Array(4 * 25)\n\n// When picking, we read a 25 pixel (5x5) array (readRenderTargetPixels)\n// We read the pixels in the order below to find what was picked.\n// This starts at the center and tries successively further points.\n// (Many points will be at equal distance to the center, their order\n// is arbitrary).\nconst pixelOrder = [12,7,13,17,11,6,8,18,16,2,14,22,10,1,3,9,19,23,21,15,5,0,4,24,20]\n\n\nconst tmpMatrix = new Matrix4()\n\nfunction onBeforeRender (this: Object3D, renderer: WebGLRenderer, scene: Scene, camera: PerspectiveCamera|OrthographicCamera, geometry: BufferGeometry, material: ShaderMaterial/*, group */) {\n const u = material.uniforms\n const updateList = []\n\n if (!u) return // See #908 - some materials may not have uniforms, ignore these\n\n if (u.objectId) {\n u.objectId.value = SupportsReadPixelsFloat ? this.id : this.id / 255\n updateList.push('objectId')\n }\n\n if (u.modelViewMatrixInverse || u.modelViewMatrixInverseTranspose ||\n u.modelViewProjectionMatrix || u.modelViewProjectionMatrixInverse\n ) {\n this.modelViewMatrix.multiplyMatrices(camera.matrixWorldInverse, this.matrixWorld)\n }\n\n if (u.modelViewMatrixInverse) {\n u.modelViewMatrixInverse.value.copy(this.modelViewMatrix).invert()\n updateList.push('modelViewMatrixInverse')\n }\n\n if (u.modelViewMatrixInverseTranspose) {\n if (u.modelViewMatrixInverse) {\n u.modelViewMatrixInverseTranspose.value.copy(\n u.modelViewMatrixInverse.value\n ).transpose()\n } else {\n u.modelViewMatrixInverseTranspose.value\n .copy(this.modelViewMatrix).invert()\n .transpose()\n }\n updateList.push('modelViewMatrixInverseTranspose')\n }\n\n if (u.modelViewProjectionMatrix) {\n u.modelViewProjectionMatrix.value.multiplyMatrices(\n camera.projectionMatrix, this.modelViewMatrix\n )\n updateList.push('modelViewProjectionMatrix')\n }\n\n if (u.modelViewProjectionMatrixInverse) {\n if (u.modelViewProjectionMatrix) {\n tmpMatrix.copy(\n u.modelViewProjectionMatrix.value\n )\n u.modelViewProjectionMatrixInverse.value.copy(\n tmpMatrix.invert()\n )\n } else {\n tmpMatrix.multiplyMatrices(\n camera.projectionMatrix, this.modelViewMatrix\n )\n u.modelViewProjectionMatrixInverse.value.copy(\n tmpMatrix.invert()\n )\n }\n updateList.push('modelViewProjectionMatrixInverse')\n }\n\n if (updateList.length) {\n const materialProperties = renderer.properties.get(material)\n\n if (materialProperties.program) {\n const gl = renderer.getContext()\n const p = materialProperties.program\n gl.useProgram(p.program)\n const pu = p.getUniforms()\n\n updateList.forEach(function (name) {\n pu.setValue(gl, name, u[ name ].value)\n })\n }\n }\n}\n\nexport type CameraType = 'perspective'|'orthographic'|'stereo'\nexport type ColorWorkflow = 'linear' | 'sRGB'\n\nexport interface ViewerSignals {\n ticked: Signal,\n rendered: Signal\n}\n\nexport interface ViewerParameters {\n fogColor: Color\n fogNear: number\n fogFar: number\n\n backgroundColor: Color\n\n cameraType: CameraType\n cameraFov: number\n cameraEyeSep: number\n cameraZ: number\n\n clipNear: number\n clipFar: number\n clipDist: number\n clipMode: string // \"scene\" or \"camera\"\n clipScale: string // \"relative\" or \"absolute\"\n\n lightColor: Color\n lightIntensity: number\n ambientColor: Color\n ambientIntensity: number\n\n sampleLevel: number\n\n outputColorSpace: ColorSpace // default is three.LinearEncoding; three.sRGBEncoding gives more correct results\n}\n\nexport interface BufferInstance {\n matrix: Matrix4\n}\n\n/**\n * Viewer class\n * @class\n * @param {String|Element} [idOrElement] - dom id or element\n */\nexport default class Viewer {\n signals: ViewerSignals\n\n container: HTMLElement\n wrapper: HTMLElement\n\n private rendering: boolean\n private renderPending: boolean\n private lastRenderedPicking: boolean\n private isStill: boolean\n private frameRequest: number\n\n sampleLevel: number\n private cDist: number\n private bRadius: number\n\n private parameters: ViewerParameters\n stats: Stats\n\n perspectiveCamera: PerspectiveCamera\n private orthographicCamera: OrthographicCamera\n private stereoCamera: StereoCamera\n camera: PerspectiveCamera|OrthographicCamera\n\n width: number\n height: number\n\n scene: Scene\n private directionalLight: DirectionalLight\n private ambientLight: AmbientLight\n rotationGroup: Group\n translationGroup: Group\n private modelGroup: Group\n private pickingGroup: Group\n private backgroundGroup: Group\n private helperGroup: Group\n\n renderer: WebGLRenderer\n private supportsHalfFloat: boolean\n\n private pickingTarget: WebGLRenderTarget\n private sampleTarget: WebGLRenderTarget\n private holdTarget: WebGLRenderTarget\n\n private compositeUniforms: {\n tForeground: Uniform\n scale: Uniform\n }\n private compositeMaterial: ShaderMaterial\n private compositeCamera: OrthographicCamera\n private compositeScene: Scene\n\n private boundingBoxMesh: LineSegments\n boundingBox = new Box3()\n private boundingBoxSize = new Vector3()\n private boundingBoxLength = 0\n\n private info = {\n memory: {\n programs: 0,\n geometries: 0,\n textures: 0\n },\n render: {\n calls: 0,\n vertices: 0,\n faces: 0,\n points: 0\n }\n }\n\n private distVector = new Vector3()\n\n constructor (idOrElement: string|HTMLElement) {\n this.signals = {\n ticked: new Signal(),\n rendered: new Signal()\n }\n\n if (typeof idOrElement === 'string') {\n const elm = document.getElementById(idOrElement)\n if (elm === null) {\n this.container = document.createElement('div')\n }else {\n this.container = elm\n }\n } else if (idOrElement instanceof HTMLElement) {\n this.container = idOrElement\n } else {\n this.container = document.createElement('div')\n }\n\n if (this.container === document.body) {\n this.width = window.innerWidth || 1\n this.height = window.innerHeight || 1\n } else {\n const box = this.container.getBoundingClientRect()\n this.width = box.width || 1\n this.height = box.height || 1\n this.container.style.overflow = 'hidden'\n }\n\n this.wrapper = document.createElement('div')\n this.wrapper.style.position = 'relative'\n this.container.appendChild(this.wrapper)\n\n this._initParams()\n this._initStats()\n this._initCamera()\n this._initScene()\n\n if (this._initRenderer() === false) {\n Log.error('Viewer: could not initialize renderer')\n return\n }\n\n this._initHelper()\n\n // fog & background\n this.setBackground()\n this.setFog()\n\n this.animate = this.animate.bind(this)\n }\n\n private _initParams () {\n this.parameters = {\n fogColor: new Color(0x000000),\n fogNear: 50,\n fogFar: 100,\n\n backgroundColor: new Color(0x000000),\n\n cameraType: 'perspective',\n cameraFov: 40,\n cameraEyeSep: 0.3,\n cameraZ: -80, // FIXME initial value should be automatically determined\n\n clipNear: 0,\n clipFar: 100,\n clipDist: 10,\n clipMode: 'scene',\n clipScale: 'relative',\n\n lightColor: new Color(0xdddddd),\n lightIntensity: 1.2,\n ambientColor: new Color(0xdddddd),\n ambientIntensity: 0.3,\n\n sampleLevel: 0,\n\n // output encoding: use srgb for a linear internal workflow, srgb-linear for traditional sRGB workflow.\n outputColorSpace: 'srgb-linear',\n }\n }\n\n private _initCamera () {\n const lookAt = new Vector3(0, 0, 0)\n const {width, height} = this\n\n this.perspectiveCamera = new PerspectiveCamera(\n this.parameters.cameraFov, width / height\n )\n this.perspectiveCamera.position.z = this.parameters.cameraZ\n this.perspectiveCamera.lookAt(lookAt)\n\n this.orthographicCamera = new OrthographicCamera(\n width / -2, width / 2, height / 2, height / -2\n )\n this.orthographicCamera.position.z = this.parameters.cameraZ\n this.orthographicCamera.lookAt(lookAt)\n\n this.stereoCamera = new StereoCamera()\n this.stereoCamera.aspect = 0.5\n this.stereoCamera.eyeSep = this.parameters.cameraEyeSep\n\n const cameraType = this.parameters.cameraType\n if (cameraType === 'orthographic') {\n this.camera = this.orthographicCamera\n } else if(cameraType === 'perspective' || cameraType === 'stereo') {\n this.camera = this.perspectiveCamera\n } else {\n throw new Error(`Unknown cameraType '${cameraType}'`)\n }\n this.camera.updateProjectionMatrix()\n }\n\n private _initStats () {\n this.stats = new Stats()\n }\n\n private _initScene () {\n if (!this.scene) {\n this.scene = new Scene()\n this.scene.name = 'scene'\n }\n\n this.rotationGroup = new Group()\n this.rotationGroup.name = 'rotationGroup'\n this.scene.add(this.rotationGroup)\n\n this.translationGroup = new Group()\n this.translationGroup.name = 'translationGroup'\n this.rotationGroup.add(this.translationGroup)\n\n this.modelGroup = new Group()\n this.modelGroup.name = 'modelGroup'\n this.translationGroup.add(this.modelGroup)\n\n this.pickingGroup = new Group()\n this.pickingGroup.name = 'pickingGroup'\n this.translationGroup.add(this.pickingGroup)\n\n this.backgroundGroup = new Group()\n this.backgroundGroup.name = 'backgroundGroup'\n this.translationGroup.add(this.backgroundGroup)\n\n this.helperGroup = new Group()\n this.helperGroup.name = 'helperGroup'\n this.translationGroup.add(this.helperGroup)\n\n // fog\n\n this.scene.fog = new Fog(this.parameters.fogColor.getHex())\n\n // light\n\n this.directionalLight = new DirectionalLight(\n this.parameters.lightColor.getHex(), this.parameters.lightIntensity\n )\n this.scene.add(this.directionalLight)\n\n this.ambientLight = new AmbientLight(\n this.parameters.ambientColor.getHex(), this.parameters.ambientIntensity\n )\n this.scene.add(this.ambientLight)\n }\n\n private _initRenderer () {\n const dpr = window.devicePixelRatio\n const {width, height} = this\n\n try {\n this.renderer = new WebGLRenderer({\n preserveDrawingBuffer: true,\n alpha: true,\n antialias: true\n })\n } catch (e) {\n this.wrapper.innerHTML = WebglErrorMessage\n return false\n }\n this.renderer.setPixelRatio(dpr)\n this.renderer.setSize(width, height)\n this.renderer.autoClear = false\n this.renderer.sortObjects = true\n this.renderer.outputColorSpace = this.parameters.outputColorSpace\n this.renderer.useLegacyLights = true\n\n const gl = this.renderer.getContext()\n // console.log(gl.getContextAttributes().antialias)\n // console.log(gl.getParameter(gl.SAMPLES))\n\n // For WebGL1, extensions must be explicitly enabled.\n // The following are builtin to WebGL2 (and don't appear as\n // extensions)\n // EXT_frag_depth, OES_element_index_uint, OES_texture_float\n // OES_texture_half_float\n\n // The WEBGL_color_buffer_float extension is replaced by\n // EXT_color_buffer_float\n\n // If not webgl2 context, explicitly check for these\n if (!this.renderer.capabilities.isWebGL2) {\n setExtensionFragDepth(this.renderer.extensions.get('EXT_frag_depth'))\n this.renderer.extensions.get('OES_element_index_uint')\n\n setSupportsReadPixelsFloat(\n (this.renderer.extensions.get('OES_texture_float') &&\n this.renderer.extensions.get('WEBGL_color_buffer_float')) ||\n (this.renderer.extensions.get('OES_texture_float') &&\n testTextureSupport(gl.FLOAT))\n )\n // picking texture\n\n this.renderer.extensions.get('OES_texture_float')\n\n this.supportsHalfFloat = (\n this.renderer.extensions.get('OES_texture_half_float') &&\n testTextureSupport(0x8D61)\n )\n\n } else {\n setExtensionFragDepth(true)\n setSupportsReadPixelsFloat(\n this.renderer.extensions.get('EXT_color_buffer_float')\n )\n this.supportsHalfFloat = true\n }\n\n this.wrapper.appendChild(this.renderer.domElement)\n\n const dprWidth = width * dpr\n const dprHeight = height * dpr\n\n\n if (Debug) {\n console.log(JSON.stringify({\n 'Browser': Browser,\n 'OES_texture_float': !!this.renderer.extensions.get('OES_texture_float'),\n 'OES_texture_half_float': !!this.renderer.extensions.get('OES_texture_half_float'),\n 'WEBGL_color_buffer_float': !!this.renderer.extensions.get('WEBGL_color_buffer_float'),\n 'testTextureSupport Float': testTextureSupport(gl.FLOAT),\n 'testTextureSupport HalfFloat': testTextureSupport(0x8D61),\n 'this.supportsHalfFloat': this.supportsHalfFloat,\n 'SupportsReadPixelsFloat': SupportsReadPixelsFloat\n }, null, 2))\n }\n\n this.pickingTarget = new WebGLRenderTarget(\n dprWidth, dprHeight,\n {\n minFilter: NearestFilter,\n magFilter: NearestFilter,\n stencilBuffer: false,\n format: RGBAFormat,\n type: SupportsReadPixelsFloat ? FloatType : UnsignedByteType\n }\n )\n this.pickingTarget.texture.generateMipmaps = false\n this.pickingTarget.texture.colorSpace = this.parameters.outputColorSpace\n\n // workaround to reset the gl state after using testTextureSupport\n // fixes some bug where nothing is rendered to the canvas\n // when animations are started on page load\n this.renderer.setRenderTarget(this.pickingTarget)\n this.renderer.clear()\n this.renderer.setRenderTarget(null!)\n\n // ssaa textures\n\n this.sampleTarget = new WebGLRenderTarget(\n dprWidth, dprHeight,\n {\n minFilter: LinearFilter,\n magFilter: LinearFilter,\n format: RGBAFormat,\n type: this.supportsHalfFloat ? HalfFloatType : (\n SupportsReadPixelsFloat ? FloatType : UnsignedByteType\n )\n }\n )\n this.sampleTarget.texture.colorSpace = this.parameters.outputColorSpace\n\n this.holdTarget = new WebGLRenderTarget(\n dprWidth, dprHeight,\n {\n minFilter: NearestFilter,\n magFilter: NearestFilter,\n format: RGBAFormat,\n type: this.supportsHalfFloat ? HalfFloatType : (\n SupportsReadPixelsFloat ? FloatType : UnsignedByteType\n )\n }\n )\n this.holdTarget.texture.colorSpace = this.parameters.outputColorSpace\n\n this.compositeUniforms = {\n 'tForeground': new Uniform(this.sampleTarget.texture),\n 'scale': new Uniform(1.0)\n }\n\n this.compositeMaterial = new ShaderMaterial({\n uniforms: this.compositeUniforms,\n vertexShader: getShader('Quad.vert'),\n fragmentShader: getShader('Quad.frag'),\n premultipliedAlpha: true,\n transparent: true,\n blending: AdditiveBlending,\n depthTest: false,\n depthWrite: false\n })\n\n this.compositeCamera = new OrthographicCamera(-1, 1, 1, -1, 0, 1)\n this.compositeScene = new Scene()\n this.compositeScene.name = 'compositeScene'\n this.compositeScene.add(new Mesh(\n new PlaneGeometry(2, 2), this.compositeMaterial\n ))\n }\n\n private _initHelper () {\n const indices = new Uint16Array([\n 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6,\n 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7\n ])\n const positions = new Float32Array(8 * 3)\n\n const bbGeometry = new BufferGeometry()\n bbGeometry.setIndex(new BufferAttribute(indices, 1))\n bbGeometry.setAttribute('position', new BufferAttribute(positions, 3))\n const bbMaterial = new ShaderMaterial({\n uniforms: { 'uColor': { value: new Color('skyblue') } },\n vertexShader: getShader('BasicLine.vert'),\n fragmentShader: getShader('BasicLine.frag')\n })\n\n this.boundingBoxMesh = new LineSegments(bbGeometry, bbMaterial)\n this.helperGroup.add(this.boundingBoxMesh)\n }\n\n updateHelper () {\n const position = ((this.boundingBoxMesh.geometry as BufferGeometry).attributes as any).position // TODO\n const array = position.array\n const {min, max} = this.boundingBox\n\n array[ 0 ] = max.x; array[ 1 ] = max.y; array[ 2 ] = max.z\n array[ 3 ] = min.x; array[ 4 ] = max.y; array[ 5 ] = max.z\n array[ 6 ] = min.x; array[ 7 ] = min.y; array[ 8 ] = max.z\n array[ 9 ] = max.x; array[ 10 ] = min.y; array[ 11 ] = max.z\n array[ 12 ] = max.x; array[ 13 ] = max.y; array[ 14 ] = min.z\n array[ 15 ] = min.x; array[ 16 ] = max.y; array[ 17 ] = min.z\n array[ 18 ] = min.x; array[ 19 ] = min.y; array[ 20 ] = min.z\n array[ 21 ] = max.x; array[ 22 ] = min.y; array[ 23 ] = min.z\n\n position.needsUpdate = true\n\n if (!this.boundingBox.isEmpty()) {\n this.boundingBoxMesh.geometry.computeBoundingSphere()\n }\n }\n\n /** Distance from origin (lookAt point) */\n get cameraDistance(): number {\n return Math.abs(this.camera.position.z)\n }\n\n /** Set distance from origin (lookAt point); along the -z axis */\n set cameraDistance(d: number) {\n this.camera.position.z = -d\n }\n\n add (buffer: Buffer, instanceList?: BufferInstance[]) {\n // Log.time( \"Viewer.add\" );\n\n if (instanceList) {\n instanceList.forEach(instance => this.addBuffer(buffer, instance))\n } else {\n this.addBuffer(buffer)\n }\n\n buffer.group.name = 'meshGroup'\n buffer.wireframeGroup.name = 'wireframeGroup'\n if (buffer.parameters.background) {\n this.backgroundGroup.add(buffer.group)\n this.backgroundGroup.add(buffer.wireframeGroup)\n } else {\n this.modelGroup.add(buffer.group)\n this.modelGroup.add(buffer.wireframeGroup)\n }\n\n if (buffer.pickable) {\n this.pickingGroup.add(buffer.pickingGroup)\n }\n\n if (Debug) this.updateHelper()\n\n // Log.timeEnd( \"Viewer.add\" );\n }\n\n addBuffer (buffer: Buffer, instance?: BufferInstance) {\n // Log.time( \"Viewer.addBuffer\" );\n\n function setUserData (object: Object3D) {\n if (object instanceof Group) {\n object.children.forEach(setUserData)\n } else {\n object.userData.buffer = buffer\n object.userData.instance = instance\n object.onBeforeRender = onBeforeRender\n }\n }\n\n const mesh = buffer.getMesh()\n if (instance) {\n mesh.applyMatrix4(instance.matrix)\n }\n setUserData(mesh)\n buffer.group.add(mesh)\n\n const wireframeMesh = buffer.getWireframeMesh()\n if (instance) {\n // wireframeMesh.applyMatrix( instance.matrix );\n wireframeMesh.matrix.copy(mesh.matrix)\n wireframeMesh.position.copy(mesh.position)\n wireframeMesh.quaternion.copy(mesh.quaternion)\n wireframeMesh.scale.copy(mesh.scale)\n }\n setUserData(wireframeMesh)\n buffer.wireframeGroup.add(wireframeMesh)\n\n if (buffer.pickable) {\n const pickingMesh = buffer.getPickingMesh()\n if (instance) {\n // pickingMesh.applyMatrix( instance.matrix );\n pickingMesh.matrix.copy(mesh.matrix)\n pickingMesh.position.copy(mesh.position)\n pickingMesh.quaternion.copy(mesh.quaternion)\n pickingMesh.scale.copy(mesh.scale)\n }\n setUserData(pickingMesh)\n buffer.pickingGroup.add(pickingMesh)\n }\n\n if (instance) {\n this._updateBoundingBox(buffer.geometry, buffer.matrix, instance.matrix)\n } else {\n this._updateBoundingBox(buffer.geometry, buffer.matrix)\n }\n\n // Log.timeEnd( \"Viewer.addBuffer\" );\n }\n\n remove (buffer: Buffer) {\n this.translationGroup.children.forEach(function (group) {\n group.remove(buffer.group)\n group.remove(buffer.wireframeGroup)\n })\n\n if (buffer.pickable) {\n this.pickingGroup.remove(buffer.pickingGroup)\n }\n\n this.updateBoundingBox()\n if (Debug) this.updateHelper()\n\n // this.requestRender();\n }\n\n private _updateBoundingBox (geometry?: BufferGeometry, matrix?: Matrix4, instanceMatrix?: Matrix4) {\n const boundingBox = this.boundingBox\n\n function updateGeometry (geometry: BufferGeometry, matrix?: Matrix4, instanceMatrix?: Matrix4) {\n if (geometry.boundingBox == null) {\n geometry.computeBoundingBox()\n }\n\n const geoBoundingBox = (geometry.boundingBox as Box3).clone()\n\n if (matrix) {\n geoBoundingBox.applyMatrix4(matrix)\n }\n if (instanceMatrix) {\n geoBoundingBox.applyMatrix4(instanceMatrix)\n }\n\n if (geoBoundingBox.min.equals(geoBoundingBox.max)) {\n // mainly to give a single impostor geometry some volume\n // as it is only expanded in the shader on the GPU\n geoBoundingBox.expandByScalar(5)\n }\n\n boundingBox.union(geoBoundingBox)\n }\n\n function updateNode (node: Mesh) {\n if (node.geometry !== undefined) {\n let matrix, instanceMatrix\n if (node.userData.buffer) {\n matrix = node.userData.buffer.matrix\n }\n if (node.userData.instance) {\n instanceMatrix = node.userData.instance.matrix\n }\n updateGeometry(node.geometry as BufferGeometry, matrix, instanceMatrix) // TODO\n }\n }\n\n if (geometry) {\n updateGeometry(geometry, matrix, instanceMatrix)\n } else {\n boundingBox.makeEmpty()\n this.modelGroup.traverse(updateNode)\n this.backgroundGroup.traverse(updateNode)\n }\n\n boundingBox.getSize(this.boundingBoxSize)\n this.boundingBoxLength = this.boundingBoxSize.length()\n }\n\n updateBoundingBox () {\n this._updateBoundingBox()\n if (Debug) this.updateHelper()\n }\n\n getPickingPixels () {\n const {width, height} = this\n\n const n = width * height * 4\n const imgBuffer = SupportsReadPixelsFloat ? new Float32Array(n) : new Uint8Array(n)\n\n this.render(true)\n this.renderer.readRenderTargetPixels(\n this.pickingTarget, 0, 0, width, height, imgBuffer\n )\n\n return imgBuffer\n }\n\n getImage (picking: boolean) {\n return new Promise(resolve => {\n if (picking) {\n const {width, height} = this\n const n = width * height * 4\n let imgBuffer = this.getPickingPixels()\n\n if (SupportsReadPixelsFloat) {\n const imgBuffer2 = new Uint8Array(n)\n for (let i = 0; i < n; ++i) {\n imgBuffer2[ i ] = Math.round(imgBuffer[ i ] * 255)\n }\n imgBuffer = imgBuffer2\n }\n\n const canvas = document.createElement('canvas')\n canvas.width = width\n canvas.height = height\n const ctx = canvas.getContext('2d')! // TODO\n const imgData = ctx.getImageData(0, 0, width, height)\n imgData.data.set(imgBuffer as any) // TODO\n ctx.putImageData(imgData, 0, 0)\n canvas.toBlob(resolve as any, 'image/png') // TODO\n } else {\n this.renderer.domElement.toBlob(resolve as any, 'image/png') // TODO\n }\n })\n }\n\n makeImage (params: Partial = {}) {\n return makeImage(this, params)\n }\n\n setLight (color: Color|number|string, intensity: number, ambientColor: Color|number|string, ambientIntensity: number) {\n const p = this.parameters\n\n if (color !== undefined) p.lightColor.set(color as string) // TODO\n if (intensity !== undefined) p.lightIntensity = intensity\n if (ambientColor !== undefined) p.ambientColor.set(ambientColor as string) // TODO\n if (ambientIntensity !== undefined) p.ambientIntensity = ambientIntensity\n\n this.requestRender()\n }\n\n setFog (color?: Color|number|string, near?: number, far?: number) {\n const p = this.parameters\n\n if (color !== undefined) p.fogColor.set(color as string) // TODO\n if (near !== undefined) p.fogNear = near\n if (far !== undefined) p.fogFar = far\n\n this.requestRender()\n }\n\n setBackground (color?: Color|number|string) {\n const p = this.parameters\n\n if (color) p.backgroundColor.set(color as string) // TODO\n\n this.setFog(p.backgroundColor)\n this.renderer.setClearColor(p.backgroundColor, 0)\n this.renderer.domElement.style.backgroundColor = p.backgroundColor.getStyle()\n\n this.requestRender()\n }\n\n setSampling (level: number) {\n if (level !== undefined) {\n this.parameters.sampleLevel = level\n this.sampleLevel = level\n }\n\n this.requestRender()\n }\n\n /**\n * Set the output color encoding, i.e. how the renderer translates\n * colorspaces as it renders to the screen.\n\n * The default is LinearEncoding, because the internals of NGL are\n * already sRGB so no translation is needed to show sRGB colors.\n * Set to sRGBEncoding to create a linear workflow, and also call\n * `setColorEncoding(LinearEncoding)` to linearize colors on input.\n * @see setColorEncoding\n */\n private setOutputEncoding (colorspace: ColorSpace) {\n this.parameters.outputColorSpace = colorspace\n this.renderer.outputColorSpace = colorspace\n this.pickingTarget.texture.colorSpace = colorspace\n this.sampleTarget.texture.colorSpace = colorspace\n this.holdTarget.texture.colorSpace = colorspace\n }\n\n /**\n * Set the internal color workflow, linear or sRGB.\n * sRGB, the default, is more \"vibrant\" at the cost of accuracy.\n * Linear gives more accurate results, especially for transparent objects.\n * In all cases, the output is always sRGB; this just affects how colors are computed internally.\n * Call this just after creating the viewer, before loading any models.\n */\n setColorWorkflow (colorspace: ColorSpace) {\n if (colorspace != 'srgb-linear' && colorspace != 'srgb')\n throw new Error(`setColorWorkflow: invalid color workflow ${colorspace}`)\n setColorSpace(colorspace == 'srgb-linear' ? 'linear' : 'sRGB')\n this.setOutputEncoding(colorspace == 'srgb-linear' ? 'srgb' : 'srgb-linear')\n // Note: this doesn't rebuild models, so existing geometry will have\n // the old color encoding.\n this.requestRender()\n }\n\n setCamera (type: CameraType, fov?: number, eyeSep?: number) {\n const p = this.parameters\n\n if (type) p.cameraType = type\n if (fov) p.cameraFov = fov\n if (eyeSep) p.cameraEyeSep = eyeSep\n\n if (p.cameraType === 'orthographic') {\n if (this.camera !== this.orthographicCamera) {\n this.camera = this.orthographicCamera\n this.camera.position.copy(this.perspectiveCamera.position)\n this.camera.up.copy(this.perspectiveCamera.up)\n this.updateZoom()\n }\n } else if (p.cameraType === 'perspective' || p.cameraType === 'stereo') {\n if (this.camera !== this.perspectiveCamera) {\n this.camera = this.perspectiveCamera\n this.camera.position.copy(this.orthographicCamera.position)\n this.camera.up.copy(this.orthographicCamera.up)\n }\n } else {\n throw new Error(`Unknown cameraType '${p.cameraType}'`)\n }\n\n this.perspectiveCamera.fov = p.cameraFov\n this.stereoCamera.eyeSep = p.cameraEyeSep\n this.camera.updateProjectionMatrix()\n\n this.requestRender()\n }\n\n setClip (near: number, far: number, dist: number, clipMode?: string, clipScale?: string) {\n const p = this.parameters\n\n if (near !== undefined) p.clipNear = near\n if (far !== undefined) p.clipFar = far\n if (dist !== undefined) p.clipDist = dist\n if (clipMode !== undefined) p.clipMode = clipMode\n if (clipScale !== undefined) p.clipScale = clipScale\n\n this.requestRender()\n }\n\n setSize (width: number, height: number) {\n this.width = width || 1\n this.height = height || 1\n\n this.perspectiveCamera.aspect = this.width / this.height\n this.orthographicCamera.left = -this.width / 2\n this.orthographicCamera.right = this.width / 2\n this.orthographicCamera.top = this.height / 2\n this.orthographicCamera.bottom = -this.height / 2\n this.camera.updateProjectionMatrix()\n\n const dpr = window.devicePixelRatio\n\n this.renderer.setPixelRatio(dpr)\n this.renderer.setSize(width, height)\n\n const dprWidth = this.width * dpr\n const dprHeight = this.height * dpr\n\n this.pickingTarget.setSize(dprWidth, dprHeight)\n this.sampleTarget.setSize(dprWidth, dprHeight)\n this.holdTarget.setSize(dprWidth, dprHeight)\n\n this.requestRender()\n }\n\n handleResize () {\n if (this.container === document.body) {\n this.setSize(window.innerWidth, window.innerHeight)\n } else {\n const box = this.container.getBoundingClientRect()\n this.setSize(box.width, box.height)\n }\n }\n\n updateInfo (reset?: boolean) {\n const { memory, render } = this.info\n\n if (reset) {\n memory.programs = 0\n memory.geometries = 0\n memory.textures = 0\n\n render.calls = 0\n render.vertices = 0\n render.points = 0\n } else {\n const rInfo = this.renderer.info\n const rMemory = rInfo.memory\n const rRender = rInfo.render\n\n memory.geometries = rMemory.geometries\n memory.textures = rMemory.textures\n\n render.calls += rRender.calls\n render.faces += rRender.triangles\n render.points += rRender.points\n }\n }\n\n animate () {\n this.signals.ticked.dispatch(this.stats)\n const delta = window.performance.now() - this.stats.startTime\n\n if (delta > 500 && !this.isStill && this.sampleLevel < 3 && this.sampleLevel !== -1) {\n const currentSampleLevel = this.sampleLevel\n this.sampleLevel = 3\n this.renderPending = true\n this.render()\n this.isStill = true\n this.sampleLevel = currentSampleLevel\n if (Debug) Log.log('rendered still frame')\n }\n\n this.frameRequest = window.requestAnimationFrame(this.animate)\n }\n\n pick (x: number, y: number) {\n if (this.parameters.cameraType === 'stereo') {\n // TODO picking broken for stereo camera\n return {\n 'pid': 0,\n 'instance': undefined,\n 'picker': undefined\n }\n }\n\n x *= window.devicePixelRatio\n y *= window.devicePixelRatio\n\n x = Math.max(x - 2, 0)\n y = Math.max(y - 2, 0)\n\n let pid = 0, instance, picker\n const pixelBuffer = SupportsReadPixelsFloat ? pixelBufferFloat : pixelBufferUint\n\n this.render(true)\n this.renderer.readRenderTargetPixels(\n this.pickingTarget, x, y, 5, 5, pixelBuffer\n )\n\n for (let i = 0; i < pixelOrder.length; i++) {\n\n const offset = pixelOrder[i] * 4\n\n const oid = Math.round(pixelBuffer[ offset + 3 ])\n const object = this.pickingGroup.getObjectById(oid)\n if (object) {\n instance = object.userData.instance\n picker = object.userData.buffer.picking\n } else {\n continue\n }\n\n if (SupportsReadPixelsFloat) {\n pid =\n ((Math.round(pixelBuffer[offset] * 255) << 16) & 0xFF0000) |\n ((Math.round(pixelBuffer[offset + 1] * 255) << 8) & 0x00FF00) |\n ((Math.round(pixelBuffer[offset + 2] * 255)) & 0x0000FF)\n } else {\n pid =\n (pixelBuffer[offset] << 16) |\n (pixelBuffer[offset + 1] << 8) |\n (pixelBuffer[offset + 2])\n }\n }\n // if( Debug ){\n // const rgba = Array.apply( [], pixelBuffer );\n // Log.log( pixelBuffer );\n // Log.log(\n // \"picked color\",\n // rgba.map( c => { return c.toPrecision( 2 ) } )\n // );\n // Log.log( \"picked pid\", pid );\n // Log.log( \"picked oid\", oid );\n // Log.log( \"picked object\", object );\n // Log.log( \"picked instance\", instance );\n // Log.log( \"picked position\", x, y );\n // Log.log( \"devicePixelRatio\", window.devicePixelRatio );\n // }\n\n return { pid, instance, picker }\n }\n\n requestRender () {\n if (this.renderPending) {\n // Log.info(\"there is still a 'render' call pending\")\n return\n }\n\n // start gathering stats anew after inactivity\n if (window.performance.now() - this.stats.startTime > 22) {\n this.stats.begin()\n this.isStill = false\n }\n\n this.renderPending = true\n\n window.requestAnimationFrame(() => {\n this.render()\n this.stats.update()\n })\n }\n\n updateZoom () {\n const fov = degToRad(this.perspectiveCamera.fov)\n const height = 2 * Math.tan(fov / 2) * this.cameraDistance\n this.orthographicCamera.zoom = this.height / height\n }\n\n /**\n * Convert an absolute clip value to a relative one using bRadius.\n *\n * 0.0 -> 50.0\n * bRadius -> 0.0\n */\n absoluteToRelative (d: number) :number {\n return 50 * (1 - d / this.bRadius)\n }\n\n /**\n * Convert a relative clip value to an absolute one using bRadius\n *\n * 0.0 -> bRadius\n * 50.0 -> 0.0\n */\n relativeToAbsolute (d: number) : number {\n return this.bRadius * (1 - d / 50)\n }\n\n /**\n * Intepret clipMode, clipScale and set the camera and fog clipping.\n * Also ensures bRadius and cDist are valid\n */\n private __updateClipping () {\n const p = this.parameters\n\n // bRadius must always be updated for material-based clipping\n // and for focus calculations\n this.bRadius = Math.max(10, this.boundingBoxLength * 0.5)\n\n // FL: Removed below, but leaving commented as I don't understand intention\n // this.bRadius += this.boundingBox.getCenter(this.distVector).length()\n\n if (!isFinite(this.bRadius)) {\n this.bRadius = 50\n }\n\n this.camera.getWorldPosition(this.distVector)\n this.cDist = this.distVector.length()\n if (!this.cDist) {\n // recover from a broken (NaN) camera position\n this.cameraDistance = Math.abs(p.cameraZ)\n this.cDist = Math.abs(p.cameraZ)\n }\n\n // fog\n const fog = this.scene.fog as Fog\n fog.color.set(p.fogColor)\n\n if (p.clipMode === 'camera') {\n // Always interpret clipScale as absolute for clipMode camera\n\n this.camera.near = p.clipNear\n this.camera.far = p.clipFar\n fog.near = p.fogNear\n fog.far = p.fogFar\n\n } else {\n // scene mode\n\n if (p.clipScale === 'absolute') {\n // absolute scene mode; offset clip planes from scene center\n // (note: positive values move near plane towards camera and rear plane away)\n\n this.camera.near = this.cDist - p.clipNear\n this.camera.far = this.cDist + p.clipFar\n fog.near = this.cDist - p.fogNear\n fog.far = this.cDist + p.fogFar\n\n } else {\n // relative scene mode (default): convert pecentages to Angstroms\n\n const nearFactor = (50 - p.clipNear) / 50\n const farFactor = -(50 - p.clipFar) / 50\n this.camera.near = this.cDist - (this.bRadius * nearFactor)\n this.camera.far = this.cDist + (this.bRadius * farFactor)\n\n const fogNearFactor = (50 - p.fogNear) / 50\n const fogFarFactor = -(50 - p.fogFar) / 50\n fog.near = this.cDist - (this.bRadius * fogNearFactor)\n fog.far = this.cDist + (this.bRadius * fogFarFactor)\n }\n }\n\n if (p.clipMode !== 'camera') {\n\n if (this.camera.type === 'PerspectiveCamera') {\n\n this.camera.near = Math.max(0.1, p.clipDist, this.camera.near)\n this.camera.far = Math.max(1, this.camera.far)\n fog.near = Math.max(0.1, fog.near)\n fog.far = Math.max(1, fog.far)\n } else if (this.camera.type === 'OrthographicCamera') {\n\n if (p.clipDist > 0) {\n this.camera.near = Math.max(p.clipDist, this.camera.near)\n }\n }\n }\n }\n\n private __updateCamera () {\n const camera = this.camera\n camera.updateMatrix()\n camera.updateMatrixWorld(true)\n camera.updateProjectionMatrix()\n\n updateMaterialUniforms(this.scene, camera, this.renderer, this.cDist, this.bRadius)\n sortProjectedPosition(this.scene, camera)\n }\n\n private __setVisibility (model: boolean, picking: boolean, background: boolean, helper: boolean) {\n this.modelGroup.visible = model\n this.pickingGroup.visible = picking\n this.backgroundGroup.visible = background\n this.helperGroup.visible = helper\n }\n\n private __updateLights () {\n this.directionalLight.color.set(this.parameters.lightColor)\n this.directionalLight.intensity = this.parameters.lightIntensity\n\n this.distVector.copy(this.camera.position).setLength(this.boundingBoxLength * 100)\n this.directionalLight.position.copy(this.camera.position).add(this.distVector)\n\n this.ambientLight.color.set(this.parameters.ambientColor)\n this.ambientLight.intensity = this.parameters.ambientIntensity\n }\n\n private __renderPickingGroup (camera: PerspectiveCamera|OrthographicCamera) {\n this.renderer.setRenderTarget(this.pickingTarget || null)\n this.renderer.clear()\n this.__setVisibility(false, true, false, false)\n this.renderer.render(this.scene, camera)\n // back to standard render target\n this.renderer.setRenderTarget(null)\n this.updateInfo()\n\n // if (Debug) {\n // this.__setVisibility(false, true, false, true);\n\n // this.renderer.clear();\n // this.renderer.render(this.scene, camera);\n // }\n }\n\n private __renderModelGroup (camera: PerspectiveCamera|OrthographicCamera, renderTarget?: WebGLRenderTarget) {\n this.renderer.setRenderTarget(renderTarget || null)\n this.renderer.clear()\n this.__setVisibility(false, false, true, false)\n this.renderer.render(this.scene, camera)\n this.renderer.clear(false, true, true)\n this.updateInfo()\n\n this.__setVisibility(true, false, false, Debug)\n this.renderer.render(this.scene, camera)\n this.renderer.setRenderTarget(null) // set back to default canvas\n this.updateInfo()\n }\n\n private __renderSuperSample (camera: PerspectiveCamera|OrthographicCamera, renderTarget?: WebGLRenderTarget) {\n // based on the Supersample Anti-Aliasing Render Pass\n // contributed to three.js by bhouston / http://clara.io/\n //\n // This manual approach to SSAA re-renders the scene ones for\n // each sample with camera jitter and accumulates the results.\n // References: https://en.wikipedia.org/wiki/Supersampling\n const offsetList = JitterVectors[ Math.max(0, Math.min(this.sampleLevel, 5)) ]\n\n const baseSampleWeight = 1.0 / offsetList.length\n const roundingRange = 1 / 32\n\n this.compositeUniforms.tForeground.value = this.sampleTarget.texture\n\n let width = this.sampleTarget.width\n const height = this.sampleTarget.height\n if (this.parameters.cameraType === 'stereo') {\n width /= 2\n }\n\n // render the scene multiple times, each slightly jitter offset\n // from the last and accumulate the results.\n for (let i = 0; i < offsetList.length; ++i) {\n const offset = offsetList[ i ]\n camera.setViewOffset(\n width, height, offset[ 0 ], offset[ 1 ], width, height\n )\n camera.updateProjectionMatrix()\n updateCameraUniforms(this.scene, camera)\n\n let sampleWeight = baseSampleWeight\n // the theory is that equal weights for each sample lead to an\n // accumulation of rounding errors.\n // The following equation varies the sampleWeight per sample\n // so that it is uniformly distributed across a range of values\n // whose rounding errors cancel each other out.\n const uniformCenteredDistribution = -0.5 + (i + 0.5) / offsetList.length\n sampleWeight += roundingRange * uniformCenteredDistribution\n this.compositeUniforms.scale.value = sampleWeight\n\n this.__renderModelGroup(camera, this.sampleTarget)\n this.renderer.setRenderTarget(this.holdTarget)\n if (i === 0) {\n this.renderer.clear()\n }\n\n this.renderer.render(this.compositeScene, this.compositeCamera)\n }\n\n this.compositeUniforms.scale.value = 1.0\n this.compositeUniforms.tForeground.value = this.holdTarget.texture\n\n camera.clearViewOffset()\n this.renderer.setRenderTarget(renderTarget || null)\n this.renderer.clear()\n this.renderer.render(this.compositeScene, this.compositeCamera)\n }\n\n private __renderStereo (picking = false, _renderTarget?: WebGLRenderTarget) {\n const stereoCamera = this.stereoCamera\n stereoCamera.update(this.perspectiveCamera);\n\n const renderer = this.renderer\n let size = new Vector2()\n renderer.getSize(size)\n\n renderer.setScissorTest(true)\n\n renderer.setScissor(0, 0, size.width / 2, size.height)\n renderer.setViewport(0, 0, size.width / 2, size.height)\n updateCameraUniforms(this.scene, stereoCamera.cameraL)\n this.__render(picking, stereoCamera.cameraL)\n\n renderer.setScissor(size.width / 2, 0, size.width / 2, size.height)\n renderer.setViewport(size.width / 2, 0, size.width / 2, size.height)\n updateCameraUniforms(this.scene, stereoCamera.cameraR)\n this.__render(picking, stereoCamera.cameraR)\n\n renderer.setScissorTest(false)\n renderer.setViewport(0, 0, size.width, size.height)\n }\n\n private __render(picking = false, camera: PerspectiveCamera|OrthographicCamera, renderTarget?: WebGLRenderTarget) {\n if (picking) {\n if (!this.lastRenderedPicking) this.__renderPickingGroup(camera)\n } else if (this.sampleLevel > 0 && this.parameters.cameraType !== 'stereo') {\n // TODO super sample broken for stereo camera\n this.__renderSuperSample(camera, renderTarget)\n } else {\n this.__renderModelGroup(camera, renderTarget)\n }\n }\n\n render (picking = false, renderTarget?: WebGLRenderTarget) {\n if (this.rendering) {\n Log.warn(\"'tried to call 'render' from within 'render'\")\n return\n }\n\n // Log.time('Viewer.render')\n\n this.rendering = true\n\n try {\n this.__updateClipping()\n this.__updateCamera()\n this.__updateLights()\n this.updateInfo(true)\n\n // render\n if (this.parameters.cameraType === 'stereo') {\n this.__renderStereo(picking, renderTarget)\n } else {\n this.__render(picking, this.camera, renderTarget)\n }\n this.lastRenderedPicking = picking\n } finally {\n this.rendering = false\n this.renderPending = false\n }\n this.signals.rendered.dispatch()\n\n // Log.timeEnd('Viewer.render')\n // Log.log(this.info.memory, this.info.render)\n }\n\n clear () {\n Log.log('scene cleared')\n this.scene.remove(this.rotationGroup)\n this._initScene()\n this.renderer.clear()\n }\n\n dispose () {\n this.renderer.dispose()\n window.cancelAnimationFrame(this.frameRequest)\n }\n}\n","/**\n * @file Mouse Observer\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector2 } from 'three'\nimport { Signal } from 'signals'\n\nimport { LeftMouseButton, RightMouseButton } from '../constants'\nimport { defaults } from '../utils'\nimport Viewer from '../viewer/viewer'\nimport MouseControls from '../controls/mouse-controls'\n\ntype Optional = Pick, K> & Omit;\n\n/**\n * @example\n * mouseObserver.signals.scrolled.add( function( delta ){ ... } );\n *\n * @typedef {Object} MouseSignals\n * @property {Signal} moved - on move: deltaX, deltaY\n * @property {Signal} scrolled - on scroll: delta\n * @property {Signal} dragged - on drag: deltaX, deltaY\n * @property {Signal} dropped - on drop\n * @property {Signal} clicked - on click\n * @property {Signal} hovered - on hover\n */\n\nfunction getTouchDistance (event: TouchEvent) {\n const dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX\n const dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY\n return Math.sqrt(dx * dx + dy * dy)\n}\n\nfunction getMouseButtons (event: MouseEvent) {\n if (typeof event === 'object') {\n if ('buttons' in event) {\n return event.buttons\n } else if ('which' in event as any) {\n const b = (event as any).which\n if (b === 2) {\n return 4\n } else if (b === 3) {\n return 2\n } else if (b > 0) {\n return 1 << (b - 1)\n }\n } else if ('button' in event as any) {\n const b = (event as any).button\n if (b === 1) {\n return 4\n } else if (b === 2) {\n return 2\n } else if (b >= 0) {\n return 1 << b\n }\n }\n }\n return 0\n}\n\nexport interface MouseSignals {\n moved: Signal // on move: deltaX, deltaY\n scrolled: Signal // on scroll: delta\n dragged: Signal // on drag: deltaX, deltaY\n dropped: Signal // on drop\n clicked: Signal // on click\n hovered: Signal // on hover\n doubleClicked: Signal\n}\n\nexport interface MouseParams {\n hoverTimeout?: number\n handleScroll?:boolean\n doubleClickSpeed?: number\n}\n\n/**\n * Mouse observer\n *\n * @example\n * // listen to mouse moving (and touch-moving) events\n * mouseObserver.moved.moved.add( function( deltaX, deltaY ){ ... } );\n *\n * @example\n * // listen to scrolling (and pinching) events\n * mouseObserver.signals.scrolled.add( function( delta ){ ... } );\n *\n * @example\n * // listen to dragging (and touch-dragging) events\n * mouseObserver.signals.dragged.add( function( deltaX, deltaY ){ ... } );\n *\n * @example\n * // listen to clicking (and tapping) events\n * mouseObserver.signals.clicked.add( function(){ ... } );\n *\n * @example\n * // listen to double clicking (and double tapping) events\n * mouseObserver.signals.doubleClicked.add( function(){ ... } );\n *\n * @example\n * // listen to hovering events\n * mouseObserver.signals.hovered.add( function(){ ... } );\n */\nclass MouseObserver {\n signals: MouseSignals = {\n moved: new Signal(),\n scrolled: new Signal(),\n dragged: new Signal(),\n dropped: new Signal(),\n clicked: new Signal(),\n hovered: new Signal(),\n doubleClicked: new Signal()\n }\n\n hoverTimeout: number\n handleScroll:boolean\n doubleClickSpeed: number\n\n viewer: Viewer\n mouse: MouseObserver\n controls: MouseControls\n\n position = new Vector2() // Position on page\n prevPosition = new Vector2() // Previous position on page\n down = new Vector2() // Position on page when clicked\n canvasPosition = new Vector2() // Position on dom element\n prevClickCP = new Vector2()\n\n moving = false // Flag indicating if the mouse is moving\n hovering = true // Flag indicating if the mouse is hovering\n scrolled = false // Flag indicating if there was a scolling event since the last mouse move\n lastMoved = Infinity // Timestamp of last mouse move\n which? = 0 // 0: No button; 1: Left button; 2: Middle button; 3: Right button\n buttons? = 0 // 0: No button; 1: Left button; 2: Right button; 4: Middle button\n pressed? = false // Flag indicating if the mouse is pressed down\n altKey = false // Flag indicating if the alt key is pressed\n ctrlKey = false // Flag indicating if the ctrl key is pressed\n metaKey = false // Flag indicating if the meta key is pressed\n shiftKey = false // Flag indicating if the shift key is pressed\n\n doubleClickPending: boolean\n lastClicked: number\n overElement: boolean\n lastTouchDistance: number\n private frameRequest: number\n\n /**\n * @param {Element} domElement - the dom element to observe mouse events in\n * @param {Object} params - parameters object\n * @param {Integer} params.hoverTimeout - timeout in ms until the {@link MouseSignals.hovered}\n * signal is fired, set to -1 to ignore hovering\n * @param {Boolean} params.handleScroll - whether or not to handle scroll events\n * @param {Integer} params.doubleClickSpeed - max time in ms to trigger double click\n */\n constructor (readonly domElement: HTMLCanvasElement, params: MouseParams = {}) {\n this.domElement.style.touchAction = 'none'\n\n this.hoverTimeout = defaults(params.hoverTimeout, 50)\n this.handleScroll = defaults(params.handleScroll, true)\n this.doubleClickSpeed = defaults(params.doubleClickSpeed, 500)\n\n this._listen = this._listen.bind(this)\n this._onMousewheel = this._onMousewheel.bind(this)\n this._onMousemove = this._onMousemove.bind(this)\n this._onMousedown = this._onMousedown.bind(this)\n this._onMouseup = this._onMouseup.bind(this)\n this._onContextmenu = this._onContextmenu.bind(this)\n this._onTouchstart = this._onTouchstart.bind(this)\n this._onTouchend = this._onTouchend.bind(this)\n this._onTouchmove = this._onTouchmove.bind(this)\n\n this._listen()\n\n const opt = { passive: false } // treat as 'passive' so preventDefault can be called\n document.addEventListener('mousewheel', this._onMousewheel, opt)\n document.addEventListener('wheel', this._onMousewheel, opt)\n document.addEventListener('MozMousePixelScroll', this._onMousewheel, opt)\n document.addEventListener('mousemove', this._onMousemove, opt)\n document.addEventListener('mousedown', this._onMousedown, opt)\n document.addEventListener('mouseup', this._onMouseup, opt)\n document.addEventListener('contextmenu', this._onContextmenu, opt)\n document.addEventListener('touchstart', this._onTouchstart, opt)\n document.addEventListener('touchend', this._onTouchend, opt)\n document.addEventListener('touchmove', this._onTouchmove, opt)\n }\n\n get key () {\n let key = 0\n if (this.altKey) key += 1\n if (this.ctrlKey) key += 2\n if (this.metaKey) key += 4\n if (this.shiftKey) key += 8\n return key\n }\n\n setParameters (params: MouseParams = {}) {\n this.hoverTimeout = defaults(params.hoverTimeout, this.hoverTimeout)\n }\n\n /**\n * listen to mouse actions\n * @emits {MouseSignals.clicked} when clicked\n * @emits {MouseSignals.hovered} when hovered\n * @return {undefined}\n */\n _listen () {\n const now = window.performance.now()\n const cp = this.canvasPosition\n if (this.doubleClickPending && now - this.lastClicked > this.doubleClickSpeed) {\n this.doubleClickPending = false\n }\n if (now - this.lastMoved > this.hoverTimeout) {\n this.moving = false\n }\n if (this.scrolled || (!this.moving && !this.hovering)) {\n this.scrolled = false\n if (this.hoverTimeout !== -1 && this.overElement) {\n this.hovering = true\n this.signals.hovered.dispatch(cp.x, cp.y)\n }\n }\n this.frameRequest = window.requestAnimationFrame(this._listen)\n }\n\n /**\n * handle mouse scroll\n * @emits {MouseSignals.scrolled} when scrolled\n * @param {Event} event - mouse event\n * @return {undefined}\n */\n _onMousewheel(event: Optional & { wheelDelta?: number, wheelDeltaY?: number }) {\n if (event.target !== this.domElement || !this.handleScroll) {\n return\n }\n event.preventDefault()\n this._setKeys(event as any)\n\n let delta = 0\n // This has to be written in a particular way to handle old browsers that\n // all send events with different properties set in different ways.\n if ('deltaY' in event && 'deltaMode' in event &&\n event.deltaY !== undefined && event.deltaMode !== undefined) {\n // all modern browsers, using WheelEvent; deltaY + down (toward user)\n if (event.deltaMode === WheelEvent.DOM_DELTA_PIXEL)\n // everything except Firefox: normally 100 per wheel click\n delta = -event.deltaY * (2.5 / 100.0)\n else if (event.deltaMode === WheelEvent.DOM_DELTA_LINE)\n // Firefox in line mode, normally 3 per wheel click\n delta = -event.deltaY * (2.5 / 3.0)\n else // page mode: 1 per wheel click\n delta = -event.deltaY * 2.5\n } else if ('deltaY' in event && !('detail' in event)) {\n // Old Firefox or IE 11: deltaY but no deltaMode; treat as pixels\n delta = -event.deltaY * (2.5 / 100.0)\n } else if (event.wheelDelta !== undefined) {\n delta = -event.wheelDelta * (2.5 / 100)\n } else if (event.wheelDeltaY !== undefined) {\n delta = -event.wheelDeltaY * (2.5 / 100)\n } else if (event.detail !== undefined){\n // Old Firefox, MouseWheelEvent\n delta = -event.detail / 3\n }\n this.signals.scrolled.dispatch(delta)\n\n setTimeout(() => {\n this.scrolled = true\n }, this.hoverTimeout)\n }\n\n /**\n * handle mouse move\n * @emits {MouseSignals.moved} when moved\n * @emits {MouseSignals.dragged} when dragged\n * @param {Event} event - mouse event\n * @return {undefined}\n */\n _onMousemove (event: MouseEvent) {\n if (event.target === this.domElement) {\n event.preventDefault()\n this.overElement = true\n } else {\n this.overElement = false\n }\n this._setKeys(event)\n this.moving = true\n this.hovering = false\n this.lastMoved = window.performance.now()\n this.prevPosition.copy(this.position)\n this.position.set(event.clientX, event.clientY)\n this._setCanvasPosition(event)\n const dx = this.prevPosition.x - this.position.x\n const dy = this.prevPosition.y - this.position.y\n this.signals.moved.dispatch(dx, dy)\n if (this.pressed) {\n this.signals.dragged.dispatch(dx, dy)\n }\n }\n\n _onMousedown (event: MouseEvent) {\n if (event.target !== this.domElement) {\n return\n }\n event.preventDefault()\n this._setKeys(event)\n this.moving = false\n this.hovering = false\n this.down.set(event.clientX, event.clientY)\n this.position.set(event.clientX, event.clientY)\n this.which = event.which\n this.buttons = getMouseButtons(event)\n this.pressed = true\n this._setCanvasPosition(event)\n }\n\n /**\n * handle mouse up\n * @emits {MouseSignals.doubleClicked} when double clicked\n * @emits {MouseSignals.dropped} when dropped\n * @param {Event} event - mouse event\n * @return {undefined}\n */\n _onMouseup (event: MouseEvent) {\n if (event.target === this.domElement) {\n event.preventDefault()\n }\n this._setKeys(event)\n const cp = this.canvasPosition\n if (this._distance() < 4) {\n this.lastClicked = window.performance.now()\n if (this.doubleClickPending && this.prevClickCP.distanceTo(cp) < 4) {\n this.signals.doubleClicked.dispatch(cp.x, cp.y)\n this.doubleClickPending = false\n }\n this.signals.clicked.dispatch(cp.x, cp.y)\n this.doubleClickPending = true\n this.prevClickCP.copy(cp)\n }\n this.which = undefined\n this.buttons = undefined\n this.pressed = undefined\n // if (this._distance() > 3 || event.which === RightMouseButton) {\n // this.signals.dropped.dispatch();\n // }\n }\n\n _onContextmenu (event: MouseEvent) {\n if (event.target === this.domElement) {\n event.preventDefault()\n }\n }\n\n _onTouchstart (event: TouchEvent) {\n if (event.target !== this.domElement) {\n return\n }\n event.preventDefault()\n this.pressed = true\n switch (event.touches.length) {\n case 1: {\n this.moving = false\n this.hovering = false\n this.down.set(\n event.touches[ 0 ].pageX,\n event.touches[ 0 ].pageY\n )\n this.position.set(\n event.touches[ 0 ].pageX,\n event.touches[ 0 ].pageY\n )\n this._setCanvasPosition(event.touches[ 0 ])\n break\n }\n\n case 2: {\n this.down.set(\n (event.touches[ 0 ].pageX + event.touches[ 1 ].pageX) / 2,\n (event.touches[ 0 ].pageY + event.touches[ 1 ].pageY) / 2\n )\n this.position.set(\n (event.touches[ 0 ].pageX + event.touches[ 1 ].pageX) / 2,\n (event.touches[ 0 ].pageY + event.touches[ 1 ].pageY) / 2\n )\n this.lastTouchDistance = getTouchDistance(event)\n }\n }\n }\n\n _onTouchend (event: TouchEvent) {\n if (event.target === this.domElement) {\n event.preventDefault()\n }\n this.which = undefined\n this.buttons = undefined\n this.pressed = undefined\n }\n\n _onTouchmove (event: TouchEvent) {\n if (event.target === this.domElement) {\n event.preventDefault()\n this.overElement = true\n } else {\n this.overElement = false\n }\n switch (event.touches.length) {\n case 1: {\n this._setKeys(event)\n this.which = LeftMouseButton\n this.buttons = 1\n this.moving = true\n this.hovering = false\n this.lastMoved = window.performance.now()\n this.prevPosition.copy(this.position)\n this.position.set(\n event.touches[ 0 ].pageX,\n event.touches[ 0 ].pageY\n )\n this._setCanvasPosition(event.touches[ 0 ])\n const dx = this.prevPosition.x - this.position.x\n const dy = this.prevPosition.y - this.position.y\n this.signals.moved.dispatch(dx, dy)\n if (this.pressed) {\n this.signals.dragged.dispatch(dx, dy)\n }\n break\n }\n\n case 2: {\n const touchDistance = getTouchDistance(event)\n const delta = touchDistance - this.lastTouchDistance\n this.lastTouchDistance = touchDistance\n this.prevPosition.copy(this.position)\n this.position.set(\n (event.touches[ 0 ].pageX + event.touches[ 1 ].pageX) / 2,\n (event.touches[ 0 ].pageY + event.touches[ 1 ].pageY) / 2\n )\n if (Math.abs(delta) > 2 && this.handleScroll &&\n this.position.distanceTo(this.prevPosition) < 2\n ) {\n this.which = 0\n this.buttons = 0\n this.signals.scrolled.dispatch(delta / 2)\n } else {\n this.which = RightMouseButton\n this.buttons = 2\n const dx = this.prevPosition.x - this.position.x\n const dy = this.prevPosition.y - this.position.y\n this.signals.moved.dispatch(dx, dy)\n if (this.pressed) {\n this.signals.dragged.dispatch(dx, dy)\n }\n }\n }\n }\n }\n\n _distance () {\n return this.position.distanceTo(this.down)\n }\n\n _setCanvasPosition (event: any) { // TODO\n const box = this.domElement.getBoundingClientRect()\n let offsetX, offsetY;\n if ('clientX' in event && 'clientY' in event) {\n offsetX = event.clientX - box.left\n offsetY = event.clientY - box.top\n } else {\n offsetX = event.offsetX\n offsetY = event.offsetY\n }\n this.canvasPosition.set(offsetX, box.height - offsetY)\n }\n\n _setKeys (event: MouseEvent|TouchEvent) {\n this.altKey = event.altKey\n this.ctrlKey = event.ctrlKey\n this.metaKey = event.metaKey\n this.shiftKey = event.shiftKey\n }\n\n dispose () {\n document.removeEventListener('mousewheel', this._onMousewheel)\n document.removeEventListener('wheel', this._onMousewheel)\n document.removeEventListener('MozMousePixelScroll', this._onMousewheel)\n document.removeEventListener('mousemove', this._onMousemove)\n document.removeEventListener('mousedown', this._onMousedown)\n document.removeEventListener('mouseup', this._onMouseup)\n document.removeEventListener('contextmenu', this._onContextmenu)\n document.removeEventListener('touchstart', this._onTouchstart)\n document.removeEventListener('touchend', this._onTouchend)\n document.removeEventListener('touchmove', this._onTouchmove)\n window.cancelAnimationFrame(this.frameRequest)\n }\n}\n\nexport default MouseObserver\n","/**\n * @file Constants\n * @author Alexander Rose \n * @private\n */\n\nexport const LeftMouseButton = 1\nexport const MiddleMouseButton = 2\nexport const RightMouseButton = 3\n","/**\n * @file Trackball Controls\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3, Matrix4, Quaternion } from 'three'\n\nimport { defaults } from '../utils'\nimport Stage from '../stage/stage'\nimport MouseObserver from '../stage/mouse-observer'\nimport Viewer from '../viewer/viewer'\nimport ViewerControls from './viewer-controls'\nimport AtomProxy from '../proxy/atom-proxy';\nimport Component from '../component/component';\n\nconst tmpRotateXMatrix = new Matrix4()\nconst tmpRotateYMatrix = new Matrix4()\nconst tmpRotateZMatrix = new Matrix4()\nconst tmpRotateMatrix = new Matrix4()\nconst tmpRotateCameraMatrix = new Matrix4()\nconst tmpRotateVector = new Vector3()\nconst tmpRotateQuaternion = new Quaternion()\nconst tmpRotateQuaternion2 = new Quaternion()\nconst tmpPanMatrix = new Matrix4()\nconst tmpPanVector = new Vector3()\nconst tmpAtomVector = new Vector3()\n\nexport interface TrackballControlsParams {\n rotateSpeed?: number\n zoomSpeed?: number\n panSpeed?: number\n}\n\n/**\n * Trackball controls\n */\nclass TrackballControls {\n viewer: Viewer\n mouse: MouseObserver\n controls: ViewerControls\n\n rotateSpeed: number\n zoomSpeed: number\n panSpeed: number\n\n constructor (readonly stage: Stage, params: TrackballControlsParams = {}) {\n this.rotateSpeed = defaults(params.rotateSpeed, 2.0)\n this.zoomSpeed = defaults(params.zoomSpeed, 1.2)\n this.panSpeed = defaults(params.panSpeed, 1.0)\n\n this.viewer = stage.viewer\n this.mouse = stage.mouseObserver\n this.controls = stage.viewerControls\n }\n\n get component (): Component|undefined {\n return this.stage.transformComponent\n }\n\n get atom (): AtomProxy|undefined {\n return this.stage.transformAtom\n }\n\n private _setPanVector (x: number, y: number, z = 0) {\n const scaleFactor = this.controls.getCanvasScaleFactor(z)\n tmpPanVector.set(x, y, 0)\n tmpPanVector.multiplyScalar(this.panSpeed * scaleFactor)\n }\n\n private _getRotateXY (x: number, y: number) {\n return [\n this.rotateSpeed * -x * 0.01,\n this.rotateSpeed * y * 0.01\n ]\n }\n\n private _getCameraRotation(m: Matrix4) {\n m.extractRotation(this.viewer.camera.matrixWorld)\n m.multiply(tmpRotateYMatrix.makeRotationY(Math.PI))\n\n return m\n }\n\n private _transformPanVector () {\n if (!this.component) return\n\n // Adjust for component and scene rotation\n tmpPanMatrix.extractRotation(this.component.transform)\n tmpPanMatrix.premultiply(this.viewer.rotationGroup.matrix)\n tmpPanMatrix.invert()\n\n // Adjust for camera rotation\n tmpPanMatrix.multiply(this._getCameraRotation(tmpRotateMatrix))\n\n tmpPanVector.applyMatrix4(tmpPanMatrix)\n }\n\n zoom (delta: number) {\n this.controls.zoom(this.zoomSpeed * delta * 0.02)\n }\n\n pan (x: number, y: number) {\n this._setPanVector(x, y)\n\n // Adjust for scene rotation\n tmpPanMatrix.copy(this.viewer.rotationGroup.matrix).invert()\n\n // Adjust for camera rotation\n tmpPanMatrix.multiply(this._getCameraRotation(tmpRotateMatrix))\n\n tmpPanVector.applyMatrix4(tmpPanMatrix)\n this.controls.translate(tmpPanVector)\n }\n\n panComponent (x: number, y: number) {\n if (!this.component) return\n\n this._setPanVector(x, y)\n this._transformPanVector()\n\n this.component.position.add(tmpPanVector)\n this.component.updateMatrix()\n }\n\n panAtom (x: number, y: number) {\n if (!this.atom || !this.component) return\n\n this.atom.positionToVector3(tmpAtomVector)\n tmpAtomVector.add(this.viewer.translationGroup.position)\n tmpAtomVector.applyMatrix4(this.viewer.rotationGroup.matrix)\n\n this._setPanVector(x, y, tmpAtomVector.z)\n this._transformPanVector()\n\n this.atom.positionAdd(tmpPanVector)\n this.component.updateRepresentations({ 'position': true })\n }\n\n rotate (x: number, y: number) {\n const [ dx, dy ] = this._getRotateXY(x, y)\n\n // rotate around screen X then screen Y\n this._getCameraRotation(tmpRotateMatrix)\n tmpRotateVector.set(1, 0, 0) // X axis\n tmpRotateVector.applyMatrix4(tmpRotateMatrix) // screen X\n tmpRotateQuaternion.setFromAxisAngle(tmpRotateVector, dy)\n\n tmpRotateVector.set(0, 1, 0) // Y axis\n tmpRotateVector.applyMatrix4(tmpRotateMatrix) // screen Y\n tmpRotateQuaternion2.setFromAxisAngle(tmpRotateVector, dx)\n\n tmpRotateQuaternion.multiply(tmpRotateQuaternion2)\n tmpRotateMatrix.makeRotationFromQuaternion(tmpRotateQuaternion)\n this.controls.applyMatrix(tmpRotateMatrix)\n }\n\n zRotate (x: number, y: number) {\n const dz = this.rotateSpeed * ((-x + y) / -2) * 0.01\n\n tmpRotateZMatrix.makeRotationZ(dz)\n this.controls.applyMatrix(tmpRotateZMatrix)\n }\n\n rotateComponent (x: number, y: number) {\n if (!this.component) return\n\n const [ dx, dy ] = this._getRotateXY(x, y)\n\n this._getCameraRotation(tmpRotateCameraMatrix)\n\n tmpRotateMatrix.extractRotation(this.component.transform)\n tmpRotateMatrix.premultiply(this.viewer.rotationGroup.matrix)\n tmpRotateMatrix.invert()\n tmpRotateMatrix.premultiply(tmpRotateCameraMatrix)\n\n tmpRotateVector.set(1, 0, 0)\n tmpRotateVector.applyMatrix4(tmpRotateMatrix)\n tmpRotateXMatrix.makeRotationAxis(tmpRotateVector, dy)\n\n tmpRotateVector.set(0, 1, 0)\n tmpRotateVector.applyMatrix4(tmpRotateMatrix)\n tmpRotateYMatrix.makeRotationAxis(tmpRotateVector, dx)\n\n tmpRotateXMatrix.multiply(tmpRotateYMatrix)\n tmpRotateQuaternion.setFromRotationMatrix(tmpRotateXMatrix)\n this.component.quaternion.premultiply(tmpRotateQuaternion)\n this.component.quaternion.normalize()\n this.component.updateMatrix()\n }\n}\n\nexport default TrackballControls\n","/**\n * @file Picking Proxy\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3, Matrix4, Vector2 } from 'three'\n\nimport Stage from '../stage/stage'\nimport StructureComponent from '../component/structure-component'\nimport MouseObserver from '../stage/mouse-observer'\nimport { Picker } from '../utils/picker'\nimport ViewerControls from './viewer-controls'\nimport Shape from '../geometry/shape'\nimport Structure from '../structure/structure'\nimport BondProxy from '../proxy/bond-proxy'\nimport AtomProxy from '../proxy/atom-proxy'\nimport Surface from '../surface/surface'\nimport Volume from '../surface/volume'\nimport Unitcell from '../symmetry/unitcell'\nimport Component from '../component/component';\n\nconst tmpVec = new Vector3()\n\nexport interface ShapePrimitive {\n name: string\n shape: Shape\n}\n\nfunction closer (x: Vector3, a: Vector3, b: Vector3) {\n return x.distanceTo(a) < x.distanceTo(b)\n}\n\n/**\n * Picking data object.\n * @typedef {Object} PickingData - picking data\n * @property {Number} [pid] - picking id\n * @property {Object} [instance] - instance data\n * @property {Integer} instance.id - instance id\n * @property {String|Integer} instance.name - instance name\n * @property {Matrix4} instance.matrix - transformation matrix of the instance\n * @property {Picker} [picker] - picker object\n */\n\nexport interface InstanceData {\n id: number\n name: number|string\n matrix: Matrix4\n}\n\nexport interface PickingData {\n pid: number\n instance: InstanceData\n picker: Picker\n}\n\n/**\n * Picking proxy class.\n */\nclass PickingProxy {\n pid: number\n picker: Picker\n instance: InstanceData\n controls: ViewerControls\n mouse: MouseObserver\n\n /**\n * Create picking proxy object\n * @param {PickingData} pickingData - picking data\n * @param {Stage} stage - stage object\n */\n constructor (pickingData: PickingData, readonly stage: Stage) {\n this.pid = pickingData.pid\n this.picker = pickingData.picker\n\n /**\n * @type {Object}\n */\n this.instance = pickingData.instance\n\n /**\n * @type {Stage}\n */\n this.stage = stage\n /**\n * @type {ViewerControls}\n */\n this.controls = stage.viewerControls\n /**\n * @type {MouseObserver}\n */\n this.mouse = stage.mouseObserver\n }\n\n /**\n * Kind of the picked data\n * @type {String}\n */\n get type () { return this.picker.type }\n\n /**\n * If the `alt` key was pressed\n * @type {Boolean}\n */\n get altKey () { return this.mouse.altKey }\n /**\n * If the `ctrl` key was pressed\n * @type {Boolean}\n */\n get ctrlKey () { return this.mouse.ctrlKey }\n /**\n * If the `meta` key was pressed\n * @type {Boolean}\n */\n get metaKey () { return this.mouse.metaKey }\n /**\n * If the `shift` key was pressed\n * @type {Boolean}\n */\n get shiftKey () { return this.mouse.shiftKey }\n\n /**\n * Position of the mouse on the canvas\n * @type {Vector2}\n */\n get canvasPosition (): Vector2 { return this.mouse.canvasPosition }\n\n /**\n * The component the picked data is part of\n * @type {Component}\n */\n get component (): Component {\n return this.stage.getComponentsByObject(this.picker.data as any).list[ 0 ] // TODO\n }\n\n /**\n * The picked object data\n * @type {Object}\n */\n get object () {\n return this.picker.getObject(this.pid)\n }\n\n /**\n * The 3d position in the scene of the picked object\n * @type {Vector3}\n */\n get position () {\n return this.picker.getPosition(this.pid, this.instance, this.component)\n }\n\n /**\n * The atom of a picked bond that is closest to the mouse\n * @type {AtomProxy}\n */\n get closestBondAtom (): AtomProxy|undefined {\n if (this.type !== 'bond' || !this.bond) return undefined\n\n const bond = this.bond\n const controls = this.controls\n const cp = this.canvasPosition\n\n const v1 = bond.atom1.positionToVector3()\n const v2 = bond.atom2.positionToVector3()\n\n v1.applyMatrix4(this.component.matrix)\n v2.applyMatrix4(this.component.matrix)\n\n const acp1 = controls.getPositionOnCanvas(v1)\n const acp2 = controls.getPositionOnCanvas(v2)\n\n return closer(cp as any, acp1, acp2) ? bond.atom1 : bond.atom2\n }\n\n /**\n * Close-by atom\n * @type {AtomProxy}\n */\n get closeAtom (): AtomProxy|undefined {\n const cp = this.canvasPosition\n const ca = this.closestBondAtom\n if (!ca) return undefined\n\n const v = ca.positionToVector3().applyMatrix4(this.component.matrix)\n\n const acp = this.controls.getPositionOnCanvas(v)\n\n ca.positionToVector3(tmpVec)\n if (this.instance) tmpVec.applyMatrix4(this.instance.matrix)\n tmpVec.applyMatrix4(this.component.matrix)\n const viewer = this.controls.viewer\n tmpVec.add(viewer.translationGroup.position)\n tmpVec.applyMatrix4(viewer.rotationGroup.matrix)\n\n const scaleFactor = this.controls.getCanvasScaleFactor(tmpVec.z)\n const sc = this.component as StructureComponent\n const radius = sc.getMaxRepresentationRadius(ca.index)\n //console.log(scaleFactor, cp.distanceTo(acp), radius/scaleFactor, radius)\n\n if (cp.distanceTo(acp) <= radius/scaleFactor) {\n return ca\n } else {\n return undefined\n }\n }\n\n /**\n * @type {Object}\n */\n get arrow () { return this._objectIfType('arrow') as ShapePrimitive }\n /**\n * @type {AtomProxy}\n */\n get atom () { return this._objectIfType('atom') as AtomProxy }\n /**\n * @type {Object}\n */\n get axes () { return this._objectIfType('axes') }\n /**\n * @type {BondProxy}\n */\n get bond () { return this._objectIfType('bond') as BondProxy }\n /**\n * @type {Object}\n */\n get box () { return this._objectIfType('box') as ShapePrimitive }\n /**\n * @type {Object}\n */\n get cone () { return this._objectIfType('cone') as ShapePrimitive }\n /**\n * @type {Object}\n */\n get clash () { return this._objectIfType('clash') as { clash: { sele1: string, sele2: string } } }\n /**\n * @type {BondProxy}\n */\n get contact () { return this._objectIfType('contact') as { type: string, atom1: AtomProxy, atom2: AtomProxy } }\n /**\n * @type {Object}\n */\n get cylinder () { return this._objectIfType('cylinder') as ShapePrimitive }\n /**\n * @type {BondProxy}\n */\n get distance () { return this._objectIfType('distance') as BondProxy }\n /**\n * @type {Object}\n */\n get ellipsoid () { return this._objectIfType('ellipsoid') as ShapePrimitive }\n /**\n * @type {Object}\n */\n get octahedron () { return this._objectIfType('octahedron') as ShapePrimitive }\n /**\n * @type {Object}\n */\n get point () { return this._objectIfType('point') as ShapePrimitive }\n /**\n * @type {Object}\n */\n get mesh () { return this._objectIfType('mesh') as { name: string, shape: Shape, serial: number } }\n /**\n * @type {Object}\n */\n get slice () { return this._objectIfType('slice') as { volume: Volume, value: number } }\n /**\n * @type {Object}\n */\n get sphere () { return this._objectIfType('sphere') as ShapePrimitive }\n /**\n * @type {Object}\n */\n get tetrahedron () { return this._objectIfType('tetrahedron') as ShapePrimitive }\n /**\n * @type {Object}\n */\n get torus () { return this._objectIfType('torus') as ShapePrimitive }\n /**\n * @type {Object}\n */\n get surface () { return this._objectIfType('surface') as { surface: Surface, index: number } }\n /**\n * @type {Object}\n */\n get unitcell () { return this._objectIfType('unitcell') as { unitcell: Unitcell, structure: Structure } }\n /**\n * @type {Object}\n */\n get unknown () { return this._objectIfType('unknown') }\n /**\n * @type {Object}\n */\n get volume () { return this._objectIfType('volume') as { volume: Volume, value: number } }\n /**\n * @type {Object}\n */\n get wideline () { return this._objectIfType('wideline') as ShapePrimitive }\n\n _objectIfType (type: string) {\n return this.type === type ? this.object : undefined\n }\n\n getLabel () {\n const atom = this.atom || this.closeAtom\n let msg = 'nothing'\n if (this.arrow) {\n msg = this.arrow.name\n } else if (atom) {\n msg = `atom: ${atom.qualifiedName()} (${atom.structure.name})`\n } else if (this.axes) {\n msg = 'axes'\n } else if (this.bond) {\n msg = `bond: ${this.bond.atom1.qualifiedName()} - ${this.bond.atom2.qualifiedName()} (${this.bond.structure.name})`\n } else if (this.box) {\n msg = this.box.name\n } else if (this.cone) {\n msg = this.cone.name\n } else if (this.clash) {\n msg = `clash: ${this.clash.clash.sele1} - ${this.clash.clash.sele2}`\n } else if (this.contact) {\n msg = `${this.contact.type}: ${this.contact.atom1.qualifiedName()} - ${this.contact.atom2.qualifiedName()} (${this.contact.atom1.structure.name})`\n } else if (this.cylinder) {\n msg = this.cylinder.name\n } else if (this.distance) {\n msg = `distance: ${this.distance.atom1.qualifiedName()} - ${this.distance.atom2.qualifiedName()} (${this.distance.structure.name})`\n } else if (this.ellipsoid) {\n msg = this.ellipsoid.name\n } else if (this.octahedron) {\n msg = this.octahedron.name\n } else if (this.point) {\n msg = this.point.name\n } else if (this.mesh) {\n msg = `mesh: ${this.mesh.name || this.mesh.serial} (${this.mesh.shape.name})`\n } else if (this.slice) {\n msg = `slice: ${this.slice.value.toPrecision(3)} (${this.slice.volume.name})`\n } else if (this.sphere) {\n msg = this.sphere.name\n } else if (this.surface) {\n msg = `surface: ${this.surface.surface.name}`\n } else if (this.tetrahedron) {\n msg = this.tetrahedron.name\n } else if (this.torus) {\n msg = this.torus.name\n } else if (this.unitcell) {\n msg = `unitcell: ${this.unitcell.unitcell.spacegroup} (${this.unitcell.structure.name})`\n } else if (this.unknown) {\n msg = 'unknown'\n } else if (this.volume) {\n msg = `volume: ${this.volume.value.toPrecision(3)} (${this.volume.volume.name})`\n } else if (this.wideline) {\n msg = this.wideline.name\n }\n return msg\n }\n}\n\nexport default PickingProxy\n","/**\n * @file Picking Controls\n * @author Alexander Rose \n * @private\n */\n\nimport PickingProxy from './picking-proxy'\nimport Stage from '../stage/stage'\nimport Viewer from '../viewer/viewer'\n\n/**\n * Picking controls\n */\nclass PickingControls {\n viewer: Viewer\n\n constructor (readonly stage: Stage) {\n this.viewer = stage.viewer\n }\n\n /**\n * get picking data\n * @param {Number} x - canvas x coordinate\n * @param {Number} y - canvas y coordinate\n * @return {PickingProxy|undefined} picking proxy\n */\n pick (x: number, y: number) {\n const pickingData = this.viewer.pick(x, y)\n\n if (pickingData.picker &&\n pickingData.picker.type !== 'ignore' &&\n pickingData.pid !== undefined\n ) {\n const pickerArray = pickingData.picker.array\n if (pickerArray && pickingData.pid >= pickerArray.length) {\n console.error('pid >= picker.array.length')\n } else {\n return new PickingProxy(pickingData, this.stage)\n }\n }\n }\n}\n\nexport default PickingControls\n","/**\n * @file Viewer Controls\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector2, Vector3, Matrix4, Quaternion, OrthographicCamera } from 'three'\nimport * as signalsWrapper from 'signals'\n\nimport {\n ensureVector2, ensureVector3, ensureMatrix4, ensureQuaternion\n} from '../utils'\nimport { degToRad } from '../math/math-utils'\nimport Stage from '../stage/stage'\nimport Viewer from '../viewer/viewer'\n\n/**\n * Orientation matrix, a 4x4 transformation matrix with rotation part\n * used for scene rotation, scale part for scene camera distance and\n * position part for scene translation\n * @typedef {Matrix4} OrientationMatrix - orientation matrix\n */\n\nconst tmpQ = new Quaternion()\nconst tmpP = new Vector3()\nconst tmpS = new Vector3()\n\nconst tmpCanvasVector = new Vector3()\nconst tmpScaleVector = new Vector3()\nconst tmpRotateMatrix = new Matrix4()\nconst tmpRotateVector = new Vector3()\nconst tmpAlignMatrix = new Matrix4()\n\n/**\n * Viewer controls\n */\nclass ViewerControls {\n signals = {\n changed: new signalsWrapper.Signal()\n }\n\n viewer: Viewer\n\n /**\n * @param {Stage} stage - the stage object\n */\n constructor (readonly stage: Stage) {\n this.viewer = stage.viewer\n }\n\n /**\n * scene center position\n * @type {Vector3}\n */\n get position () {\n return this.viewer.translationGroup.position\n }\n\n /**\n * scene rotation\n * @type {Quaternion}\n */\n get rotation () {\n return this.viewer.rotationGroup.quaternion\n }\n\n /**\n * Trigger render and emit changed event\n * @emits {ViewerControls.signals.changed}\n * @return {undefined}\n */\n changed () {\n this.viewer.requestRender()\n this.signals.changed.dispatch()\n }\n\n getPositionOnCanvas (position: Vector3, optionalTarget?: Vector2) {\n const canvasPosition = ensureVector2(optionalTarget)\n const viewer = this.viewer\n\n tmpCanvasVector.copy(position)\n .add(viewer.translationGroup.position)\n .applyMatrix4(viewer.rotationGroup.matrix)\n .project(viewer.camera)\n\n return canvasPosition.set(\n (tmpCanvasVector.x + 1) * viewer.width / 2,\n (tmpCanvasVector.y + 1) * viewer.height / 2\n )\n }\n\n getCanvasScaleFactor (z = 0) {\n const camera = this.viewer.camera\n if (camera instanceof OrthographicCamera) {\n return 1 / camera.zoom\n } else {\n z = Math.abs(z)\n z += this.getCameraDistance()\n const fov = degToRad(camera.fov)\n const unitHeight = 2.0 * z * Math.tan(fov / 2)\n return unitHeight / this.viewer.height\n }\n }\n\n /**\n * get scene orientation\n * @param {Matrix4} optionalTarget - pre-allocated target matrix\n * @return {OrientationMatrix} scene orientation\n */\n getOrientation (optionalTarget?: Matrix4) {\n const m = ensureMatrix4(optionalTarget)\n\n m.copy(this.viewer.rotationGroup.matrix)\n const z = this.getCameraDistance()\n m.scale(tmpScaleVector.set(z, z, z))\n m.setPosition(this.viewer.translationGroup.position)\n\n return m\n }\n\n /**\n * set scene orientation\n * @param {OrientationMatrix|Array} orientation - scene orientation\n * @return {undefined}\n */\n orient (orientation?: Matrix4) {\n ensureMatrix4(orientation).decompose(tmpP, tmpQ, tmpS)\n\n const v = this.viewer\n v.rotationGroup.setRotationFromQuaternion(tmpQ)\n v.translationGroup.position.copy(tmpP)\n v.cameraDistance = tmpS.z\n v.updateZoom()\n this.changed()\n }\n\n /**\n * translate scene\n * @param {Vector3|Array} vector - translation vector\n * @return {undefined}\n */\n translate (vector: Vector3|number[]) {\n this.viewer.translationGroup.position\n .add(ensureVector3(vector))\n this.changed()\n }\n\n /**\n * center scene\n * @param {Vector3|Array} position - center position\n * @return {undefined}\n */\n center (position: Vector3|number[]) {\n this.viewer.translationGroup.position\n .copy(ensureVector3(position)).negate()\n this.changed()\n }\n\n /**\n * \"zoom\" scene by moving camera closer to origin\n * @param {Number} delta - zoom change\n * @return {undefined}\n */\n zoom (delta: number) {\n this.distance(this.getCameraDistance() * (1 - delta))\n }\n\n /**\n * get camera distance\n */\n getCameraDistance(): number {\n return this.viewer.cameraDistance\n }\n\n /**\n * camera distance\n * @param {Number} z - distance\n * @return {undefined}\n */\n distance (distance: number) {\n // Math.abs because distance used to be \"z\", normally negative.\n // Math.max to prevent us from getting _too_ close.\n this.viewer.cameraDistance = Math.max(Math.abs(distance), 0.2)\n this.viewer.updateZoom()\n this.changed()\n }\n\n /**\n * spin scene on axis\n * @param {Vector3|Array} axis - rotation axis\n * @param {Number} angle - amount to spin\n * @return {undefined}\n */\n spin (axis: Vector3|number[], angle: number) {\n tmpRotateMatrix.copy(this.viewer.rotationGroup.matrix).invert()\n tmpRotateVector\n .copy(ensureVector3(axis)).applyMatrix4(tmpRotateMatrix)\n\n this.viewer.rotationGroup.rotateOnAxis(tmpRotateVector, angle)\n this.changed()\n }\n\n /**\n * rotate scene\n * @param {Quaternion|Array} quaternion - rotation quaternion\n * @return {undefined}\n */\n rotate (quaternion: Quaternion|number[]) {\n this.viewer.rotationGroup\n .setRotationFromQuaternion(ensureQuaternion(quaternion))\n this.changed()\n }\n\n /**\n * align scene to basis matrix\n * @param {Matrix4|Array} basis - basis matrix\n * @return {undefined}\n */\n align (basis: Matrix4|number[]) {\n tmpAlignMatrix.copy(ensureMatrix4(basis)).invert()\n\n this.viewer.rotationGroup.setRotationFromMatrix(tmpAlignMatrix)\n this.changed()\n }\n\n /**\n * apply rotation matrix to scene\n * @param {Matrix4|Array} matrix - rotation matrix\n * @return {undefined}\n */\n applyMatrix (matrix: Matrix4|number[]) {\n this.viewer.rotationGroup.applyMatrix4(ensureMatrix4(matrix))\n this.changed()\n }\n}\n\nexport default ViewerControls\n","/**\n * @file Animation\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3, Quaternion } from 'three'\n\nimport { defaults, ensureVector3, ensureQuaternion } from '../utils'\nimport { lerp, smoothstep } from '../math/math-utils'\nimport ViewerControls from '../controls/viewer-controls'\nimport Stats from '../viewer/stats'\n\n/**\n * Animation. Base animation class.\n * @interface\n */\nabstract class Animation {\n duration: number\n controls: ViewerControls\n\n alpha: number\n startTime: number\n\n pausedTime = -1\n elapsedDuration = 0\n pausedDuration = 0\n ignoreGlobalToggle = false\n\n private _paused = false\n private _resolveList: Function[] = []\n private _hold: boolean\n\n constructor (duration: number|undefined, controls: ViewerControls, ...args: any[]) {\n this.duration = defaults(duration, 1000)\n this.controls = controls\n\n this.startTime = window.performance.now()\n\n this._init(...args)\n }\n\n /**\n * True when animation has finished\n */\n get done () {\n return this.alpha === 1\n }\n\n /**\n * True when animation is paused\n */\n get paused () {\n return this._paused\n }\n\n /**\n * init animation\n */\n abstract _init (...args: any[]): void\n\n /**\n * called on every tick\n */\n abstract _tick (stats?: Stats): void\n\n tick (stats: Stats) {\n if (this._paused) return\n\n this.elapsedDuration = stats.currentTime - this.startTime - this.pausedDuration\n\n if (this.duration === 0) {\n this.alpha = 1\n } else {\n this.alpha = smoothstep(0, 1, this.elapsedDuration / this.duration)\n }\n\n this._tick(stats)\n\n if (this.done) {\n this._resolveList.forEach(resolve => resolve())\n }\n\n return this.done\n }\n\n /**\n * Pause animation\n * @param {boolean} [hold] - put animation on a hold which\n * must be release before it can be resumed\n */\n pause (hold?: boolean) {\n if (hold) this._hold = true\n\n if (this.pausedTime === -1) {\n this.pausedTime = window.performance.now()\n }\n this._paused = true\n }\n\n /**\n * Resume animation\n * @param {Boolean} [releaseHold] - release a hold on the animation\n */\n resume (releaseHold?: boolean) {\n if (!releaseHold && this._hold) return\n\n this.pausedDuration += window.performance.now() - this.pausedTime\n this._paused = false\n this._hold = false\n this.pausedTime = -1\n }\n\n /**\n * Toggle animation\n */\n toggle () {\n if (this._paused) {\n this.resume()\n } else {\n this.pause()\n }\n }\n\n /**\n * Promise-like interface\n */\n then (callback: Function) {\n let p: Promise\n\n if (this.done) {\n p = Promise.resolve()\n } else {\n p = new Promise(resolve => this._resolveList.push(resolve))\n }\n\n return p.then(callback as any)\n }\n}\n\nexport default Animation\n\n/**\n * Spin animation. Spin around an axis.\n */\nexport class SpinAnimation extends Animation {\n axis: Vector3\n angle: number\n\n constructor (duration: number|undefined, controls: ViewerControls, ...args: any[]) {\n super(defaults(duration, Infinity), controls, ...args)\n }\n\n _init (axis: number[]|Vector3, angle: number) {\n if (Array.isArray(axis)) {\n this.axis = new Vector3().fromArray(axis)\n } else {\n this.axis = defaults(axis, new Vector3(0, 1, 0))\n }\n this.angle = defaults(angle, 0.01)\n }\n\n _tick (stats: Stats) {\n if (!this.axis || !this.angle) return\n\n this.controls.spin(\n this.axis, this.angle * stats.lastDuration / 16\n )\n }\n}\n\n/**\n * Rock animation. Rock around an axis.\n */\nexport class RockAnimation extends Animation {\n axis: Vector3\n angleStep: number\n angleEnd: number\n angleSum = 0\n direction = 1\n\n constructor (duration: number|undefined, controls: ViewerControls, ...args: any[]) {\n super(defaults(duration, Infinity), controls, ...args)\n }\n\n _init (axis: number[]|Vector3, angleStep: number, angleEnd: number) {\n if (Array.isArray(axis)) {\n this.axis = new Vector3().fromArray(axis)\n } else {\n this.axis = defaults(axis, new Vector3(0, 1, 0))\n }\n this.angleStep = defaults(angleStep, 0.01)\n this.angleEnd = defaults(angleEnd, 0.2)\n }\n\n _tick (stats: Stats) {\n if (!this.axis || !this.angleStep || !this.angleEnd) return\n\n const alpha = smoothstep(\n 0, 1, Math.abs(this.angleSum) / this.angleEnd\n )\n const angle = this.angleStep * this.direction * (1.1 - alpha)\n\n this.controls.spin(\n this.axis, angle * stats.lastDuration / 16\n )\n\n this.angleSum += this.angleStep\n\n if (this.angleSum >= this.angleEnd) {\n this.direction *= -1\n this.angleSum = -this.angleEnd\n }\n }\n}\n\n/**\n * Move animation. Move from one position to another.\n */\nexport class MoveAnimation extends Animation {\n moveFrom: Vector3\n moveTo: Vector3\n\n _init (moveFrom: number[]|Vector3, moveTo: number[]|Vector3) {\n this.moveFrom = ensureVector3(defaults(moveFrom, new Vector3()))\n this.moveTo = ensureVector3(defaults(moveTo, new Vector3()))\n }\n\n _tick (/* stats */) {\n this.controls.position.lerpVectors(\n this.moveFrom, this.moveTo, this.alpha\n ).negate()\n this.controls.changed()\n }\n}\n\n/**\n * Zoom animation. Gradually change the zoom level.\n */\nexport class ZoomAnimation extends Animation {\n zoomFrom: number\n zoomTo: number\n\n _init (zoomFrom: number, zoomTo: number) {\n this.zoomFrom = zoomFrom\n this.zoomTo = zoomTo\n }\n\n _tick () {\n this.controls.distance(lerp(this.zoomFrom, this.zoomTo, this.alpha))\n }\n}\n\n/**\n * Rotate animation. Rotate from one orientation to another.\n */\nexport class RotateAnimation extends Animation {\n rotateFrom: Quaternion\n rotateTo: Quaternion\n\n private _currentRotation = new Quaternion()\n\n _init (rotateFrom: number[]|Quaternion, rotateTo: number[]|Quaternion) {\n this.rotateFrom = ensureQuaternion(rotateFrom)\n this.rotateTo = ensureQuaternion(rotateTo)\n\n this._currentRotation = new Quaternion()\n }\n\n _tick () {\n this._currentRotation\n .copy(this.rotateFrom)\n .slerp(this.rotateTo, this.alpha)\n\n this.controls.rotate(this._currentRotation)\n }\n}\n\n/**\n * Value animation. Call callback with interpolated value.\n */\nexport class ValueAnimation extends Animation {\n valueFrom: number\n valueTo: number\n callback: Function\n\n _init (valueFrom: number, valueTo: number, callback: Function) {\n this.valueFrom = valueFrom\n this.valueTo = valueTo\n\n this.callback = callback\n }\n\n _tick (/* stats */) {\n this.callback(lerp(this.valueFrom, this.valueTo, this.alpha))\n }\n}\n\n/**\n * Timeout animation. Call callback after duration.\n */\nexport class TimeoutAnimation extends Animation {\n callback: Function\n\n _init (callback: Function) {\n this.callback = callback\n }\n\n _tick () {\n if (this.alpha === 1) this.callback()\n }\n}\n\n/**\n * Animation list.\n */\nexport class AnimationList {\n _list: Animation[]\n _resolveList: Function[] = []\n\n constructor (list: Animation[] = []) {\n this._list = list\n }\n\n /**\n * True when all animations have finished\n */\n get done () {\n return this._list.every(animation => {\n return animation.done\n })\n }\n\n /**\n * Promise-like interface\n */\n then (callback: Function) {\n let p: Promise\n\n if (this.done) {\n p = Promise.resolve()\n } else {\n p = new Promise(resolve => {\n this._resolveList.push(resolve)\n this._list.forEach(animation => {\n animation.then(() => {\n this._resolveList.forEach(callback => {\n callback()\n })\n this._resolveList.length = 0\n })\n })\n })\n }\n\n return p.then(callback as any)\n }\n}\n","/**\n * @file Animation Controls\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3, Quaternion, Matrix4 } from 'three'\n\nimport { ensureMatrix4 } from '../utils'\nimport Animation, {\n SpinAnimation, RockAnimation, MoveAnimation, ZoomAnimation,\n RotateAnimation, ValueAnimation, TimeoutAnimation, AnimationList\n} from '../animation/animation'\nimport Stage from '../stage/stage'\nimport Component from '../component/component'\nimport Viewer from '../viewer/viewer'\nimport Stats from '../viewer/stats'\nimport ViewerControls from './viewer-controls'\n\n/**\n * Animation controls\n */\nclass AnimationControls {\n viewer: Viewer\n controls: ViewerControls\n\n animationList: Animation[] = []\n finishedList: Animation[] = []\n\n /**\n * Create animation controls\n * @param {Stage} stage - the stage object\n */\n constructor (readonly stage: Stage) {\n this.viewer = stage.viewer\n this.controls = stage.viewerControls\n }\n\n /**\n * True when all animations are paused\n * @type {Boolean}\n */\n get paused () {\n return this.animationList.every((animation: Animation) => animation.paused)\n }\n\n /**\n * Add an animation\n */\n add (animation: Animation) {\n if (animation.duration === 0) {\n animation.tick(this.viewer.stats)\n } else {\n this.animationList.push(animation)\n }\n\n return animation\n }\n\n /**\n * Remove an animation\n */\n remove (animation: Animation) {\n const list = this.animationList\n const index = list.indexOf(animation)\n\n if (index > -1) {\n list.splice(index, 1)\n }\n }\n\n /**\n * Run all animations\n */\n run (stats: Stats) {\n const finishedList = this.finishedList\n const animationList = this.animationList\n\n const n = animationList.length\n for (let i = 0; i < n; ++i) {\n const animation = animationList[ i ]\n // tick returns true when finished\n if (animation.tick(stats)) {\n finishedList.push(animation)\n }\n }\n\n const m = finishedList.length\n if (m) {\n for (let j = 0; j < m; ++j) {\n this.remove(finishedList[ j ])\n }\n finishedList.length = 0\n }\n }\n\n /**\n * Add a spin animation\n * @param {Vector3} axis - axis to spin around\n * @param {Number} angle - amount to spin per frame, radians\n * @param {Number} duration - animation time in milliseconds\n * @return {SpinAnimation} the animation\n */\n spin (axis: Vector3|number[], angle?: number, duration?: number) {\n return this.add(\n new SpinAnimation(duration, this.controls, axis, angle)\n )\n }\n\n /**\n * Add a rock animation\n * @param {Vector3} axis - axis to rock around\n * @param {Number} angle - amount to spin per frame, radians\n * @param {Number} end - maximum extend of motion, radians\n * @param {Number} duration - animation time in milliseconds\n * @return {SpinAnimation} the animation\n */\n rock (axis: Vector3|number[], angle?: number, end?: number, duration?: number) {\n return this.add(\n new RockAnimation(duration, this.controls, axis, angle, end)\n )\n }\n\n /**\n * Add a rotate animation\n * @param {Quaternion} rotateTo - target rotation\n * @param {Number} duration - animation time in milliseconds\n * @return {RotateAnimation} the animation\n */\n rotate (rotateTo: Quaternion|number[], duration?: number) {\n const rotateFrom = this.viewer.rotationGroup.quaternion.clone()\n\n return this.add(\n new RotateAnimation(duration, this.controls, rotateFrom, rotateTo)\n )\n }\n\n /**\n * Add a move animation\n * @param {Vector3} moveTo - target position\n * @param {Number} duration - animation time in milliseconds\n * @return {MoveAnimation} the animation\n */\n move (moveTo: Vector3|number[], duration?: number) {\n const moveFrom = this.controls.position.clone().negate()\n\n return this.add(\n new MoveAnimation(duration, this.controls, moveFrom, moveTo)\n )\n }\n\n /**\n * Add a zoom animation\n * @param {Number} zoomTo - target distance\n * @param {Number} duration - animation time in milliseconds\n * @return {ZoomAnimation} the animation\n */\n zoom (zoomTo: number, duration?: number) {\n const zoomFrom = this.viewer.camera.position.z\n\n return this.add(\n new ZoomAnimation(duration, this.controls, zoomFrom, zoomTo)\n )\n }\n\n /**\n * Add a zoom and a move animation\n * @param {Vector3} moveTo - target position\n * @param {Number} zoomTo - target distance\n * @param {Number} duration - animation time in milliseconds\n * @return {Array} the animations\n */\n zoomMove (moveTo: Vector3, zoomTo: number, duration?: number) {\n return new AnimationList([\n this.move(moveTo, duration),\n this.zoom(zoomTo, duration)\n ])\n }\n\n /**\n * Add an orient animation\n * @param {OrientationMatrix|Array} orientTo - target orientation\n * @param {Number} duration - animation time in milliseconds\n * @return {Array} the animations\n */\n orient (orientTo: Matrix4|number[], duration?: number) {\n const p = new Vector3()\n const q = new Quaternion()\n const s = new Vector3()\n\n ensureMatrix4(orientTo).decompose(p, q, s)\n\n return new AnimationList([\n this.move(p.negate(), duration),\n this.rotate(q, duration),\n this.zoom(-s.x, duration)\n ])\n }\n\n /**\n * Add a value animation\n * @param {Number} valueFrom - start value\n * @param {Number} valueTo - target value\n * @param {Function} callback - called on every tick\n * @param {Number} duration - animation time in milliseconds\n * @return {ValueAnimation} the animation\n */\n value (valueFrom: number, valueTo: number, callback: Function, duration?: number) {\n return this.add(\n new ValueAnimation(duration, this.controls, valueFrom, valueTo, callback)\n )\n }\n\n /**\n * Add a timeout animation\n * @param {Function} callback - called after duration\n * @param {Number} duration - timeout in milliseconds\n * @return {TimeoutAnimation} the animation\n */\n timeout (callback: Function, duration?: number) {\n return this.add(\n new TimeoutAnimation(duration, this.controls, callback)\n )\n }\n\n /**\n * Add a component spin animation\n * @param {Component} component - object to move\n * @param {Vector3} axis - axis to spin around\n * @param {Number} angle - amount to spin per frame, radians\n * @param {Number} duration - animation time in milliseconds\n * @return {SpinAnimation} the animation\n */\n spinComponent (component: Component, axis?: Vector3|number[], angle?: number, duration?: number) {\n return this.add(\n // TODO\n new SpinAnimation(duration, component.controls as any, axis, angle)\n )\n }\n\n /**\n * Add a component rock animation\n * @param {Component} component - object to move\n * @param {Vector3} axis - axis to rock around\n * @param {Number} angle - amount to spin per frame, radians\n * @param {Number} end - maximum extend of motion, radians\n * @param {Number} duration - animation time in milliseconds\n * @return {SpinAnimation} the animation\n */\n rockComponent (component: Component, axis: Vector3|number[], angle?: number, end?: number, duration?: number) {\n return this.add(\n // TODO\n new RockAnimation(duration, component.controls as any, axis, angle, end)\n )\n }\n\n /**\n * Add a component move animation\n * @param {Component} component - object to move\n * @param {Vector3} moveTo - target position\n * @param {Number} duration - animation time in milliseconds\n * @return {MoveAnimation} the animation\n */\n moveComponent (component: Component, moveTo: Vector3|number[], duration?: number) {\n const moveFrom = component.controls.position.clone().negate()\n\n return this.add(\n // TODO\n new MoveAnimation(duration, component.controls as any, moveFrom, moveTo)\n )\n }\n\n /**\n * Pause all animations\n * @return {undefined}\n */\n pause () {\n this.animationList.forEach(animation => animation.pause())\n }\n\n /**\n * Resume all animations\n * @return {undefined}\n */\n resume () {\n this.animationList.forEach(animation => animation.resume())\n }\n\n /**\n * Toggle all animations\n * @return {undefined}\n */\n toggle () {\n if (this.paused) {\n this.resume()\n } else {\n this.pause()\n }\n }\n\n /**\n * Clear all animations\n * @return {undefined}\n */\n clear () {\n this.animationList.length = 0\n }\n\n dispose () {\n this.clear()\n }\n}\n\nexport default AnimationControls\n","/**\n * @file Queue\n * @author Alexander Rose \n * @private\n */\n\nclass Queue {\n queue: T[] = []\n pending = false\n\n constructor(readonly fn: Function, argList?: T[]) {\n this.next = this.next.bind(this)\n\n if (argList) {\n for (let i = 0, il = argList.length; i < il; ++i) {\n this.queue.push(argList[ i ])\n }\n this.next()\n }\n }\n\n private run (arg: any) {\n this.fn(arg, this.next)\n }\n\n private next () {\n const arg = this.queue.shift()\n if (arg !== undefined) {\n this.pending = true\n setTimeout(() => this.run(arg))\n } else {\n this.pending = false\n }\n }\n\n push (arg: T) {\n this.queue.push(arg)\n if (!this.pending) this.next()\n }\n\n kill () {\n this.queue.length = 0\n }\n\n length () {\n return this.queue.length\n }\n}\n\nexport default Queue\n","/**\n * @file Representation\n * @author Alexander Rose \n * @private\n */\n\nimport { Color, Vector3, Matrix4 } from 'three'\n\nimport { Debug, Log, ColormakerRegistry, ExtensionFragDepth } from '../globals'\nimport { defaults } from '../utils'\nimport Queue from '../utils/queue'\nimport Counter from '../utils/counter'\nimport Viewer from '../viewer/viewer'\nimport { BufferParameters, BufferSide, default as Buffer } from '../buffer/buffer';\nimport { ColorData, ColormakerParameters, ColorMode } from '../color/colormaker';\nimport { GenericColor } from '../types'\n\nexport interface RepresentationParameters {\n name: string\n lazy: boolean,\n clipNear: number,\n clipRadius: number,\n clipCenter: Vector3,\n flatShaded: boolean,\n opacity: number,\n depthWrite: boolean,\n side: BufferSide,\n wireframe: boolean,\n colorData: ColorData,\n colorScheme: string,\n colorScale: string | number[],\n colorReverse: boolean,\n colorValue: GenericColor,\n colorDomain: number[],\n colorMode: ColorMode,\n colorSpace: 'sRGB' | 'linear',\n roughness: number,\n metalness: number,\n diffuse: GenericColor,\n diffuseInterior: boolean,\n useInteriorColor: boolean,\n interiorColor: GenericColor,\n interiorDarkening: number,\n disablePicking: boolean,\n matrix: Matrix4\n quality: string,\n visible: boolean,\n color: GenericColor,\n sphereDetail: number,\n radialSegments: number,\n openEnded: boolean\n disableImpostor: boolean\n [key: string]: any//boolean | number | undefined | Color | string | Vector3 | Matrix4 | number[]\n}\n/**\n * Representation parameter object.\n * @typedef {Object} RepresentationParameters - representation parameters\n * @property {Boolean} [lazy] - only build & update the representation when visible\n * otherwise defer changes until set visible again\n * @property {Integer} [clipNear] - position of camera near/front clipping plane\n * in percent of scene bounding box\n * @property {Integer} [clipRadius] - radius of clipping sphere\n * @property {Vector3} [clipCenter] - position of for spherical clipping\n * @property {Boolean} [flatShaded] - render flat shaded\n * @property {Float} [opacity] - translucency: 1 is fully opaque, 0 is fully transparent\n * @property {Boolean} [depthWrite] - depth write\n * @property {String} [side] - which triangle sides to render, \"front\" front-side,\n * \"back\" back-side, \"double\" front- and back-side\n * @property {Boolean} [wireframe] - render as wireframe\n * @property {ColorData} [colorData] - atom or bond indexed data for coloring\n * @property {String} [colorScheme] - color scheme\n * @property {String} [colorScale] - color scale, either a string for a\n * predefined scale or an array of\n * colors to be used as the scale\n * @property {Boolean} [colorReverse] - reverse color scale\n * @property {Color} [colorValue] - color value\n * @property {Integer[]} [colorDomain] - scale value range\n * @property {Integer} colorDomain.0 - min value\n * @property {Integer} colorDomain.1 - max value\n * @property {String} [colorMode] - color mode, one of rgb, hsv, hsl, hsi, lab, hcl\n * @property {Float} [roughness] - how rough the material is, between 0 and 1\n * @property {Float} [metalness] - how metallic the material is, between 0 and 1\n * @property {Color} [diffuse] - diffuse color for lighting\n * @property {Boolean} [diffuseInterior] - diffuse interior, i.e. ignore normal\n * @property {Boolean} [useInteriorColor] - use interior color\n * @property {Color} [interiorColor] - interior color\n * @property {Float} [interiorDarkening] - interior darkening: 0 no darking, 1 fully darkened\n * @property {Boolean} [disablePicking] - disable picking\n */\n\n/**\n * Representation object\n * @interface\n * @param {Object} object - the object to be represented\n * @param {Viewer} viewer - a viewer object\n * @param {RepresentationParameters} [params] - representation parameters\n */\nclass Representation {\n parameters: any\n type: string\n viewer: Viewer\n tasks: Counter\n private queue: Queue\n bufferList: Buffer[]\n\n lazy: boolean\n lazyProps: { build: boolean, bufferParams: BufferParameters | {}, what: {}}\n protected name: string\n protected clipNear: number\n protected clipRadius: number\n protected clipCenter: Vector3\n protected flatShaded: boolean\n protected opacity: number\n protected depthWrite: boolean\n protected side: BufferSide\n protected wireframe: boolean\n protected colorData: ColorData\n protected colorScheme: string\n protected colorScale: string | string[]\n protected colorReverse: boolean\n protected colorValue: number\n protected colorDomain: number[]\n protected colorMode: ColorMode\n protected roughness: number\n protected metalness: number\n protected diffuse: GenericColor\n protected diffuseInterior?: boolean\n protected useInteriorColor?: boolean\n protected interiorColor: GenericColor\n protected interiorDarkening: number\n protected disablePicking: boolean\n protected sphereDetail: number\n protected radialSegments: number\n protected openEnded: boolean\n protected disableImpostor: boolean\n protected disposed: boolean\n\n protected matrix: Matrix4\n\n private quality: string\n visible: boolean\n\n protected manualAttach: ()=> any\n\n protected toBePrepared: boolean\n\n [key: string]: any\n\n constructor (object: any, viewer: Viewer, params: Partial) {\n // eslint-disable-next-line no-unused-vars\n // const p = params || {}\n\n this.type = ''\n\n this.parameters = {\n\n lazy: {\n type: 'boolean'\n },\n\n clipNear: {\n type: 'range', step: 1, max: 100, min: 0, buffer: true\n },\n clipRadius: {\n type: 'number', precision: 1, max: 1000, min: 0, buffer: true\n },\n clipCenter: {\n type: 'vector3', precision: 1, buffer: true\n },\n flatShaded: {\n type: 'boolean', buffer: true\n },\n opacity: {\n type: 'range', step: 0.01, max: 1, min: 0, buffer: true\n },\n depthWrite: {\n type: 'boolean', buffer: true\n },\n side: {\n type: 'select',\n buffer: true,\n options: { front: 'front', back: 'back', double: 'double' }\n },\n wireframe: {\n type: 'boolean', buffer: true\n },\n\n colorData: {\n type: 'hidden',\n update: 'color',\n },\n\n colorScheme: {\n type: 'select',\n update: 'color',\n options: {}\n },\n colorScale: {\n type: 'select',\n update: 'color',\n options: ColormakerRegistry.getScales()\n },\n colorReverse: {\n type: 'boolean', update: 'color'\n },\n colorValue: {\n type: 'color', update: 'color'\n },\n colorDomain: {\n type: 'hidden', update: 'color'\n },\n colorMode: {\n type: 'select',\n update: 'color',\n options: ColormakerRegistry.getModes()\n },\n\n roughness: {\n type: 'range', step: 0.01, max: 1, min: 0, buffer: true\n },\n metalness: {\n type: 'range', step: 0.01, max: 1, min: 0, buffer: true\n },\n diffuse: {\n type: 'color', buffer: true\n },\n\n diffuseInterior: {\n type: 'boolean', buffer: true\n },\n useInteriorColor: {\n type: 'boolean', buffer: true\n },\n interiorColor: {\n type: 'color', buffer: true\n },\n interiorDarkening: {\n type: 'range', step: 0.01, max: 1, min: 0, buffer: true\n },\n\n matrix: {\n type: 'hidden', buffer: true\n },\n\n disablePicking: {\n type: 'boolean', rebuild: true\n }\n\n }\n\n /**\n * @type {Viewer}\n */\n this.viewer = viewer\n\n /**\n * Counter that keeps track of tasks related to the creation of\n * the representation, including surface calculations.\n * @type {Counter}\n */\n this.tasks = new Counter()\n\n /**\n * @type {Queue}\n * @private\n */\n this.queue = new Queue(this.make.bind(this))\n\n /**\n * @type {Array}\n * @private\n */\n this.bufferList = []\n\n if (this.parameters.colorScheme) {\n this.parameters.colorScheme.options = ColormakerRegistry.getSchemes()\n }\n\n this.toBePrepared = false\n }\n\n init (params: Partial) {\n const p = params || {}\n\n this.clipNear = defaults(p.clipNear, 0)\n this.clipRadius = defaults(p.clipRadius, 0)\n this.clipCenter = defaults(p.clipCenter, new Vector3())\n this.flatShaded = defaults(p.flatShaded, false)\n this.side = defaults(p.side, 'double')\n this.opacity = defaults(p.opacity, 1.0)\n this.depthWrite = defaults(p.depthWrite, true)\n this.wireframe = defaults(p.wireframe, false)\n\n this.setColor(p.color, p)\n\n this.colorData = defaults(p.colorData, undefined)\n this.colorScheme = defaults(p.colorScheme, 'uniform')\n this.colorScale = defaults(p.colorScale, '')\n this.colorReverse = defaults(p.colorReverse, false)\n this.colorValue = defaults(p.colorValue, 0x909090)\n this.colorDomain = defaults(p.colorDomain, undefined)\n this.colorMode = defaults(p.colorMode, 'hcl')\n\n this.visible = defaults(p.visible, true)\n this.quality = defaults(p.quality, undefined)\n\n this.roughness = defaults(p.roughness, 0.4)\n this.metalness = defaults(p.metalness, 0.0)\n this.diffuse = defaults(p.diffuse, 0xffffff)\n\n this.diffuseInterior = defaults(p.diffuseInterior, false)\n this.useInteriorColor = defaults(p.useInteriorColor, false)\n this.interiorColor = defaults(p.interiorColor, 0x222222)\n this.interiorDarkening = defaults(p.interiorDarkening, 0)\n\n this.lazy = defaults(p.lazy, false)\n this.lazyProps = {\n build: false,\n bufferParams: {},\n what: {}\n }\n\n this.matrix = defaults(p.matrix, new Matrix4())\n\n this.disablePicking = defaults(p.disablePicking, false)\n\n // handle common parameters when applicable\n\n const tp = this.parameters\n\n if (tp.sphereDetail === true) {\n tp.sphereDetail = {\n type: 'integer', max: 3, min: 0, rebuild: 'impostor'\n }\n }\n if (tp.radialSegments === true) {\n tp.radialSegments = {\n type: 'integer', max: 25, min: 5, rebuild: 'impostor'\n }\n }\n if (tp.openEnded === true) {\n tp.openEnded = {\n type: 'boolean', rebuild: 'impostor', buffer: true\n }\n }\n if (tp.disableImpostor === true) {\n tp.disableImpostor = {\n type: 'boolean', rebuild: true\n }\n }\n\n if (p.quality === 'low') {\n if (tp.sphereDetail) this.sphereDetail = 0\n if (tp.radialSegments) this.radialSegments = 5\n } else if (p.quality === 'medium') {\n if (tp.sphereDetail) this.sphereDetail = 1\n if (tp.radialSegments) this.radialSegments = 10\n } else if (p.quality === 'high') {\n if (tp.sphereDetail) this.sphereDetail = 2\n if (tp.radialSegments) this.radialSegments = 20\n } else {\n if (tp.sphereDetail) {\n this.sphereDetail = defaults(p.sphereDetail, 1)\n }\n if (tp.radialSegments) {\n this.radialSegments = defaults(p.radialSegments, 10)\n }\n }\n\n if (tp.openEnded) {\n this.openEnded = defaults(p.openEnded, true)\n }\n\n if (tp.disableImpostor) {\n this.disableImpostor = defaults(p.disableImpostor, false)\n }\n\n }\n\n getColorParams (p?: {[k: string]: any}): { scheme: string, [k: string]: any } & ColormakerParameters {\n return Object.assign({\n\n data: this.colorData,\n scheme: this.colorScheme,\n scale: this.colorScale,\n reverse: this.colorReverse,\n value: this.colorValue,\n domain: this.colorDomain,\n mode: this.colorMode,\n colorSpace: this.colorSpace,\n\n }, p)\n }\n\n getBufferParams (p: {[k: string]: any} = {}) {\n return Object.assign({\n\n clipNear: this.clipNear,\n clipRadius: this.clipRadius,\n clipCenter: this.clipCenter,\n flatShaded: this.flatShaded,\n opacity: this.opacity,\n depthWrite: this.depthWrite,\n side: this.side,\n wireframe: this.wireframe,\n\n roughness: this.roughness,\n metalness: this.metalness,\n diffuse: this.diffuse,\n\n diffuseInterior: this.diffuseInterior,\n useInteriorColor: this.useInteriorColor,\n interiorColor: this.interiorColor,\n interiorDarkening: this.interiorDarkening,\n\n matrix: this.matrix,\n\n disablePicking: this.disablePicking\n\n }, p)\n }\n\n setColor (value: number | string | Color | undefined , p?: Partial) {\n const types = Object.keys(ColormakerRegistry.getSchemes())\n\n if (typeof value === 'string' && types.includes(value.toLowerCase())) {\n if (p) {\n p.colorScheme = value\n } else {\n this.setParameters({ colorScheme: value })\n }\n } else if (value !== undefined) {\n let val = new Color(value as string).getHex() //TODO\n if (p) {\n p.colorScheme = 'uniform'\n p.colorValue = val\n } else {\n this.setParameters({\n colorScheme: 'uniform', colorValue: val\n })\n }\n }\n\n return this\n }\n\n // TODO\n prepare (cb: ()=> void) {\n\n }\n\n create () {\n // this.bufferList.length = 0;\n }\n\n update (what?: any) {\n this.build()\n }\n\n build (updateWhat?: {[k: string]: boolean}) {\n if (this.lazy && (!this.visible || !this.opacity)) {\n this.lazyProps.build = true\n return\n }\n\n if (!this.toBePrepared) {\n this.tasks.increment()\n this.make()\n return\n }\n\n // don't let tasks accumulate\n if (this.queue.length() > 0) {\n this.tasks.change(1 - this.queue.length())\n this.queue.kill()\n } else {\n this.tasks.increment()\n }\n\n this.queue.push(updateWhat || false)\n }\n\n make (updateWhat?: boolean, callback?: () => void) {\n if (Debug) Log.time('Representation.make ' + this.type)\n\n const _make = () => {\n if (updateWhat) {\n this.update(updateWhat)\n this.viewer.requestRender()\n this.tasks.decrement()\n if (callback) callback()\n } else {\n this.clear()\n this.create()\n if (!this.manualAttach && !this.disposed) {\n if (Debug) Log.time('Representation.attach ' + this.type)\n this.attach(() => {\n if (Debug) Log.timeEnd('Representation.attach ' + this.type)\n this.tasks.decrement()\n if (callback) callback()\n })\n }\n }\n\n if (Debug) Log.timeEnd('Representation.make ' + this.type)\n }\n\n if (this.toBePrepared) {\n this.prepare(_make)\n } else {\n _make()\n }\n }\n\n attach (callback: () => void) {\n this.setVisibility(this.visible)\n\n callback()\n }\n\n /**\n * Set the visibility of the representation\n * @param {Boolean} value - visibility flag\n * @param {Boolean} [noRenderRequest] - whether or not to request a re-render from the viewer\n * @return {Representation} this object\n */\n setVisibility (value: boolean, noRenderRequest?: boolean): Representation {\n this.visible = value\n\n if (this.visible && this.opacity) {\n const lazyProps = this.lazyProps\n const bufferParams = lazyProps.bufferParams\n const what = lazyProps.what\n\n if (lazyProps.build) {\n lazyProps.build = false\n this.build()\n return this\n } else if (Object.keys(bufferParams).length || Object.keys(what).length) {\n lazyProps.bufferParams = {}\n lazyProps.what = {}\n this.updateParameters(bufferParams, what)\n }\n }\n\n this.bufferList.forEach(function (buffer) {\n buffer.setVisibility(value)\n })\n\n if (!noRenderRequest) this.viewer.requestRender()\n\n return this\n }\n\n /**\n * Set the visibility of the representation\n * @param {RepresentationParameters} params - parameters object\n * @param {Object} [what] - buffer data attributes to be updated,\n * note that this needs to be implemented in the\n * derived classes. Generally it allows more\n * fine-grained control over updating than\n * forcing a rebuild.\n * @param {Boolean} what.position - update position data\n * @param {Boolean} what.color - update color data\n * @param {Boolean} [rebuild] - whether or not to rebuild the representation\n * @return {Representation} this object\n */\n setParameters (params: Partial, what:{[propName: string]: any} = {}, rebuild = false) {\n const p = params || {}\n const tp = this.parameters\n const bufferParams: BufferParameters = {}\n\n if (!this.opacity && p.opacity !== undefined) {\n if (this.lazyProps.build) {\n this.lazyProps.build = false\n rebuild = true\n } else {\n Object.assign(bufferParams, this.lazyProps.bufferParams)\n Object.assign(what, this.lazyProps.what)\n this.lazyProps.bufferParams = {}\n this.lazyProps.what = {}\n }\n }\n\n this.setColor(p.color, p)\n\n for (let name in p) {\n if (p[ name ] === undefined) continue\n if (tp[ name ] == undefined ) continue // Skip nulls as well as undefined\n\n if (tp[ name ].int) p[ name ] = parseInt(p[ name ] as string)\n if (tp[ name ].float) p[ name ] = parseFloat(p[ name ] as string)\n\n // no value change\n if (p[ name ] === this[ name ] && (\n !p[ name ].equals || p[ name ].equals(this[ name ])\n )) continue\n\n if (this[ name ] && this[ name ].copy && p[ name ].copy) {\n this[ name ].copy(p[ name ])\n } else if (this[ name ] && this[ name ].set) {\n this[ name ].set(p[ name ])\n } else {\n this[ name ] = p[ name ]\n }\n\n // buffer param\n if (tp[ name ].buffer) {\n if (tp[ name ].buffer === true) {\n (bufferParams[ name as keyof BufferParameters ] as any) = p[ name ]\n } else {\n let key: (keyof BufferParameters) = tp[ name ].buffer;\n (bufferParams[ key ] as any) = p[ name ]\n }\n }\n\n // mark for update\n if (tp[ name ].update) {\n what[ tp[ name ].update ] = true\n }\n\n // mark for rebuild\n if (tp[ name ].rebuild &&\n !(tp[ name ].rebuild === 'impostor' &&\n ExtensionFragDepth && !this.disableImpostor)\n ) {\n rebuild = true\n }\n }\n\n //\n\n if (rebuild) {\n this.build()\n } else {\n this.updateParameters(bufferParams, what)\n }\n\n return this\n }\n\n updateParameters (bufferParams: BufferParameters | {} = {}, what?: any) {\n if (this.lazy && (!this.visible || !this.opacity) && bufferParams.hasOwnProperty('opacity') === false) {\n Object.assign(this.lazyProps.bufferParams, bufferParams)\n Object.assign(this.lazyProps.what, what)\n return\n }\n\n this.bufferList.forEach(function (buffer) {\n buffer.setParameters(bufferParams)\n })\n\n if (Object.keys(what).length) {\n this.update(what) // update buffer attribute\n }\n\n this.viewer.requestRender()\n }\n\n getParameters () {\n const params: Partial = {\n lazy: this.lazy,\n visible: this.visible,\n quality: this.quality\n }\n\n Object.keys(this.parameters).forEach(name => {\n if (this.parameters[ name ] !== null) {\n params[ name ] = this[ name ]\n }\n })\n\n return params\n }\n\n clear () {\n this.bufferList.forEach(buffer => {\n this.viewer.remove(buffer)\n buffer.dispose()\n })\n this.bufferList.length = 0\n\n this.viewer.requestRender()\n }\n\n dispose () {\n this.disposed = true\n this.queue.kill()\n this.tasks.dispose()\n this.clear()\n }\n}\n\nexport default Representation\n","/**\n * @file Worker\n * @author Alexander Rose \n * @private\n */\n\nimport { Log, Debug, WorkerRegistry } from '../globals'\n\nexport default class _Worker {\n\n pending = 0\n postCount = 0\n onmessageDict: { [k: number]: Function|undefined } = {}\n onerrorDict: { [k: number]: Function|undefined } = {}\n\n name: string\n blobUrl: string\n worker: Worker\n\n constructor (name: string) {\n\n this.name = name\n this.blobUrl = window.URL.createObjectURL(WorkerRegistry.get(name))\n this.worker = new Worker(this.blobUrl)\n\n WorkerRegistry.activeWorkerCount += 1\n\n this.worker.onmessage = (event: any) => {\n this.pending -= 1\n const postId = event.data.__postId\n\n if (Debug) Log.timeEnd('Worker.postMessage ' + name + ' #' + postId)\n\n const onmessage = this.onmessageDict[ postId ]\n if (onmessage) {\n onmessage.call(this.worker, event)\n } else {\n // Log.debug('No onmessage', postId, name)\n }\n\n delete this.onmessageDict[ postId ]\n delete this.onerrorDict[ postId ]\n }\n\n this.worker.onerror = (event: any) => {\n this.pending -= 1\n if (event.data) {\n const postId = event.data.__postId\n\n const onerror = this.onerrorDict[ postId ]\n if (onerror) {\n onerror.call(this.worker, event)\n } else {\n Log.error('Worker.onerror', postId, name, event)\n }\n\n delete this.onmessageDict[ postId ]\n delete this.onerrorDict[ postId ]\n } else {\n Log.error('Worker.onerror', name, event)\n }\n }\n }\n\n post (aMessage: any = {}, transferList?: any, onmessage?: Function, onerror?: Function) {\n this.onmessageDict[ this.postCount ] = onmessage\n this.onerrorDict[ this.postCount ] = onerror\n\n aMessage.__name = this.name\n aMessage.__postId = this.postCount\n aMessage.__debug = Debug\n\n if (Debug) Log.time(`Worker.postMessage ${this.name} #${this.postCount}`)\n\n try {\n this.worker.postMessage(aMessage, transferList)\n } catch (error) {\n Log.error('worker.post:', error)\n this.worker.postMessage(aMessage)\n }\n\n this.pending += 1\n this.postCount += 1\n\n return this\n }\n\n terminate () {\n if (this.worker) {\n this.worker.terminate()\n window.URL.revokeObjectURL(this.blobUrl)\n WorkerRegistry.activeWorkerCount -= 1\n } else {\n Log.log('no worker to terminate')\n }\n }\n}\n","/**\n * @file Worker Pool\n * @author Alexander Rose \n * @private\n */\n\nimport Worker from './worker'\n\nclass WorkerPool {\n maxCount: number\n pool: Worker[] = []\n count = 0\n name: string\n\n constructor (name: string, maxCount = 2) {\n this.maxCount = Math.min(8, maxCount)\n this.name = name\n }\n\n post (aMessage: any = {}, transferList?: any, onmessage?: Function, onerror?: Function) {\n const worker = this.getNextWorker()\n if (worker) {\n worker.post(aMessage, transferList, onmessage, onerror)\n } else {\n console.error('unable to get worker from pool')\n }\n\n return this\n }\n\n terminate () {\n this.pool.forEach(function (worker) {\n worker.terminate()\n })\n }\n\n getNextWorker () {\n let nextWorker\n let minPending = Infinity\n\n for (let i = 0; i < this.maxCount; ++i) {\n if (i >= this.count) {\n nextWorker = new Worker(this.name)\n this.pool.push(nextWorker)\n this.count += 1\n break\n }\n\n const worker = this.pool[ i ]\n\n if (worker.pending === 0) {\n nextWorker = worker\n break\n } else if (worker.pending < minPending) {\n minPending = worker.pending\n nextWorker = worker\n }\n }\n\n return nextWorker\n }\n}\n\nWorkerPool.prototype.constructor = WorkerPool\n\nexport default WorkerPool\n","/**\n * @file Vector Utils\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3 } from 'three'\n\nimport { NumberArray } from '../types'\nimport { EPS } from './math-constants'\n\n/**\n * Calculate the two intersection points\n * Converted to JavaScript from\n * {@link http://paulbourke.net/geometry/pointlineplane/lineline.c}\n */\nexport function lineLineIntersect (p1: Vector3, p2: Vector3, p3: Vector3, p4: Vector3) {\n const p13 = new Vector3()\n const p43 = new Vector3()\n const p21 = new Vector3()\n let d1343, d4321, d1321, d4343, d2121\n let denom, numer\n\n p13.x = p1.x - p3.x\n p13.y = p1.y - p3.y\n p13.z = p1.z - p3.z\n p43.x = p4.x - p3.x\n p43.y = p4.y - p3.y\n p43.z = p4.z - p3.z\n if (Math.abs(p43.x) < EPS && Math.abs(p43.y) < EPS && Math.abs(p43.z) < EPS) { return null }\n\n p21.x = p2.x - p1.x\n p21.y = p2.y - p1.y\n p21.z = p2.z - p1.z\n if (Math.abs(p21.x) < EPS && Math.abs(p21.y) < EPS && Math.abs(p21.z) < EPS) { return null }\n\n d1343 = p13.x * p43.x + p13.y * p43.y + p13.z * p43.z\n d4321 = p43.x * p21.x + p43.y * p21.y + p43.z * p21.z\n d1321 = p13.x * p21.x + p13.y * p21.y + p13.z * p21.z\n d4343 = p43.x * p43.x + p43.y * p43.y + p43.z * p43.z\n d2121 = p21.x * p21.x + p21.y * p21.y + p21.z * p21.z\n\n denom = d2121 * d4343 - d4321 * d4321\n if (Math.abs(denom) < EPS) { return null }\n numer = d1343 * d4321 - d1321 * d4343\n\n const mua = numer / denom\n const mub = (d1343 + d4321 * mua) / d4343\n\n const pa = new Vector3(\n p1.x + mua * p21.x,\n p1.y + mua * p21.y,\n p1.z + mua * p21.z\n )\n const pb = new Vector3(\n p3.x + mub * p43.x,\n p3.y + mub * p43.y,\n p3.z + mub * p43.z\n )\n\n return [ pa, pb ]\n}\n\nexport function calculateMeanVector3 (array: NumberArray) {\n const n = array.length\n const m = n / 3\n\n let x = 0\n let y = 0\n let z = 0\n\n for (let i = 0; i < n; i += 3) {\n x += array[ i + 0 ]\n y += array[ i + 1 ]\n z += array[ i + 2 ]\n }\n\n return new Vector3(x / m, y / m, z / m)\n}\n\nexport function isPointOnSegment (p: Vector3, l1: Vector3, l2: Vector3) {\n const len = l1.distanceTo(l2)\n\n return p.distanceTo(l1) <= len && p.distanceTo(l2) <= len\n}\n\nexport function projectPointOnVector (point: Vector3, vector: Vector3, origin?: Vector3) {\n if (origin) {\n point.sub(origin).projectOnVector(vector).add(origin)\n } else {\n point.projectOnVector(vector)\n }\n\n return point\n}\n\nexport function computeBoundingBox (array: NumberArray) {\n let minX = +Infinity\n let minY = +Infinity\n let minZ = +Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n let maxZ = -Infinity\n for (let i = 0, l = array.length; i < l; i += 3) {\n const x = array[ i ]\n const y = array[ i + 1 ]\n const z = array[ i + 2 ]\n if (x < minX) minX = x\n if (y < minY) minY = y\n if (z < minZ) minZ = z\n if (x > maxX) maxX = x\n if (y > maxY) maxY = y\n if (z > maxZ) maxZ = z\n }\n return [\n v3new([ minX, minY, minZ ]),\n v3new([ maxX, maxY, maxZ ])\n ]\n}\n(computeBoundingBox as any).__deps = [ v3new ]\n\nexport function applyMatrix4toVector3array (m: Float32Array, a: Float32Array) {\n for (let i = 0, il = a.length; i < il; i += 3) {\n const x = a[ i ]\n const y = a[ i + 1 ]\n const z = a[ i + 2 ]\n a[ i ] = m[ 0 ] * x + m[ 4 ] * y + m[ 8 ] * z + m[ 12 ]\n a[ i + 1 ] = m[ 1 ] * x + m[ 5 ] * y + m[ 9 ] * z + m[ 13 ]\n a[ i + 2 ] = m[ 2 ] * x + m[ 6 ] * y + m[ 10 ] * z + m[ 14 ]\n }\n}\n\nexport function applyMatrix3toVector3array (m: Float32Array, a: Float32Array) {\n for (let i = 0, il = a.length; i < il; i += 3) {\n const x = a[ i ]\n const y = a[ i + 1 ]\n const z = a[ i + 2 ]\n a[ i ] = m[ 0 ] * x + m[ 3 ] * y + m[ 6 ] * z\n a[ i + 1 ] = m[ 1 ] * x + m[ 4 ] * y + m[ 7 ] * z\n a[ i + 2 ] = m[ 2 ] * x + m[ 5 ] * y + m[ 8 ] * z\n }\n}\n\nexport function normalizeVector3array (a: Float32Array) {\n for (let i = 0, il = a.length; i < il; i += 3) {\n const x = a[ i ]\n const y = a[ i + 1 ]\n const z = a[ i + 2 ]\n const len2 = x * x + y * y + z * z\n if (len2 > 0) { // avoid divide by zero\n const s = 1 / Math.sqrt(len2)\n a[ i ] = x * s\n a[ i + 1 ] = y * s\n a[ i + 2 ] = z * s\n }\n // else leave as all zeros\n }\n}\n\nexport function v3new (array?: NumberArray) {\n return new Float32Array(array as any || 3) // TODO\n}\n\nexport function v3cross (out: Float32Array, a: Float32Array, b: Float32Array) {\n const ax = a[0]\n const ay = a[1]\n const az = a[2]\n const bx = b[0]\n const by = b[1]\n const bz = b[2]\n out[0] = ay * bz - az * by\n out[1] = az * bx - ax * bz\n out[2] = ax * by - ay * bx\n}\n\nexport function v3dot (a: Float32Array, b: Float32Array) {\n return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]\n}\n\nexport function v3sub (out: Float32Array, a: Float32Array, b: Float32Array) {\n out[0] = a[0] - b[0]\n out[1] = a[1] - b[1]\n out[2] = a[2] - b[2]\n}\n\nexport function v3add (out: Float32Array, a: Float32Array, b: Float32Array) {\n out[0] = a[0] + b[0]\n out[1] = a[1] + b[1]\n out[2] = a[2] + b[2]\n}\n\nexport function v3fromArray (out: Float32Array, array: Float32Array, offset = 0) {\n out[0] = array[offset]\n out[1] = array[offset + 1]\n out[2] = array[offset + 2]\n}\n\nexport function v3toArray (input: Float32Array, array: Float32Array, offset = 0) {\n array[offset] = input[0]\n array[offset + 1] = input[1]\n array[offset + 2] = input[2]\n}\n\nexport function v3forEach (array: Float32Array, fn: (i: Float32Array, j: Float32Array, k: Float32Array) => void, b: Float32Array) {\n const a = v3new()\n for (let i = 0, n = array.length; i < n; i += 3) {\n v3fromArray(a, array, i)\n fn(a, a, b)\n v3toArray(a, array, i)\n }\n}\n(v3forEach as any).__deps = [ v3new, v3fromArray, v3toArray ]\n\nexport function v3length2 (a: Float32Array) {\n return a[0] * a[0] + a[1] * a[1] + a[2] * a[2]\n}\n\nexport function v3length (a: Float32Array) {\n return Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2])\n}\n\nexport function v3divide (out: Float32Array, a: Float32Array, b: Float32Array) {\n out[0] = a[0] / b[0]\n out[1] = a[1] / b[1]\n out[2] = a[2] / b[2]\n}\n\nexport function v3multiply (out: Float32Array, a: Float32Array, b: Float32Array) {\n out[0] = a[0] * b[0]\n out[1] = a[1] * b[1]\n out[2] = a[2] * b[2]\n}\n\nexport function v3divideScalar (out: Float32Array, a: Float32Array, s: number) {\n v3multiplyScalar(out, a, 1 / s)\n}\n(v3divideScalar as any).__deps = [ v3multiplyScalar ]\n\nexport function v3multiplyScalar (out: Float32Array, a: Float32Array, s: number) {\n out[0] = a[0] * s\n out[1] = a[1] * s\n out[2] = a[2] * s\n}\n\nexport function v3normalize (out: Float32Array, a: Float32Array) {\n const length2 = v3length2(a)\n if (length2 == 0) {\n out[0] = a[0]\n out[1] = a[1]\n out[2] = a[2]\n } else {\n v3multiplyScalar(out, a, 1 / Math.sqrt(length2))\n }\n}\n(v3normalize as any).__deps = [ v3multiplyScalar, v3length2 ]\n\nexport function v3subScalar (out: Float32Array, a: Float32Array, s: number) {\n out[0] = a[0] - s\n out[1] = a[1] - s\n out[2] = a[2] - s\n}\n\nexport function v3addScalar (out: Float32Array, a: Float32Array, s: number) {\n out[0] = a[0] + s\n out[1] = a[1] + s\n out[2] = a[2] + s\n}\n\nexport function v3floor (out: Float32Array, a: Float32Array) {\n out[0] = Math.floor(a[0])\n out[1] = Math.floor(a[1])\n out[2] = Math.floor(a[2])\n}\n\nexport function v3ceil (out: Float32Array, a: Float32Array) {\n out[0] = Math.ceil(a[0])\n out[1] = Math.ceil(a[1])\n out[2] = Math.ceil(a[2])\n}\n\nexport function v3round (out: Float32Array, a: Float32Array) {\n out[0] = Math.round(a[0])\n out[1] = Math.round(a[1])\n out[2] = Math.round(a[2])\n}\n\nexport function v3negate (out: Float32Array, a: Float32Array) {\n out[0] = -a[0]\n out[1] = -a[1]\n out[2] = -a[2]\n}\n\nexport function v3angle (a: Float32Array, b: Float32Array) {\n const ax = a[0]\n const ay = a[1]\n const az = a[2]\n const bx = b[0]\n const by = b[1]\n const bz = b[2]\n const cx = ay * bz - az * by\n const cy = az * bx - ax * bz\n const cz = ax * by - ay * bx\n const s = Math.sqrt(cx * cx + cy * cy + cz * cz)\n const c = ax * bx + ay * by + az * bz\n return Math.atan2(s, c)\n}\n","/**\n * @file Dash\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3 } from 'three'\n\nimport { CylinderBufferData } from '../buffer/cylinder-buffer'\nimport { WideLineBufferData } from '../buffer/wideline-buffer'\nimport {\n calculateDirectionArray, calculateCenterArray,\n replicateArrayEntries, replicateArray3Entries\n} from '../math/array-utils'\n\nexport function getFixedCountDashData (data: T, segmentCount: number = 9) {\n\n const s = Math.floor(segmentCount / 2)\n const n = data.position1.length / 3\n const sn = s * n\n const sn3 = sn * 3\n const step = 1 / segmentCount\n\n const direction = calculateDirectionArray(data.position1, data.position2)\n const position1 = new Float32Array(sn3)\n const position2 = new Float32Array(sn3)\n\n const v = new Vector3()\n\n for (let i = 0; i < n; ++i) {\n const i3 = i * 3\n v.set(direction[ i3 ], direction[ i3 + 1 ], direction[ i3 + 2 ])\n\n const x = data.position1[ i3 ]\n const y = data.position1[ i3 + 1 ]\n const z = data.position1[ i3 + 2 ]\n\n for (let j = 0; j < s; ++j) {\n const j3 = s * i3 + j * 3\n\n const f1 = step * (j * 2 + 1)\n const f2 = step * (j * 2 + 2)\n\n position1[ j3 ] = x + v.x * f1\n position1[ j3 + 1 ] = y + v.y * f1\n position1[ j3 + 2 ] = z + v.z * f1\n\n position2[ j3 ] = x + v.x * f2\n position2[ j3 + 1 ] = y + v.y * f2\n position2[ j3 + 2 ] = z + v.z * f2\n }\n }\n\n const position = calculateCenterArray(position1, position2) as Float32Array\n const color = replicateArray3Entries(data.color!, s) // TODO\n const color2 = color\n\n const d: any = { position, position1, position2, color, color2 }\n\n if ((data as any).radius) { // TODO\n d.radius = replicateArrayEntries((data as any).radius, s) // TODO\n }\n\n if (data.picking && data.picking.array) {\n data.picking.array = replicateArrayEntries(data.picking.array, s)\n d.picking = data.picking\n }\n if (data.primitiveId) {\n d.primitiveId = replicateArrayEntries(data.primitiveId, s)\n }\n\n return d as T\n}\n\nexport function getFixedLengthDashData (data: T, segmentLength: number = 0.1) {\n\n const direction = calculateDirectionArray(data.position1, data.position2)\n const pos1: number[] = []\n const pos2: number[] = []\n const col: number[] = []\n const rad: number[]|undefined = (data as any).radius ? [] : undefined\n const pick: number[]|undefined = (data as any).picking ? [] : undefined\n const id: number[]|undefined = (data as any).primitiveId ? [] : undefined\n\n const v = new Vector3()\n const n = data.position1.length / 3\n\n let k = 0\n\n for (let i = 0; i < n; ++i) {\n const i3 = i * 3\n v.set(direction[ i3 ], direction[ i3 + 1 ], direction[ i3 + 2 ])\n\n const vl = v.length()\n const segmentCount = vl / segmentLength\n const s = Math.floor(segmentCount / 2)\n const step = 1 / segmentCount\n\n const x = data.position1[ i3 ]\n const y = data.position1[ i3 + 1 ]\n const z = data.position1[ i3 + 2 ]\n\n for (let j = 0; j < s; ++j) {\n const j3 = k * 3 + j * 3\n\n const f1 = step * (j * 2 + 1)\n const f2 = step * (j * 2 + 2)\n\n pos1[ j3 ] = x + v.x * f1\n pos1[ j3 + 1 ] = y + v.y * f1\n pos1[ j3 + 2 ] = z + v.z * f1\n\n pos2[ j3 ] = x + v.x * f2\n pos2[ j3 + 1 ] = y + v.y * f2\n pos2[ j3 + 2 ] = z + v.z * f2\n\n if (data.color) {\n col[ j3 ] = data.color[ i3 ]\n col[ j3 + 1 ] = data.color[ i3 + 1 ]\n col[ j3 + 2 ] = data.color[ i3 + 2 ]\n }\n\n if (rad) rad[ k + j ] = (data as any).radius[ i ]\n if (pick) {\n if ((data as any).picking.array) {\n pick[ k + j ] = (data as any).picking.array[ i ]\n } else {\n pick[ k + j ] = i\n }\n }\n if (id) id[ k + j ] = (data as any).primitiveId[ i ]\n }\n\n k += s\n }\n\n const position1 = new Float32Array(pos1)\n const position2 = new Float32Array(pos2)\n const position = calculateCenterArray(position1, position2) as Float32Array\n const color = new Float32Array(col)\n const color2 = color\n\n const d: any = { position, position1, position2, color, color2 }\n\n if (rad) d.radius = new Float32Array(rad)\n if (pick && data.picking) {\n data.picking.array = new Float32Array(pick)\n d.picking = data.picking\n }\n if (id) d.primitiveId = new Float32Array(id)\n\n return d as T\n}\n\nexport function getFixedLengthWrappedDashData (data: T, segmentLength: number = 0.1) {\n\n const direction = calculateDirectionArray(data.position1, data.position2)\n const pos1: number[] = []\n const pos2: number[] = []\n const col: number[] = []\n const rad: number[]|undefined = (data as any).radius ? [] : undefined\n const pick: number[]|undefined = (data as any).picking ? [] : undefined\n const id: number[]|undefined = (data as any).primitiveId ? [] : undefined\n\n const v = new Vector3()\n const n = data.position1.length / 3\n\n let remaining = segmentLength\n let drawing = true\n\n let k = 0\n let k3 = 0\n let kprev = 0\n\n for (let i = 0; i < n; ++i) {\n const i3 = i * 3\n const x = data.position1[ i3 ]\n const y = data.position1[ i3 + 1 ]\n const z = data.position1[ i3 + 2 ]\n\n v.set(direction[ i3 ], direction[ i3 + 1 ], direction[ i3 + 2 ])\n const vl = v.length()\n\n if (drawing) {\n pos1[ k3 ] = x\n pos1[ k3 + 1 ] = y\n pos1[ k3 + 2 ] = z\n }\n\n let dist = remaining\n const inv = 1 / vl\n while (dist < vl) {\n const a = drawing ? pos2 : pos1\n a[ k3 ] = x + v.x * dist * inv\n a[ k3 + 1 ] = y + v.y * dist * inv\n a[ k3 + 2 ] = z + v.z * dist * inv\n if (drawing) {\n k++\n k3 = k * 3\n }\n drawing = !drawing\n remaining = segmentLength\n dist += segmentLength\n }\n\n if (drawing) {\n pos2[ k3 ] = data.position2[ i3 ]\n pos2[ k3 + 1 ] = data.position2[ i3 + 1 ]\n pos2[ k3 + 2 ] = data.position2[ i3 + 2 ]\n k++\n k3 = k * 3\n }\n\n remaining = dist - vl\n\n for (let j = kprev; j < k ; j++){\n if (data.color) {\n const j3 = j * 3\n col[ j3 ] = data.color[ i3 ]\n col[ j3 + 1 ] = data.color[ i3 + 1 ]\n col[ j3 + 2 ] = data.color[ i3 + 2 ]\n }\n\n if (rad) rad[ j ] = (data as any).radius[ i ]\n if (pick) {\n if ((data as any).picking.array) {\n pick[ j ] = (data as any).picking.array[ i ]\n } else {\n pick[ j ] = i\n }\n }\n if (id) id[ j ] = (data as any).primitiveId[ i ]\n }\n\n kprev = k\n\n }\n\n if (!drawing && n > 0) {\n const k3 = k * 3\n pos2[ k3 ] = data.position2[ 3 * n - 3 ]\n pos2[ k3 + 1 ] = data.position2[ 3 * n - 2 ]\n pos2[ k3 + 1 ] = data.position2[ 3 * n - 1 ]\n }\n\n const position1 = new Float32Array(pos1)\n const position2 = new Float32Array(pos2)\n const position = calculateCenterArray(position1, position2) as Float32Array\n const color = new Float32Array(col)\n const color2 = color\n\n const d: any = { position, position1, position2, color, color2 }\n\n if (rad) d.radius = new Float32Array(rad)\n if (pick && data.picking) {\n data.picking.array = new Float32Array(pick)\n d.picking = data.picking\n }\n if (id) d.primitiveId = new Float32Array(id)\n\n return d as T\n}\n","/**\n * @file Primitive\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3, Color, Box3 } from 'three'\n\nimport { BufferRegistry, PickerRegistry } from '../globals'\nimport Shape from './shape'\nimport { getFixedLengthDashData } from './dash'\n\nfunction addElement (elm: any, array: any[]) {\n if (elm.toArray !== undefined) {\n elm = elm.toArray()\n } else if (elm.x !== undefined) {\n elm = [ elm.x, elm.y, elm.z ]\n } else if (elm.r !== undefined) {\n elm = [ elm.r, elm.g, elm.b ]\n }\n array.push.apply(array, elm)\n}\n\nconst tmpVec = new Vector3()\n\nexport type PrimitiveFields = { [k: string]: string }\n\n/**\n * Base class for geometry primitives\n * @interface\n */\nexport abstract class Primitive {\n static type = ''\n static fields: PrimitiveFields = {}\n\n static get Picker () { return PickerRegistry.get(this.type) }\n static get Buffer () { return BufferRegistry.get(this.type) }\n\n static getShapeKey (name: string) {\n return this.type + name[0].toUpperCase() + name.substr(1)\n }\n\n static expandBoundingBox (box: Box3, data: any) {}\n\n static valueToShape (shape: Shape, name: string, value: any) {\n const data = shape._primitiveData[this.getShapeKey(name)]\n const type = this.fields[name]\n\n switch (type) {\n case 'v3':\n case 'c':\n addElement(value, data)\n break\n default:\n data.push(value)\n }\n }\n\n static objectToShape (shape: Shape, data: any) {\n Object.keys(this.fields).forEach(name => {\n this.valueToShape(shape, name, data[name])\n })\n this.valueToShape(shape, 'name', data.name)\n this.expandBoundingBox(shape.boundingBox, data)\n }\n\n static valueFromShape (shape: Shape, pid: number, name: string) {\n const data = shape._primitiveData[this.getShapeKey(name)]\n const type = this.fields[name]\n\n switch (type) {\n case 'v3':\n return new Vector3().fromArray(data, 3 * pid)\n case 'c':\n return new Color().fromArray(data, 3 * pid)\n default:\n return data[pid]\n }\n }\n\n static objectFromShape (shape: Shape, pid: number) {\n let name = this.valueFromShape(shape, pid, 'name')\n if (name === undefined) {\n name = `${this.type}: ${pid} (${shape.name})`\n }\n const o: any = { shape, name }\n\n Object.keys(this.fields).forEach(name => {\n o[name] = this.valueFromShape(shape, pid, name)\n })\n\n return o\n }\n\n static arrayFromShape (shape: Shape, name: string) {\n const data = shape._primitiveData[this.getShapeKey(name)]\n const type = this.fields[name]\n\n switch (type) {\n case 's':\n return data\n default:\n return new Float32Array(data)\n }\n }\n\n static dataFromShape (shape: Shape) {\n const data: any = {}\n\n if (this.Picker) {\n data.picking = new this.Picker(shape)\n }\n\n Object.keys(this.fields).forEach(name => {\n data[name] = this.arrayFromShape(shape, name)\n })\n\n return data\n }\n\n static bufferFromShape (shape: Shape, params: any) {\n return new this.Buffer(this.dataFromShape(shape), params)\n }\n}\n\n/**\n * Sphere geometry primitive\n */\nexport class SpherePrimitive extends Primitive {\n static type = 'sphere'\n\n static fields = {\n position: 'v3',\n color: 'c',\n radius: 'f'\n }\n\n static positionFromShape (shape: Shape, pid: number) {\n return this.valueFromShape(shape, pid, 'position')\n }\n\n static expandBoundingBox (box: Box3, data: any) {\n box.expandByPoint(tmpVec.fromArray(data.position))\n }\n}\n\n/**\n * Box geometry primitive\n */\nexport class BoxPrimitive extends Primitive {\n static type = 'box'\n\n static fields = {\n position: 'v3',\n color: 'c',\n size: 'f',\n heightAxis: 'v3',\n depthAxis: 'v3'\n }\n\n static positionFromShape (shape: Shape, pid: number) {\n return this.valueFromShape(shape, pid, 'position')\n }\n\n static expandBoundingBox (box: Box3, data: any) {\n box.expandByPoint(tmpVec.fromArray(data.position))\n }\n}\n\n/**\n * Octahedron geometry primitive\n */\nexport class OctahedronPrimitive extends BoxPrimitive {\n static type = 'octahedron'\n}\n\n/**\n * Tetrahedron geometry primitive\n */\nexport class TetrahedronPrimitive extends BoxPrimitive {\n static type = 'tetrahedron'\n}\n\n/**\n * Cylinder geometry primitive\n */\nexport class CylinderPrimitive extends Primitive {\n static type = 'cylinder'\n\n static fields = {\n position1: 'v3',\n position2: 'v3',\n color: 'c',\n radius: 'f'\n }\n\n static positionFromShape (shape: Shape, pid: number) {\n const p1 = this.valueFromShape(shape, pid, 'position1')\n const p2 = this.valueFromShape(shape, pid, 'position2')\n return p1.add(p2).multiplyScalar(0.5)\n }\n\n static expandBoundingBox (box: Box3, data: any) {\n box.expandByPoint(tmpVec.fromArray(data.position1))\n box.expandByPoint(tmpVec.fromArray(data.position2))\n }\n\n static bufferFromShape (shape: Shape, params: any = {}) {\n let data = this.dataFromShape(shape)\n if (this.type === 'cylinder' && params.dashedCylinder) {\n data = getFixedLengthDashData(data)\n }\n return new this.Buffer(data, params)\n }\n}\n\n/**\n * Arrow geometry primitive\n */\nexport class ArrowPrimitive extends CylinderPrimitive {\n static type = 'arrow'\n}\n\n/**\n * Cone geometry primitive\n */\nexport class ConePrimitive extends CylinderPrimitive {\n static type = 'cone'\n}\n\n/**\n * Ellipsoid geometry primitive\n */\nexport class EllipsoidPrimitive extends SpherePrimitive {\n static type = 'ellipsoid'\n\n static fields = {\n position: 'v3',\n color: 'c',\n radius: 'f',\n majorAxis: 'v3',\n minorAxis: 'v3'\n }\n}\n\n/**\n * Torus geometry primitive\n */\nexport class TorusPrimitive extends EllipsoidPrimitive {\n static type = 'torus'\n}\n\n/**\n * Text geometry primitive\n */\nexport class TextPrimitive extends Primitive {\n static type = 'text'\n\n static fields = {\n position: 'v3',\n color: 'c',\n size: 'f',\n text: 's'\n }\n\n static positionFromShape (shape: Shape, pid: number) {\n return this.valueFromShape(shape, pid, 'position')\n }\n\n static expandBoundingBox (box: Box3, data: any) {\n box.expandByPoint(tmpVec.fromArray(data.position))\n }\n}\n\n/**\n * Point primitive\n */\nexport class PointPrimitive extends Primitive {\n static type = 'point'\n\n static fields = {\n position: 'v3',\n color: 'c',\n }\n\n static positionFromShape (shape: Shape, pid: number) {\n return this.valueFromShape(shape, pid, 'position')\n }\n\n static expandBoundingBox (box: Box3, data: any) {\n box.expandByPoint(tmpVec.fromArray(data.position))\n }\n}\n\n/**\n * Wideline geometry primitive\n */\nexport class WidelinePrimitive extends Primitive {\n static type = 'wideline'\n\n static fields = {\n position1: 'v3',\n position2: 'v3',\n color: 'c'\n }\n\n static positionFromShape (shape: Shape, pid: number) {\n const p1 = this.valueFromShape(shape, pid, 'position1')\n const p2 = this.valueFromShape(shape, pid, 'position2')\n return p1.add(p2).multiplyScalar(0.5)\n }\n\n static expandBoundingBox (box: Box3, data: any) {\n box.expandByPoint(tmpVec.fromArray(data.position1))\n box.expandByPoint(tmpVec.fromArray(data.position2))\n }\n}\n","/**\n * @file Spatial Hash\n * @author Alexander Rose \n * @private\n */\n\nimport { Box3 } from 'three'\n\nexport type Positions = {\n x: ArrayLike,\n y: ArrayLike,\n z: ArrayLike,\n count?:number\n}\n\nfunction createBoundingBox(positions: Positions) {\n const { x, y, z } = positions\n const boundingBox = new Box3()\n const count = x.length\n const { min, max } = boundingBox\n\n for (let i = 0; i < count; i++) {\n min.x = Math.min(x[i], min.x)\n min.y = Math.min(y[i], min.y)\n min.z = Math.min(z[i], min.z)\n max.x = Math.max(x[i], max.x)\n max.y = Math.max(y[i], max.y)\n max.z = Math.max(z[i], max.z)\n }\n\n return boundingBox\n}\n\nexport default class SpatialHash {\n exp = 3\n\n minX: number\n minY: number\n minZ: number\n\n boundX: number\n boundY: number\n boundZ: number\n\n grid: Uint32Array\n bucketCount: Uint16Array\n bucketOffset: Uint32Array\n bucketArray: Int32Array\n\n xArray: ArrayLike\n yArray: ArrayLike\n zArray: ArrayLike\n\n constructor(positions: Positions, boundingBox?: Box3) {\n const bb = boundingBox || createBoundingBox(positions)\n this.minX = bb.min.x\n this.minY = bb.min.y\n this.minZ = bb.min.z\n this.boundX = ((bb.max.x - this.minX) >> this.exp) + 1\n this.boundY = ((bb.max.y - this.minY) >> this.exp) + 1\n this.boundZ = ((bb.max.z - this.minZ) >> this.exp) + 1\n\n const n = this.boundX * this.boundY * this.boundZ\n const an = (positions.count !== undefined) ? positions.count : positions.x.length\n\n const xArray = positions.x\n const yArray = positions.y\n const zArray = positions.z\n\n let count = 0\n const grid = new Uint32Array(n)\n const bucketIndex = new Int32Array(an)\n for (let i = 0; i < an; ++i) {\n const x = (xArray[ i ] - this.minX) >> this.exp\n const y = (yArray[ i ] - this.minY) >> this.exp\n const z = (zArray[ i ] - this.minZ) >> this.exp\n const idx = (((x * this.boundY) + y) * this.boundZ) + z\n if ((grid[ idx ] += 1) === 1) {\n count += 1\n }\n bucketIndex[ i ] = idx\n }\n\n const bucketCount = new Uint16Array(count)\n for (let i = 0, j = 0; i < n; ++i) {\n const c = grid[ i ]\n if (c > 0) {\n grid[ i ] = j + 1\n bucketCount[ j ] = c\n j += 1\n }\n }\n\n const bucketOffset = new Uint32Array(count)\n for (let i = 1; i < count; ++i) {\n bucketOffset[ i ] += bucketOffset[ i - 1 ] + bucketCount[ i - 1 ]\n }\n\n const bucketFill = new Uint16Array(count)\n const bucketArray = new Int32Array(an)\n for (let i = 0; i < an; ++i) {\n const bucketIdx = grid[ bucketIndex[ i ] ]\n if (bucketIdx > 0) {\n const k = bucketIdx - 1\n bucketArray[ bucketOffset[ k ] + bucketFill[ k ] ] = i\n bucketFill[ k ] += 1\n }\n }\n\n this.grid = grid\n this.bucketCount = bucketCount\n this.bucketOffset = bucketOffset\n this.bucketArray = bucketArray\n\n this.xArray = xArray\n this.yArray = yArray\n this.zArray = zArray\n }\n\n within (x: number, y: number, z: number, r: number) {\n const result: number[] = []\n\n this.eachWithin(x, y, z, r, atomIndex => result.push(atomIndex))\n\n return result\n }\n\n eachWithin (x: number, y: number, z: number, r: number, callback: (atomIndex: number, dSq: number) => void) {\n const rSq = r * r\n\n const loX = Math.max(0, (x - r - this.minX) >> this.exp)\n const loY = Math.max(0, (y - r - this.minY) >> this.exp)\n const loZ = Math.max(0, (z - r - this.minZ) >> this.exp)\n\n const hiX = Math.min(this.boundX, ((x + r - this.minX) >> this.exp) + 1)\n const hiY = Math.min(this.boundY, ((y + r - this.minY) >> this.exp) + 1)\n const hiZ = Math.min(this.boundZ, ((z + r - this.minZ) >> this.exp) + 1)\n\n for (let ix = loX; ix < hiX; ++ix) {\n for (let iy = loY; iy < hiY; ++iy) {\n for (let iz = loZ; iz < hiZ; ++iz) {\n const idx = (((ix * this.boundY) + iy) * this.boundZ) + iz\n const bucketIdx = this.grid[ idx ]\n\n if (bucketIdx > 0) {\n const k = bucketIdx - 1\n const offset = this.bucketOffset[ k ]\n const count = this.bucketCount[ k ]\n const end = offset + count\n\n for (let i = offset; i < end; ++i) {\n const atomIndex = this.bucketArray[ i ]\n const dx = this.xArray[ atomIndex ] - x\n const dy = this.yArray[ atomIndex ] - y\n const dz = this.zArray[ atomIndex ] - z\n\n const dSq = dx * dx + dy * dy + dz * dz\n if (dSq <= rSq) callback(atomIndex, dSq)\n }\n }\n }\n }\n }\n }\n}","/**\n * @file Store\n * @author Alexander Rose \n * @private\n */\n\nimport { Log } from '../globals'\nimport { getTypedArray, TypedArrayString } from '../utils'\n\nexport type StoreField = [string, number, TypedArrayString]\n\n/**\n * Store base class\n * @interface\n */\nexport default class Store {\n [k: string]: any\n\n length: number\n count: number\n\n _fields: StoreField[]\n get _defaultFields(): StoreField[] { return [] }\n\n /**\n * @param {Integer} [size] - initial size\n */\n constructor (size = 0) {\n this._fields = this._defaultFields\n this._init(0)\n }\n\n /**\n * Initialize the store\n * @param {Integer} size - size to initialize\n * @return {undefined}\n */\n _init (size: number) {\n this.length = size\n this.count = 0\n\n for (let i = 0, il = this._fields.length; i < il; ++i) {\n const [name, size, type]: StoreField = this._fields[ i ]\n this._initField(name, size, type)\n }\n }\n\n /**\n * Initialize a field\n * @param {String} name - field name\n * @param {Integer} size - element size\n * @param {String} type - data type, one of int8, int16, int32,\n * uint8, uint16, uint32, float32\n * @return {undefined}\n */\n _initField (name: string, size: number, type: TypedArrayString) {\n this[ name ] = getTypedArray(type, this.length * size)\n }\n\n /**\n * Add a field\n * @param {String} name - field name\n * @param {Integer} size - element size\n * @param {String} type - data type, one of int8, int16, int32,\n * uint8, uint16, uint32, float32\n * @return {undefined}\n */\n addField (name: string, size: number, type: TypedArrayString) {\n this._fields.push([name, size, type])\n this._initField(name, size, type)\n }\n\n /**\n * Resize the store to the new size\n * @param {Integer} size - new size\n * @return {undefined}\n */\n resize (size?: number) {\n // Log.time( \"Store.resize\" );\n\n this.length = Math.round(size || 0)\n this.count = Math.min(this.count, this.length)\n\n for (let i = 0, il = this._fields.length; i < il; ++i) {\n const name = this._fields[ i ][ 0 ]\n const itemSize = this._fields[ i ][ 1 ]\n const arraySize = this.length * itemSize\n const tmpArray = new this[ name ].constructor(arraySize)\n\n if (this[ name ].length > arraySize) {\n tmpArray.set(this[ name ].subarray(0, arraySize))\n } else {\n tmpArray.set(this[ name ])\n }\n this[ name ] = tmpArray\n }\n\n // Log.timeEnd( \"Store.resize\" );\n }\n\n /**\n * Resize the store to 1.5 times its current size if full\n * @return {undefined}\n */\n growIfFull () {\n if (this.count >= this.length) {\n const size = Math.round(this.length * 1.5)\n this.resize(Math.max(256, size))\n }\n }\n\n /**\n * Copy data from one store to another\n * @param {Store} other - store to copy from\n * @param {Integer} thisOffset - offset to start copying to\n * @param {Integer} otherOffset - offset to start copying from\n * @param {Integer} length - number of entries to copy\n * @return {undefined}\n */\n copyFrom (other: Store, thisOffset: number, otherOffset: number, length: number) {\n for (let i = 0, il = this._fields.length; i < il; ++i) {\n const name = this._fields[ i ][ 0 ]\n const itemSize = this._fields[ i ][ 1 ]\n const thisField = this[ name ]\n const otherField = other[ name ]\n\n for (let j = 0; j < length; ++j) {\n const thisIndex = itemSize * (thisOffset + j)\n const otherIndex = itemSize * (otherOffset + j)\n for (let k = 0; k < itemSize; ++k) {\n thisField[ thisIndex + k ] = otherField[ otherIndex + k ]\n }\n }\n }\n }\n\n /**\n * Copy data within this store\n * @param {Integer} thisOffset - offset to start copying to\n * @param {Integer} otherOffset - offset to start copying from\n * @param {Integer} length - number of entries to copy\n * @return {undefined}\n */\n copyWithin (offsetTarget: number, offsetSource: number, length: number) {\n for (let i = 0, il = this._fields.length; i < il; ++i) {\n const name = this._fields[ i ][ 0 ]\n const itemSize = this._fields[ i ][ 1 ]\n const thisField = this[ name ]\n\n for (let j = 0; j < length; ++j) {\n const targetIndex = itemSize * (offsetTarget + j)\n const sourceIndex = itemSize * (offsetSource + j)\n for (let k = 0; k < itemSize; ++k) {\n thisField[ targetIndex + k ] = thisField[ sourceIndex + k ]\n }\n }\n }\n }\n\n /**\n * Sort entries in the store given the compare function\n * @param {[type]} compareFunction - function to sort by\n * @return {undefined}\n */\n sort (compareFunction: (a: any, b: any) => number) {\n Log.time('Store.sort')\n\n const thisStore = this\n const tmpStore = new (this.constructor as any)(1)\n\n function swap (index1: number, index2: number) {\n if (index1 === index2) return\n tmpStore.copyFrom(thisStore, 0, index1, 1)\n thisStore.copyWithin(index1, index2, 1)\n thisStore.copyFrom(tmpStore, index2, 0, 1)\n }\n\n function quicksort (left: number, right: number) {\n if (left < right) {\n let pivot = Math.floor((left + right) / 2)\n let leftNew = left\n let rightNew = right\n do {\n while (compareFunction(leftNew, pivot) < 0) {\n leftNew += 1\n }\n while (compareFunction(rightNew, pivot) > 0) {\n rightNew -= 1\n }\n if (leftNew <= rightNew) {\n if (leftNew === pivot) {\n pivot = rightNew\n } else if (rightNew === pivot) {\n pivot = leftNew\n }\n swap(leftNew, rightNew)\n leftNew += 1\n rightNew -= 1\n }\n } while (leftNew <= rightNew)\n quicksort(left, rightNew)\n quicksort(leftNew, right)\n }\n }\n\n quicksort(0, this.count - 1)\n\n Log.timeEnd('Store.sort')\n }\n\n /**\n * Empty the store\n * @return {undefined}\n */\n clear () {\n this.count = 0\n }\n\n /**\n * Dispose of the store entries and fields\n * @return {undefined}\n */\n dispose () {\n\n for (let i = 0, il = this._fields.length; i < il; ++i) {\n const name = this._fields[ i ][ 0 ]\n delete this[ name ]\n }\n }\n}\n","/**\n * @file Contact Store\n * @author Alexander Rose \n * @private\n */\n\nimport Store, { StoreField } from './store'\n\n/**\n * Bond store\n */\nexport default class ContactStore extends Store {\n index1: Uint32Array\n index2: Uint32Array\n type: Uint8Array\n\n get _defaultFields () {\n return [\n [ 'index1', 1, 'int32' ],\n [ 'index2', 1, 'int32' ],\n [ 'type', 1, 'int8' ]\n ] as StoreField[]\n }\n\n addContact (index1: number, index2: number, type?: number) {\n this.growIfFull()\n\n const i = this.count\n\n if (index1 < index2) {\n this.index1[ i ] = index1\n this.index2[ i ] = index2\n } else {\n this.index2[ i ] = index1\n this.index1[ i ] = index2\n }\n if (type) this.type[ i ] = type\n\n this.count += 1\n }\n}","/**\n * @file Bit array\n * @author Alexander Rose \n * @author Paul Pillot \n * @private\n */\n\n/**\n * Compute the Hamming weight of a 32-bit unsigned integer\n * @param {Integer} v - a 32-bit unsigned integer\n * @return {Integer} the Hamming weight\n */\nfunction hammingWeight (v: number) {\n // works with signed or unsigned shifts\n v -= ((v >>> 1) & 0x55555555)\n v = (v & 0x33333333) + ((v >>> 2) & 0x33333333)\n return ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24\n}\n\n/**\n * Bit array\n *\n * Based heavily on https://github.com/lemire/FastBitSet.js\n * which is licensed under the Apache License, Version 2.0.\n */\nexport default class BitArray {\n private _words: Uint32Array\n public length: number\n\n /**\n * @param {Integer} length - array length\n * @param {Boolean} [setAll] - initialize with true\n */\n constructor (length: number, setAll?: boolean) {\n this.length = length\n this._words = new Uint32Array((length + 32) >>> 5)\n if (setAll === true) {\n this.setAll()\n }\n }\n\n /**\n * Get value at index\n * @param {Integer} index - the index\n * @return {Boolean} value\n */\n get (index: number) {\n return (this._words[ index >>> 5 ] & (1 << index)) !== 0\n }\n\n /**\n * Set value at index to true\n * @param {Integer} index - the index\n * @return {undefined}\n */\n set (index: number) {\n this._words[ index >>> 5 ] |= 1 << index\n }\n\n /**\n * Set value at index to false\n * @param {Integer} index - the index\n * @return {undefined}\n */\n clear (index: number) {\n this._words[ index >>> 5 ] &= ~(1 << index)\n }\n\n /**\n * Flip value at index\n * @param {Integer} index - the index\n * @return {undefined}\n */\n flip (index: number) {\n this._words[ index >>> 5 ] ^= 1 << index\n }\n\n _assignRange (start: number, end: number, value: boolean) {\n if (end < start) return\n const words = this._words\n const wordValue = value === true ? 0xFFFFFFFF : 0\n const wordStart = start >>> 5\n const wordEnd = end >>> 5\n // set complete words when applicable\n for (let k = wordStart + 1; k < wordEnd; ++k) {\n words[ k ] = wordValue\n }\n // set parts of the range not spanning complete words\n const startWord = wordStart << 5\n const endWord = wordEnd << 5\n if (value === true) {\n if (end - start < 32) {\n for (let i = start, n = end + 1; i < n; ++i) {\n words[ i >>> 5 ] |= 1 << i\n }\n } else {\n for (let i = start, n = startWord + 32; i < n; ++i) {\n words[ i >>> 5 ] |= 1 << i\n }\n for (let i = endWord, n = end + 1; i < n; ++i) {\n words[ i >>> 5 ] |= 1 << i\n }\n }\n } else {\n if (end - start < 32) {\n for (let i = start, n = end + 1; i < n; ++i) {\n words[ i >>> 5 ] &= ~(1 << i)\n }\n } else {\n for (let i = start, n = startWord + 32; i < n; ++i) {\n words[ i >>> 5 ] &= ~(1 << i)\n }\n for (let i = endWord, n = end + 1; i < n; ++i) {\n words[ i >>> 5 ] &= ~(1 << i)\n }\n }\n }\n return this\n }\n\n /**\n * Set bits of the given range\n * @param {Integer} start - start index\n * @param {Integer} end - end index\n * @return {BitArray} this object\n */\n setRange (start: number, end: number) {\n return this._assignRange(start, end, true)\n }\n\n /**\n * Clear bits of the given range\n * @param {Integer} start - start index\n * @param {Integer} end - end index\n * @return {BitArray} this object\n */\n clearRange (start: number, end: number) {\n return this._assignRange(start, end, false)\n }\n\n /**\n * Set bits at all given indices\n * @param {...Integer} arguments - indices\n * @return {Boolean} this object\n */\n setBits (...indices: number[]) {\n const words = this._words\n const n = indices.length\n for (let i = 0; i < n; ++i) {\n const index = indices[ i ]\n words[ index >>> 5 ] |= 1 << index\n }\n return this\n }\n\n /**\n * Clear bits at all given indices\n * @param {...Integer} arguments - indices\n * @return {Boolean} this object\n */\n clearBits (...indices: number[]) {\n const words = this._words\n const n = indices.length\n for (let i = 0; i < n; ++i) {\n const index = indices[ i ]\n words[ index >>> 5 ] &= ~(1 << index)\n }\n return this\n }\n\n /**\n * Set all bits of the array\n * @return {BitArray} this object\n */\n setAll () {\n return this._assignRange(0, this.length - 1, true)\n }\n\n /**\n * Clear all bits of the array\n * @return {BitArray} this object\n */\n clearAll () {\n return this._assignRange(0, this.length - 1, false)\n }\n\n /**\n * Flip all the values in the array\n * @return {BitArray} this object\n */\n flipAll () {\n const count = this._words.length\n const words = this._words\n const bs = 32 - this.length % 32\n for (let k = 0; k < count - 1; ++k) {\n words[k] = ~words[ k ]\n }\n words[ count - 1 ] = (~(words[ count - 1 ] << bs)) >>> bs\n return this\n }\n\n _isRangeValue (start: number, end: number, value: boolean) {\n if (end < start) return\n const words = this._words\n const wordValue = value === true ? 0xFFFFFFFF : 0\n const wordStart = start >>> 5\n const wordEnd = end >>> 5\n // set complete words when applicable\n for (let k = wordStart + 1; k < wordEnd; ++k) {\n if (words[ k ] !== wordValue) return false\n }\n // set parts of the range not spanning complete words\n if (end - start < 32) {\n for (let i = start, n = end + 1; i < n; ++i) {\n if (!!(words[ i >>> 5 ] & (1 << i)) !== value) return false\n }\n } else {\n const startWord = wordStart << 5\n const endWord = wordEnd << 5\n for (let i = start, n = startWord + 32; i < n; ++i) {\n if (!!(words[ i >>> 5 ] & (1 << i)) !== value) return false\n }\n for (let i = endWord, n = end + 1; i < n; ++i) {\n if (!!(words[ i >>> 5 ] & (1 << i)) !== value) return false\n }\n }\n return true\n }\n\n /**\n * Test if bits in given range are set\n * @param {Integer} start - start index\n * @param {Integer} end - end index\n * @return {BitArray} this object\n */\n isRangeSet (start: number, end: number) {\n return this._isRangeValue(start, end, true)\n }\n\n /**\n * Test if bits in given range are clear\n * @param {Integer} start - start index\n * @param {Integer} end - end index\n * @return {BitArray} this object\n */\n isRangeClear (start: number, end: number) {\n return this._isRangeValue(start, end, false)\n }\n\n /**\n * Test if all bits in the array are set\n * @return {Boolean} test result\n */\n isAllSet () {\n return this._isRangeValue(0, this.length - 1, true)\n }\n\n /**\n * Test if all bits in the array are clear\n * @return {Boolean} test result\n */\n isAllClear () {\n return this._isRangeValue(0, this.length - 1, false)\n }\n\n /**\n * Test if bits at all given indices are set\n * @param {...Integer} arguments - indices\n * @return {Boolean} test result\n */\n isSet (...indices: number[]) {\n const words = this._words\n const n = indices.length\n for (let i = 0; i < n; ++i) {\n const index = indices[ i ]\n if ((words[ index >>> 5 ] & (1 << index)) === 0) return false\n }\n return true\n }\n\n /**\n * Test if bits at all given indices are clear\n * @param {...Integer} arguments - indices\n * @return {Boolean} test result\n */\n isClear (...indices: number[]) {\n const words = this._words\n const n = indices.length\n for (let i = 0; i < n; ++i) {\n const index = indices[ i ]\n if ((words[ index >>> 5 ] & (1 << index)) !== 0) return false\n }\n return true\n }\n\n /**\n * Test if two BitArrays are identical in all their values\n * @param {BitArray} otherBitarray - the other BitArray\n * @return {Boolean} test result\n */\n isEqualTo (otherBitarray: BitArray) {\n const words1 = this._words\n const words2 = otherBitarray._words\n const count = Math.min(words1.length, words2.length)\n for (let k = 0; k < count; ++k) {\n if (words1[ k ] !== words2[ k ]) {\n return false\n }\n }\n return true\n }\n\n /**\n * How many set bits?\n * @return {Integer} number of set bits\n */\n getSize () {\n const count = this._words.length\n const words = this._words\n let size = 0\n for (let i = 0; i < count; ++i) {\n size += hammingWeight(words[ i ])\n }\n return size\n }\n\n /**\n * Calculate difference betwen this and another bit array.\n * Store result in this object.\n * @param {BitArray} otherBitarray - the other bit array\n * @return {BitArray} this object\n */\n difference (otherBitarray: BitArray) {\n const words1 = this._words\n const words2 = otherBitarray._words\n const count = Math.min(words1.length, words2.length)\n for (let k = 0; k < count; ++k) {\n words1[ k ] = words1[ k ] & ~words2[ k ]\n }\n for (let k = words1.length; k < count; ++k) {\n words1[ k ] = 0\n }\n return this\n }\n\n /**\n * Calculate union betwen this and another bit array.\n * Store result in this object.\n * @param {BitArray} otherBitarray - the other bit array\n * @return {BitArray} this object\n */\n union (otherBitarray: BitArray) {\n const words1 = this._words\n const words2 = otherBitarray._words\n const count = Math.min(words1.length, words2.length)\n for (let k = 0; k < count; ++k) {\n words1[ k ] |= words2[ k ]\n }\n for (let k = words1.length; k < count; ++k) {\n words1[ k ] = 0\n }\n return this\n }\n\n /**\n * Calculate intersection betwen this and another bit array.\n * Store result in this object.\n * @param {BitArray} otherBitarray - the other bit array\n * @return {BitArray} this object\n */\n intersection (otherBitarray: BitArray) {\n const words1 = this._words\n const words2 = otherBitarray._words\n const count = Math.min(words1.length, words2.length)\n for (let k = 0; k < count; ++k) {\n words1[ k ] &= words2[ k ]\n }\n for (let k = words1.length; k < count; ++k) {\n words1[ k ] = 0\n }\n return this\n }\n\n /**\n * Test if there is any intersection betwen this and another bit array.\n * @param {BitArray} otherBitarray - the other bit array\n * @return {Boolean} test result\n */\n intersects (otherBitarray: BitArray) {\n const words1 = this._words\n const words2 = otherBitarray._words\n const count = Math.min(words1.length, words2.length)\n for (let k = 0; k < count; ++k) {\n if ((words1[ k ] & words2[ k ]) !== 0) {\n return true\n }\n }\n return false\n }\n\n /**\n * Calculate the number of bits in common betwen this and another bit array.\n * @param {BitArray} otherBitarray - the other bit array\n * @return {Integer} size\n */\n getIntersectionSize (otherBitarray: BitArray) {\n const words1 = this._words\n const words2 = otherBitarray._words\n const count = Math.min(words1.length, words2.length)\n let size = 0\n for (let k = 0; k < count; ++k) {\n size += hammingWeight(words1[ k ] & words2[ k ])\n }\n return size\n }\n\n /**\n * Calculate intersection betwen this and another bit array.\n * Store result in a new bit array.\n * @param {BitArray} otherBitarray - the other bit array\n * @return {BitArray} the new bit array\n */\n makeIntersection (otherBitarray: BitArray) {\n const words1 = this._words\n const words2 = otherBitarray._words\n const count = Math.min(words1.length, words2.length)\n const wordsA = new Uint32Array(count)\n const intersection = Object.create(BitArray.prototype)\n intersection._words = wordsA\n intersection.length = Math.min(this.length, otherBitarray.length)\n for (let k = 0; k < count; ++k) {\n wordsA[ k ] = words1[ k ] & words2[ k ]\n }\n return intersection\n }\n\n /**\n * Iterate over all set bits in the array\n * @param {function( index: Integer, i: Integer )} callback - the callback\n * @return {undefined}\n */\n forEach (callback: (index: number, i: number) => any) {\n const count = this._words.length\n const words = this._words\n let i = 0\n for (let k = 0; k < count; ++k) {\n let w = words[ k ]\n while (w !== 0) {\n const t = w & -w\n const index = (k << 5) + hammingWeight(t - 1)\n callback(index, i)\n w ^= t\n ++i\n }\n }\n }\n\n /**\n * Get an array with the set bits\n * @return {Array} bit indices\n */\n toArray () {\n const words = this._words\n const answer = new Array(this.getSize())\n const count = this._words.length\n let pos = 0\n for (let k = 0; k < count; ++k) {\n let w = words[ k ]\n while (w !== 0) {\n const t = w & -w\n answer[ pos++ ] = (k << 5) + hammingWeight(t - 1)\n w ^= t\n }\n }\n return answer\n }\n\n toString () {\n return '{' + this.toArray().join(',') + '}'\n }\n\n toSeleString () {\n const sele = this.toArray().join(',')\n return sele ? '@' + sele : 'NONE'\n }\n\n /**\n * Clone this object\n * @return {BitArray} the cloned object\n */\n clone () {\n const clone = Object.create(BitArray.prototype)\n clone.length = this.length\n clone._words = new Uint32Array(this._words)\n return clone\n }\n}","/**\n * @file Adjacency List\n * @author Alexander Rose \n * @private\n */\n\nexport interface Edges {\n nodeArray1: ArrayLike\n nodeArray2: ArrayLike\n edgeCount: number\n nodeCount: number\n}\n\nexport interface AdjacencyList {\n /* number of edges for each node */\n countArray: Uint8Array\n /* offset into indexArray for each node */\n offsetArray: Int32Array\n /* edge indices, grouped by nodes */\n indexArray: Int32Array\n}\n\nexport function createAdjacencyList (edges: Edges): AdjacencyList {\n const { edgeCount, nodeCount, nodeArray1, nodeArray2 } = edges\n\n const countArray = new Uint8Array(nodeCount)\n const offsetArray = new Int32Array(nodeCount)\n\n // count edges per node\n for (let i = 0; i < edgeCount; ++i) {\n countArray[ nodeArray1[ i ] ] += 1\n countArray[ nodeArray2[ i ] ] += 1\n }\n\n // get offsets to node edges\n for (let i = 1; i < nodeCount; ++i) {\n offsetArray[ i ] += offsetArray[ i - 1 ] + countArray[ i - 1 ]\n }\n\n // prepare index array\n const bondCount2 = edgeCount * 2\n const indexArray = new Int32Array(bondCount2)\n for (let j = 0; j < bondCount2; ++j) {\n indexArray[ j ] = -1\n }\n\n // build index array\n for (let i = 0; i < edgeCount; ++i) {\n const idx1 = nodeArray1[ i ]\n const idx2 = nodeArray2[ i ]\n let j1 = offsetArray[ idx1 ]\n while (indexArray[ j1 ] !== -1 && j1 < bondCount2) {\n j1 += 1\n }\n indexArray[ j1 ] = i\n let j2 = offsetArray[ idx2 ]\n while (indexArray[ j2 ] !== -1 && j2 < bondCount2) {\n j2 += 1\n }\n indexArray[ j2 ] = i\n }\n\n return { countArray, offsetArray, indexArray }\n}\n","/**\n * @file Features\n * @author Alexander Rose \n */\n\nimport AtomProxy from '../../proxy/atom-proxy'\n\nexport interface Features {\n types: FeatureType[]\n groups: FeatureGroup[]\n centers: { x: number[], y: number[], z: number[] }\n atomSets: number[][]\n}\n\nexport const enum FeatureType {\n Unknown = 0,\n PositiveCharge = 1,\n NegativeCharge = 2,\n AromaticRing = 3,\n HydrogenDonor = 4,\n HydrogenAcceptor = 5,\n HalogenDonor = 6,\n HalogenAcceptor = 7,\n Hydrophobic = 8,\n WeakHydrogenDonor = 9,\n IonicTypePartner = 10,\n DativeBondPartner = 11,\n TransitionMetal = 12,\n IonicTypeMetal = 13\n}\n\nexport const enum FeatureGroup {\n Unknown = 0,\n QuaternaryAmine = 1,\n TertiaryAmine = 2,\n Sulfonium = 3,\n SulfonicAcid = 4,\n Sulfate = 5,\n Phosphate = 6,\n Halocarbon = 7,\n Guanidine = 8,\n Acetamidine = 9,\n Carboxylate = 10\n}\n\nexport function createFeatures (): Features {\n return {\n types: [],\n groups: [],\n centers: { x: [], y: [], z: [] },\n atomSets: []\n }\n}\n\nexport interface FeatureState {\n type: FeatureType\n group: FeatureGroup\n x: number\n y: number\n z: number\n atomSet: number[]\n}\n\nexport function createFeatureState(type = FeatureType.Unknown, group = FeatureGroup.Unknown): FeatureState {\n return { type, group, x: 0, y: 0, z: 0, atomSet: [] }\n}\n\nexport function addAtom (state: FeatureState, atom: AtomProxy) {\n state.x += atom.x\n state.y += atom.y\n state.z += atom.z\n state.atomSet.push(atom.index)\n}\n\nexport function addFeature (features: Features, state: FeatureState) {\n const n = state.atomSet.length\n if (n > 0) {\n const { types, groups, centers, atomSets } = features\n types.push(state.type)\n groups.push(state.group)\n centers.x.push(state.x / n)\n centers.y.push(state.y / n)\n centers.z.push(state.z / n)\n atomSets.push(state.atomSet)\n }\n}\n","/**\n * @file Structure Constants\n * @author Alexander Rose \n * @private\n */\n\n// entity types\nexport const UnknownEntity = 0\nexport const PolymerEntity = 1\nexport const NonPolymerEntity = 2\nexport const MacrolideEntity = 3\nexport const WaterEntity = 4\n\n// molecule types\nexport const UnknownType = 0\nexport const WaterType = 1\nexport const IonType = 2\nexport const ProteinType = 3\nexport const RnaType = 4\nexport const DnaType = 5\nexport const SaccharideType = 6\n\n// backbone types\nexport const UnknownBackboneType = 0\nexport const ProteinBackboneType = 1\nexport const RnaBackboneType = 2\nexport const DnaBackboneType = 3\nexport const CgProteinBackboneType = 4\nexport const CgRnaBackboneType = 5\nexport const CgDnaBackboneType = 6\n\n// chemical component types\nexport const ChemCompProtein = [\n 'D-BETA-PEPTIDE, C-GAMMA LINKING', 'D-GAMMA-PEPTIDE, C-DELTA LINKING',\n 'D-PEPTIDE COOH CARBOXY TERMINUS', 'D-PEPTIDE NH3 AMINO TERMINUS', 'D-PEPTIDE LINKING',\n 'L-BETA-PEPTIDE, C-GAMMA LINKING', 'L-GAMMA-PEPTIDE, C-DELTA LINKING',\n 'L-PEPTIDE COOH CARBOXY TERMINUS', 'L-PEPTIDE NH3 AMINO TERMINUS', 'L-PEPTIDE LINKING',\n 'PEPTIDE LINKING', 'PEPTIDE-LIKE'\n]\nexport const ChemCompRna = [\n 'RNA OH 3 PRIME TERMINUS', 'RNA OH 5 PRIME TERMINUS', 'RNA LINKING'\n]\nexport const ChemCompDna = [\n 'DNA OH 3 PRIME TERMINUS', 'DNA OH 5 PRIME TERMINUS', 'DNA LINKING',\n 'L-DNA LINKING', 'L-RNA LINKING'\n]\nexport const ChemCompSaccharide = [\n 'D-SACCHARIDE', 'D-SACCHARIDE 1,4 AND 1,4 LINKING', 'D-SACCHARIDE 1,4 AND 1,6 LINKING',\n 'L-SACCHARIDE', 'L-SACCHARIDE 1,4 AND 1,4 LINKING', 'L-SACCHARIDE 1,4 AND 1,6 LINKING',\n 'SACCHARIDE'\n]\nexport const ChemCompOther = [\n 'OTHER'\n]\nexport const ChemCompNonPolymer = [\n 'NON-POLYMER'\n]\nexport const ChemCompHetero = ChemCompNonPolymer.concat(ChemCompOther, ChemCompSaccharide)\n\n// secondary structure\nexport const SecStrucHelix = [ 'h', 'g', 'i' ]\nexport const SecStrucSheet = [ 'e', 'b' ]\nexport const SecStrucTurn = [ 's', 't', 'l', '' ]\n\nexport const AtomicNumbers: { [e: string]: number | undefined } = {\n 'H': 1, 'D': 1, 'T': 1, 'HE': 2, 'LI': 3, 'BE': 4, 'B': 5, 'C': 6, 'N': 7, 'O': 8, 'F': 9, 'NE': 10, 'NA': 11, 'MG': 12, 'AL': 13, 'SI': 14, 'P': 15, 'S': 16, 'CL': 17, 'AR': 18, 'K': 19, 'CA': 20, 'SC': 21, 'TI': 22, 'V': 23, 'CR': 24, 'MN': 25, 'FE': 26, 'CO': 27, 'NI': 28, 'CU': 29, 'ZN': 30, 'GA': 31, 'GE': 32, 'AS': 33, 'SE': 34, 'BR': 35, 'KR': 36, 'RB': 37, 'SR': 38, 'Y': 39, 'ZR': 40, 'NB': 41, 'MO': 42, 'TC': 43, 'RU': 44, 'RH': 45, 'PD': 46, 'AG': 47, 'CD': 48, 'IN': 49, 'SN': 50, 'SB': 51, 'TE': 52, 'I': 53, 'XE': 54, 'CS': 55, 'BA': 56, 'LA': 57, 'CE': 58, 'PR': 59, 'ND': 60, 'PM': 61, 'SM': 62, 'EU': 63, 'GD': 64, 'TB': 65, 'DY': 66, 'HO': 67, 'ER': 68, 'TM': 69, 'YB': 70, 'LU': 71, 'HF': 72, 'TA': 73, 'W': 74, 'RE': 75, 'OS': 76, 'IR': 77, 'PT': 78, 'AU': 79, 'HG': 80, 'TL': 81, 'PB': 82, 'BI': 83, 'PO': 84, 'AT': 85, 'RN': 86, 'FR': 87, 'RA': 88, 'AC': 89, 'TH': 90, 'PA': 91, 'U': 92, 'NP': 93, 'PU': 94, 'AM': 95, 'CM': 96, 'BK': 97, 'CF': 98, 'ES': 99, 'FM': 100, 'MD': 101, 'NO': 102, 'LR': 103, 'RF': 104, 'DB': 105, 'SG': 106, 'BH': 107, 'HS': 108, 'MT': 109, 'DS': 110, 'RG': 111, 'CN': 112, 'NH': 113, 'FL': 114, 'MC': 115, 'LV': 116, 'TS': 117, 'OG': 118\n}\nexport const DefaultAtomicNumber = 0\n\n/**\n * Enum mapping element to atomic number\n */\nexport const enum Elements {\n H = 1, D = 1, T = 1, HE = 2, LI = 3, BE = 4, B = 5, C = 6, N = 7, O = 8, F = 9, NE = 10, NA = 11, MG = 12, AL = 13, SI = 14, P = 15, S = 16, CL = 17, AR = 18, K = 19, CA = 20, SC = 21, TI = 22, V = 23, CR = 24, MN = 25, FE = 26, CO = 27, NI = 28, CU = 29, ZN = 30, GA = 31, GE = 32, AS = 33, SE = 34, BR = 35, KR = 36, RB = 37, SR = 38, Y = 39, ZR = 40, NB = 41, MO = 42, TC = 43, RU = 44, RH = 45, PD = 46, AG = 47, CD = 48, IN = 49, SN = 50, SB = 51, TE = 52, I = 53, XE = 54, CS = 55, BA = 56, LA = 57, CE = 58, PR = 59, ND = 60, PM = 61, SM = 62, EU = 63, GD = 64, TB = 65, DY = 66, HO = 67, ER = 68, TM = 69, YB = 70, LU = 71, HF = 72, TA = 73, W = 74, RE = 75, OS = 76, IR = 77, PT = 78, AU = 79, HG = 80, TL = 81, PB = 82, BI = 83, PO = 84, AT = 85, RN = 86, FR = 87, RA = 88, AC = 89, TH = 90, PA = 91, U = 92, NP = 93, PU = 94, AM = 95, CM = 96, BK = 97, CF = 98, ES = 99, FM = 100, MD = 101, NO = 102, LR = 103, RF = 104, DB = 105, SG = 106, BH = 107, HS = 108, MT = 109, DS = 110, RG = 111, CN = 112, NH = 113, FL = 114, MC = 115, LV = 116, TS = 117, OG = 118\n}\n\n// https://doi.org/10.1515/pac-2015-0305 (table 2, 3, and 4)\nexport const AtomWeights: { [e: number]: number | undefined } = {\n 1: 1.008, 2: 4.0026, 3: 6.94, 4: 9.0122, 5: 10.81, 6: 10.81, 7: 14.007, 8: 15.999, 9: 18.998, 10: 20.180, 11: 22.990, 12: 24.305, 13: 26.982, 14: 28.085, 15: 30.974, 16: 32.06, 17: 35.45, 18: 39.948, 19: 39.098, 20: 40.078, 21: 44.956, 22: 47.867, 23: 50.942, 24: 51.996, 25: 54.938, 26: 55.845, 27: 58.933, 28: 58.693, 29: 63.546, 30: 65.38, 31: 69.723, 32: 72.630, 33: 74.922, 34: 78.971, 35: 79.904, 36: 83.798, 37: 85.468, 38: 87.62, 39: 88.906, 40: 91.224, 41: 92.906, 42: 95.95, 43: 96.906, 44: 101.07, 45: 102.91, 46: 106.42, 47: 107.87, 48: 112.41, 49: 114.82, 50: 118.71, 51: 121.76, 52: 127.60, 53: 127.60, 54: 131.29, 55: 132.91, 56: 137.33, 57: 138.91, 58: 140.12, 59: 140.91, 60: 144.24, 61: 144.912, 62: 150.36, 63: 151.96, 64: 157.25, 65: 158.93, 66: 162.50, 67: 164.93, 68: 167.26, 69: 168.93, 70: 173.05, 71: 174.97, 72: 178.49, 73: 180.95, 74: 183.84, 75: 186.21, 76: 190.23, 77: 192.22, 78: 195.08, 79: 196.97, 80: 200.59, 81: 204.38, 82: 207.2, 83: 208.98, 84: 1.97, 85: 2.02, 86: 2.2, 87: 3.48, 88: 2.83, 89: 2.0, 90: 232.04, 91: 231.04, 92: 238.03, 93: 237.048, 94: 244.064, 95: 243.061, 96: 247.070, 97: 247.070, 98: 251.079, 99: 252.083, 100: 257.095, 101: 258.098, 102: 259.101, 103: 262.110, 104: 267.122, 105: 270.131, 106: 271.134, 107: 270.133, 108: 270.134, 109: 278.156, 110: 281.165, 111: 281.166, 112: 285.177, 113: 286.182, 114: 289.190, 115: 289.194, 116: 293.204, 117: 293.208, 118: 294.214\n}\nexport const DefaultAtomWeight = 10.81 // C\n\n// http://dx.doi.org/10.1021/jp8111556 (or 2.0)\nexport const VdwRadii: { [e: number]: number | undefined } = {\n 1: 1.1, 2: 1.4, 3: 1.81, 4: 1.53, 5: 1.92, 6: 1.7, 7: 1.55, 8: 1.52, 9: 1.47, 10: 1.54, 11: 2.27, 12: 1.73, 13: 1.84, 14: 2.1, 15: 1.8, 16: 1.8, 17: 1.75, 18: 1.88, 19: 2.75, 20: 2.31, 21: 2.3, 22: 2.15, 23: 2.05, 24: 2.05, 25: 2.05, 26: 2.05, 27: 2.0, 28: 2.0, 29: 2.0, 30: 2.1, 31: 1.87, 32: 2.11, 33: 1.85, 34: 1.9, 35: 1.83, 36: 2.02, 37: 3.03, 38: 2.49, 39: 2.4, 40: 2.3, 41: 2.15, 42: 2.1, 43: 2.05, 44: 2.05, 45: 2.0, 46: 2.05, 47: 2.1, 48: 2.2, 49: 2.2, 50: 1.93, 51: 2.17, 52: 2.06, 53: 1.98, 54: 2.16, 55: 3.43, 56: 2.68, 57: 2.5, 58: 2.48, 59: 2.47, 60: 2.45, 61: 2.43, 62: 2.42, 63: 2.4, 64: 2.38, 65: 2.37, 66: 2.35, 67: 2.33, 68: 2.32, 69: 2.3, 70: 2.28, 71: 2.27, 72: 2.25, 73: 2.2, 74: 2.1, 75: 2.05, 76: 2.0, 77: 2.0, 78: 2.05, 79: 2.1, 80: 2.05, 81: 1.96, 82: 2.02, 83: 2.07, 84: 1.97, 85: 2.02, 86: 2.2, 87: 3.48, 88: 2.83, 89: 2.0, 90: 2.4, 91: 2.0, 92: 2.3, 93: 2.0, 94: 2.0, 95: 2.0, 96: 2.0, 97: 2.0, 98: 2.0, 99: 2.0, 100: 2.0, 101: 2.0, 102: 2.0, 103: 2.0, 104: 2.0, 105: 2.0, 106: 2.0, 107: 2.0, 108: 2.0, 109: 2.0, 110: 2.0, 111: 2.0, 112: 2.0, 113: 2.0, 114: 2.0, 115: 2.0, 116: 2.0, 117: 2.0, 118: 2.0\n}\nexport const DefaultVdwRadius = 2.0 // C\n\n// Peter Rose (peter.rose@rcsb.org), private communication, average accross PDB\nexport const ResidueRadii: { [k: string]: number } = {\n '2QY': 6.58,\n 'CY0': 11.98,\n '2QZ': 2.52,\n 'CY1': 6.59,\n 'HHK': 5.11,\n 'CXM': 4.69,\n 'HHI': 4.58,\n 'CY4': 4.57,\n 'S12': 18.57,\n 'CY3': 2.79,\n 'C5C': 5.35,\n 'PFX': 11.84,\n '2R3': 6.94,\n '2R1': 3.78,\n 'ILX': 4.99,\n '32S': 5.68,\n 'BTK': 8.59,\n '32T': 5.72,\n 'FAK': 9.8,\n 'B27': 2.78,\n 'ILM': 3.84,\n 'C4R': 5.63,\n '32L': 6.75,\n 'SYS': 3.01,\n '1MH': 5.04,\n 'ILE': 3.65,\n 'YNM': 6.39,\n '2RX': 4.91,\n 'B3A': 2.48,\n 'GEE': 4.76,\n '7MN': 7.34,\n 'B3E': 5.4,\n 'ARG': 6.33,\n '200': 6.89,\n 'HIP': 5.47,\n 'HIA': 4.64,\n 'B3K': 5.89,\n 'HIC': 5.76,\n 'B3L': 4.96,\n 'B3M': 5.07,\n 'ARM': 6.86,\n 'ARO': 7.35,\n 'AR4': 8.42,\n 'PG1': 10.67,\n 'YOF': 6.44,\n 'IML': 3.74,\n 'SXE': 6.65,\n 'HIQ': 7.98,\n 'PFF': 6.31,\n 'HIS': 4.52,\n '0TD': 3.62,\n 'C3Y': 5.24,\n '1OP': 11.55,\n '02Y': 4.77,\n '02V': 4.83,\n 'ASB': 5.59,\n '30V': 8.53,\n 'S2P': 4.81,\n 'ASP': 3.55,\n 'ASN': 3.54,\n '2OR': 6.91,\n 'QMM': 6.13,\n '2P0': 8.52,\n 'ASL': 5.36,\n 'HFA': 5.14,\n '5PG': 5.69,\n 'B3X': 4.38,\n 'AS9': 4.1,\n 'ARV': 7.59,\n 'B3U': 6.06,\n 'S2C': 7.54,\n 'B3T': 3.34,\n '175': 5.64,\n 'GFT': 8.18,\n 'HG7': 6.8,\n 'B3Q': 4.48,\n 'ASA': 3.64,\n '02K': 2.94,\n 'B3Y': 7.45,\n 'PHD': 5.35,\n 'C6C': 6.42,\n 'BUC': 5.8,\n 'HGL': 8.07,\n 'PHE': 5.06,\n '03Y': 2.6,\n 'PHA': 5.11,\n 'OCY': 5.0,\n '4PH': 6.79,\n '5OH': 4.7,\n '31Q': 10.46,\n 'BTR': 7.98,\n '3PX': 4.7,\n '1PA': 8.07,\n 'ASX': 3.54,\n 'IOR': 7.23,\n '03E': 3.38,\n 'PHL': 5.17,\n 'KWS': 5.09,\n 'PHI': 7.12,\n 'NAL': 7.22,\n 'S1H': 19.21,\n '2ML': 3.86,\n '2MR': 7.35,\n 'GHG': 4.83,\n 'TYY': 6.54,\n '2MT': 3.67,\n '56A': 13.01,\n 'SVA': 5.46,\n 'TYX': 8.31,\n 'TYS': 8.59,\n 'TYR': 6.38,\n 'TYQ': 6.43,\n 'HLU': 3.99,\n 'MYK': 19.47,\n 'TYO': 7.71,\n 'HLX': 4.98,\n 'TYN': 9.87,\n 'TYJ': 6.25,\n 'TYI': 6.49,\n 'LYH': 5.13,\n 'LYF': 12.19,\n 'SUN': 6.73,\n 'LYR': 18.28,\n 'TYB': 6.46,\n '11W': 14.39,\n 'LYS': 5.54,\n 'LYN': 4.8,\n '11Q': 4.85,\n 'LYO': 4.71,\n 'LYZ': 1.76,\n 'TXY': 6.44,\n 'MYN': 4.71,\n 'TY5': 10.6,\n 'HMR': 5.09,\n '01W': 8.55,\n 'LYX': 13.36,\n 'TY8': 7.22,\n 'TY2': 6.49,\n 'KYN': 6.18,\n 'KYQ': 9.75,\n 'CZZ': 5.14,\n 'IIL': 3.81,\n 'HNC': 10.41,\n 'OIC': 4.62,\n 'LVN': 2.89,\n 'QIL': 3.84,\n 'JJL': 8.3,\n 'VAH': 3.88,\n 'JJJ': 7.5,\n 'JJK': 7.43,\n 'VAD': 2.56,\n 'CYW': 4.65,\n '0QL': 5.72,\n '143': 8.22,\n 'SVX': 7.04,\n 'CYJ': 11.64,\n 'SVY': 7.1,\n 'SVZ': 6.6,\n 'CYG': 8.03,\n 'CYF': 13.54,\n 'SVV': 5.09,\n 'GL3': 2.72,\n '8SP': 14.26,\n 'CYS': 2.78,\n '004': 4.33,\n 'CYR': 10.33,\n 'PLJ': 3.71,\n 'EXY': 7.37,\n 'HL2': 3.75,\n 'A5N': 5.21,\n 'CYQ': 5.67,\n 'CZ2': 5.16,\n 'LWY': 4.12,\n 'PM3': 8.78,\n 'OHS': 6.98,\n 'OHI': 5.35,\n '3TY': 8.42,\n 'CYD': 8.55,\n 'DYS': 7.87,\n 'DAH': 6.47,\n '4IK': 11.81,\n '3EG': 3.66,\n 'AYA': 3.65,\n '4IN': 6.31,\n 'DAB': 3.48,\n '4HT': 6.03,\n 'RGL': 7.03,\n 'DAM': 2.49,\n 'NFA': 5.04,\n 'WFP': 6.07,\n '2JC': 2.97,\n 'HAR': 7.55,\n '2JG': 5.67,\n 'MH6': 1.72,\n '2JF': 9.13,\n '3FG': 4.96,\n 'MGN': 4.84,\n 'AZH': 5.36,\n 'AZK': 6.03,\n 'ZBZ': 7.79,\n 'TBG': 2.58,\n 'VAL': 2.51,\n 'MGG': 7.34,\n 'AZS': 5.61,\n 'FHL': 9.75,\n '2JH': 4.56,\n 'IEL': 7.07,\n 'FHO': 6.75,\n 'DA2': 7.79,\n 'FH7': 6.99,\n 'ME0': 4.52,\n '3GL': 4.84,\n 'MDO': 5.03,\n 'AZY': 7.37,\n 'A8E': 3.76,\n 'ZCL': 6.71,\n 'MDH': 2.58,\n 'LA2': 14.07,\n '4FW': 6.1,\n 'YCM': 5.32,\n 'MDF': 4.95,\n 'YCP': 3.01,\n 'TEF': 8.63,\n 'FGP': 4.34,\n 'UF0': 19.72,\n 'XCN': 4.57,\n 'FGL': 2.56,\n 'MF3': 6.37,\n 'MEQ': 5.13,\n 'LAA': 3.23,\n 'IGL': 5.52,\n 'MET': 4.49,\n 'NIY': 6.81,\n 'QCS': 5.18,\n 'TCQ': 8.56,\n 'MEN': 4.33,\n '4HL': 8.79,\n 'MEA': 4.95,\n 'EFC': 5.28,\n 'LAL': 2.41,\n '2HF': 5.52,\n 'KBE': 5.64,\n 'OCS': 3.94,\n 'CAF': 5.46,\n 'NC1': 11.4,\n 'NBQ': 9.82,\n 'CAB': 4.19,\n 'MBQ': 9.55,\n '193': 7.38,\n '192': 2.44,\n '0WZ': 7.61,\n 'CAS': 5.35,\n 'NB8': 11.98,\n 'OBS': 11.71,\n '1AC': 2.42,\n 'PCA': 3.48,\n 'MCL': 9.73,\n 'LBY': 7.75,\n 'GAU': 4.67,\n 'PBF': 9.75,\n 'MCG': 6.46,\n 'DDE': 6.86,\n '19W': 3.94,\n 'MD5': 9.33,\n 'MD6': 6.44,\n 'MD3': 8.41,\n 'MCS': 7.56,\n 'OBF': 3.64,\n 'UAL': 4.68,\n 'PAT': 6.05,\n 'IAM': 8.88,\n 'PAQ': 8.77,\n 'FDL': 9.49,\n 'NCB': 3.45,\n 'LCK': 9.81,\n 'DDZ': 2.52,\n '2FM': 5.54,\n 'IAR': 6.77,\n 'OAS': 4.8,\n 'HBN': 8.8,\n 'TA4': 5.55,\n '1C3': 7.43,\n 'ECX': 5.51,\n 'PF5': 6.28,\n 'RE3': 5.29,\n 'FCL': 6.25,\n 'ECC': 4.79,\n 'LDH': 7.06,\n 'NCY': 2.91,\n 'CCS': 4.58,\n 'PEC': 6.54,\n '2CO': 4.45,\n 'LE1': 2.72,\n 'HCM': 5.53,\n '07O': 8.05,\n 'HCL': 4.96,\n 'NEP': 6.94,\n 'PE1': 8.01,\n 'LEF': 4.37,\n 'FC0': 5.18,\n 'LED': 4.34,\n 'HCS': 4.09,\n 'DBU': 2.49,\n 'RE0': 5.53,\n 'LEN': 3.82,\n '1E3': 8.71,\n 'BB9': 2.56,\n 'BB8': 5.14,\n 'PCS': 5.05,\n 'BB7': 4.56,\n 'BB6': 2.62,\n 'LEU': 3.83,\n 'DBZ': 7.08,\n 'LET': 11.29,\n 'DBY': 6.46,\n 'ICY': 7.76,\n 'MAA': 2.4,\n 'CGA': 7.91,\n '5CS': 8.34,\n 'UGY': 3.7,\n 'LGY': 11.71,\n 'N10': 8.96,\n 'AAR': 6.39,\n 'FT6': 7.5,\n 'MOD': 12.62,\n '5CW': 7.21,\n 'PVH': 4.58,\n 'BBC': 6.42,\n 'YYA': 7.3,\n 'O12': 14.08,\n 'NOT': 7.15,\n 'KGC': 9.88,\n 'MP4': 5.86,\n '0CS': 4.07,\n 'MP8': 3.75,\n 'VLL': 2.54,\n 'VLM': 2.51,\n 'BCS': 8.03,\n 'MNL': 4.9,\n 'AA4': 4.47,\n 'SAC': 3.49,\n 'BCX': 2.99,\n '3CF': 6.47,\n 'SAH': 11.7,\n 'NNH': 6.86,\n 'CGU': 4.71,\n 'SIB': 12.41,\n 'TLY': 8.78,\n 'SIC': 4.81,\n 'VMS': 8.82,\n 'TMD': 6.76,\n 'MMO': 6.53,\n 'PXU': 2.46,\n '4AW': 6.22,\n 'OTH': 3.6,\n 'DLS': 6.84,\n 'MME': 4.99,\n 'DM0': 6.99,\n '0FL': 2.76,\n 'SBL': 8.96,\n 'CDV': 3.72,\n 'OTY': 6.51,\n 'PYA': 7.75,\n '2AS': 3.57,\n 'DMH': 4.92,\n 'ELY': 7.42,\n 'GVL': 9.6,\n 'FVA': 2.9,\n 'SAR': 2.48,\n '4BF': 6.92,\n 'EME': 4.69,\n 'CDE': 2.51,\n '3AR': 7.86,\n '3AH': 9.11,\n 'AC5': 2.44,\n 'FTR': 6.08,\n 'MLL': 3.76,\n 'NPH': 11.66,\n 'NPI': 6.9,\n 'DMT': 6.67,\n 'PYX': 11.3,\n 'MLE': 3.87,\n 'PYL': 9.67,\n 'ZZU': 6.94,\n 'H5M': 3.61,\n 'SCH': 4.46,\n 'DMK': 3.52,\n 'FTY': 9.07,\n '2AG': 3.7,\n 'ABA': 2.55,\n 'ZZJ': 2.44,\n 'MLZ': 6.8,\n 'MLY': 6.88,\n 'KCX': 7.28,\n 'ZZD': 8.16,\n '3A5': 5.37,\n 'LHC': 7.75,\n '9AT': 2.47,\n 'OZT': 3.4,\n 'THO': 2.62,\n 'THR': 2.5,\n 'DFI': 3.93,\n 'MKD': 6.42,\n '4CY': 4.6,\n 'SDP': 6.07,\n 'DFO': 3.94,\n '0A0': 3.45,\n '4DB': 9.73,\n 'ML3': 6.26,\n 'BG1': 8.02,\n 'SD4': 4.57,\n 'THC': 3.8,\n 'SCS': 5.48,\n 'TH5': 4.65,\n 'BFD': 5.33,\n 'AEI': 6.34,\n 'TH6': 2.85,\n 'SCY': 4.53,\n 'TIS': 4.81,\n 'SEE': 4.53,\n 'BHD': 3.48,\n 'SEB': 8.18,\n 'SEC': 2.96,\n 'SEP': 4.8,\n 'CLH': 7.13,\n 'TIH': 5.02,\n 'CLG': 13.62,\n 'SEN': 6.43,\n 'XXA': 7.34,\n 'SEL': 2.46,\n 'SE7': 4.19,\n '4CF': 7.72,\n 'G8M': 3.57,\n 'BH2': 3.51,\n 'UN2': 3.22,\n 'VR0': 10.51,\n 'MK8': 4.76,\n 'DHA': 2.32,\n 'LMQ': 4.69,\n 'SFE': 5.01,\n 'AHB': 3.47,\n 'OXX': 7.05,\n 'BIF': 9.63,\n 'IZO': 4.47,\n 'NMM': 8.25,\n '0BN': 7.0,\n 'HZP': 3.12,\n 'NMC': 4.23,\n 'DHL': 2.69,\n '9DS': 9.29,\n 'SER': 2.41,\n 'CHG': 4.2,\n 'MIR': 6.54,\n 'AGQ': 7.79,\n 'SET': 2.46,\n 'MIS': 6.32,\n '4FB': 3.08,\n '0AR': 8.46,\n 'LME': 3.99,\n 'FZN': 24.42,\n 'AGT': 9.04,\n 'IYR': 6.46,\n '9DN': 9.31,\n 'CHP': 5.75,\n 'UNK': 1.64,\n 'XX1': 9.92,\n 'AGM': 6.57,\n '0AH': 5.78,\n 'LLP': 10.22,\n '0AF': 6.72,\n '4DP': 9.28,\n 'HYP': 2.25,\n 'DIR': 5.8,\n 'LLY': 8.71,\n '0AK': 6.11,\n 'NLE': 4.67,\n 'OYL': 6.42,\n 'WVL': 4.69,\n '0A8': 8.1,\n 'NLY': 6.37,\n 'MHO': 4.89,\n 'VOL': 2.55,\n '0A1': 7.1,\n 'MHL': 3.92,\n 'NLP': 4.81,\n 'NLQ': 4.65,\n 'MHW': 2.74,\n 'BIL': 4.7,\n 'NLO': 4.8,\n 'MHU': 7.51,\n 'XW1': 9.36,\n 'LLO': 10.13,\n 'SGB': 6.88,\n 'MHV': 3.6,\n 'MHS': 4.51,\n '0A9': 5.17,\n '0LF': 9.96,\n 'HT7': 6.82,\n 'X2W': 6.6,\n 'YPZ': 9.38,\n 'I58': 6.73,\n 'FLA': 2.4,\n 'M0H': 4.83,\n 'HSL': 2.46,\n 'FLE': 6.17,\n 'KOR': 10.1,\n '1VR': 3.89,\n 'HSO': 4.56,\n 'TTS': 9.41,\n 'RVX': 7.01,\n 'TTQ': 7.71,\n 'H14': 5.27,\n 'HTI': 7.8,\n 'ONH': 6.14,\n 'LP6': 8.58,\n 'ONL': 4.83,\n 'AHH': 5.06,\n 'HS8': 7.4,\n 'HS9': 4.71,\n 'BL2': 5.82,\n 'AHP': 5.26,\n '6HN': 7.34,\n 'HRP': 5.46,\n 'POM': 3.6,\n 'WPA': 5.11,\n '2ZC': 4.29,\n 'CPC': 2.65,\n 'AIB': 2.4,\n 'XSN': 3.47,\n 'M2S': 5.28,\n 'GND': 6.67,\n 'GNC': 4.6,\n 'MVA': 2.56,\n 'OLZ': 5.32,\n 'M2L': 6.15,\n 'TRF': 6.69,\n 'NZH': 7.66,\n 'SRZ': 5.27,\n 'OLD': 10.47,\n 'CME': 5.86,\n 'CMH': 5.3,\n 'ALA': 2.38,\n 'TRQ': 7.36,\n 'PPN': 7.24,\n 'TRP': 6.07,\n 'TRO': 5.82,\n 'TRN': 5.95,\n 'NYS': 8.1,\n 'ALC': 5.26,\n 'U3X': 11.7,\n 'HVA': 2.58,\n 'TS9': 3.92,\n 'TRX': 7.27,\n 'TRW': 11.8,\n 'LPL': 7.51,\n 'GMA': 4.4,\n 'OMT': 5.07,\n 'CMT': 3.54,\n 'GME': 4.66,\n 'NYB': 6.07,\n 'PR3': 5.12,\n 'LPD': 2.48,\n 'GLU': 4.49,\n '1X6': 6.84,\n 'LPG': 2.39,\n 'GLX': 4.52,\n 'PR4': 4.52,\n 'CML': 6.16,\n 'FME': 4.52,\n 'HTR': 6.48,\n 'PR7': 4.66,\n 'Z3E': 7.2,\n 'GLZ': 2.39,\n 'BMT': 6.37,\n 'WRP': 8.16,\n 'GLY': 2.37,\n 'OMY': 6.11,\n 'MTY': 5.46,\n 'OMX': 6.15,\n 'GLN': 4.46,\n '2XA': 8.25,\n '28X': 7.84,\n '7JA': 9.46,\n 'FLT': 9.65,\n 'GLJ': 3.7,\n 'OMH': 5.26,\n 'TSY': 4.26,\n 'PRV': 4.28,\n 'CS4': 11.21,\n 'DOA': 12.33,\n '23P': 5.42,\n 'CS3': 8.24,\n '6CL': 6.47,\n 'PRR': 5.58,\n 'KST': 11.58,\n 'CS1': 7.23,\n 'PRS': 2.63,\n 'ZYJ': 11.4,\n 'IT1': 9.75,\n 'UU5': 4.98,\n 'ESB': 6.69,\n 'UU4': 2.49,\n 'ESC': 5.65,\n 'LSO': 10.58,\n 'ZYK': 11.45,\n '9NV': 8.99,\n '23F': 5.27,\n 'ORN': 4.25,\n 'HOX': 6.61,\n 'CSD': 3.95,\n 'FP9': 3.03,\n 'DO2': 4.44,\n 'SLL': 11.53,\n 'P3Q': 9.54,\n 'ORQ': 6.04,\n 'MSL': 5.21,\n 'DNP': 2.45,\n 'CSB': 3.51,\n 'WLU': 4.24,\n 'CSA': 5.7,\n 'MT2': 5.51,\n 'CSO': 3.53,\n 'TPO': 4.73,\n 'MSP': 13.11,\n '23S': 6.09,\n 'MSO': 4.96,\n 'PRO': 2.41,\n 'TPL': 5.41,\n 'DNS': 8.79,\n 'CSK': 3.91,\n 'Z70': 7.4,\n 'CSJ': 7.51,\n 'DNW': 7.97,\n 'PRK': 9.15,\n 'GSU': 11.81,\n 'LTA': 6.57,\n 'HPE': 6.63,\n 'TPQ': 6.48,\n 'PRJ': 5.26,\n 'PSW': 4.65,\n 'L3O': 3.89,\n 'CSU': 4.89,\n 'ALY': 7.38,\n 'M3L': 7.12,\n 'CSW': 3.68,\n 'XPR': 7.68,\n 'D4P': 5.66,\n 'FOE': 8.17,\n 'SLZ': 5.69,\n 'CSP': 5.26,\n 'TQI': 7.68,\n 'ALT': 2.72,\n 'CSR': 5.42,\n 'CSS': 3.61,\n 'M3R': 7.18,\n 'ALO': 2.57,\n 'R4K': 4.67,\n 'SMF': 9.0,\n 'MSA': 2.73,\n 'SMC': 3.39,\n 'CSX': 3.47,\n 'SME': 4.8,\n 'ETA': 2.4,\n 'CSZ': 3.6,\n '22G': 8.8,\n 'MSE': 4.62,\n 'ALN': 6.16,\n 'PSH': 7.26,\n 'CTE': 7.27,\n 'DON': 6.72,\n 'CTH': 3.45,\n 'U2X': 11.54,\n '6CW': 7.56,\n 'TQZ': 6.97,\n '3YM': 6.52,\n 'OSE': 4.49,\n '2VA': 9.82,\n 'TQQ': 7.76,\n 'NRG': 8.35,\n 'BPE': 7.24,\n 'F2F': 6.25,\n '1TQ': 8.58,\n 'I2M': 3.13,\n 'NVA': 3.76,\n 'R1A': 8.2,\n 'QPA': 6.95,\n 'C1X': 11.63,\n 'FRD': 5.05,\n 'HR7': 6.98,\n 'SNC': 3.93,\n 'QPH': 5.15,\n '26B': 8.39,\n 'DPQ': 6.54,\n 'DPP': 2.51,\n '2TY': 8.65,\n 'TNR': 6.88,\n 'PTH': 8.35,\n 'DPL': 3.58,\n 'APK': 8.79,\n '1TY': 8.84,\n 'HRG': 7.36,\n 'PTM': 8.74,\n '1U8': 3.62,\n 'PTR': 8.64,\n 'LVG': 3.01,\n '6FL': 4.85,\n 'SOC': 4.05,\n 'KPI': 9.79,\n 'IPG': 2.91,\n 'P2Y': 2.51,\n 'N2C': 3.55,\n 'T0I': 7.34,\n 'MPH': 5.29,\n 'R2T': 4.71,\n 'TOX': 6.78,\n 'P2Q': 9.8,\n 'GPL': 10.77,\n 'MPJ': 5.07,\n 'F2Y': 6.2,\n 'T11': 8.58,\n '9NR': 9.33,\n 'FPR': 8.85,\n '9NF': 8.93,\n 'KPY': 10.17,\n '9NE': 9.77,\n 'TOQ': 7.5,\n 'MPQ': 4.2,\n 'FPK': 3.08,\n 'HQA': 7.25,\n 'SOY': 10.94\n}\nexport const DefaultResidueRadius = 5.0\n\n// http://dx.doi.org/10.1039/b801115j (or 1.6)\nexport const CovalentRadii: { [e: number]: number | undefined } = {\n 1: 0.31, 2: 0.28, 3: 1.28, 4: 0.96, 5: 0.84, 6: 0.76, 7: 0.71, 8: 0.66, 9: 0.57, 10: 0.58, 11: 1.66, 12: 1.41, 13: 1.21, 14: 1.11, 15: 1.07, 16: 1.05, 17: 1.02, 18: 1.06, 19: 2.03, 20: 1.76, 21: 1.7, 22: 1.6, 23: 1.53, 24: 1.39, 25: 1.39, 26: 1.32, 27: 1.26, 28: 1.24, 29: 1.32, 30: 1.22, 31: 1.22, 32: 1.2, 33: 1.19, 34: 1.2, 35: 1.2, 36: 1.16, 37: 2.2, 38: 1.95, 39: 1.9, 40: 1.75, 41: 1.64, 42: 1.54, 43: 1.47, 44: 1.46, 45: 1.42, 46: 1.39, 47: 1.45, 48: 1.44, 49: 1.42, 50: 1.39, 51: 1.39, 52: 1.38, 53: 1.39, 54: 1.4, 55: 2.44, 56: 2.15, 57: 2.07, 58: 2.04, 59: 2.03, 60: 2.01, 61: 1.99, 62: 1.98, 63: 1.98, 64: 1.96, 65: 1.94, 66: 1.92, 67: 1.92, 68: 1.89, 69: 1.9, 70: 1.87, 71: 1.87, 72: 1.75, 73: 1.7, 74: 1.62, 75: 1.51, 76: 1.44, 77: 1.41, 78: 1.36, 79: 1.36, 80: 1.32, 81: 1.45, 82: 1.46, 83: 1.48, 84: 1.4, 85: 1.5, 86: 1.5, 87: 2.6, 88: 2.21, 89: 2.15, 90: 2.06, 91: 2.0, 92: 1.96, 93: 1.9, 94: 1.87, 95: 1.8, 96: 1.69, 97: 1.6, 98: 1.6, 99: 1.6, 100: 1.6, 101: 1.6, 102: 1.6, 103: 1.6, 104: 1.6, 105: 1.6, 106: 1.6, 107: 1.6, 108: 1.6, 109: 1.6, 110: 1.6, 111: 1.6, 112: 1.6, 113: 1.6, 114: 1.6, 115: 1.6, 116: 1.6, 117: 1.6, 118: 1.6\n}\nexport const DefaultCovalentRadius = 1.6\n\nexport const Valences: { [e: number]: number[] | undefined } = {\n 1: [ 1 ],\n 2: [ 0 ],\n 3: [ 1 ],\n 4: [ 2 ],\n 5: [ 3 ],\n 6: [ 4 ],\n 7: [ 3 ],\n 8: [ 2 ],\n 9: [ 1 ],\n 10: [ 0 ],\n 11: [ 1 ],\n 12: [ 2 ],\n 13: [ 6 ],\n 14: [ 6 ],\n 15: [ 3, 5, 7 ],\n 16: [ 2, 4, 6 ],\n 17: [ 1 ],\n 18: [ 0 ],\n 19: [ 1 ],\n 20: [ 2 ],\n\n 31: [ 3 ],\n 32: [ 4 ],\n 33: [ 3, 5 ],\n 34: [ 2, 4, 6 ],\n 35: [ 1 ],\n 36: [ 0 ],\n 37: [ 1 ],\n 38: [ 2 ],\n\n 49: [ 3 ],\n 50: [ 4 ],\n 51: [ 3, 5 ],\n 52: [ 2 ],\n 53: [ 1, 2, 5 ],\n 54: [ 0, 2 ],\n 55: [ 1 ],\n 56: [ 2 ],\n\n 81: [ 3 ],\n 82: [ 4 ],\n 83: [ 3 ],\n 84: [ 2 ],\n 85: [ 1 ],\n 86: [ 0 ],\n 87: [ 1 ],\n 88: [ 2 ]\n}\nexport const DefaultValence = -1\n\nexport const OuterShellElectronCounts: { [e: number]: number | undefined } = {\n1: 1, 2: 2, 3: 1, 4: 2, 5: 3, 6: 4, 7: 5, 8: 6, 9: 7, 10: 8, 11: 1, 12: 2, 13: 3, 14: 4, 15: 5, 16: 6, 17: 7, 18: 8, 19: 1, 20: 2, 21: 3, 22: 4, 23: 5, 24: 6, 25: 7, 26: 8, 27: 9, 28: 10, 29: 11, 30: 2, 31: 3, 32: 4, 33: 5, 34: 6, 35: 7, 36: 8, 37: 1, 38: 2, 39: 3, 40: 4, 41: 5, 42: 6, 43: 7, 44: 8, 45: 9, 46: 10, 47: 11, 48: 2, 49: 3, 50: 4, 51: 5, 52: 6, 53: 7, 54: 8, 55: 1, 56: 2, 57: 3, 58: 4, 59: 3, 60: 4, 61: 5, 62: 6, 63: 7, 64: 8, 65: 9, 66: 10, 67: 11, 68: 12, 69: 13, 70: 14, 71: 15, 72: 4, 73: 5, 74: 6, 75: 7, 76: 8, 77: 9, 78: 10, 79: 11, 80: 2, 81: 3, 82: 4, 83: 5, 84: 6, 85: 7, 86: 8, 87: 1, 88: 2, 89: 3, 90: 4, 91: 3, 92: 4, 93: 5, 94: 6, 95: 7, 96: 8, 97: 9, 98: 10, 99: 11, 100: 12, 101: 13, 102: 14, 103: 15, 104: 2, 105: 2, 106: 2, 107: 2, 108: 2, 109: 2, 110: 2, 111: 2, 112: 2, 113: 3, 114: 4, 115: 5, 116: 6, 117: 7, 118: 8\n}\nexport const DefaultOuterShellElectronCount = 2\n\n// http://blanco.biomol.uci.edu/Whole_residue_HFscales.txt\n// https://www.nature.com/articles/nsb1096-842\nexport const ResidueHydrophobicity: { [k: string]: [number, number, number] } = {\n // AA DGwif DGwoct Oct-IF\n 'ALA': [ 0.17, 0.50, 0.33 ],\n 'ARG': [ 0.81, 1.81, 1.00 ],\n 'ASN': [ 0.42, 0.85, 0.43 ],\n 'ASP': [ 1.23, 3.64, 2.41 ],\n 'ASH': [ -0.07, 0.43, 0.50 ],\n 'CYS': [ -0.24, -0.02, 0.22 ],\n 'GLN': [ 0.58, 0.77, 0.19 ],\n 'GLU': [ 2.02, 3.63, 1.61 ],\n 'GLH': [ -0.01, 0.11, 0.12 ],\n 'GLY': [ 0.01, 1.15, 1.14 ],\n // \"His+\": [ 0.96, 2.33, 1.37 ],\n 'HIS': [ 0.17, 0.11, -0.06 ],\n 'ILE': [ -0.31, -1.12, -0.81 ],\n 'LEU': [ -0.56, -1.25, -0.69 ],\n 'LYS': [ 0.99, 2.80, 1.81 ],\n 'MET': [ -0.23, -0.67, -0.44 ],\n 'PHE': [ -1.13, -1.71, -0.58 ],\n 'PRO': [ 0.45, 0.14, -0.31 ],\n 'SER': [ 0.13, 0.46, 0.33 ],\n 'THR': [ 0.14, 0.25, 0.11 ],\n 'TRP': [ -1.85, -2.09, -0.24 ],\n 'TYR': [ -0.94, -0.71, 0.23 ],\n 'VAL': [ 0.07, -0.46, -0.53 ]\n}\nexport const DefaultResidueHydrophobicity = [ 0.00, 0.00, 0.00 ]\n\nexport const AA1: { [k: string]: string } = {\n 'HIS': 'H',\n 'ARG': 'R',\n 'LYS': 'K',\n 'ILE': 'I',\n 'PHE': 'F',\n 'LEU': 'L',\n 'TRP': 'W',\n 'ALA': 'A',\n 'MET': 'M',\n 'PRO': 'P',\n 'CYS': 'C',\n 'ASN': 'N',\n 'VAL': 'V',\n 'GLY': 'G',\n 'SER': 'S',\n 'GLN': 'Q',\n 'TYR': 'Y',\n 'ASP': 'D',\n 'GLU': 'E',\n 'THR': 'T',\n\n 'SEC': 'U', // as per IUPAC definition\n 'PYL': 'O', // as per IUPAC definition\n}\n\nexport const AA3 = Object.keys(AA1)\n\nexport const RnaBases = [ 'A', 'C', 'T', 'G', 'U', 'I' ]\n\nexport const DnaBases = [ 'DA', 'DC', 'DT', 'DG', 'DU', 'DI' ]\n\nexport const PurinBases = [ 'A', 'G', 'I', 'DA', 'DG', 'DI' ]\n\nexport const Bases = RnaBases.concat(DnaBases)\n\nexport const WaterNames = [\n 'SOL', 'WAT', 'HOH', 'H2O', 'W', 'DOD', 'D3O', 'TIP3', 'TIP4', 'SPC'\n]\n\n// all chemical components with the word \"ion\" in their name, Sep 2016\n//\n// SET SESSION group_concat_max_len = 1000000;\n// SELECT GROUP_CONCAT(id_ ORDER BY id_ ASC SEPARATOR '\", \"') from\n// (\n// SELECT count(obj_id) as c, id_\n// FROM pdb.chem_comp WHERE name LIKE \"% ION%\"\n// GROUP BY id_\n// ) AS t1;\nexport const IonNames = [\n '118', '119', '1AL', '1CU', '2FK', '2HP', '2OF', '3CO',\n '3MT', '3NI', '3OF', '3P8', '4MO', '4PU', '543', '6MO', 'ACT', 'AG', 'AL',\n 'ALF', 'AM', 'ATH', 'AU', 'AU3', 'AUC', 'AZI', 'BA', 'BCT', 'BEF', 'BF4', 'BO4',\n 'BR', 'BS3', 'BSY', 'CA', 'CAC', 'CD', 'CD1', 'CD3', 'CD5', 'CE', 'CHT', 'CL',\n 'CO', 'CO3', 'CO5', 'CON', 'CR', 'CS', 'CSB', 'CU', 'CU1', 'CU3', 'CUA', 'CUZ',\n 'CYN', 'DME', 'DMI', 'DSC', 'DTI', 'DY', 'E4N', 'EDR', 'EMC', 'ER3', 'EU',\n 'EU3', 'F', 'FE', 'FE2', 'FPO', 'GA', 'GD3', 'GEP', 'HAI', 'HG', 'HGC', 'IN',\n 'IOD', 'IR', 'IR3', 'IRI', 'IUM', 'K', 'KO4', 'LA', 'LCO', 'LCP', 'LI', 'LU',\n 'MAC', 'MG', 'MH2', 'MH3', 'MLI', 'MLT', 'MMC', 'MN', 'MN3', 'MN5', 'MN6',\n 'MO1', 'MO2', 'MO3', 'MO4', 'MO5', 'MO6', 'MOO', 'MOS', 'MOW', 'MW1', 'MW2',\n 'MW3', 'NA', 'NA2', 'NA5', 'NA6', 'NAO', 'NAW', 'NCO', 'NET', 'NH4', 'NI',\n 'NI1', 'NI2', 'NI3', 'NO2', 'NO3', 'NRU', 'O4M', 'OAA', 'OC1', 'OC2', 'OC3',\n 'OC4', 'OC5', 'OC6', 'OC7', 'OC8', 'OCL', 'OCM', 'OCN', 'OCO', 'OF1', 'OF2',\n 'OF3', 'OH', 'OS', 'OS4', 'OXL', 'PB', 'PBM', 'PD', 'PDV', 'PER', 'PI', 'PO3',\n 'PO4', 'PR', 'PT', 'PT4', 'PTN', 'RB', 'RH3', 'RHD', 'RU', 'SB', 'SCN', 'SE4',\n 'SEK', 'SM', 'SMO', 'SO3', 'SO4', 'SR', 'T1A', 'TB', 'TBA', 'TCN', 'TEA', 'TH',\n 'THE', 'TL', 'TMA', 'TRA', 'UNX', 'V', 'VN3', 'VO4', 'W', 'WO5', 'Y1', 'YB',\n 'YB2', 'YH', 'YT3', 'ZCM', 'ZN', 'ZN2', 'ZN3', 'ZNO', 'ZO3',\n // additional ion names\n 'OHX'\n]\n\n// all chemical components with the word \"%saccharide%\" in their type, Sep 2016\n//\n// SET SESSION group_concat_max_len = 1000000;\n// select GROUP_CONCAT(id_ ORDER BY id_ ASC SEPARATOR '\", \"') from\n// (\n// SELECT count(obj_id), id_\n// FROM pdb.chem_comp WHERE type like \"%SACCHARIDE%\"\n// GROUP BY id_\n// ) AS t1;\nexport const SaccharideNames = [\n '045', '0AT', '0BD', '0MK', '0NZ', '0TS', '0V4', '0XY', '0YT', '10M',\n '147', '149', '14T', '15L', '16G', '18T', '18Y', '1AR', '1BW', '1GL', '1GN',\n '1JB', '1LL', '1NA', '1S3', '26M', '26Q', '26R', '26V', '26W', '26Y', '27C',\n '289', '291', '293', '2DG', '2F8', '2FG', '2FL', '2FP', '2GL', '2M4', '2M5',\n '32O', '34V', '3CM', '3DO', '3DY', '3FM', '3LR', '3MF', '3MG', '3SA', '3ZW',\n '46D', '46M', '46Z', '48Z', '4CQ', '4GC', '4NN', '50A', '5DI', '5GF', '5MM',\n '5RP', '5SA', '5SP', '64K', '6PG', '6SA', '7JZ', '7SA', 'A1Q', 'A2G', 'AAB',\n 'AAL', 'AAO', 'ABC', 'ABD', 'ABE', 'ABF', 'ABL', 'ACG', 'ACI', 'ACR', 'ACX',\n 'ADA', 'ADG', 'ADR', 'AF1', 'AFD', 'AFL', 'AFO', 'AFP', 'AFR', 'AGC', 'AGH',\n 'AGL', 'AHR', 'AIG', 'ALL', 'ALX', 'AMU', 'AOG', 'AOS', 'ARA', 'ARB', 'ARE',\n 'ARI', 'ASG', 'ASO', 'AXP', 'AXR', 'B0D', 'B16', 'B2G', 'B4G', 'B6D', 'B8D',\n 'B9D', 'BBK', 'BCD', 'BDG', 'BDP', 'BDR', 'BEM', 'BFP', 'BGC', 'BGL', 'BGP',\n 'BGS', 'BHG', 'BMA', 'BMX', 'BNG', 'BNX', 'BOG', 'BRI', 'BXF', 'BXP', 'BXX',\n 'BXY', 'C3X', 'C4X', 'C5X', 'CAP', 'CBI', 'CBK', 'CBS', 'CDR', 'CEG', 'CGF',\n 'CHO', 'CR1', 'CR6', 'CRA', 'CT3', 'CTO', 'CTR', 'CTT', 'D6G', 'DAF', 'DAG',\n 'DDA', 'DDB', 'DDL', 'DEL', 'DFR', 'DFX', 'DG0', 'DGC', 'DGD', 'DGM', 'DGS',\n 'DIG', 'DLF', 'DLG', 'DMU', 'DNO', 'DOM', 'DP5', 'DQQ', 'DQR', 'DR2', 'DR3',\n 'DR4', 'DRI', 'DSR', 'DT6', 'DVC', 'E4P', 'E5G', 'EAG', 'EBG', 'EBQ', 'EGA',\n 'EJT', 'EPG', 'ERE', 'ERI', 'F1P', 'F1X', 'F6P', 'FBP', 'FCA', 'FCB', 'FCT',\n 'FDP', 'FDQ', 'FFC', 'FIX', 'FMO', 'FRU', 'FSI', 'FU4', 'FUB', 'FUC', 'FUD',\n 'FUL', 'FXP', 'G16', 'G1P', 'G2F', 'G3I', 'G4D', 'G4S', 'G6D', 'G6P', 'G6S',\n 'GAC', 'GAD', 'GAL', 'GC1', 'GC4', 'GCD', 'GCN', 'GCO', 'GCS', 'GCT', 'GCU',\n 'GCV', 'GCW', 'GCX', 'GE1', 'GFG', 'GFP', 'GIV', 'GL0', 'GL2', 'GL5', 'GL6',\n 'GL7', 'GL9', 'GLA', 'GLB', 'GLC', 'GLD', 'GLF', 'GLG', 'GLO', 'GLP', 'GLS',\n 'GLT', 'GLW', 'GMH', 'GN1', 'GNX', 'GP1', 'GP4', 'GPH', 'GPM', 'GQ1', 'GQ2',\n 'GQ4', 'GS1', 'GS4', 'GSA', 'GSD', 'GTE', 'GTH', 'GTK', 'GTR', 'GTZ', 'GU0',\n 'GU1', 'GU2', 'GU3', 'GU4', 'GU5', 'GU6', 'GU8', 'GU9', 'GUF', 'GUP', 'GUZ',\n 'GYP', 'GYV', 'H2P', 'HDL', 'HMS', 'HS2', 'HSD', 'HSG', 'HSH', 'HSJ', 'HSQ',\n 'HSR', 'HSU', 'HSX', 'HSY', 'HSZ', 'IAB', 'IDG', 'IDR', 'IDS', 'IDT', 'IDU',\n 'IDX', 'IDY', 'IMK', 'IN1', 'IPT', 'ISL', 'KBG', 'KD2', 'KDA', 'KDM', 'KDO',\n 'KFN', 'KO1', 'KO2', 'KTU', 'L6S', 'LAG', 'LAI', 'LAK', 'LAO', 'LAT', 'LB2',\n 'LBT', 'LCN', 'LDY', 'LGC', 'LGU', 'LM2', 'LMT', 'LMU', 'LOG', 'LOX', 'LPK',\n 'LSM', 'LTM', 'LVZ', 'LXB', 'LXZ', 'M1F', 'M3M', 'M6P', 'M8C', 'MA1', 'MA2',\n 'MA3', 'MAB', 'MAG', 'MAL', 'MAN', 'MAT', 'MAV', 'MAW', 'MBG', 'MCU', 'MDA',\n 'MDM', 'MDP', 'MFA', 'MFB', 'MFU', 'MG5', 'MGA', 'MGL', 'MLB', 'MMA', 'MMN',\n 'MN0', 'MRP', 'MTT', 'MUG', 'MVP', 'MXY', 'N1L', 'N9S', 'NAA', 'NAG', 'NBG',\n 'NDG', 'NED', 'NG1', 'NG6', 'NGA', 'NGB', 'NGC', 'NGE', 'NGF', 'NGL', 'NGS',\n 'NGY', 'NHF', 'NM6', 'NM9', 'NTF', 'NTO', 'NTP', 'NXD', 'NYT', 'OPG', 'OPM',\n 'ORP', 'OX2', 'P3M', 'P53', 'P6P', 'PA5', 'PNA', 'PNG', 'PNW', 'PRP', 'PSJ',\n 'PSV', 'PTQ', 'QDK', 'QPS', 'QV4', 'R1P', 'R1X', 'R2B', 'R5P', 'RAA', 'RAE',\n 'RAF', 'RAM', 'RAO', 'RAT', 'RB5', 'RBL', 'RCD', 'RDP', 'REL', 'RER', 'RF5',\n 'RG1', 'RGG', 'RHA', 'RIB', 'RIP', 'RNS', 'RNT', 'ROB', 'ROR', 'RPA', 'RST',\n 'RUB', 'RUU', 'RZM', 'S6P', 'S7P', 'SA0', 'SCR', 'SDD', 'SF6', 'SF9', 'SG4',\n 'SG5', 'SG6', 'SG7', 'SGA', 'SGC', 'SGD', 'SGN', 'SGS', 'SHB', 'SHG', 'SI3',\n 'SIO', 'SOE', 'SOL', 'SSG', 'SUC', 'SUP', 'SUS', 'T6P', 'T6T', 'TAG', 'TCB',\n 'TDG', 'TGK', 'TGY', 'TH1', 'TIA', 'TM5', 'TM6', 'TM9', 'TMR', 'TMX', 'TOA',\n 'TOC', 'TRE', 'TYV', 'UCD', 'UDC', 'VG1', 'X0X', 'X1X', 'X2F', 'X4S', 'X5S',\n 'X6X', 'XBP', 'XDN', 'XDP', 'XIF', 'XIM', 'XLF', 'XLS', 'XMM', 'XUL', 'XXR',\n 'XYP', 'XYS', 'YO5', 'Z3Q', 'Z6J', 'Z9M', 'ZDC', 'ZDM'\n]\n\nexport const ProteinBackboneAtoms = [\n 'CA', 'C', 'N', 'O',\n 'O1', 'O2', 'OC1', 'OC2', 'OX1', 'OXT', 'OT1', 'OT2',\n 'H', 'H1', 'H2', 'H3', 'HA', 'HN',\n 'BB'\n]\n\nexport const NucleicBackboneAtoms = [\n 'P', 'OP1', 'OP2', 'HOP2', 'HOP3',\n \"O2'\", \"O3'\", \"O4'\", \"O5'\", \"C1'\", \"C2'\", \"C3'\", \"C4'\", \"C5'\",\n \"H1'\", \"H2'\", \"H2''\", \"HO2'\", \"H3'\", \"H4'\", \"H5'\", \"H5''\", \"HO3'\", \"HO5'\",\n 'O2*', 'O3*', 'O4*', 'O5*', 'C1*', 'C2*', 'C3*', 'C4*', 'C5*'\n]\n\nexport const ResidueTypeAtoms: { [k: number]: { [k: string]: string|string[] } } = {}\n\nResidueTypeAtoms[ ProteinBackboneType ] = {\n trace: 'CA',\n direction1: 'C',\n direction2: [ 'O', 'OC1', 'O1', 'OX1', 'OXT', 'OT1', 'OT2' ],\n backboneStart: 'N',\n backboneEnd: 'C'\n}\n\nResidueTypeAtoms[ RnaBackboneType ] = {\n trace: [ \"C4'\", 'C4*' ],\n direction1: [ \"C1'\", 'C1*' ],\n direction2: [ \"C3'\", 'C3*' ],\n backboneStart: 'P',\n backboneEnd: [ \"O3'\", 'O3*' ]\n}\n\nResidueTypeAtoms[ DnaBackboneType ] = {\n trace: [ \"C3'\", 'C3*' ],\n direction1: [ \"C2'\", 'C2*' ],\n direction2: [ \"O4'\", 'O4*' ],\n backboneStart: 'P',\n backboneEnd: [ \"O3'\", 'O3*' ]\n}\n\nResidueTypeAtoms[ CgProteinBackboneType ] = {\n trace: [ 'CA', 'BB' ],\n backboneStart: [ 'CA', 'BB' ],\n backboneEnd: [ 'CA', 'BB' ]\n}\n\nResidueTypeAtoms[ CgRnaBackboneType ] = {\n trace: [ \"C4'\", 'C4*', 'P' ],\n backboneStart: [ \"C4'\", 'C4*', 'P' ],\n backboneEnd: [ \"C4'\", 'C4*', 'P' ]\n}\n\nResidueTypeAtoms[ CgDnaBackboneType ] = {\n trace: [ \"C3'\", 'C3*', \"C2'\", 'P' ], // C2' is used in martini ff\n backboneStart: [ \"C3'\", 'C3*', \"C2'\", 'P' ],\n backboneEnd: [ \"C3'\", 'C3*', \"C2'\", 'P' ]\n}\n\nResidueTypeAtoms[ UnknownBackboneType ] = {}\n\n// Mappings taken from Meeko: https://github.com/forlilab/Meeko/blob/develop/meeko/utils/autodock4_atom_types_elements.py\nexport const PDBQTSpecialElements = {\n 'HD': 'H',\n 'HS': 'H',\n 'A': 'C',\n 'NA': 'N',\n 'NS': 'N',\n 'OA': 'O',\n 'OS': 'O',\n 'SA': 'S',\n 'G0': 'C',\n 'G1': 'C',\n 'G2': 'C',\n 'G3': 'C',\n 'CG0': 'C',\n 'CG1': 'C',\n 'CG2': 'C',\n 'CG3': 'C',\n 'W': 'O'\n}","/**\n * @file Geometry\n * @author Fred Ludlow \n * @author Alexander Rose \n */\n\nimport { Vector3 } from 'three'\n\nimport { Elements } from '../structure/structure-constants'\nimport { degToRad } from '../math/math-utils'\nimport AtomProxy from '../proxy/atom-proxy'\n\n// Changed numbering so they're mostly inline with coordination number\n// from VSEPR\nexport const enum AtomGeometry {\n Spherical = 0,\n Terminal = 1,\n Linear = 2,\n Trigonal = 3,\n Tetrahedral = 4,\n TrigonalBiPyramidal = 5,\n Octahedral = 6,\n SquarePlanar = 7, // Okay, it breaks down somewhere!\n Unknown = 8\n}\n\nexport function assignGeometry (totalCoordination: number): AtomGeometry {\n switch(totalCoordination){\n case 0:\n return AtomGeometry.Spherical\n case 1:\n return AtomGeometry.Terminal\n case 2:\n return AtomGeometry.Linear\n case 3:\n return AtomGeometry.Trigonal\n case 4:\n return AtomGeometry.Tetrahedral\n default:\n return AtomGeometry.Unknown\n }\n}\n\nexport const Angles = new Map([\n [ AtomGeometry.Linear, degToRad(180) ],\n [ AtomGeometry.Trigonal, degToRad(120) ],\n [ AtomGeometry.Tetrahedral, degToRad(109.4721) ],\n [ AtomGeometry.Octahedral, degToRad(90) ]\n])\n\n/**\n * Calculate the angles x-1-2 for all x where x is a heavy atom bonded to ap1.\n * @param {AtomProxy} ap1 First atom (angle centre)\n * @param {AtomProxy} ap2 Second atom\n * @return {number[]} Angles in radians\n */\nexport function calcAngles (ap1: AtomProxy, ap2: AtomProxy): number[] {\n let angles: number[] = []\n const d1 = new Vector3()\n const d2 = new Vector3()\n d1.subVectors(ap2 as any, ap1 as any)\n ap1.eachBondedAtom( x => {\n if (x.number !== Elements.H) {\n d2.subVectors(x as any, ap1 as any)\n angles.push(d1.angleTo(d2))\n }\n })\n return angles\n}\n\n/**\n * Find two neighbours of ap1 to define a plane (if possible) and\n * measure angle out of plane to ap2\n * @param {AtomProxy} ap1 First atom (angle centre)\n * @param {AtomProxy} ap2 Second atom (out-of-plane)\n * @return {number} Angle from plane to second atom\n */\nexport function calcPlaneAngle (ap1: AtomProxy, ap2: AtomProxy): number | undefined {\n const x1 = ap1.clone()\n\n const v12 = new Vector3()\n v12.subVectors(ap2 as any, ap1 as any)\n\n const neighbours = [new Vector3(), new Vector3()]\n let ni = 0\n ap1.eachBondedAtom( x => {\n if (ni > 1) { return }\n if (x.number !== Elements.H) {\n x1.index = x.index\n neighbours[ni++].subVectors(x as any, ap1 as any)\n }\n })\n if (ni === 1) {\n x1.eachBondedAtom( x => {\n if (ni > 1) { return }\n if (x.number !== Elements.H && x.index !== ap1.index){\n neighbours[ni++].subVectors(x as any, ap1 as any)\n }\n })\n }\n if (ni !== 2) {\n return\n }\n\n const cp = neighbours[0].cross(neighbours[1])\n return Math.abs((Math.PI / 2) - cp.angleTo(v12))\n}\n","/**\n * @file Valence Model\n * @author Fred Ludlow \n * @author Alexander Rose \n */\n\n/**\n * Reworked ValenceModel\n *\n * TODO:\n * Ensure proper treatment of disorder/models. e.g. V257 N in 5vim\n * Formal charge of 255 for SO4 anion (e.g. 5ghl)\n * Have removed a lot of explicit features (as I think they're more\n * generally captured by better VM).\n * Could we instead have a \"delocalised negative/positive\" charge\n * feature and flag these up?\n *\n */\nimport { Data } from '../structure/data'\nimport AtomProxy from '../proxy/atom-proxy'\nimport { AtomGeometry, assignGeometry } from './geometry'\nimport { Elements } from '../structure/structure-constants'\n\n/**\n * Are we involved in some kind of pi system. Either explicitly forming\n * double bond or N, O next to a double bond, except:\n *\n * N,O with degree 4 cannot be conjugated.\n * N,O adjacent to P=O or S=O do not qualify (keeps sulfonamide N sp3 geom)\n */\nfunction isConjugated (a: AtomProxy) {\n const _bp = a.structure.getBondProxy()\n const atomicNumber = a.number\n const hetero = atomicNumber === Elements.O || atomicNumber === Elements.N\n\n if (hetero && a.bondCount === 4) {\n return false\n }\n\n let flag = false\n\n a.eachBond(b => {\n if (b.bondOrder > 1) {\n flag = true\n return\n }\n if (hetero) {\n const a2 = b.getOtherAtom(a)\n\n a2.eachBond(b2 => {\n if (b2.bondOrder > 1) {\n const atomicNumber2 = a2.number\n if (\n (atomicNumber2 === Elements.P || atomicNumber2 === Elements.S) &&\n b2.getOtherAtom(a2).number === Elements.O\n ) {\n return\n }\n flag = true\n }\n }, _bp) // Avoid reuse of structure._bp\n }\n })\n\n return flag\n}\n\n/* function hasExplicitCharge(r: ResidueProxy) {\n let flag = false\n r.eachAtom(a => {\n if (a.formalCharge != null && a.formalCharge !== 0) flag = true\n })\n return flag\n}\n\nfunction hasExplicitHydrogen(r: ResidueProxy) {\n let flag = false\n r.eachAtom(a => {\n if (a.number === Elements.H) flag = true\n })\n return flag\n} */\n\nexport function explicitValence (a: AtomProxy) {\n let v = 0\n a.eachBond(b => v += b.bondOrder)\n return v\n}\n\n/**\n * Attempts to produce a consistent charge and implicit\n * H-count for an atom.\n *\n * If both params.assignCharge and params.assignH, this\n * approximately followsthe rules described in\n * https://docs.eyesopen.com/toolkits/python/oechemtk/valence.html#openeye-hydrogen-count-model\n *\n * If only charge or hydrogens are to be assigned it takes\n * a much simpler view and deduces one from the other\n *\n * @param {AtomProxy} a Atom to analyze\n * @param {assignChargeHParams} params What to assign\n */\nexport function calculateHydrogensCharge (a: AtomProxy, params: ValenceModelParams) {\n const hydrogenCount = a.bondToElementCount(Elements.H)\n let charge = a.formalCharge || 0\n\n const assignCharge = (params.assignCharge === 'always' ||\n (params.assignCharge === 'auto' && charge === 0))\n const assignH = (params.assignH === 'always' ||\n (params.assignH === 'auto' && hydrogenCount === 0))\n\n const degree = a.bondCount\n const valence = explicitValence(a)\n\n const conjugated = isConjugated(a)\n const multiBond = (valence - degree > 0)\n\n\n let implicitHCount = 0\n let geom = AtomGeometry.Unknown\n\n switch (a.number) {\n case Elements.H:\n if (assignCharge){\n if (degree === 0){\n charge = 1\n geom = AtomGeometry.Spherical\n } else if (degree === 1) {\n charge = 0\n geom = AtomGeometry.Terminal\n }\n }\n break\n\n case Elements.C:\n // TODO: Isocyanide?\n if (assignCharge) {\n charge = 0 // Assume carbon always neutral\n }\n if (assignH) {\n // Carbocation/carbanion are 3-valent\n implicitHCount = Math.max(0, 4 - valence - Math.abs(charge))\n }\n // Carbocation is planar, carbanion is tetrahedral\n geom = assignGeometry(degree + implicitHCount + Math.max(0, -charge))\n break\n\n case Elements.N:\n if (assignCharge) {\n if (!assignH) { // Trust input H explicitly:\n charge = valence - 3\n } else if (conjugated && valence < 4) {\n // Neutral unless amidine/guanidine double-bonded N:\n if (degree - hydrogenCount === 1 && valence - hydrogenCount === 2) {\n charge = 1\n } else {\n charge = 0\n }\n } else {\n // Sulfonamide nitrogen and classed as sp3 in conjugation model but\n // they won't be charged\n // Don't assign charge to nitrogens bound to metals\n let flag = false\n a.eachBondedAtom(ba => {\n if (ba.number === Elements.S || ba.isMetal()) flag = true\n })\n if (flag) charge = 0\n else charge = 1\n // TODO: Planarity sanity check?\n }\n\n }\n\n if (assignH) {\n // NH4+ -> 4, 1' amide -> 2, nitro N/N+ depiction -> 0\n implicitHCount = Math.max(0, 3 - valence + charge)\n }\n\n if (conjugated && !multiBond) {\n // Amide, anilinic N etc. cannot consider lone-pair for geometry purposes\n // Anilinic N geometry is depenent on ring electronics, for our purposes we\n // assume it's trigonal!\n geom = assignGeometry(degree + implicitHCount - charge)\n } else {\n // Everything else, pyridine, amine, nitrile, lp plays normal role:\n geom = assignGeometry(degree + implicitHCount + 1 - charge)\n }\n break\n\n case Elements.O:\n if (assignCharge) {\n if (!assignH) {\n charge = valence - 2 //\n }\n if (valence === 1) {\n a.eachBondedAtom(ba => {\n ba.eachBond(b => {\n const oa = b.getOtherAtom(ba)\n if (oa.index !== a.index && oa.number === Elements.O && b.bondOrder === 2){\n charge = -1\n }\n })\n })\n }\n }\n if (assignH) {\n // ethanol -> 1, carboxylate -> -1\n implicitHCount = Math.max(0, 2 - valence + charge)\n }\n if (conjugated && !multiBond){\n // carboxylate OH, phenol OH, one lone-pair taken up with conjugation\n geom = assignGeometry(degree + implicitHCount - charge + 1)\n } else {\n // Carbonyl (trigonal)\n geom = assignGeometry(degree + implicitHCount - charge + 2)\n }\n break\n\n // Only handles thiols/thiolates/thioether/sulfonium. Sulfoxides and higher\n // oxidiation states are assumed neutral S (charge carried on O if required)\n case Elements.S:\n if (assignCharge) {\n if (!assignH) {\n if (valence <= 3 && !a.bondToElementCount(Elements.O)) {\n charge = valence - 2 // e.g. explicitly deprotonated thiol\n } else {\n charge = 0\n }\n }\n }\n if (assignH){\n if (valence < 2){\n implicitHCount = Math.max(0, 2 - valence + charge)\n }\n }\n if (valence <= 3){\n // Thiol, thiolate, tioether -> tetrahedral\n geom = assignGeometry(degree + implicitHCount - charge + 2)\n }\n\n break\n\n case Elements.F:\n case Elements.CL:\n case Elements.BR:\n case Elements.I:\n case Elements.AT:\n // Never implicitly protonate halides\n if (assignCharge) {\n charge = valence - 1\n }\n break\n\n case Elements.LI:\n case Elements.NA:\n case Elements.K:\n case Elements.RB:\n case Elements.CS:\n case Elements.FR:\n if (assignCharge) {\n charge = 1 - valence\n }\n break\n\n case Elements.BE:\n case Elements.MG:\n case Elements.CA:\n case Elements.SR:\n case Elements.BA:\n case Elements.RA:\n if (assignCharge) {\n charge = 2 - valence\n }\n break\n\n default:\n console.warn('Requested charge, protonation for an unhandled element', a.element)\n }\n\n return [ charge, implicitHCount, implicitHCount + hydrogenCount, geom ]\n}\n\n\nexport interface ValenceModel {\n charge: Int8Array,\n implicitH: Int8Array,\n totalH: Int8Array,\n idealGeometry: Int8Array\n}\n\nexport interface ValenceModelParams {\n assignCharge: string,\n assignH: string\n}\n\nexport function ValenceModel (data: Data, params: ValenceModelParams) {\n const structure = data.structure\n const n = structure.atomCount\n\n const charge = new Int8Array(n)\n const implicitH = new Int8Array(n)\n const totalH = new Int8Array(n)\n const idealGeometry = new Int8Array(n)\n\n structure.eachAtom(a => {\n const i = a.index\n const [ chg, implH, totH, geom ] = calculateHydrogensCharge(a, params)\n charge[ i ] = chg\n implicitH[ i ] = implH\n totalH[ i ] = totH\n idealGeometry[ i ] = geom\n })\n\n return { charge, implicitH, totalH, idealGeometry }\n}","\nimport Structure from './structure'\nimport SpatialHash from '../geometry/spatial-hash'\nimport { ValenceModel } from '../chemistry/valence-model'\n\nexport interface Data {\n structure: Structure\n '@spatialLookup': SpatialHash | undefined\n '@valenceModel': ValenceModel | undefined\n}\n\nexport function createData(structure: Structure): Data {\n return {\n structure,\n '@spatialLookup': undefined,\n '@valenceModel': undefined\n }\n}\n\nexport function spatialLookup(data: Data): SpatialHash {\n if (data['@spatialLookup']) return data['@spatialLookup']!\n const lookup = new SpatialHash(data.structure.atomStore, data.structure.boundingBox)\n data['@spatialLookup'] = lookup\n return lookup\n}\n\nexport function valenceModel(data: Data): ValenceModel {\n if (data['@valenceModel']) return data['@valenceModel']!\n const valenceModel = ValenceModel(data, {assignCharge: 'auto', assignH: 'auto'})\n data['@valenceModel'] = valenceModel\n return valenceModel\n}\n","/**\n * @file Functional Groups\n * @author Alexander Rose \n */\n\nimport AtomProxy from '../proxy/atom-proxy'\nimport { Elements } from '../structure/structure-constants'\n\n/**\n * Nitrogen in a quaternary amine\n */\nexport function isQuaternaryAmine (a: AtomProxy) {\n return (\n a.number === 7 &&\n a.bondCount === 4 &&\n a.bondToElementCount(Elements.H) === 0\n )\n}\n\n/**\n * Nitrogen in a tertiary amine\n */\nexport function isTertiaryAmine (a: AtomProxy, idealValence: number) {\n return (\n a.number === 7 &&\n a.bondCount >= 3 &&\n idealValence === 3\n )\n}\n\n/**\n * Nitrogen in an imide\n */\nexport function isImide (a: AtomProxy) {\n let flag = false\n if (a.number === Elements.N && (a.bondCount - a.bondToElementCount(Elements.H)) === 2) {\n let carbonylCount = 0\n a.eachBondedAtom(ba => {\n if (isCarbonyl(ba)) ++carbonylCount\n })\n flag = carbonylCount === 2\n }\n return flag\n}\n\n/**\n * Nitrogen in an amide\n */\nexport function isAmide (a: AtomProxy) {\n let flag = false\n if (a.number === Elements.N && (a.bondCount - a.bondToElementCount(Elements.H)) === 2) {\n let carbonylCount = 0\n a.eachBondedAtom(ba => {\n if (isCarbonyl(ba)) ++carbonylCount\n })\n flag = carbonylCount === 1\n }\n return flag\n}\n\n/**\n * Sulfur in a sulfonium group\n */\nexport function isSulfonium (a: AtomProxy) {\n return (\n a.number === 16 &&\n a.bondCount === 3 &&\n a.bondToElementCount(Elements.H) === 0\n )\n}\n\n/**\n * Sulfur in a sulfonic acid or sulfonate group\n */\nexport function isSulfonicAcid (a: AtomProxy) {\n return (\n a.number === 16 &&\n a.bondToElementCount(Elements.O) === 3\n )\n}\n\n/**\n * Sulfur in a sulfate group\n */\nexport function isSulfate (a: AtomProxy) {\n return (\n a.number === 16 &&\n a.bondToElementCount(Elements.O) === 4\n )\n}\n\n/**\n * Phosphor in a phosphate group\n */\nexport function isPhosphate (a: AtomProxy) {\n return (\n a.number === 15 &&\n a.bondToElementCount(Elements.O) === a.bondCount\n )\n}\n\n/**\n * Halogen with one bond to a carbon\n */\nexport function isHalocarbon (a: AtomProxy) {\n return (\n a.isHalogen() &&\n a.bondCount === 1 &&\n a.bondToElementCount(Elements.C) === 1\n )\n}\n\n/**\n * Carbon in a carbonyl/acyl group\n */\nexport function isCarbonyl (a: AtomProxy) {\n let flag = false\n if (a.number === Elements.C) {\n a.eachBond(b => {\n if (b.bondOrder === 2 && b.getOtherAtom(a).number === Elements.O) {\n flag = true\n }\n })\n }\n return flag\n}\n\n/**\n * Carbon in a carboxylate group\n */\nexport function isCarboxylate (a: AtomProxy) {\n let terminalOxygenCount = 0\n if (\n a.number === 6 &&\n a.bondToElementCount(Elements.O) === 2 &&\n a.bondToElementCount(Elements.C) === 1\n ) {\n a.eachBondedAtom(ba => {\n if (ba.number === 8 && ba.bondCount - ba.bondToElementCount(Elements.H) === 1) {\n ++terminalOxygenCount\n }\n })\n }\n return terminalOxygenCount === 2\n}\n\n/**\n * Carbon in a guanidine group\n */\nexport function isGuanidine (a: AtomProxy) {\n let terminalNitrogenCount = 0\n if (\n a.number === 6 &&\n a.bondCount === 3 &&\n a.bondToElementCount(Elements.N) === 3\n ) {\n a.eachBondedAtom(ba => {\n if (ba.bondCount - ba.bondToElementCount(Elements.H) === 1) {\n ++terminalNitrogenCount\n }\n })\n }\n return terminalNitrogenCount === 2\n}\n\n/**\n * Carbon in a acetamidine group\n */\nexport function isAcetamidine (a: AtomProxy) {\n let terminalNitrogenCount = 0\n if (\n a.number === 6 &&\n a.bondCount === 3 &&\n a.bondToElementCount(Elements.N) === 2 &&\n a.bondToElementCount(Elements.C) === 1\n ) {\n a.eachBondedAtom(ba => {\n if (ba.bondCount - ba.bondToElementCount(Elements.H) === 1) {\n ++terminalNitrogenCount\n }\n })\n }\n return terminalNitrogenCount === 2\n}\n\nconst PolarElements = [\n Elements.N, Elements.O, Elements.S,\n Elements.F, Elements.CL, Elements.BR, Elements.I\n]\n\nexport function isPolar (a: AtomProxy) {\n return PolarElements.includes(a.number)\n}\n\nexport function hasPolarNeighbour (a: AtomProxy) {\n let flag = false\n a.eachBondedAtom(ba => {\n if (isPolar(ba)) flag = true\n })\n return flag\n}\n\nexport function hasAromaticNeighbour (a: AtomProxy) {\n let flag = false\n a.eachBondedAtom(function (bap) {\n if (bap.aromatic) flag = true\n })\n return flag\n}\n","/**\n * @file Charged\n * @author Alexander Rose \n * @author Fred Ludlow \n */\n\nimport { Vector3 } from 'three'\n\nimport { defaults } from '../../utils'\nimport { radToDeg } from '../../math/math-utils'\nimport Structure from '../../structure/structure'\nimport { AA3, Bases, Elements } from '../../structure/structure-constants'\nimport { valenceModel } from '../../structure/data'\nimport {\n isGuanidine, isAcetamidine, isSulfonicAcid, isPhosphate, isSulfate, isCarboxylate\n} from '../functional-groups'\nimport {\n Features, FeatureType, FeatureGroup,\n addAtom, addFeature, createFeatureState,\n} from './features'\nimport { Contacts, ContactType, ContactDefaultParams, invalidAtomContact } from './contact'\n\nconst PositvelyCharged = [ 'ARG', 'HIS', 'LYS' ]\nconst NegativelyCharged = [ 'GLU', 'ASP' ]\n\nexport function addPositiveCharges (structure: Structure, features: Features) {\n const { charge } = valenceModel(structure.data)\n const atomInGroupDict: { [atomIndex: number]: true } = {}\n\n structure.eachResidue(r => {\n if (PositvelyCharged.includes(r.resname)) {\n const state = createFeatureState(FeatureType.PositiveCharge)\n r.eachAtom(a => {\n if (a.number === Elements.N && a.isSidechain()) {\n addAtom(state, a)\n }\n })\n addFeature(features, state)\n } else if(!AA3.includes(r.resname) && !r.isNucleic()) {\n r.eachAtom(a => {\n let addGroup = false\n const state = createFeatureState(FeatureType.PositiveCharge)\n if (isGuanidine(a)) {\n state.group = FeatureGroup.Guanidine\n addGroup = true\n } else if (isAcetamidine(a)) {\n state.group = FeatureGroup.Acetamidine\n addGroup = true\n }\n if (addGroup) {\n a.eachBondedAtom(a => {\n if (a.number === Elements.N) {\n atomInGroupDict[a.index] = true\n addAtom(state, a)\n }\n })\n addFeature(features, state)\n }\n })\n r.eachAtom(a => {\n const state = createFeatureState(FeatureType.PositiveCharge)\n if (charge[a.index] > 0) {\n if (!atomInGroupDict[a.index]) {\n addAtom(state, a)\n addFeature(features, state)\n }\n }\n })\n }\n })\n}\n\nexport function addNegativeCharges (structure: Structure, features: Features) {\n const { charge } = valenceModel(structure.data)\n const atomInGroupDict: { [atomIndex: number]: true } = {}\n\n structure.eachResidue(r => {\n if (NegativelyCharged.includes(r.resname)) {\n const state = createFeatureState(FeatureType.NegativeCharge)\n r.eachAtom(a => {\n if (a.number === Elements.O && a.isSidechain()) {\n addAtom(state, a)\n }\n })\n addFeature(features, state)\n } else if (Bases.includes(r.resname)) {\n const state = createFeatureState(FeatureType.NegativeCharge)\n r.eachAtom(a => {\n if (isPhosphate(a)) {\n state.group = FeatureGroup.Phosphate\n a.eachBondedAtom(a => {\n if (a.number === Elements.O) addAtom(state, a)\n })\n addFeature(features, state)\n }\n })\n } else if(!AA3.includes(r.resname) && !Bases.includes(r.resname)) {\n r.eachAtom(a => {\n let addGroup = false\n const state = createFeatureState(FeatureType.NegativeCharge)\n if (isSulfonicAcid(a)) {\n state.group = FeatureGroup.SulfonicAcid\n addGroup = true\n } else if (isPhosphate(a)) {\n state.group = FeatureGroup.Phosphate\n addGroup = true\n } else if (isSulfate(a)) {\n state.group = FeatureGroup.Sulfate\n addGroup = true\n } else if (isCarboxylate(a)) {\n state.group = FeatureGroup.Carboxylate\n addGroup = true\n }\n if (addGroup) {\n a.eachBondedAtom(a => {\n if (a.number === Elements.O) {\n atomInGroupDict[a.index] = true\n addAtom(state, a)\n }\n })\n addFeature(features, state)\n }\n })\n r.eachAtom(a => {\n const state = createFeatureState(FeatureType.NegativeCharge)\n if (charge[a.index] < 0) {\n if (!atomInGroupDict[a.index]) {\n addAtom(state, a)\n addFeature(features, state)\n }\n }\n })\n }\n })\n}\n\nexport function addAromaticRings (structure: Structure, features: Features) {\n const a = structure.getAtomProxy()\n structure.eachResidue(r => {\n const rings = r.getAromaticRings()\n if (rings) {\n const offset = r.atomOffset\n rings.forEach(ring => {\n const state = createFeatureState(FeatureType.AromaticRing)\n ring.forEach(i => {\n a.index = i + offset\n addAtom(state, a)\n })\n addFeature(features, state)\n })\n }\n })\n}\n\nfunction isIonicInteraction (ti: FeatureType, tj: FeatureType) {\n return (\n (ti === FeatureType.NegativeCharge && tj === FeatureType.PositiveCharge) ||\n (ti === FeatureType.PositiveCharge && tj === FeatureType.NegativeCharge)\n )\n}\n\nfunction isPiStacking (ti: FeatureType, tj: FeatureType) {\n return ti === FeatureType.AromaticRing && tj === FeatureType.AromaticRing\n}\n\nfunction isCationPi (ti: FeatureType, tj: FeatureType) {\n return (\n (ti === FeatureType.AromaticRing && tj === FeatureType.PositiveCharge) ||\n (ti === FeatureType.PositiveCharge && tj === FeatureType.AromaticRing)\n )\n}\n\nexport interface ChargedContactsParams {\n maxIonicDist?: number\n maxPiStackingDist?: number\n maxPiStackingOffset?: number\n maxPiStackingAngle?: number\n maxCationPiDist?: number\n maxCationPiOffset?: number\n masterModelIndex?: number\n}\n\nexport function addChargedContacts (structure: Structure, contacts: Contacts, params: ChargedContactsParams = {}) {\n const maxIonicDist = defaults(params.maxIonicDist, ContactDefaultParams.maxIonicDist)\n const maxPiStackingDist = defaults(params.maxPiStackingDist, ContactDefaultParams.maxPiStackingDist)\n const maxPiStackingOffset = defaults(params.maxPiStackingOffset, ContactDefaultParams.maxPiStackingOffset)\n const maxPiStackingAngle = defaults(params.maxPiStackingAngle, ContactDefaultParams.maxPiStackingAngle)\n const maxCationPiDist = defaults(params.maxCationPiDist, ContactDefaultParams.maxCationPiDist)\n const maxCationPiOffset = defaults(params.maxCationPiOffset, ContactDefaultParams.maxCationPiOffset)\n const masterIdx = defaults(params.masterModelIndex, ContactDefaultParams.masterModelIndex)\n\n const maxDistance = Math.max(maxIonicDist + 2, maxPiStackingDist, maxCationPiDist)\n // const maxSaltBridgeDistSq = maxSaltBridgeDist * maxSaltBridgeDist\n const maxPiStackingDistSq = maxPiStackingDist * maxPiStackingDist\n const maxCationPiDistSq = maxCationPiDist * maxCationPiDist\n\n const { features, spatialHash, contactStore, featureSet } = contacts\n const { types, centers, atomSets } = features\n const { x, y, z } = centers\n const n = types.length\n\n const ax = structure.atomStore.x\n const ay = structure.atomStore.y\n const az = structure.atomStore.z\n\n const ap1 = structure.getAtomProxy()\n const ap2 = structure.getAtomProxy()\n\n const areAtomSetsWithinDist = function (atomSet1: number[], atomSet2: number[], maxDist: number) {\n const sn = atomSet1.length\n const sm = atomSet2.length\n for (let si = 0; si < sn; ++si) {\n ap1.index = atomSet1[ si ]\n for (let sj = 0; sj < sm; ++sj) {\n ap2.index = atomSet2[ sj ]\n if (ap1.distanceTo(ap2) <= maxDist) {\n return true\n }\n }\n }\n return false\n }\n\n const v1 = new Vector3()\n const v2 = new Vector3()\n const v3 = new Vector3()\n const d1 = new Vector3()\n const d2 = new Vector3()\n const n1 = new Vector3()\n const n2 = new Vector3()\n\n const getNormal = function (atoms: number[], normal: Vector3) {\n v1.set(ax[ atoms[ 0 ] ], ay[ atoms[ 0 ] ], az[ atoms[ 0 ] ])\n v2.set(ax[ atoms[ 1 ] ], ay[ atoms[ 1 ] ], az[ atoms[ 1 ] ])\n v3.set(ax[ atoms[ 2 ] ], ay[ atoms[ 2 ] ], az[ atoms[ 2 ] ])\n d1.subVectors(v1, v2)\n d2.subVectors(v1, v3)\n normal.crossVectors(d1, d2)\n }\n\n const getOffset = function (i: number, j: number, normal: Vector3) {\n v1.set(x[ i ], y[ i ], z[ i ])\n v2.set(x[ j ], y[ j ], z[ j ])\n return v1.sub(v2).projectOnPlane(normal).add(v2).distanceTo(v2)\n }\n\n const add = function (i: number, j: number, ct: ContactType) {\n featureSet.setBits(i, j)\n contactStore.addContact(i, j, ct)\n }\n\n for (let i = 0; i < n; ++i) {\n spatialHash.eachWithin(x[i], y[i], z[i], maxDistance, (j, dSq) => {\n if (j <= i) return\n\n ap1.index = atomSets[ i ][ 0 ]\n ap2.index = atomSets[ j ][ 0 ]\n\n if (invalidAtomContact(ap1, ap2, masterIdx)) return\n\n const ti = types[ i ]\n const tj = types[ j ]\n\n if (isIonicInteraction(ti, tj)) {\n if (areAtomSetsWithinDist(atomSets[ i ], atomSets[ j ], maxIonicDist)) {\n add(i, j, ContactType.IonicInteraction)\n }\n } else if (isPiStacking(ti, tj)) {\n if (dSq <= maxPiStackingDistSq) {\n getNormal(atomSets[ i ], n1)\n getNormal(atomSets[ j ], n2)\n\n const angle = radToDeg(n1.angleTo(n2))\n const offset = Math.min(getOffset(i, j, n2), getOffset(j, i, n1))\n if (offset <= maxPiStackingOffset) {\n if (angle <= maxPiStackingAngle || angle >= 180 - maxPiStackingAngle) {\n add(i, j, ContactType.PiStacking) // parallel\n } else if (angle <= maxPiStackingAngle + 90 && angle >= 90 - maxPiStackingAngle) {\n add(i, j, ContactType.PiStacking) // t-shaped\n }\n }\n }\n } else if (isCationPi(ti, tj)) {\n if (dSq <= maxCationPiDistSq) {\n const [ l, k ] = ti === FeatureType.AromaticRing ? [ i, j ] : [ j, i ]\n\n getNormal(atomSets[ l ], n1)\n const offset = getOffset(k, l, n1)\n if (offset <= maxCationPiOffset) {\n add(l, k, ContactType.CationPi)\n }\n }\n }\n })\n }\n}\n","/**\n * @file Hydrogen Bonds\n * @author Alexander Rose \n * @author Fred Ludlow \n */\nimport { defaults } from '../../utils'\nimport { degToRad } from '../../math/math-utils'\nimport Structure from '../../structure/structure'\nimport AtomProxy from '../../proxy/atom-proxy'\nimport { valenceModel } from '../../structure/data'\nimport { Elements } from '../../structure/structure-constants'\nimport { Angles, AtomGeometry, calcAngles, calcPlaneAngle } from '../geometry'\nimport {\n Features, FeatureType,\n addAtom, addFeature, createFeatureState,\n} from './features'\nimport { Contacts, ContactType, ContactDefaultParams, invalidAtomContact } from './contact'\n\n\n// Geometric characteristics of hydrogen bonds involving sulfur atoms in proteins\n// https://doi.org/10.1002/prot.22327\n\n// Satisfying Hydrogen Bonding Potential in Proteins (HBPLUS)\n// https://doi.org/10.1006/jmbi.1994.1334\n// http://www.csb.yale.edu/userguides/datamanip/hbplus/hbplus_descrip.html\n\n/**\n * Potential hydrogen donor\n */\nexport function addHydrogenDonors (structure: Structure, features: Features) {\n const { totalH } = valenceModel(structure.data)\n\n structure.eachAtom(a => {\n const state = createFeatureState(FeatureType.HydrogenDonor)\n\n const an = a.number\n if (isHistidineNitrogen(a)) {\n // include both nitrogen atoms in histidine due to\n // their often ambiguous protonation assignment\n addAtom(state, a)\n addFeature(features, state)\n } else if (\n totalH[ a.index ] > 0 &&\n (an === Elements.N || an === Elements.O || an === Elements.S)\n ) {\n addAtom(state, a)\n addFeature(features, state)\n }\n })\n}\n\n/**\n * Weak hydrogen donor.\n */\nexport function addWeakHydrogenDonors (structure: Structure, features: Features) {\n const { totalH } = valenceModel(structure.data)\n\n structure.eachAtom(a => {\n if (\n a.number === Elements.C &&\n totalH[ a.index ] > 0 &&\n (\n a.bondToElementCount(Elements.N) > 0 ||\n a.bondToElementCount(Elements.O) > 0 ||\n inAromaticRingWithElectronNegativeElement(a)\n )\n ) {\n const state = createFeatureState(FeatureType.WeakHydrogenDonor)\n addAtom(state, a)\n addFeature(features, state)\n }\n })\n}\n\nfunction inAromaticRingWithElectronNegativeElement (a: AtomProxy) {\n if (!a.isAromatic()) return false\n\n const ringData = a.residueType.getRings()\n if (!ringData) return false\n\n let hasElement = false\n const rings = ringData.rings\n rings.forEach(ring => {\n if (hasElement) return // already found one\n if (ring.some(idx => (a.index - a.residueAtomOffset) === idx)) { // in ring\n hasElement = ring.some(idx => {\n const atomTypeId = a.residueType.atomTypeIdList[ idx ]\n const number = a.atomMap.get(atomTypeId).number\n return number === Elements.N || number === Elements.O\n })\n }\n })\n\n return hasElement\n}\n\n/**\n * Potential hydrogen acceptor\n */\nexport function addHydrogenAcceptors (structure: Structure, features: Features) {\n const { charge, implicitH, idealGeometry } = valenceModel(structure.data)\n\n structure.eachAtom(a => {\n const state = createFeatureState(FeatureType.HydrogenAcceptor)\n\n const an = a.number\n if (an === Elements.O) {\n // Basically assume all oxygen atoms are acceptors!\n addAtom(state, a)\n addFeature(features, state)\n }else if (an === Elements.N) {\n if (isHistidineNitrogen(a)) {\n // include both nitrogen atoms in histidine due to\n // their often ambiguous protonation assignment\n addAtom(state, a)\n addFeature(features, state)\n } else if (charge[ a.index ] < 1){\n // Neutral nitrogen might be an acceptor\n // It must have at least one lone pair not conjugated\n const totalBonds = a.bondCount + implicitH[ a.index ]\n const ig = idealGeometry[ a.index ]\n if (\n (ig === AtomGeometry.Tetrahedral && totalBonds < 4) ||\n (ig === AtomGeometry.Trigonal && totalBonds < 3) ||\n (ig === AtomGeometry.Linear && totalBonds < 2)\n ) {\n addAtom(state, a)\n addFeature(features, state)\n }\n }\n }else if (an === 16) { // S\n if (a.resname === 'CYS' || a.resname === 'MET' || a.formalCharge === -1) {\n addAtom(state, a)\n addFeature(features, state)\n }\n }\n })\n}\n\n/**\n * Atom that is only bound to carbon or hydrogen\n */\n// function isHydrocarbon (atom: AtomProxy) {\n// let flag = true\n// atom.eachBondedAtom(ap => {\n// const e = ap.element\n// if (e !== 'C' && e !== 'H') flag = false\n// })\n// return flag\n// }\n\nfunction isHistidineNitrogen (ap: AtomProxy) {\n return ap.resname === 'HIS' && ap.number == Elements.N && ap.isRing()\n}\n\nfunction isBackboneHydrogenBond (ap1: AtomProxy, ap2: AtomProxy) {\n return ap1.isBackbone() && ap2.isBackbone()\n}\n\nfunction isWaterHydrogenBond (ap1: AtomProxy, ap2: AtomProxy) {\n return ap1.isWater() && ap2.isWater()\n}\n\nfunction isHydrogenBond (ti: FeatureType, tj: FeatureType) {\n return (\n (ti === FeatureType.HydrogenAcceptor && tj === FeatureType.HydrogenDonor) ||\n (ti === FeatureType.HydrogenDonor && tj === FeatureType.HydrogenAcceptor)\n )\n}\n\nfunction isWeakHydrogenBond (ti: FeatureType, tj: FeatureType){\n return (\n (ti === FeatureType.WeakHydrogenDonor && tj === FeatureType.HydrogenAcceptor) ||\n (ti === FeatureType.HydrogenAcceptor && tj === FeatureType.WeakHydrogenDonor)\n )\n}\n\nfunction getHydrogenBondType (ap1: AtomProxy, ap2: AtomProxy) {\n if (isWaterHydrogenBond(ap1, ap2)) {\n return ContactType.WaterHydrogenBond\n } else if (isBackboneHydrogenBond(ap1, ap2)) {\n return ContactType.BackboneHydrogenBond\n } else {\n return ContactType.HydrogenBond\n }\n}\n\nexport interface HydrogenBondParams {\n maxHbondDist?: number\n maxHbondSulfurDist?: number\n maxHbondAccAngle?: number\n maxHbondDonAngle?: number\n maxHbondAccPlaneAngle?: number\n maxHbondDonPlaneAngle?: number\n backboneHbond?: boolean\n waterHbond?: boolean\n masterModelIndex?: number\n}\n\n/**\n * All pairs of hydrogen donor and acceptor atoms\n */\nexport function addHydrogenBonds (structure: Structure, contacts: Contacts, params: HydrogenBondParams = {}) {\n const maxHbondDist = defaults(params.maxHbondDist, ContactDefaultParams.maxHbondDist)\n const maxHbondSulfurDist = defaults(params.maxHbondSulfurDist, ContactDefaultParams.maxHbondSulfurDist)\n const maxHbondAccAngle = degToRad(defaults(params.maxHbondAccAngle, ContactDefaultParams.maxHbondAccAngle))\n const maxHbondDonAngle = degToRad(defaults(params.maxHbondDonAngle, ContactDefaultParams.maxHbondDonAngle))\n const maxHbondAccPlaneAngle = degToRad(defaults(params.maxHbondAccPlaneAngle, ContactDefaultParams.maxHbondAccPlaneAngle))\n const maxHbondDonPlaneAngle = degToRad(defaults(params.maxHbondDonPlaneAngle, ContactDefaultParams.maxHbondDonPlaneAngle))\n const masterIdx = defaults(params.masterModelIndex, ContactDefaultParams.masterModelIndex)\n\n const maxDist = Math.max(maxHbondDist, maxHbondSulfurDist)\n const maxHbondDistSq = maxHbondDist * maxHbondDist\n\n const { features, spatialHash, contactStore, featureSet } = contacts\n const { types, centers, atomSets } = features\n const { x, y, z } = centers\n const n = types.length\n\n const { idealGeometry } = valenceModel(structure.data)\n\n const donor = structure.getAtomProxy()\n const acceptor = structure.getAtomProxy()\n\n for (let i = 0; i < n; ++i) {\n spatialHash.eachWithin(x[i], y[i], z[i], maxDist, (j, dSq) => {\n if (j <= i) return\n\n const ti = types[ i ]\n const tj = types[ j ]\n\n const isWeak = isWeakHydrogenBond(ti, tj)\n if (!isWeak && !isHydrogenBond(ti, tj)) return\n\n const [ l, k ] = tj === FeatureType.HydrogenAcceptor ? [ i, j ] : [ j, i ]\n\n donor.index = atomSets[ l ][ 0 ]\n acceptor.index = atomSets[ k ][ 0 ]\n\n if (acceptor.index === donor.index) return // DA to self\n\n if (invalidAtomContact(donor, acceptor, masterIdx)) return\n if (donor.number !== Elements.S && acceptor.number !== Elements.S && dSq > maxHbondDistSq) return\n if (donor.connectedTo(acceptor)) return\n\n const donorAngles = calcAngles(donor, acceptor)\n const idealDonorAngle = Angles.get(idealGeometry[donor.index]) || degToRad(120)\n if (donorAngles.some(donorAngle => {\n return Math.abs(idealDonorAngle - donorAngle) > maxHbondDonAngle\n })) return\n\n if (idealGeometry[donor.index] === AtomGeometry.Trigonal){\n const outOfPlane = calcPlaneAngle(donor, acceptor)\n if (outOfPlane !== undefined && outOfPlane > maxHbondDonPlaneAngle) return\n }\n\n const acceptorAngles = calcAngles(acceptor, donor)\n const idealAcceptorAngle = Angles.get(idealGeometry[acceptor.index]) || degToRad(120)\n if (acceptorAngles.some(acceptorAngle => {\n // Do not limit large acceptor angles\n return idealAcceptorAngle - acceptorAngle > maxHbondAccAngle\n })) return\n\n if (idealGeometry[acceptor.index] === AtomGeometry.Trigonal){\n const outOfPlane = calcPlaneAngle(acceptor, donor)\n if (outOfPlane !== undefined && outOfPlane > maxHbondAccPlaneAngle) return\n }\n\n featureSet.setBits(l, k)\n const bondType = isWeak ? ContactType.WeakHydrogenBond : getHydrogenBondType(donor, acceptor)\n contactStore.addContact(l, k, bondType)\n })\n }\n}\n","/**\n * @file Metal Binding\n * @author Alexander Rose \n */\n\nimport { defaults } from '../../utils'\nimport Structure from '../../structure/structure'\n// import { valenceModel } from '../../structure/data'\nimport { Elements, AA3, Bases } from '../../structure/structure-constants'\n// import { hasAromaticNeighbour } from '../functional-groups'\nimport {\n Features, FeatureType,\n addAtom, addFeature, createFeatureState,\n} from './features'\nimport { Contacts, ContactType, ContactDefaultParams, invalidAtomContact } from './contact'\n\nconst IonicTypeMetals = [\n Elements.LI, Elements.NA, Elements.K, Elements.RB, Elements.CS,\n Elements.MG, Elements.CA, Elements.SR, Elements.BA, Elements.AL,\n Elements.GA, Elements.IN, Elements.TL, Elements.SC, Elements.SN,\n Elements.PB, Elements.BI, Elements.SB, Elements.HG\n]\n\n/**\n * Metal binding partners (dative bond or ionic-type interaction)\n */\nexport function addMetalBinding (structure: Structure, features: Features) {\n structure.eachAtom(a => {\n let dative = false\n let ionic = false\n\n const isStandardAminoacid = AA3.includes(a.resname)\n const isStandardBase = Bases.includes(a.resname)\n\n if (!isStandardAminoacid && !isStandardBase) {\n if (a.isHalogen() || a.number === Elements.O || a.number === Elements.S) {\n dative = true\n ionic = true\n } else if (a.number === Elements.N) {\n dative = true\n }\n } else if (isStandardAminoacid){\n // main chain oxygen atom or oxygen, nitrogen and sulfur from specific amino acids\n if (a.number === Elements.O) {\n if(['ASP', 'GLU', 'SER', 'THR', 'TYR', 'ASN', 'GLN'].includes(a.resname) && a.isSidechain()) {\n dative = true\n ionic = true\n } else if (a.isBackbone()) {\n dative = true\n ionic = true\n }\n } else if (a.number === Elements.S && 'CYS' === a.resname) {\n dative = true\n ionic = true\n } else if (a.number === Elements.N) {\n if(a.resname === 'HIS' && a.isSidechain()) {\n dative = true\n }\n }\n } else if (isStandardBase){\n // http://pubs.acs.org/doi/pdf/10.1021/acs.accounts.6b00253\n // http://onlinelibrary.wiley.com/doi/10.1002/anie.200900399/full\n if (a.number === Elements.O && a.isBackbone()) {\n dative = true\n ionic = true\n } else if(['N3', 'N4', 'N7'].includes(a.atomname)) {\n dative = true\n } else if(['O2', 'O4', 'O6'].includes(a.atomname)) {\n dative = true\n ionic = true\n }\n }\n if (dative) {\n const state = createFeatureState(FeatureType.DativeBondPartner)\n addAtom(state, a)\n addFeature(features, state)\n }\n if (ionic) {\n const state = createFeatureState(FeatureType.IonicTypePartner)\n addAtom(state, a)\n addFeature(features, state)\n }\n })\n}\n\n/**\n * Metal Pi complexation partner\n */\n// export function addMetalPiPartners (structure: Structure, features: Features) {\n// const { charge } = valenceModel(structure.data)\n\n// structure.eachAtom(a => {\n// const state = createFeatureState(FeatureType.MetalPiPartner)\n\n// const resname = a.resname\n// const element = a.element\n// const atomname = a.atomname\n// if (!a.isPolymer()) {\n// // water oxygen, as well as oxygen from carboxylate, phosphoryl, phenolate, alcohol;\n// // nitrogen from imidazole; sulfur from thiolate\n// if (element === 'O') {\n// // Water oxygen\n// if (a.bondCount === 0 || a.isWater()) {\n// addAtom(state, a)\n// addFeature(features, state)\n// return\n// }\n// // Oxygen in alcohol (R-[O]-H)\n// if (a.bondCount === 2 && charge[ a.index ] || a.hasBondToElement('H')) {\n// addAtom(state, a)\n// addFeature(features, state)\n// return\n// }\n// // Phenolate oxygen\n// if (hasAromaticNeighbour(a) && !a.aromatic) {\n// addAtom(state, a)\n// addFeature(features, state)\n// return\n// }\n// // Carboxylic acid oxygen\n// if (a.bondToElementCount('C') === 1) {\n// let flag = false\n// a.eachBondedAtom(ba => {\n// if (ba.element === 'C' && ba.bondToElementCount('O') === 2 && ba.bondToElementCount('C') === 1) {\n// flag = true\n// }\n// })\n// if (flag) {\n// addAtom(state, a)\n// addFeature(features, state)\n// return\n// }\n// }\n// // Phosphoryl oxygen\n// if (a.bondToElementCount('P') === 1) {\n// let flag = false\n// a.eachBondedAtom(ba => {\n// if (ba.element === 'P' && ba.bondToElementCount('O') >= 3) {\n// flag = true\n// }\n// })\n// if (flag) {\n// addAtom(state, a)\n// addFeature(features, state)\n// return\n// }\n// }\n// } else if (element === 'N') {\n// // Imidazole/pyrrole or similar\n// if (a.bondToElementCount('C') === 2) {\n// addAtom(state, a)\n// addFeature(features, state)\n// return\n// }\n// } else if (element === 'S') {\n// // Thiolate\n// if (hasAromaticNeighbour(a) && !a.aromatic) {\n// addAtom(state, a)\n// addFeature(features, state)\n// return\n// }\n// // Sulfur in Iron sulfur cluster\n// const ironCount = a.bondToElementCount('FE')\n// if (ironCount > 0 && ironCount === a.bondCount) {\n// addAtom(state, a)\n// addFeature(features, state)\n// return\n// }\n// }\n// }\n// })\n// }\n\nexport function addMetals (structure: Structure, features: Features) {\n structure.eachAtom(a => {\n if (a.isTransitionMetal() || a.number === Elements.ZN || a.number === Elements.CD) {\n const state = createFeatureState(FeatureType.TransitionMetal)\n addAtom(state, a)\n addFeature(features, state)\n } else if (IonicTypeMetals.includes(a.number)) {\n const state = createFeatureState(FeatureType.IonicTypeMetal)\n addAtom(state, a)\n addFeature(features, state)\n }\n })\n}\n\nfunction isMetalComplex (ti: FeatureType, tj: FeatureType) {\n if (ti === FeatureType.TransitionMetal) {\n return (\n tj === FeatureType.DativeBondPartner ||\n tj === FeatureType.TransitionMetal\n )\n } else if (ti === FeatureType.IonicTypeMetal) {\n return (\n tj === FeatureType.IonicTypePartner\n )\n }\n}\n\nexport interface MetalComplexationParams {\n maxMetalDist?: number\n masterModelIndex?: number\n}\n\n/**\n * Metal complexes of metals and appropriate groups in protein and ligand, including water\n */\nexport function addMetalComplexation (structure: Structure, contacts: Contacts, params: MetalComplexationParams = {}) {\n const maxMetalDist = defaults(params.maxMetalDist, ContactDefaultParams.maxMetalDist)\n const masterIdx = defaults(params.masterModelIndex, ContactDefaultParams.masterModelIndex)\n\n const { features, spatialHash, contactStore, featureSet } = contacts\n const { types, centers, atomSets } = features\n const { x, y, z } = centers\n const n = types.length\n\n const ap1 = structure.getAtomProxy()\n const ap2 = structure.getAtomProxy()\n\n for (let i = 0; i < n; ++i) {\n spatialHash.eachWithin(x[i], y[i], z[i], maxMetalDist, (j, dSq) => {\n if (j <= i) return\n\n ap1.index = atomSets[ i ][ 0 ]\n ap2.index = atomSets[ j ][ 0 ]\n\n if (invalidAtomContact(ap1, ap2, masterIdx)) return\n\n const m1 = ap1.isMetal()\n const m2 = ap2.isMetal()\n if (!m1 && !m2) return\n\n const [ ti, tj ] = m1 ? [ types[ i ],types[ j ] ] : [ types[ j ],types[ i ] ]\n\n if (isMetalComplex(ti, tj)) {\n featureSet.setBits(i, j)\n contactStore.addContact(i, j, ContactType.MetalCoordination)\n }\n })\n }\n}\n","/**\n * @file Halogen Bonds\n * @author Alexander Rose \n * @author Fred Ludlow \n */\n\nimport { defaults } from '../../utils'\nimport Structure from '../../structure/structure'\nimport { Elements } from '../../structure/structure-constants'\nimport { degToRad } from '../../math/math-utils'\nimport {\n Features, FeatureType,\n addAtom, addFeature, createFeatureState,\n} from './features'\nimport { Contacts, ContactType, ContactDefaultParams, invalidAtomContact } from './contact'\nimport { calcAngles } from '../geometry'\n\nconst halBondElements = [17, 35, 53, 85]\n\n/**\n * Halogen bond donors (X-C, with X one of Cl, Br, I or At) not F!\n */\nexport function addHalogenDonors (structure: Structure, features: Features) {\n structure.eachAtom(a => {\n if (halBondElements.includes(a.number) && a.bondToElementCount(Elements.C) === 1) {\n const state = createFeatureState(FeatureType.HalogenDonor)\n addAtom(state, a)\n addFeature(features, state)\n }\n })\n}\n\nconst X = [ Elements.N, Elements.O, Elements.S ]\nconst Y = [ Elements.C, Elements.N, Elements.P, Elements.S ]\n\n/**\n * Halogen bond acceptors (Y-{O|N|S}, with Y=C,P,N,S)\n */\nexport function addHalogenAcceptors (structure: Structure, features: Features) {\n structure.eachAtom(a => {\n if (X.includes(a.number)) {\n let flag = false\n a.eachBondedAtom(ba => {\n if (Y.includes(ba.number)) {\n flag = true\n }\n })\n if (flag) {\n const state = createFeatureState(FeatureType.HalogenAcceptor)\n addAtom(state, a)\n addFeature(features, state)\n }\n }\n })\n}\n\nfunction isHalogenBond (ti: FeatureType, tj: FeatureType) {\n return (\n (ti === FeatureType.HalogenAcceptor && tj === FeatureType.HalogenDonor) ||\n (ti === FeatureType.HalogenDonor && tj === FeatureType.HalogenAcceptor)\n )\n}\n\nexport interface HalogenBondsParams {\n maxHalogenBondDist?: number\n maxHalogenBondAngle?: number\n masterModelIndex?: number\n}\n\n// http://www.pnas.org/content/101/48/16789.full\nconst OptimalHalogenAngle = degToRad(180) // adjusted from 165 to account for spherical statistics\nconst OptimalAcceptorAngle = degToRad(120)\n\n/**\n * All pairs of halogen donor and acceptor atoms\n */\nexport function addHalogenBonds (structure: Structure, contacts: Contacts, params: HalogenBondsParams = {}) {\n const maxHalogenBondDist = defaults(params.maxHalogenBondDist, ContactDefaultParams.maxHalogenBondDist)\n const maxHalogenBondAngle = degToRad(defaults(params.maxHalogenBondAngle, ContactDefaultParams.maxHalogenBondAngle))\n const masterIdx = defaults(params.masterModelIndex, ContactDefaultParams.masterModelIndex)\n\n const { features, spatialHash, contactStore, featureSet } = contacts\n const { types, centers, atomSets } = features\n const { x, y, z } = centers\n const n = types.length\n\n const ap1 = structure.getAtomProxy()\n const ap2 = structure.getAtomProxy()\n\n for (let i = 0; i < n; ++i) {\n spatialHash.eachWithin(x[i], y[i], z[i], maxHalogenBondDist, (j, dSq) => {\n if (j <= i) return\n\n ap1.index = atomSets[ i ][ 0 ]\n ap2.index = atomSets[ j ][ 0 ]\n\n if (invalidAtomContact(ap1, ap2, masterIdx)) return\n if (!isHalogenBond(types[ i ], types[ j ])) return\n\n const [ halogen, acceptor ] = types[ i ] === FeatureType.HalogenDonor ? [ ap1, ap2 ] : [ ap2, ap1 ]\n\n const halogenAngles = calcAngles(halogen, acceptor)\n // Singly bonded halogen only (not bromide ion for example)\n if (halogenAngles.length !== 1) return\n if (OptimalHalogenAngle - halogenAngles[0] > maxHalogenBondAngle) return\n\n const acceptorAngles = calcAngles(acceptor, halogen)\n // Angle must be defined. Excludes water as acceptor. Debatable\n if (acceptorAngles.length === 0) return\n if (acceptorAngles.some(acceptorAngle => {\n return (OptimalAcceptorAngle - acceptorAngle > maxHalogenBondAngle)\n })) return\n\n\n featureSet.setBits(i, j)\n contactStore.addContact(i, j, ContactType.HalogenBond)\n\n })\n }\n}\n","/**\n * @file Refine Contacts\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3 } from 'three'\n\nimport { Debug, Log } from '../../globals'\nimport { defaults } from '../../utils'\nimport Structure from '../../structure/structure'\nimport AtomProxy from '../../proxy/atom-proxy'\nimport { Elements } from '../../structure/structure-constants'\nimport { FrozenContacts, ContactType, ContactDefaultParams, isMasterContact } from './contact'\nimport { FeatureType } from './features'\n\nexport interface LineOfSightParams {\n lineOfSightDistFactor?: number\n masterModelIndex?: number\n}\n\n// also allows intra-residue contacts\nexport function invalidAtomContact (ap1: AtomProxy, ap2: AtomProxy, masterIdx: number) {\n return !isMasterContact(ap1, ap2, masterIdx) && (\n ap1.modelIndex !== ap2.modelIndex ||\n (ap1.altloc && ap2.altloc && ap1.altloc !== ap2.altloc)\n )\n}\n\nexport function refineLineOfSight (structure: Structure, contacts: FrozenContacts, params: LineOfSightParams = {}) {\n if (Debug) Log.time('refineLineOfSight')\n\n const lineOfSightDistFactor = defaults(params.lineOfSightDistFactor, ContactDefaultParams.lineOfSightDistFactor)\n const masterIdx = defaults(params.masterModelIndex, ContactDefaultParams.masterModelIndex)\n\n const spatialHash = structure.spatialHash!\n const { contactSet, contactStore, features } = contacts\n const { index1, index2 } = contactStore\n const { centers, atomSets } = features\n const { x, y, z } = centers\n\n const ac1 = structure.getAtomProxy()\n const ac2 = structure.getAtomProxy()\n const aw = structure.getAtomProxy()\n\n const c1 = new Vector3()\n const c2 = new Vector3()\n\n const lineOfSightDist = 3 * lineOfSightDistFactor\n const lineOfSightDistFactorSq = lineOfSightDistFactor * lineOfSightDistFactor\n\n contactSet.forEach(i => {\n c1.set(x[index1[i]], y[index1[i]], z[index1[i]])\n c2.set(x[index2[i]], y[index2[i]], z[index2[i]])\n\n const cx = ( c1.x + c2.x ) / 2\n const cy = ( c1.y + c2.y ) / 2\n const cz = ( c1.z + c2.z ) / 2\n\n const as1 = atomSets[ index1[ i ] ]\n const as2 = atomSets[ index2[ i ] ]\n\n ac1.index = as1[ 0 ]\n ac2.index = as2[ 0 ]\n\n spatialHash.eachWithin(cx, cy, cz, lineOfSightDist, (j, dSq) => {\n aw.index = j\n if (\n aw.number !== Elements.H &&\n (aw.vdw * aw.vdw * lineOfSightDistFactorSq) > dSq &&\n !invalidAtomContact(ac1, aw, masterIdx) &&\n !invalidAtomContact(ac2, aw, masterIdx) &&\n !as1.includes(j) &&\n !as2.includes(j) &&\n // to ignore atoms in the center of functional groups\n c1.distanceToSquared(aw as any) > 1 &&\n c2.distanceToSquared(aw as any) > 1\n ) {\n contactSet.clear(i)\n if (Debug) Log.log('removing', ac1.qualifiedName(), ac2.qualifiedName(), 'because', aw.qualifiedName())\n }\n })\n })\n\n if (Debug) Log.timeEnd('refineLineOfSight')\n}\n\n/**\n * For atoms interacting with several atoms in the same residue\n * only the one with the closest distance is kept.\n */\nexport function refineHydrophobicContacts (structure: Structure, contacts: FrozenContacts) {\n const { contactSet, contactStore, features } = contacts\n const { type, index1, index2 } = contactStore\n const { atomSets } = features\n\n const ap1 = structure.getAtomProxy()\n const ap2 = structure.getAtomProxy()\n\n const residueContactDict: { [k: string]: number[] } = {}\n\n /* keep only closest contact between residues */\n const handleResidueContact = function (dist: number, i: number, key: string) {\n const [ minDist, minIndex ] = residueContactDict[ key ] || [ Infinity, -1 ]\n if (dist < minDist) {\n if (minIndex !== -1) contactSet.clear(minIndex)\n residueContactDict[ key ] = [ dist, i ]\n } else {\n contactSet.clear(i)\n }\n }\n\n contactSet.forEach(i => {\n if (type[ i ] !== ContactType.Hydrophobic) return\n\n ap1.index = atomSets[ index1[ i ] ][ 0 ]\n ap2.index = atomSets[ index2[ i ] ][ 0 ]\n\n const dist = ap1.distanceTo(ap2)\n handleResidueContact(dist, i, `${ap1.index}|${ap2.residueIndex}`)\n handleResidueContact(dist, i, `${ap2.index}|${ap1.residueIndex}`)\n })\n}\n\nfunction isHydrogenBondType (type: number) {\n return (\n type === ContactType.HydrogenBond ||\n type === ContactType.WaterHydrogenBond ||\n type === ContactType.BackboneHydrogenBond\n )\n}\n\n/**\n * Remove weak hydrogen bonds when the acceptor is involved in\n * a normal/strong hydrogen bond\n */\nexport function refineWeakHydrogenBonds (structure: Structure, contacts: FrozenContacts) {\n const { contactSet, contactStore, features, adjacencyList } = contacts\n const { type, index1, index2 } = contactStore\n const { types } = features\n\n contactSet.forEach(i => {\n if (type[ i ] !== ContactType.WeakHydrogenBond) return\n\n let accFeat: number\n if (types[ index1[ i ] ] === FeatureType.WeakHydrogenDonor) {\n accFeat = index2[ i ]\n } else {\n accFeat = index1[ i ]\n }\n\n const n = adjacencyList.countArray[ accFeat ]\n const offset = adjacencyList.offsetArray[ accFeat ]\n for (let j = 0; j < n; ++j) {\n const ci = adjacencyList.indexArray[ offset + j ]\n if (isHydrogenBondType(type[ ci ])) {\n contactSet.clear(i)\n return\n }\n }\n })\n}\n\n/**\n * Remove hydrogen bonds between groups that also form\n * a salt bridge between each other\n */\nexport function refineSaltBridges (structure: Structure, contacts: FrozenContacts) {\n const { contactSet, contactStore, features } = contacts\n const { type, index1, index2 } = contactStore\n const { atomSets } = features\n\n const ionicInteractionDict: { [atomIndex: number]: number[] } = {}\n\n const add = function(idx: number, i: number) {\n if (!ionicInteractionDict[ idx ]) ionicInteractionDict[ idx ] = []\n ionicInteractionDict[ idx ].push(i)\n }\n\n contactSet.forEach(i => {\n if (type[ i ] !== ContactType.IonicInteraction) return\n atomSets[ index1[ i ] ].forEach(idx => add(idx, i))\n atomSets[ index2[ i ] ].forEach(idx => add(idx, i))\n })\n\n contactSet.forEach(i => {\n if (!isHydrogenBondType(type[ i ])) return\n\n const iil1 = ionicInteractionDict[ atomSets[ index1[ i ] ][ 0 ] ]\n const iil2 = ionicInteractionDict[ atomSets[ index2[ i ] ][ 0 ] ]\n if (!iil1 || !iil2) return\n\n const n = iil1.length\n for (let j = 0; j < n; ++j) {\n if (iil2.includes(iil1[j])) {\n contactSet.clear(i)\n return\n }\n }\n })\n}\n\n/**\n * Remove hydrophobic and cation-pi interactions between groups that also form\n * a pi-stacking interaction between each other\n */\nexport function refinePiStacking (structure: Structure, contacts: FrozenContacts) {\n const { contactSet, contactStore, features } = contacts\n const { type, index1, index2 } = contactStore\n const { atomSets } = features\n\n const piStackingDict: { [atomIndex: number]: number[] } = {}\n\n const add = function(idx: number, i: number) {\n if (!piStackingDict[ idx ]) piStackingDict[ idx ] = []\n piStackingDict[ idx ].push(i)\n }\n\n contactSet.forEach(i => {\n if (type[ i ] !== ContactType.PiStacking) return\n atomSets[ index1[ i ] ].forEach(idx => add(idx, i))\n atomSets[ index2[ i ] ].forEach(idx => add(idx, i))\n })\n\n contactSet.forEach(i => {\n if (\n type[ i ] !== ContactType.Hydrophobic &&\n type[ i ] !== ContactType.CationPi\n ) return\n\n const pil1 = piStackingDict[ atomSets[ index1[ i ] ][ 0 ] ]\n const pil2 = piStackingDict[ atomSets[ index2[ i ] ][ 0 ] ]\n if (!pil1 || !pil2) return\n\n const n = pil1.length\n for (let j = 0; j < n; ++j) {\n if (pil2.includes(pil1[j])) {\n contactSet.clear(i)\n return\n }\n }\n })\n}\n\n/**\n * Remove ionic interactions between groups that also form\n * a metal coordination between each other\n */\nexport function refineMetalCoordination (structure: Structure, contacts: FrozenContacts) {\n const { contactSet, contactStore, features } = contacts\n const { type, index1, index2 } = contactStore\n const { atomSets } = features\n\n const ionicInteractionDict: { [atomIndex: number]: number[] } = {}\n\n const add = function(idx: number, i: number) {\n if (!ionicInteractionDict[ idx ]) ionicInteractionDict[ idx ] = []\n ionicInteractionDict[ idx ].push(i)\n }\n\n contactSet.forEach(i => {\n if (type[ i ] !== ContactType.IonicInteraction) return\n atomSets[ index1[ i ] ].forEach(idx => add(idx, i))\n atomSets[ index2[ i ] ].forEach(idx => add(idx, i))\n })\n\n contactSet.forEach(i => {\n if (type[ i ] !== ContactType.MetalCoordination) return\n\n const iil1 = ionicInteractionDict[ atomSets[ index1[ i ] ][ 0 ] ]\n const iil2 = ionicInteractionDict[ atomSets[ index2[ i ] ][ 0 ] ]\n if (!iil1 || !iil2) return\n\n const n = iil1.length\n for (let j = 0; j < n; ++j) {\n if (iil2.includes(iil1[j])) {\n contactSet.clear(iil1[j])\n return\n }\n }\n })\n}\n\n// TODO: refactor refineSaltBridges, refinePiStacking and refineMetalCoordination to be DRY\n","/**\n * @file Contact\n * @author Alexander Rose \n */\n\nimport { Color } from 'three'\n\nimport { Debug, Log } from '../../globals'\nimport { createParams } from '../../utils'\nimport { TextBufferData } from '../../buffer/text-buffer'\nimport Structure from '../../structure/structure'\nimport AtomProxy from '../../proxy/atom-proxy'\nimport SpatialHash from '../../geometry/spatial-hash'\nimport { calculateCenterArray, calculateDirectionArray, uniformArray } from '../../math/array-utils'\nimport ContactStore from '../../store/contact-store'\nimport BitArray from '../../utils/bitarray'\nimport Selection from '../../selection/selection'\nimport { ContactPicker } from '../../utils/picker'\nimport { createAdjacencyList, AdjacencyList } from '../../utils/adjacency-list'\nimport { createFeatures, Features } from './features'\nimport { addAromaticRings, addNegativeCharges, addPositiveCharges, addChargedContacts } from './charged'\nimport { addHydrogenAcceptors, addHydrogenDonors, addHydrogenBonds, addWeakHydrogenDonors } from './hydrogen-bonds'\nimport { addMetalBinding, addMetals, addMetalComplexation } from './metal-binding'\nimport { addHydrophobic, addHydrophobicContacts } from './hydrophobic'\nimport { addHalogenAcceptors, addHalogenDonors, addHalogenBonds } from './halogen-bonds'\nimport {\n refineLineOfSight,\n refineHydrophobicContacts, refineSaltBridges, refinePiStacking, refineMetalCoordination\n} from './refine-contacts'\n\nexport interface Contacts {\n features: Features\n spatialHash: SpatialHash\n contactStore: ContactStore\n featureSet: BitArray\n}\n\nexport interface FrozenContacts extends Contacts {\n contactSet: BitArray\n adjacencyList: AdjacencyList\n}\n\nexport const enum ContactType {\n Unknown = 0,\n IonicInteraction = 1,\n CationPi = 2,\n PiStacking = 3,\n HydrogenBond = 4,\n HalogenBond = 5,\n Hydrophobic = 6,\n MetalCoordination = 7,\n WeakHydrogenBond = 8,\n WaterHydrogenBond = 9,\n BackboneHydrogenBond = 10\n}\n\nexport const ContactDefaultParams = {\n maxHydrophobicDist: 4.0,\n maxHbondDist: 3.5,\n maxHbondSulfurDist: 4.1,\n maxHbondAccAngle: 45,\n maxHbondDonAngle: 45,\n maxHbondAccPlaneAngle: 90,\n maxHbondDonPlaneAngle: 30,\n maxPiStackingDist: 5.5,\n maxPiStackingOffset: 2.0,\n maxPiStackingAngle: 30,\n maxCationPiDist: 6.0,\n maxCationPiOffset: 2.0,\n maxIonicDist: 5.0,\n maxHalogenBondDist: 4.0,\n maxHalogenBondAngle: 30,\n maxMetalDist: 3.0,\n refineSaltBridges: true,\n masterModelIndex: -1,\n lineOfSightDistFactor: 1.0\n}\n\nexport function isMasterContact (ap1: AtomProxy, ap2: AtomProxy, masterIdx: number) {\n return (\n (ap1.modelIndex === masterIdx && ap2.modelIndex !== masterIdx) ||\n (ap2.modelIndex === masterIdx && ap1.modelIndex !== masterIdx)\n )\n}\n\nexport function invalidAtomContact (ap1: AtomProxy, ap2: AtomProxy, masterIdx: number) {\n return !isMasterContact(ap1, ap2, masterIdx) && (\n ap1.modelIndex !== ap2.modelIndex ||\n ap1.residueIndex === ap2.residueIndex ||\n (ap1.altloc && ap2.altloc && ap1.altloc !== ap2.altloc)\n )\n}\n\nexport function createContacts (features: Features): Contacts {\n const { types, centers } = features\n\n const spatialHash = new SpatialHash(centers)\n const contactStore = new ContactStore()\n const featureSet = new BitArray(types.length, false)\n\n return { features, spatialHash, contactStore, featureSet }\n}\n\nexport function createFrozenContacts (contacts: Contacts): FrozenContacts {\n const { index1, index2, count } = contacts.contactStore\n\n const adjacencyList = createAdjacencyList({\n nodeArray1: index1,\n nodeArray2: index2,\n edgeCount: count,\n nodeCount: contacts.featureSet.length\n })\n const contactSet = new BitArray(contacts.contactStore.count, true)\n\n return Object.assign({ adjacencyList, contactSet }, contacts)\n}\n\nfunction calculateFeatures (structure: Structure) {\n const features = createFeatures()\n\n if (Debug) Log.time('calculateFeatures')\n\n addPositiveCharges(structure, features)\n addNegativeCharges(structure, features)\n addAromaticRings(structure, features)\n\n addHydrogenAcceptors(structure, features)\n addHydrogenDonors(structure, features)\n addWeakHydrogenDonors(structure, features)\n\n addMetalBinding(structure, features)\n addMetals(structure, features)\n\n addHydrophobic(structure, features)\n\n addHalogenAcceptors(structure, features)\n addHalogenDonors(structure, features)\n\n if (Debug) Log.timeEnd('calculateFeatures')\n\n return features\n}\n\nexport function calculateContacts (structure: Structure, params = ContactDefaultParams) {\n const features = calculateFeatures(structure)\n const contacts = createContacts(features)\n\n if (Debug) Log.time('calculateContacts')\n\n addChargedContacts(structure, contacts, params)\n addHydrogenBonds(structure, contacts, params)\n addMetalComplexation(structure, contacts, params)\n addHydrophobicContacts(structure, contacts, params)\n addHalogenBonds(structure, contacts, params)\n\n const frozenContacts = createFrozenContacts(contacts)\n\n refineLineOfSight(structure, frozenContacts, params)\n refineHydrophobicContacts(structure, frozenContacts)\n if (params.refineSaltBridges) refineSaltBridges(structure, frozenContacts)\n refinePiStacking(structure, frozenContacts)\n refineMetalCoordination(structure, frozenContacts)\n\n if (Debug) Log.timeEnd('calculateContacts')\n\n return frozenContacts\n}\n\nexport function contactTypeName (type: ContactType) {\n switch (type) {\n case ContactType.HydrogenBond:\n case ContactType.WaterHydrogenBond:\n case ContactType.BackboneHydrogenBond:\n return 'hydrogen bond'\n case ContactType.Hydrophobic:\n return 'hydrophobic contact'\n case ContactType.HalogenBond:\n return 'halogen bond'\n case ContactType.IonicInteraction:\n return 'ionic interaction'\n case ContactType.MetalCoordination:\n return 'metal coordination'\n case ContactType.CationPi:\n return 'cation-pi interaction'\n case ContactType.PiStacking:\n return 'pi-pi stacking'\n case ContactType.WeakHydrogenBond:\n return 'weak hydrogen bond'\n default:\n return 'unknown contact'\n }\n}\n\nexport const ContactDataDefaultParams = {\n hydrogenBond: true,\n hydrophobic: true,\n halogenBond: true,\n ionicInteraction: true,\n metalCoordination: true,\n cationPi: true,\n piStacking: true,\n weakHydrogenBond: true,\n waterHydrogenBond: true,\n backboneHydrogenBond: true,\n radius: 1,\n filterSele: ''\n}\nexport type ContactDataParams = typeof ContactDataDefaultParams\n | { filterSele: string|[string, string] }\n\nexport const ContactLabelDefaultParams = {\n unit: '',\n size: 2.0\n}\n\nexport type ContactLabelParams = typeof ContactLabelDefaultParams\n\nconst tmpColor = new Color()\nfunction contactColor (type: ContactType) {\n switch (type) {\n case ContactType.HydrogenBond:\n case ContactType.WaterHydrogenBond:\n case ContactType.BackboneHydrogenBond:\n return tmpColor.setHex(0x2B83BA).toArray()\n case ContactType.Hydrophobic:\n return tmpColor.setHex(0x808080).toArray()\n case ContactType.HalogenBond:\n return tmpColor.setHex(0x40FFBF).toArray()\n case ContactType.IonicInteraction:\n return tmpColor.setHex(0xF0C814).toArray()\n case ContactType.MetalCoordination:\n return tmpColor.setHex(0x8C4099).toArray()\n case ContactType.CationPi:\n return tmpColor.setHex(0xFF8000).toArray()\n case ContactType.PiStacking:\n return tmpColor.setHex(0x8CB366).toArray()\n case ContactType.WeakHydrogenBond:\n return tmpColor.setHex(0xC5DDEC).toArray()\n default:\n return tmpColor.setHex(0xCCCCCC).toArray()\n }\n}\n\nexport interface ContactData {\n position1: Float32Array,\n position2: Float32Array,\n color: Float32Array,\n color2: Float32Array,\n radius: Float32Array,\n picking: ContactPicker\n}\n\nexport function getContactData (contacts: FrozenContacts, structure: Structure, params: ContactDataParams): ContactData {\n const p = createParams(params, ContactDataDefaultParams)\n const types: ContactType[] = []\n if (p.hydrogenBond) types.push(ContactType.HydrogenBond)\n if (p.hydrophobic) types.push(ContactType.Hydrophobic)\n if (p.halogenBond) types.push(ContactType.HalogenBond)\n if (p.ionicInteraction) types.push(ContactType.IonicInteraction)\n if (p.metalCoordination) types.push(ContactType.MetalCoordination)\n if (p.cationPi) types.push(ContactType.CationPi)\n if (p.piStacking) types.push(ContactType.PiStacking)\n if (p.weakHydrogenBond) types.push(ContactType.WeakHydrogenBond)\n if (p.waterHydrogenBond) types.push(ContactType.WaterHydrogenBond)\n if (p.backboneHydrogenBond) types.push(ContactType.BackboneHydrogenBond)\n\n const { features, contactSet, contactStore } = contacts\n const { centers, atomSets } = features\n const { x, y, z } = centers\n const { index1, index2, type } = contactStore\n\n const position1: number[] = []\n const position2: number[] = []\n const color: number[] = []\n const radius: number[] = []\n const picking: number[] = []\n\n let filterSet: BitArray | BitArray[] | undefined\n if (p.filterSele) {\n if (Array.isArray(p.filterSele)) {\n filterSet = p.filterSele.map(sele => {\n return structure.getAtomSet(new Selection(sele))\n })\n } else {\n filterSet = structure.getAtomSet(new Selection(p.filterSele))\n }\n }\n\n contactSet.forEach(i => {\n const ti = type[ i ]\n if (!types.includes(ti)) return\n\n if (filterSet) {\n const idx1 = atomSets[index1[i]][0]\n const idx2 = atomSets[index2[i]][0]\n\n if (Array.isArray(filterSet)) {\n if (!(filterSet[0].isSet(idx1) && filterSet[1].isSet(idx2) || (filterSet[1].isSet(idx1) && filterSet[0].isSet(idx2)))) return\n } else {\n if (!filterSet.isSet(idx1) && !filterSet.isSet(idx2)) return\n }\n }\n\n const k = index1[i]\n const l = index2[i]\n position1.push(x[k], y[k], z[k])\n position2.push(x[l], y[l], z[l])\n color.push(...contactColor(ti))\n radius.push(p.radius)\n picking.push(i)\n })\n\n return {\n position1: new Float32Array(position1),\n position2: new Float32Array(position2),\n color: new Float32Array(color),\n color2: new Float32Array(color),\n radius: new Float32Array(radius),\n picking: new ContactPicker(picking, contacts, structure)\n }\n}\n\nexport function getLabelData (contactData: ContactData, params: ContactLabelParams): TextBufferData {\n\n const position = calculateCenterArray(contactData.position1, contactData.position2)\n const text: string[] = []\n\n const direction = calculateDirectionArray(contactData.position1, contactData.position2)\n\n const n = direction.length / 3\n for (let i=0; i\n */\n\nimport { defaults } from '../../utils'\nimport Structure from '../../structure/structure'\nimport { Elements } from '../../structure/structure-constants'\nimport {\n Features, FeatureType,\n addAtom, addFeature, createFeatureState,\n} from './features'\nimport { Contacts, ContactType, ContactDefaultParams, invalidAtomContact } from './contact'\n\n/**\n * Hydrophobic carbon (only bonded to carbon or hydrogen); fluorine\n */\nexport function addHydrophobic (structure: Structure, features: Features) {\n structure.eachAtom(a => {\n const state = createFeatureState(FeatureType.Hydrophobic)\n let flag = false\n if (a.number === Elements.C) {\n flag = true\n a.eachBondedAtom(ap => {\n const an = ap.number\n if (an !== Elements.C && an !== Elements.H) flag = false\n })\n } else if (a.number === Elements.F) {\n flag = true\n }\n if (flag) {\n addAtom(state, a)\n addFeature(features, state)\n }\n })\n}\n\nfunction isHydrophobicContact (ti: FeatureType, tj: FeatureType) {\n return ti === FeatureType.Hydrophobic && tj === FeatureType.Hydrophobic\n}\n\nexport interface HydrophobicContactsParams {\n maxHydrophobicDist?: number\n masterModelIndex?: number\n}\n\n/**\n * All hydrophobic contacts\n */\nexport function addHydrophobicContacts (structure: Structure, contacts: Contacts, params: HydrophobicContactsParams = {}) {\n const maxHydrophobicDist = defaults(params.maxHydrophobicDist, ContactDefaultParams.maxHydrophobicDist)\n const masterIdx = defaults(params.masterModelIndex, ContactDefaultParams.masterModelIndex)\n\n const { features, spatialHash, contactStore, featureSet } = contacts\n const { types, centers, atomSets } = features\n const { x, y, z } = centers\n const n = types.length\n\n const ap1 = structure.getAtomProxy()\n const ap2 = structure.getAtomProxy()\n\n for (let i = 0; i < n; ++i) {\n spatialHash.eachWithin(x[i], y[i], z[i], maxHydrophobicDist, (j, dSq) => {\n if (j <= i) return\n\n ap1.index = atomSets[ i ][ 0 ]\n ap2.index = atomSets[ j ][ 0 ]\n\n if (invalidAtomContact(ap1, ap2, masterIdx)) return\n if (ap1.number === Elements.F && ap2.number === Elements.F) return\n if (ap1.connectedTo(ap2)) return\n\n if (isHydrophobicContact(types[ i ], types[ j ])) {\n featureSet.setBits(i, j)\n contactStore.addContact(i, j, ContactType.Hydrophobic)\n }\n })\n }\n}\n","/**\n * @file Picker\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3 } from 'three'\n\nimport { PickerRegistry } from '../globals'\nimport { calculateMeanVector3 } from '../math/vector-utils'\nimport Selection from '../selection/selection'\nimport {\n ArrowPrimitive, BoxPrimitive, ConePrimitive, CylinderPrimitive,\n EllipsoidPrimitive, OctahedronPrimitive, SpherePrimitive,\n TetrahedronPrimitive, TorusPrimitive, PointPrimitive, WidelinePrimitive\n} from '../geometry/primitive'\nimport { contactTypeName, Contacts } from '../chemistry/interactions/contact'\nimport { TypedArray } from '../types';\nimport Component from '../component/component';\nimport { Shape, Structure, Volume } from '../ngl';\nimport BondStore from '../store/bond-store';\nimport Validation from '../structure/validation';\nimport PrincipalAxes from '../math/principal-axes';\nimport Surface from '../surface/surface';\nimport Unitcell from '../symmetry/unitcell';\nimport BondProxy from '../proxy/bond-proxy';\nimport AtomProxy from '../proxy/atom-proxy';\n\n/**\n * Picker class\n * @interface\n */\nclass Picker {\n array: number[]|TypedArray|undefined\n /**\n * @param {Array|TypedArray} [array] - mapping\n */\n constructor (array?: number[]|TypedArray) {\n this.array = array\n }\n\n get type () { return '' }\n get data () { return {} }\n\n /**\n * Get the index for the given picking id\n * @param {Integer} pid - the picking id\n * @return {Integer} the index\n */\n getIndex (pid: number) {\n return this.array ? this.array[ pid ] : pid\n }\n\n /**\n * Get object data\n * @abstract\n * @param {Integer} pid - the picking id\n * @return {Object} the object data\n */\n getObject (pid: number) {\n return {}\n }\n\n _applyTransformations (vector: Vector3, instance: any, component: Component) {\n if (instance) {\n vector.applyMatrix4(instance.matrix)\n }\n if (component) {\n vector.applyMatrix4(component.matrix)\n }\n return vector\n }\n\n /**\n * Get object position\n * @abstract\n * @param {Integer} pid - the picking id\n * @return {Vector3} the object position\n */\n _getPosition (pid: number) {\n return new Vector3()\n }\n\n /**\n * Get position for the given picking id\n * @param {Integer} pid - the picking id\n * @param {Object} instance - the instance that should be applied\n * @param {Component} component - the component of the picked object\n * @return {Vector3} the position\n */\n getPosition (pid: number, instance: any, component: Component) {\n return this._applyTransformations(\n this._getPosition(pid), instance, component\n )\n }\n}\n\n/**\n * Shape picker class\n * @interface\n */\nclass ShapePicker extends Picker {\n shape: Shape\n /**\n * @param {Shape} shape - shape object\n */\n constructor (shape: Shape) {\n super()\n this.shape = shape\n }\n\n get primitive (): any { return }\n\n get data () { return this.shape }\n get type () { return this.primitive.type }\n\n getObject (pid: number) {\n return this.primitive.objectFromShape(this.shape, this.getIndex(pid))\n }\n\n _getPosition (pid: number) {\n return this.primitive.positionFromShape(this.shape, this.getIndex(pid))\n }\n}\n\n//\n\nclass CylinderPicker extends ShapePicker {\n get primitive () { return CylinderPrimitive }\n}\n\nclass ArrowPicker extends ShapePicker {\n get primitive () { return ArrowPrimitive }\n}\n\nclass AtomPicker extends Picker {\n structure: Structure\n constructor (array: Float32Array, structure: Structure) {\n super(array)\n this.structure = structure\n }\n\n get type () { return 'atom' }\n get data () { return this.structure }\n\n getObject (pid: number): AtomProxy {\n return this.structure.getAtomProxy(this.getIndex(pid))\n }\n\n _getPosition (pid: number) {\n return new Vector3().copy(this.getObject(pid) as any)\n }\n}\n\nclass AxesPicker extends Picker {\n axes: PrincipalAxes\n constructor (axes: PrincipalAxes) {\n super()\n this.axes = axes\n }\n\n get type () { return 'axes' }\n get data () { return this.axes }\n\n getObject (/* pid */) {\n return {\n axes: this.axes\n }\n }\n\n _getPosition (/* pid */) {\n return this.axes.center.clone()\n }\n}\n\nclass BondPicker extends Picker {\n structure: Structure\n bondStore: BondStore\n constructor (array: number[]|TypedArray|undefined, structure: Structure, bondStore?: BondStore) {\n super(array)\n this.structure = structure\n this.bondStore = bondStore || structure.bondStore\n }\n\n get type () { return 'bond' }\n get data () { return this.structure }\n\n getObject (pid: number): BondProxy {\n const bp = this.structure.getBondProxy(this.getIndex(pid))\n bp.bondStore = this.bondStore\n return bp\n }\n\n _getPosition (pid: number) {\n const b = this.getObject(pid)\n return new Vector3()\n .copy(b.atom1 as any)\n .add(b.atom2 as any)\n .multiplyScalar(0.5)\n }\n}\n\nclass ContactPicker extends Picker {\n contacts: Contacts\n structure: Structure\n constructor (array: number[]|TypedArray|undefined, contacts: Contacts, structure: Structure) {\n super(array)\n this.contacts = contacts\n this.structure = structure\n }\n\n get type () { return 'contact' }\n get data () { return this.contacts }\n\n getObject (pid: number) {\n const idx = this.getIndex(pid)\n const { features, contactStore } = this.contacts\n const { centers, atomSets } = features\n const { x, y, z } = centers\n const { index1, index2, type } = contactStore\n const k = index1[idx]\n const l = index2[idx]\n return {\n center1: new Vector3(x[k], y[k], z[k]),\n center2: new Vector3(x[l], y[l], z[l]),\n atom1: this.structure.getAtomProxy(atomSets[k][0]),\n atom2: this.structure.getAtomProxy(atomSets[l][0]),\n type: contactTypeName(type[idx])\n }\n }\n\n _getPosition (pid: number) {\n const { center1, center2 } = this.getObject(pid)\n return new Vector3().addVectors(center1, center2).multiplyScalar(0.5)\n }\n}\n\nclass ConePicker extends ShapePicker {\n get primitive () { return ConePrimitive }\n}\n\nclass ClashPicker extends Picker {\n validation: Validation\n structure: Structure\n constructor (array: number[]|TypedArray|undefined, validation: Validation, structure: Structure) {\n super(array)\n this.validation = validation\n this.structure = structure\n }\n\n get type () { return 'clash' }\n get data () { return this.validation }\n\n getObject (pid: number) {\n const val = this.validation\n const idx = this.getIndex(pid)\n return {\n validation: val,\n index: idx,\n clash: val.clashArray[ idx ]\n }\n }\n\n _getAtomProxyFromSele (sele: string) {\n const selection = new Selection(sele)\n const idx = this.structure.getAtomIndices(selection)![ 0 ]\n return this.structure.getAtomProxy(idx)\n }\n\n _getPosition (pid: number) {\n const clash = this.getObject(pid).clash\n const ap1 = this._getAtomProxyFromSele(clash.sele1)\n const ap2 = this._getAtomProxyFromSele(clash.sele2)\n return new Vector3().copy(ap1 as any).add(ap2 as any).multiplyScalar(0.5)\n }\n}\n\nclass DistancePicker extends BondPicker {\n get type () { return 'distance' }\n}\n\nclass EllipsoidPicker extends ShapePicker {\n get primitive () { return EllipsoidPrimitive }\n}\n\nclass OctahedronPicker extends ShapePicker {\n get primitive () { return OctahedronPrimitive }\n}\n\nclass BoxPicker extends ShapePicker {\n get primitive () { return BoxPrimitive }\n}\n\nclass IgnorePicker extends Picker {\n get type () { return 'ignore' }\n}\n\nexport interface MeshData {\n name: string|undefined\n serial: number\n index: Uint32Array|Uint16Array|number[]\n normal?: Float32Array|number[]\n position: Float32Array|number[]\n color: Float32Array|number[]\n}\nclass MeshPicker extends ShapePicker {\n mesh: MeshData\n __position: Vector3\n\n constructor (shape: Shape, mesh: MeshData) {\n super(shape)\n this.mesh = mesh\n }\n\n get type () { return 'mesh' }\n\n getObject (/* pid */) {\n const m = this.mesh\n return {\n shape: this.shape,\n name: m.name,\n serial: m.serial\n }\n }\n\n _getPosition (/* pid */) {\n if (!this.__position) {\n this.__position = calculateMeanVector3(this.mesh.position as any)\n }\n return this.__position\n }\n}\n\nclass SpherePicker extends ShapePicker {\n get primitive () { return SpherePrimitive }\n}\n\nclass SurfacePicker extends Picker {\n surface: Surface\n constructor (array: number[]|TypedArray|undefined, surface: Surface) {\n super(array)\n this.surface = surface\n }\n\n get type () { return 'surface' }\n get data () { return this.surface }\n\n getObject (pid: number) {\n return {\n surface: this.surface,\n index: this.getIndex(pid)\n }\n }\n\n _getPosition (/* pid */) {\n return this.surface.center.clone()\n }\n}\n\nclass TetrahedronPicker extends ShapePicker {\n get primitive () { return TetrahedronPrimitive }\n}\n\nclass TorusPicker extends ShapePicker {\n get primitive () { return TorusPrimitive }\n}\n\nclass UnitcellPicker extends Picker {\n unitcell: Unitcell\n structure: Structure\n\n constructor (unitcell: Unitcell, structure: Structure) {\n super()\n this.unitcell = unitcell\n this.structure = structure\n }\n\n get type () { return 'unitcell' }\n get data () { return this.unitcell }\n\n getObject (/* pid */) {\n return {\n unitcell: this.unitcell,\n structure: this.structure\n }\n }\n\n _getPosition (/* pid */) {\n return this.unitcell.getCenter(this.structure)\n }\n}\n\nclass UnknownPicker extends Picker {\n get type () { return 'unknown' }\n}\n\nclass VolumePicker extends Picker {\n volume: Volume\n constructor (array: TypedArray, volume: Volume) {\n super(array)\n this.volume = volume\n }\n\n get type () { return 'volume' }\n get data () { return this.volume }\n\n getObject (pid: number) {\n const vol = this.volume\n const idx = this.getIndex(pid)\n return {\n volume: vol,\n index: idx,\n value: vol.data[ idx ]\n }\n }\n\n _getPosition (pid: number) {\n const dp = this.volume.position\n const idx = this.getIndex(pid)\n return new Vector3(\n dp[ idx * 3 ],\n dp[ idx * 3 + 1 ],\n dp[ idx * 3 + 2 ]\n )\n }\n}\n\nclass SlicePicker extends VolumePicker {\n get type () { return 'slice' }\n}\n\nclass PointPicker extends ShapePicker {\n get primitive () { return PointPrimitive }\n}\n\nclass WidelinePicker extends ShapePicker {\n get primitive () { return WidelinePrimitive }\n}\n\nPickerRegistry.add('arrow', ArrowPicker)\nPickerRegistry.add('box', BoxPicker)\nPickerRegistry.add('cone', ConePicker)\nPickerRegistry.add('cylinder', CylinderPicker)\nPickerRegistry.add('ellipsoid', EllipsoidPicker)\nPickerRegistry.add('octahedron', OctahedronPicker)\nPickerRegistry.add('sphere', SpherePicker)\nPickerRegistry.add('tetrahedron', TetrahedronPicker)\nPickerRegistry.add('torus', TorusPicker)\nPickerRegistry.add('point', PointPicker)\nPickerRegistry.add('wideline', WidelinePicker)\n\nexport {\n Picker,\n ShapePicker,\n ArrowPicker,\n AtomPicker,\n AxesPicker,\n BondPicker,\n BoxPicker,\n ConePicker,\n ContactPicker,\n CylinderPicker,\n ClashPicker,\n DistancePicker,\n EllipsoidPicker,\n IgnorePicker,\n OctahedronPicker,\n MeshPicker,\n SlicePicker,\n SpherePicker,\n SurfacePicker,\n TetrahedronPicker,\n TorusPicker,\n UnitcellPicker,\n UnknownPicker,\n VolumePicker,\n PointPicker,\n WidelinePicker\n}\n","/**\n * @file Marching Cubes\n * @author Alexander Rose \n * @private\n */\n\nimport { getUintArray } from '../utils'\n\nfunction getEdgeTable () {\n return new Uint32Array([\n 0x0, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c,\n 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,\n 0x190, 0x99, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c,\n 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,\n 0x230, 0x339, 0x33, 0x13a, 0x636, 0x73f, 0x435, 0x53c,\n 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,\n 0x3a0, 0x2a9, 0x1a3, 0xaa, 0x7a6, 0x6af, 0x5a5, 0x4ac,\n 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,\n 0x460, 0x569, 0x663, 0x76a, 0x66, 0x16f, 0x265, 0x36c,\n 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,\n 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff, 0x3f5, 0x2fc,\n 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,\n 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55, 0x15c,\n 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,\n 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc,\n 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,\n 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc,\n 0xcc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,\n 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c,\n 0x15c, 0x55, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,\n 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc,\n 0x2fc, 0x3f5, 0xff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,\n 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c,\n 0x36c, 0x265, 0x16f, 0x66, 0x76a, 0x663, 0x569, 0x460,\n 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac,\n 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa, 0x1a3, 0x2a9, 0x3a0,\n 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c,\n 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33, 0x339, 0x230,\n 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c,\n 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99, 0x190,\n 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c,\n 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0\n ])\n}\n\nfunction getTriTable (): Int32Array {\n return new Int32Array([\n -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1,\n 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1,\n 3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1,\n 3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1,\n 9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1,\n 1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1,\n 9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1,\n 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1,\n 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1,\n 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1,\n 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1,\n 3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1,\n 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1,\n 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1,\n 4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1,\n 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1,\n 1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1,\n 5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1,\n 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1,\n 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1,\n 0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1,\n 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1,\n 10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1,\n 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1,\n 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1,\n 5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1,\n 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1,\n 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1,\n 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1,\n 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1,\n 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1,\n 2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1,\n 7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1,\n 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1,\n 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1,\n 11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1,\n 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1,\n 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1,\n 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1,\n 11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1,\n 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1,\n 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1,\n 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1,\n 2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1,\n 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1,\n 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1,\n 6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1,\n 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1,\n 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1,\n 6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1,\n 5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1,\n 1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1,\n 10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1,\n 6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1,\n 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1,\n 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1,\n 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1,\n 3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1,\n 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1,\n 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1,\n 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1,\n 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1,\n 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1,\n 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1,\n 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1,\n 10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1,\n 10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1,\n 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1,\n 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1,\n 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1,\n 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1,\n 10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1,\n 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1,\n 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1,\n 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1,\n 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1,\n 8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1,\n 3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1,\n 6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1,\n 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1,\n 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1,\n 10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1,\n 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1,\n 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1,\n 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1,\n 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1,\n 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1,\n 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1,\n 11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1,\n 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1,\n 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1,\n 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1,\n 10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1,\n 2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1,\n 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1,\n 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1,\n 2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1,\n 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1,\n 10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1,\n 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1,\n 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1,\n 7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1,\n 6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1,\n 8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1,\n 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1,\n 6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1,\n 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1,\n 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1,\n 10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1,\n 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1,\n 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1,\n 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1,\n 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1,\n 10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1,\n 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1,\n 10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1,\n 5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1,\n 11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1,\n 9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1,\n 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1,\n 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1,\n 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1,\n 7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1,\n 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1,\n 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1,\n 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1,\n 9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1,\n 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1,\n 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1,\n 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1,\n 6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1,\n 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1,\n 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1,\n 6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1,\n 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1,\n 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1,\n 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1,\n 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1,\n 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1,\n 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1,\n 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1,\n 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1,\n 10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1,\n 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1,\n 5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1,\n 10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1,\n 11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1,\n 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1,\n 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1,\n 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1,\n 2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1,\n 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1,\n 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1,\n 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1,\n 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1,\n 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1,\n 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1,\n 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1,\n 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1,\n 10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1,\n 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1,\n 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1,\n 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1,\n 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1,\n 5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1,\n 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1,\n 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1,\n 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1,\n 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1,\n 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1,\n 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1,\n 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1,\n 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1,\n 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1,\n 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1,\n 11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1,\n 11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1,\n 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1,\n 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1,\n 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1,\n 1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1,\n 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1,\n 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1,\n 0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1,\n 3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1,\n 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1,\n 0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1,\n 9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1,\n 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1\n ])\n}\n\ninterface MarchingCubes {\n new (field: number[], nx: number, ny: number, nz: number, atomindex: number[]): void\n triangulate: (_isolevel: number, _noNormals: boolean, _box: number[][]|undefined, _contour: boolean, _wrap: boolean) => {\n position: Float32Array\n normal: undefined|Float32Array\n index: Uint32Array|Uint16Array\n atomindex: Int32Array|undefined\n contour: boolean\n }\n}\nfunction MarchingCubes (this: MarchingCubes, field: number[], nx: number, ny: number, nz: number, atomindex: number[]) {\n // Based on alteredq / http://alteredqualia.com/\n // port of greggman's ThreeD version of marching cubes to Three.js\n // http://webglsamples.googlecode.com/hg/blob/blob.html\n //\n // Adapted for NGL by Alexander Rose\n\n // Triangles are constructed between points on cube edges.\n // allowedContours[edge1][edge1] indicates which lines from a given\n // triangle should be shown in line mode.\n\n // Values are bitmasks:\n // In loop over cubes we keep another bitmask indicating whether our current\n // cell is the first x-value (1),\n // first y-value (2) or first z-value (4) of the current loop.\n // We draw all lines on leading faces but only draw trailing face lines the first\n // time through the loop\n // A value of 8 below means the edge is always drawn (leading face)\n\n // E.g. the first row, lines between edge0 and other edges in the bottom\n // x-y plane are only drawn for the first value of z, edges in the\n // x-z plane are only drawn for the first value of y. No other lines\n // are drawn as they're redundant\n // The line between edge 1 and 5 is always drawn as it's on the leading edge\n var allowedContours = [\n\n [ 0, 4, 4, 4, 2, 0, 0, 0, 2, 2, 0, 0 ], // 1 2 3 4 8 9\n [ 4, 0, 4, 4, 0, 8, 0, 0, 0, 8, 8, 0 ], // 0 2 3 5 9 10\n [ 4, 4, 0, 4, 0, 0, 8, 0, 0, 0, 8, 8 ], // 0 1 3 6 10 11\n [ 4, 4, 4, 0, 0, 0, 0, 1, 1, 0, 0, 1 ], // 0 1 2 7 8 11\n [ 2, 0, 0, 0, 0, 8, 8, 8, 2, 2, 0, 0 ], // 0 5 6 7 8 9\n [ 0, 8, 0, 0, 8, 0, 8, 8, 0, 8, 8, 0 ], // And rotate it\n [ 0, 0, 8, 0, 8, 8, 0, 8, 0, 0, 8, 8 ],\n [ 0, 0, 0, 1, 8, 8, 8, 0, 1, 0, 0, 1 ],\n [ 2, 0, 0, 1, 2, 0, 0, 1, 0, 2, 0, 1 ], // 0 3 4 7 9 11\n [ 2, 8, 0, 0, 2, 8, 0, 0, 2, 0, 8, 0 ], // And rotate some more\n [ 0, 8, 8, 0, 0, 8, 8, 0, 0, 8, 0, 8 ],\n [ 0, 0, 8, 1, 0, 0, 8, 1, 1, 0, 8, 0 ]\n\n ]\n\n var isolevel = 0\n var noNormals = false\n var contour = false\n var wrap = false\n var isNegativeIso = false\n var normalFactor = -1\n\n\n var n = nx * ny * nz\n\n // deltas\n var yd = nx\n var zd = nx * ny\n\n var normalCache: Float32Array, vertexIndex: Int32Array\n var count: number, icount: number\n\n var ilist = new Int32Array(12)\n\n var positionArray: number[] = []\n var normalArray: number[] = []\n var indexArray: number[] = []\n var atomindexArray: number[] = []\n\n var edgeTable = getEdgeTable()\n var triTable = getTriTable()\n\n var mx: number, my: number, mz: number\n\n //\n\n this.triangulate = function (_isolevel: number, _noNormals: boolean, _box: number[][]|undefined, _contour: boolean, _wrap: boolean) {\n isolevel = _isolevel\n isNegativeIso = isolevel < 0.0\n contour = _contour\n wrap = _wrap\n // Normals currently disabled in contour mode for performance (unused)\n noNormals = _noNormals || contour\n\n if (!noNormals) {\n normalFactor = isolevel > 0 ? -1.0 : 1.0\n if (!normalCache) {\n normalCache = new Float32Array(n * 3)\n } \n }\n\n var vIndexLength = n * 3\n\n if (!vertexIndex || vertexIndex.length !== vIndexLength) {\n vertexIndex = new Int32Array(vIndexLength)\n }\n\n count = 0\n icount = 0\n\n if (_box !== undefined) {\n var min = _box[ 0 ].map(Math.round)\n var max = _box[ 1 ].map(Math.round)\n\n mx = nx * Math.ceil(Math.abs(min[ 0 ]) / nx)\n my = ny * Math.ceil(Math.abs(min[ 1 ]) / ny)\n mz = nz * Math.ceil(Math.abs(min[ 2 ]) / nz)\n\n triangulate(\n min[ 0 ], min[ 1 ], min[ 2 ],\n max[ 0 ], max[ 1 ], max[ 2 ]\n )\n } else {\n mx = my = mz = 0\n\n triangulate()\n }\n\n positionArray.length = count * 3\n if (!noNormals) normalArray.length = count * 3\n indexArray.length = icount\n if (atomindex) atomindexArray.length = count\n\n return {\n position: new Float32Array(positionArray),\n normal: noNormals ? undefined : new Float32Array(normalArray),\n index: getUintArray(indexArray, positionArray.length / 3),\n atomindex: atomindex ? new Int32Array(atomindexArray) : undefined,\n contour: contour\n }\n }\n\n // polygonization\n\n function lerp (a: number, b: number, t: number) { return a + (b - a) * t }\n\n function index (x: number, y: number, z: number) {\n x = (x + mx) % nx\n y = (y + my) % ny\n z = (z + mz) % nz\n return ((zd * z) + yd * y) + x\n }\n\n function VIntX (q: number, offset: number, x: number, y: number, z: number, valp1: number, valp2: number) {\n var _q = 3 * q\n\n if (vertexIndex[ _q ] < 0) {\n var mu = (isolevel - valp1) / (valp2 - valp1)\n var nc = normalCache\n\n var c = count * 3\n\n positionArray[ c ] = x + mu\n positionArray[ c + 1 ] = y\n positionArray[ c + 2 ] = z\n\n if (!noNormals) {\n var q3 = q * 3\n\n normalArray[ c ] = normalFactor * lerp(nc[ q3 ], nc[ q3 + 3 ], mu)\n normalArray[ c + 1 ] = normalFactor * lerp(nc[ q3 + 1 ], nc[ q3 + 4 ], mu)\n normalArray[ c + 2 ] = normalFactor * lerp(nc[ q3 + 2 ], nc[ q3 + 5 ], mu)\n }\n\n if (atomindex) atomindexArray[ count ] = atomindex[ q + Math.round(mu) ]\n\n vertexIndex[ _q ] = count\n ilist[ offset ] = count\n\n count += 1\n } else {\n ilist[ offset ] = vertexIndex[ _q ]\n }\n }\n\n function VIntY (q: number, offset: number, x: number, y: number, z: number, valp1: number, valp2: number) {\n var _q = 3 * q + 1\n\n if (vertexIndex[ _q ] < 0) {\n var mu = (isolevel - valp1) / (valp2 - valp1)\n var nc = normalCache\n\n var c = count * 3\n\n positionArray[ c ] = x\n positionArray[ c + 1 ] = y + mu\n positionArray[ c + 2 ] = z\n\n if (!noNormals) {\n var q3 = q * 3\n var q6 = q3 + yd * 3\n\n normalArray[ c ] = normalFactor * lerp(nc[ q3 ], nc[ q6 ], mu)\n normalArray[ c + 1 ] = normalFactor * lerp(nc[ q3 + 1 ], nc[ q6 + 1 ], mu)\n normalArray[ c + 2 ] = normalFactor * lerp(nc[ q3 + 2 ], nc[ q6 + 2 ], mu)\n }\n\n if (atomindex) atomindexArray[ count ] = atomindex[ q + Math.round(mu) * yd ]\n\n vertexIndex[ _q ] = count\n ilist[ offset ] = count\n\n count += 1\n } else {\n ilist[ offset ] = vertexIndex[ _q ]\n }\n }\n\n function VIntZ (q: number, offset: number, x: number, y: number, z: number, valp1: number, valp2: number) {\n var _q = 3 * q + 2\n\n if (vertexIndex[ _q ] < 0) {\n var mu = (isolevel - valp1) / (valp2 - valp1)\n var nc = normalCache\n\n var c = count * 3\n\n positionArray[ c ] = x\n positionArray[ c + 1 ] = y\n positionArray[ c + 2 ] = z + mu\n\n if (!noNormals) {\n var q3 = q * 3\n var q6 = q3 + zd * 3\n\n normalArray[ c ] = normalFactor * lerp(nc[ q3 ], nc[ q6 ], mu)\n normalArray[ c + 1 ] = normalFactor * lerp(nc[ q3 + 1 ], nc[ q6 + 1 ], mu)\n normalArray[ c + 2 ] = normalFactor * lerp(nc[ q3 + 2 ], nc[ q6 + 2 ], mu)\n }\n\n if (atomindex) atomindexArray[ count ] = atomindex[ q + Math.round(mu) * zd ]\n\n vertexIndex[ _q ] = count\n ilist[ offset ] = count\n\n count += 1\n } else {\n ilist[ offset ] = vertexIndex[ _q ]\n }\n }\n\n function compNorm (q: number) {\n var q3 = q * 3\n\n if (normalCache[ q3 ] === 0.0) {\n normalCache[ q3 ] = field[ (q - 1 + n) % n ] - field[ (q + 1) % n ]\n normalCache[ q3 + 1 ] = field[ (q - yd + n) % n ] - field[ (q + yd) % n ]\n normalCache[ q3 + 2 ] = field[ (q - zd + n) % n ] - field[ (q + zd) % n ]\n }\n }\n\n function polygonize (fx: number, fy: number, fz: number, q: number, edgeFilter: number) {\n // cache indices\n var q1\n var qy\n var qz\n var q1y\n var q1z\n var qyz\n var q1yz\n if (wrap) {\n q = index(fx, fy, fz)\n q1 = index(fx + 1, fy, fz)\n qy = index(fx, fy + 1, fz)\n qz = index(fx, fy, fz + 1)\n q1y = index(fx + 1, fy + 1, fz)\n q1z = index(fx + 1, fy, fz + 1)\n qyz = index(fx, fy + 1, fz + 1)\n q1yz = index(fx + 1, fy + 1, fz + 1)\n } else {\n q1 = q + 1\n qy = q + yd\n qz = q + zd\n q1y = qy + 1\n q1z = qz + 1\n qyz = qy + zd\n q1yz = qyz + 1\n }\n\n var cubeindex = 0\n var field0 = field[ q ]\n var field1 = field[ q1 ]\n var field2 = field[ qy ]\n var field3 = field[ q1y ]\n var field4 = field[ qz ]\n var field5 = field[ q1z ]\n var field6 = field[ qyz ]\n var field7 = field[ q1yz ]\n\n if (field0 < isolevel) cubeindex |= 1\n if (field1 < isolevel) cubeindex |= 2\n if (field2 < isolevel) cubeindex |= 8\n if (field3 < isolevel) cubeindex |= 4\n if (field4 < isolevel) cubeindex |= 16\n if (field5 < isolevel) cubeindex |= 32\n if (field6 < isolevel) cubeindex |= 128\n if (field7 < isolevel) cubeindex |= 64\n\n // if cube is entirely in/out of the surface - bail, nothing to draw\n\n var bits = edgeTable[ cubeindex ]\n if (bits === 0) return 0\n\n var fx2 = fx + 1\n var fy2 = fy + 1\n var fz2 = fz + 1\n\n // top of the cube\n\n if (bits & 1) {\n if (!noNormals) {\n compNorm(q)\n compNorm(q1)\n }\n VIntX(q, 0, fx, fy, fz, field0, field1)\n }\n\n if (bits & 2) {\n if (!noNormals) {\n compNorm(q1)\n compNorm(q1y)\n }\n VIntY(q1, 1, fx2, fy, fz, field1, field3)\n }\n\n if (bits & 4) {\n if (!noNormals) {\n compNorm(qy)\n compNorm(q1y)\n }\n VIntX(qy, 2, fx, fy2, fz, field2, field3)\n }\n\n if (bits & 8) {\n if (!noNormals) {\n compNorm(q)\n compNorm(qy)\n }\n VIntY(q, 3, fx, fy, fz, field0, field2)\n }\n\n // bottom of the cube\n\n if (bits & 16) {\n if (!noNormals) {\n compNorm(qz)\n compNorm(q1z)\n }\n VIntX(qz, 4, fx, fy, fz2, field4, field5)\n }\n\n if (bits & 32) {\n if (!noNormals) {\n compNorm(q1z)\n compNorm(q1yz)\n }\n VIntY(q1z, 5, fx2, fy, fz2, field5, field7)\n }\n\n if (bits & 64) {\n if (!noNormals) {\n compNorm(qyz)\n compNorm(q1yz)\n }\n VIntX(qyz, 6, fx, fy2, fz2, field6, field7)\n }\n\n if (bits & 128) {\n if (!noNormals) {\n compNorm(qz)\n compNorm(qyz)\n }\n VIntY(qz, 7, fx, fy, fz2, field4, field6)\n }\n\n // vertical lines of the cube\n\n if (bits & 256) {\n if (!noNormals) {\n compNorm(q)\n compNorm(qz)\n }\n VIntZ(q, 8, fx, fy, fz, field0, field4)\n }\n\n if (bits & 512) {\n if (!noNormals) {\n compNorm(q1)\n compNorm(q1z)\n }\n VIntZ(q1, 9, fx2, fy, fz, field1, field5)\n }\n\n if (bits & 1024) {\n if (!noNormals) {\n compNorm(q1y)\n compNorm(q1yz)\n }\n VIntZ(q1y, 10, fx2, fy2, fz, field3, field7)\n }\n\n if (bits & 2048) {\n if (!noNormals) {\n compNorm(qy)\n compNorm(qyz)\n }\n VIntZ(qy, 11, fx, fy2, fz, field2, field6)\n }\n\n var triIndex = cubeindex << 4 // re-purpose cubeindex into an offset into triTable\n\n var e1\n var e2\n var e3\n var i = 0\n\n // here is where triangles are created\n\n while (triTable[ triIndex + i ] !== -1) {\n e1 = triTable[ triIndex + i ]\n e2 = triTable[ triIndex + i + 1 ]\n e3 = triTable[ triIndex + i + 2 ]\n\n if (contour) {\n if (allowedContours[ e1 ][ e2 ] & edgeFilter) {\n indexArray[ icount++ ] = ilist[ e1 ]\n indexArray[ icount++ ] = ilist[ e2 ]\n }\n if (allowedContours[ e2 ][ e3 ] & edgeFilter) {\n indexArray[ icount++ ] = ilist[ e2 ]\n indexArray[ icount++ ] = ilist[ e3 ]\n }\n if (allowedContours[ e1 ][ e3 ] & edgeFilter) {\n indexArray[ icount++ ] = ilist[ e1 ]\n indexArray[ icount++ ] = ilist[ e3 ]\n }\n } else {\n indexArray[ icount++ ] = ilist[ isNegativeIso ? e1 : e2 ]\n indexArray[ icount++ ] = ilist[ isNegativeIso ? e2 : e1 ]\n indexArray[ icount++ ] = ilist[ e3 ]\n }\n\n i += 3\n }\n }\n\n function triangulate (xBeg?: number, yBeg?: number, zBeg?: number, xEnd?: number, yEnd?: number, zEnd?: number) {\n let q\n let q3\n let x\n let y\n let z\n let yOffset\n let zOffset\n\n xBeg = xBeg !== undefined ? xBeg : 0\n yBeg = yBeg !== undefined ? yBeg : 0\n zBeg = zBeg !== undefined ? zBeg : 0\n\n xEnd = xEnd !== undefined ? xEnd : nx - 1\n yEnd = yEnd !== undefined ? yEnd : ny - 1\n zEnd = zEnd !== undefined ? zEnd : nz - 1\n\n if (!wrap) {\n if (noNormals) {\n xBeg = Math.max(0, xBeg)\n yBeg = Math.max(0, yBeg)\n zBeg = Math.max(0, zBeg)\n\n xEnd = Math.min(nx - 1, xEnd)\n yEnd = Math.min(ny - 1, yEnd)\n zEnd = Math.min(nz - 1, zEnd)\n } else {\n xBeg = Math.max(1, xBeg)\n yBeg = Math.max(1, yBeg)\n zBeg = Math.max(1, zBeg)\n\n xEnd = Math.min(nx - 2, xEnd)\n yEnd = Math.min(ny - 2, yEnd)\n zEnd = Math.min(nz - 2, zEnd)\n }\n }\n\n let xBeg2, yBeg2, zBeg2, xEnd2, yEnd2, zEnd2\n\n if (!wrap) {\n // init part of the vertexIndex\n // (takes a significant amount of time to do for all)\n\n xBeg2 = Math.max(0, xBeg - 2)\n yBeg2 = Math.max(0, yBeg - 2)\n zBeg2 = Math.max(0, zBeg - 2)\n\n xEnd2 = Math.min(nx, xEnd + 2)\n yEnd2 = Math.min(ny, yEnd + 2)\n zEnd2 = Math.min(nz, zEnd + 2)\n\n for (z = zBeg2; z < zEnd2; ++z) {\n zOffset = zd * z\n for (y = yBeg2; y < yEnd2; ++y) {\n yOffset = zOffset + yd * y\n for (x = xBeg2; x < xEnd2; ++x) {\n q = 3 * (yOffset + x)\n vertexIndex[ q ] = -1\n vertexIndex[ q + 1 ] = -1\n vertexIndex[ q + 2 ] = -1\n }\n }\n }\n } else {\n xBeg2 = xBeg - 2\n yBeg2 = yBeg - 2\n zBeg2 = zBeg - 2\n\n xEnd2 = xEnd + 2\n yEnd2 = yEnd + 2\n zEnd2 = zEnd + 2\n\n for (z = zBeg2; z < zEnd2; ++z) {\n for (y = yBeg2; y < yEnd2; ++y) {\n for (x = xBeg2; x < xEnd2; ++x) {\n q3 = index(x, y, z) * 3\n vertexIndex[ q3 ] = -1\n vertexIndex[ q3 + 1 ] = -1\n vertexIndex[ q3 + 2 ] = -1\n }\n }\n }\n }\n\n if (!wrap) {\n // clip space where the isovalue is too low\n\n var __break\n var __xBeg = xBeg; var __yBeg = yBeg; var __zBeg = zBeg\n var __xEnd = xEnd; var __yEnd = yEnd; var __zEnd = zEnd\n\n __break = false\n for (z = zBeg; z < zEnd; ++z) {\n for (y = yBeg; y < yEnd; ++y) {\n for (x = xBeg; x < xEnd; ++x) {\n q = ((nx * ny) * z) + (nx * y) + x\n if (field[ q ] >= isolevel) {\n __zBeg = z\n __break = true\n break\n }\n }\n if (__break) break\n }\n if (__break) break\n }\n\n __break = false\n for (y = yBeg; y < yEnd; ++y) {\n for (z = __zBeg; z < zEnd; ++z) {\n for (x = xBeg; x < xEnd; ++x) {\n q = ((nx * ny) * z) + (nx * y) + x\n if (field[ q ] >= isolevel) {\n __yBeg = y\n __break = true\n break\n }\n }\n if (__break) break\n }\n if (__break) break\n }\n\n __break = false\n for (x = xBeg; x < xEnd; ++x) {\n for (y = __yBeg; y < yEnd; ++y) {\n for (z = __zBeg; z < zEnd; ++z) {\n q = ((nx * ny) * z) + (nx * y) + x\n if (field[ q ] >= isolevel) {\n __xBeg = x\n __break = true\n break\n }\n }\n if (__break) break\n }\n if (__break) break\n }\n\n __break = false\n for (z = zEnd; z >= zBeg; --z) {\n for (y = yEnd; y >= yBeg; --y) {\n for (x = xEnd; x >= xBeg; --x) {\n q = ((nx * ny) * z) + (nx * y) + x\n if (field[ q ] >= isolevel) {\n __zEnd = z\n __break = true\n break\n }\n }\n if (__break) break\n }\n if (__break) break\n }\n\n __break = false\n for (y = yEnd; y >= yBeg; --y) {\n for (z = __zEnd; z >= zBeg; --z) {\n for (x = xEnd; x >= xBeg; --x) {\n q = ((nx * ny) * z) + (nx * y) + x\n if (field[ q ] >= isolevel) {\n __yEnd = y\n __break = true\n break\n }\n }\n if (__break) break\n }\n if (__break) break\n }\n\n __break = false\n for (x = xEnd; x >= xBeg; --x) {\n for (y = __yEnd; y >= yBeg; --y) {\n for (z = __zEnd; z >= zBeg; --z) {\n q = ((nx * ny) * z) + (nx * y) + x\n if (field[ q ] >= isolevel) {\n __xEnd = x\n __break = true\n break\n }\n }\n if (__break) break\n }\n if (__break) break\n }\n\n //\n\n if (noNormals) {\n xBeg = Math.max(0, __xBeg - 1)\n yBeg = Math.max(0, __yBeg - 1)\n zBeg = Math.max(0, __zBeg - 1)\n\n xEnd = Math.min(nx - 1, __xEnd + 1)\n yEnd = Math.min(ny - 1, __yEnd + 1)\n zEnd = Math.min(nz - 1, __zEnd + 1)\n } else {\n xBeg = Math.max(1, __xBeg - 1)\n yBeg = Math.max(1, __yBeg - 1)\n zBeg = Math.max(1, __zBeg - 1)\n\n xEnd = Math.min(nx - 2, __xEnd + 1)\n yEnd = Math.min(ny - 2, __yEnd + 1)\n zEnd = Math.min(nz - 2, __zEnd + 1)\n }\n }\n\n // polygonize part of the grid\n var edgeFilter = 15\n for (z = zBeg; z < zEnd; ++z, edgeFilter &= ~4) {\n zOffset = zd * z\n edgeFilter |= 2\n for (y = yBeg; y < yEnd; ++y, edgeFilter &= ~2) {\n yOffset = zOffset + yd * y\n edgeFilter |= 1\n for (x = xBeg; x < xEnd; ++x, edgeFilter &= ~1) {\n q = yOffset + x\n polygonize(x, y, z, q, edgeFilter)\n }\n }\n }\n }\n}\nObject.assign(MarchingCubes, {__deps: [ getEdgeTable, getTriTable, getUintArray ]})\n\nexport default MarchingCubes\n","/**\n * @file Matrix Utils\n * @private\n * @author Alexander Rose \n *\n * svd methods from Eugene Zatepyakin / http://inspirit.github.io/jsfeat/\n */\n\nimport { NumberArray } from '../types'\nimport { v3new, v3cross } from './vector-utils'\n\nexport class Matrix {\n size: number\n data: Float32Array\n\n constructor (readonly cols: number, readonly rows: number) {\n this.size = this.cols * this.rows\n this.data = new Float32Array(this.size)\n }\n\n copyTo (matrix: Matrix) {\n matrix.data.set(this.data)\n }\n}\n\nexport function transpose (At: Matrix, A: Matrix) {\n let i = 0\n let j = 0\n const nrows = A.rows\n const ncols = A.cols\n let Ai = 0\n let Ati = 0\n let pAt = 0\n const ad = A.data\n const atd = At.data\n\n for (; i < nrows; Ati += 1, Ai += ncols, i++) {\n pAt = Ati\n for (j = 0; j < ncols; pAt += nrows, j++) atd[pAt] = ad[Ai + j]\n }\n}\n\n// C = A * B\nexport function multiply (C: Matrix, A: Matrix, B: Matrix) {\n let i = 0\n let j = 0\n let k = 0\n let Ap = 0\n let pA = 0\n let pB = 0\n let _pB = 0\n let Cp = 0\n const ncols = A.cols\n const nrows = A.rows\n const mcols = B.cols\n const ad = A.data\n const bd = B.data\n const cd = C.data\n let sum = 0.0\n\n for (; i < nrows; Ap += ncols, i++) {\n for (_pB = 0, j = 0; j < mcols; Cp++, _pB++, j++) {\n pB = _pB\n pA = Ap\n sum = 0.0\n for (k = 0; k < ncols; pA++, pB += mcols, k++) {\n sum += ad[pA] * bd[pB]\n }\n cd[Cp] = sum\n }\n }\n}\n\n// C = A * B'\nexport function multiplyABt (C: Matrix, A: Matrix, B: Matrix) {\n let i = 0\n let j = 0\n let k = 0\n let Ap = 0\n let pA = 0\n let pB = 0\n let Cp = 0\n const ncols = A.cols\n const nrows = A.rows\n const mrows = B.rows\n const ad = A.data\n const bd = B.data\n const cd = C.data\n let sum = 0.0\n\n for (; i < nrows; Ap += ncols, i++) {\n for (pB = 0, j = 0; j < mrows; Cp++, j++) {\n pA = Ap\n sum = 0.0\n for (k = 0; k < ncols; pA++, pB++, k++) {\n sum += ad[pA] * bd[pB]\n }\n cd[Cp] = sum\n }\n }\n}\n\n// C = A' * B\nexport function multiplyAtB (C: Matrix, A: Matrix, B: Matrix) {\n let i = 0\n let j = 0\n let k = 0\n let Ap = 0\n let pA = 0\n let pB = 0\n let _pB = 0\n let Cp = 0\n const ncols = A.cols\n const nrows = A.rows\n const mcols = B.cols\n const ad = A.data\n const bd = B.data\n const cd = C.data\n let sum = 0.0\n\n for (; i < ncols; Ap++, i++) {\n for (_pB = 0, j = 0; j < mcols; Cp++, _pB++, j++) {\n pB = _pB\n pA = Ap\n sum = 0.0\n for (k = 0; k < nrows; pA += ncols, pB += mcols, k++) {\n sum += ad[pA] * bd[pB]\n }\n cd[Cp] = sum\n }\n }\n}\n\nexport function invert3x3 (from: Matrix, to: Matrix) {\n const A = from.data\n const invA = to.data\n const t1 = A[4]\n const t2 = A[8]\n const t4 = A[5]\n const t5 = A[7]\n const t8 = A[0]\n\n const t9 = t8 * t1\n const t11 = t8 * t4\n const t13 = A[3]\n const t14 = A[1]\n const t15 = t13 * t14\n const t17 = A[2]\n const t18 = t13 * t17\n const t20 = A[6]\n const t21 = t20 * t14\n const t23 = t20 * t17\n const t26 = 1.0 / (t9 * t2 - t11 * t5 - t15 * t2 + t18 * t5 + t21 * t4 - t23 * t1)\n invA[0] = (t1 * t2 - t4 * t5) * t26\n invA[1] = -(t14 * t2 - t17 * t5) * t26\n invA[2] = -(-t14 * t4 + t17 * t1) * t26\n invA[3] = -(t13 * t2 - t4 * t20) * t26\n invA[4] = (t8 * t2 - t23) * t26\n invA[5] = -(t11 - t18) * t26\n invA[6] = -(-t13 * t5 + t1 * t20) * t26\n invA[7] = -(t8 * t5 - t21) * t26\n invA[8] = (t9 - t15) * t26\n}\n\nexport function mat3x3determinant (M: Matrix) {\n const md = M.data\n return md[0] * md[4] * md[8] -\n md[0] * md[5] * md[7] -\n md[3] * md[1] * md[8] +\n md[3] * md[2] * md[7] +\n md[6] * md[1] * md[5] -\n md[6] * md[2] * md[4]\n}\n\n// C = A * B\nexport function multiply3x3 (C: Matrix, A: Matrix, B: Matrix) {\n const Cd = C.data\n const Ad = A.data\n const Bd = B.data\n const m10 = Ad[0]\n const m11 = Ad[1]\n const m12 = Ad[2]\n const m13 = Ad[3]\n const m14 = Ad[4]\n const m15 = Ad[5]\n const m16 = Ad[6]\n const m17 = Ad[7]\n const m18 = Ad[8]\n\n const m20 = Bd[0]\n const m21 = Bd[1]\n const m22 = Bd[2]\n const m23 = Bd[3]\n const m24 = Bd[4]\n const m25 = Bd[5]\n const m26 = Bd[6]\n const m27 = Bd[7]\n const m28 = Bd[8]\n\n Cd[0] = m10 * m20 + m11 * m23 + m12 * m26\n Cd[1] = m10 * m21 + m11 * m24 + m12 * m27\n Cd[2] = m10 * m22 + m11 * m25 + m12 * m28\n Cd[3] = m13 * m20 + m14 * m23 + m15 * m26\n Cd[4] = m13 * m21 + m14 * m24 + m15 * m27\n Cd[5] = m13 * m22 + m14 * m25 + m15 * m28\n Cd[6] = m16 * m20 + m17 * m23 + m18 * m26\n Cd[7] = m16 * m21 + m17 * m24 + m18 * m27\n Cd[8] = m16 * m22 + m17 * m25 + m18 * m28\n}\n\nexport function meanRows (A: Matrix) {\n const nrows = A.rows\n const ncols = A.cols\n const Ad = A.data\n const mean = new Array(ncols)\n\n for (let j = 0; j < ncols; ++j) {\n mean[ j ] = 0.0\n }\n\n for (let i = 0, p = 0; i < nrows; ++i) {\n for (let j = 0; j < ncols; ++j, ++p) {\n mean[ j ] += Ad[ p ]\n }\n }\n\n for (let j = 0; j < ncols; ++j) {\n mean[ j ] /= nrows\n }\n\n return mean\n}\n\nexport function meanCols (A: Matrix) {\n const nrows = A.rows\n const ncols = A.cols\n const Ad = A.data\n const mean = new Array(nrows)\n\n for (let j = 0; j < nrows; ++j) {\n mean[ j ] = 0.0\n }\n\n for (let i = 0, p = 0; i < ncols; ++i) {\n for (let j = 0; j < nrows; ++j, ++p) {\n mean[ j ] += Ad[ p ]\n }\n }\n\n for (let j = 0; j < nrows; ++j) {\n mean[ j ] /= ncols\n }\n\n return mean\n}\n\nexport function subRows (A: Matrix, row: number[]) {\n const nrows = A.rows\n const ncols = A.cols\n const Ad = A.data\n\n for (let i = 0, p = 0; i < nrows; ++i) {\n for (let j = 0; j < ncols; ++j, ++p) {\n Ad[ p ] -= row[ j ]\n }\n }\n}\n\nexport function subCols (A: Matrix, col: number[]) {\n const nrows = A.rows\n const ncols = A.cols\n const Ad = A.data\n\n for (let i = 0, p = 0; i < ncols; ++i) {\n for (let j = 0; j < nrows; ++j, ++p) {\n Ad[ p ] -= col[ j ]\n }\n }\n}\n\nexport function addRows (A: Matrix, row: number[]) {\n const nrows = A.rows\n const ncols = A.cols\n const Ad = A.data\n\n for (let i = 0, p = 0; i < nrows; ++i) {\n for (let j = 0; j < ncols; ++j, ++p) {\n Ad[ p ] += row[ j ]\n }\n }\n}\n\nexport function addCols (A: Matrix, col: number[]) {\n const nrows = A.rows\n const ncols = A.cols\n const Ad = A.data\n\n for (let i = 0, p = 0; i < ncols; ++i) {\n for (let j = 0; j < nrows; ++j, ++p) {\n Ad[ p ] += col[ j ]\n }\n }\n}\n\nexport function swap (A: NumberArray, i0: number, i1: number, t: number) {\n t = A[i0]\n A[i0] = A[i1]\n A[i1] = t\n}\n\nexport function hypot (a: number, b: number) {\n a = Math.abs(a)\n b = Math.abs(b)\n if (a > b) {\n b /= a\n return a * Math.sqrt(1.0 + b * b)\n }\n if (b > 0) {\n a /= b\n return b * Math.sqrt(1.0 + a * a)\n }\n return 0.0\n}\n\nconst EPSILON = 0.0000001192092896\nconst FLT_MIN = 1E-37\n\nexport function JacobiSVDImpl (At: NumberArray, astep: number, _W: NumberArray, Vt: NumberArray, vstep: number, m: number, n: number, n1: number) {\n const eps = EPSILON * 2.0\n const minval = FLT_MIN\n let i = 0\n let j = 0\n let k = 0\n let iter = 0\n const maxIter = Math.max(m, 30)\n let Ai = 0\n let Aj = 0\n let Vi = 0\n let Vj = 0\n let changed = 0\n let c = 0.0\n let s = 0.0\n let t = 0.0\n let t0 = 0.0\n let t1 = 0.0\n let sd = 0.0\n let beta = 0.0\n let gamma = 0.0\n let delta = 0.0\n let a = 0.0\n let p = 0.0\n let b = 0.0\n let seed = 0x1234\n let val = 0.0\n let val0 = 0.0\n let asum = 0.0\n\n const W = new Float64Array(n << 3)\n\n for (; i < n; i++) {\n for (k = 0, sd = 0; k < m; k++) {\n t = At[i * astep + k]\n sd += t * t\n }\n W[i] = sd\n\n if (Vt) {\n for (k = 0; k < n; k++) {\n Vt[i * vstep + k] = 0\n }\n Vt[i * vstep + i] = 1\n }\n }\n\n for (; iter < maxIter; iter++) {\n changed = 0\n\n for (i = 0; i < n - 1; i++) {\n for (j = i + 1; j < n; j++) {\n Ai = (i * astep) | 0\n Aj = (j * astep) | 0\n a = W[i]\n p = 0\n b = W[j]\n\n k = 2\n p += At[Ai] * At[Aj]\n p += At[Ai + 1] * At[Aj + 1]\n\n for (; k < m; k++) { p += At[Ai + k] * At[Aj + k] }\n\n if (Math.abs(p) <= eps * Math.sqrt(a * b)) continue\n\n p *= 2.0\n beta = a - b\n gamma = hypot(p, beta)\n if (beta < 0) {\n delta = (gamma - beta) * 0.5\n s = Math.sqrt(delta / gamma)\n c = (p / (gamma * s * 2.0))\n } else {\n c = Math.sqrt((gamma + beta) / (gamma * 2.0))\n s = (p / (gamma * c * 2.0))\n }\n\n a = 0.0\n b = 0.0\n\n k = 2 // unroll\n t0 = c * At[Ai] + s * At[Aj]\n t1 = -s * At[Ai] + c * At[Aj]\n At[Ai] = t0; At[Aj] = t1\n a += t0 * t0; b += t1 * t1\n\n t0 = c * At[Ai + 1] + s * At[Aj + 1]\n t1 = -s * At[Ai + 1] + c * At[Aj + 1]\n At[Ai + 1] = t0; At[Aj + 1] = t1\n a += t0 * t0; b += t1 * t1\n\n for (; k < m; k++) {\n t0 = c * At[Ai + k] + s * At[Aj + k]\n t1 = -s * At[Ai + k] + c * At[Aj + k]\n At[Ai + k] = t0; At[Aj + k] = t1\n\n a += t0 * t0; b += t1 * t1\n }\n\n W[i] = a\n W[j] = b\n\n changed = 1\n\n if (Vt) {\n Vi = (i * vstep) | 0\n Vj = (j * vstep) | 0\n\n k = 2\n t0 = c * Vt[Vi] + s * Vt[Vj]\n t1 = -s * Vt[Vi] + c * Vt[Vj]\n Vt[Vi] = t0; Vt[Vj] = t1\n\n t0 = c * Vt[Vi + 1] + s * Vt[Vj + 1]\n t1 = -s * Vt[Vi + 1] + c * Vt[Vj + 1]\n Vt[Vi + 1] = t0; Vt[Vj + 1] = t1\n\n for (; k < n; k++) {\n t0 = c * Vt[Vi + k] + s * Vt[Vj + k]\n t1 = -s * Vt[Vi + k] + c * Vt[Vj + k]\n Vt[Vi + k] = t0; Vt[Vj + k] = t1\n }\n }\n }\n }\n if (changed === 0) break\n }\n\n for (i = 0; i < n; i++) {\n for (k = 0, sd = 0; k < m; k++) {\n t = At[i * astep + k]\n sd += t * t\n }\n W[i] = Math.sqrt(sd)\n }\n\n for (i = 0; i < n - 1; i++) {\n j = i\n for (k = i + 1; k < n; k++) {\n if (W[j] < W[k]) { j = k }\n }\n if (i !== j) {\n swap(W, i, j, sd)\n if (Vt) {\n for (k = 0; k < m; k++) {\n swap(At, i * astep + k, j * astep + k, t)\n }\n\n for (k = 0; k < n; k++) {\n swap(Vt, i * vstep + k, j * vstep + k, t)\n }\n }\n }\n }\n\n for (i = 0; i < n; i++) {\n _W[i] = W[i]\n }\n\n if (!Vt) {\n return\n }\n\n for (i = 0; i < n1; i++) {\n sd = i < n ? W[i] : 0\n\n while (sd <= minval) {\n // if we got a zero singular value, then in order to get the corresponding left singular vector\n // we generate a random vector, project it to the previously computed left singular vectors,\n // subtract the projection and normalize the difference.\n val0 = (1.0 / m)\n for (k = 0; k < m; k++) {\n seed = (seed * 214013 + 2531011)\n val = (((seed >> 16) & 0x7fff) & 256) !== 0 ? val0 : -val0\n At[i * astep + k] = val\n }\n for (iter = 0; iter < 2; iter++) {\n for (j = 0; j < i; j++) {\n sd = 0\n for (k = 0; k < m; k++) {\n sd += At[i * astep + k] * At[j * astep + k]\n }\n asum = 0.0\n for (k = 0; k < m; k++) {\n t = (At[i * astep + k] - sd * At[j * astep + k])\n At[i * astep + k] = t\n asum += Math.abs(t)\n }\n asum = asum ? 1.0 / asum : 0\n for (k = 0; k < m; k++) {\n At[i * astep + k] *= asum\n }\n }\n }\n sd = 0\n for (k = 0; k < m; k++) {\n t = At[i * astep + k]\n sd += t * t\n }\n sd = Math.sqrt(sd)\n }\n\n s = (1.0 / sd)\n for (k = 0; k < m; k++) {\n At[i * astep + k] *= s\n }\n }\n}\n\nexport function svd (A: Matrix, W: Matrix, U: Matrix, V: Matrix) {\n let at = 0\n let i = 0\n const _m = A.rows\n const _n = A.cols\n let m = _m\n let n = _n\n\n if (m < n) {\n at = 1\n i = m\n m = n\n n = i\n }\n\n const amt = new Matrix(m, m)\n const wmt = new Matrix(1, n)\n const vmt = new Matrix(n, n)\n\n if (at === 0) {\n transpose(amt, A)\n } else {\n for (i = 0; i < _n * _m; i++) {\n amt.data[i] = A.data[i]\n }\n for (; i < n * m; i++) {\n amt.data[i] = 0\n }\n }\n\n JacobiSVDImpl(amt.data, m, wmt.data, vmt.data, n, m, n, m)\n\n if (W) {\n for (i = 0; i < n; i++) {\n W.data[i] = wmt.data[i]\n }\n for (; i < _n; i++) {\n W.data[i] = 0\n }\n }\n\n if (at === 0) {\n if (U) transpose(U, amt)\n if (V) transpose(V, vmt)\n } else {\n if (U) transpose(U, vmt)\n if (V) transpose(V, amt)\n }\n}\n\n//\n\nexport function m4new () {\n return new Float32Array([\n 1, 0, 0, 0,\n 0, 1, 0, 0,\n 0, 0, 1, 0,\n 0, 0, 0, 1\n ])\n}\n\nexport function m4set (out: Float32Array, n11: number, n12: number, n13: number, n14: number, n21: number, n22: number, n23: number, n24: number, n31: number, n32: number, n33: number, n34: number, n41: number, n42: number, n43: number, n44: number) {\n out[ 0 ] = n11; out[ 4 ] = n12; out[ 8 ] = n13; out[ 12 ] = n14\n out[ 1 ] = n21; out[ 5 ] = n22; out[ 9 ] = n23; out[ 13 ] = n24\n out[ 2 ] = n31; out[ 6 ] = n32; out[ 10 ] = n33; out[ 14 ] = n34\n out[ 3 ] = n41; out[ 7 ] = n42; out[ 11 ] = n43; out[ 15 ] = n44\n}\n\nexport function m4identity (out: Float32Array) {\n m4set(out,\n 1, 0, 0, 0,\n 0, 1, 0, 0,\n 0, 0, 1, 0,\n 0, 0, 0, 1\n )\n}\n(m4identity as any).__deps = [ m4set ]\n\nexport function m4multiply (out: Float32Array, a: Float32Array, b: Float32Array) {\n const a11 = a[ 0 ]\n const a12 = a[ 4 ]\n const a13 = a[ 8 ]\n const a14 = a[ 12 ]\n const a21 = a[ 1 ]\n const a22 = a[ 5 ]\n const a23 = a[ 9 ]\n const a24 = a[ 13 ]\n const a31 = a[ 2 ]\n const a32 = a[ 6 ]\n const a33 = a[ 10 ]\n const a34 = a[ 14 ]\n const a41 = a[ 3 ]\n const a42 = a[ 7 ]\n const a43 = a[ 11 ]\n const a44 = a[ 15 ]\n\n const b11 = b[ 0 ]\n const b12 = b[ 4 ]\n const b13 = b[ 8 ]\n const b14 = b[ 12 ]\n const b21 = b[ 1 ]\n const b22 = b[ 5 ]\n const b23 = b[ 9 ]\n const b24 = b[ 13 ]\n const b31 = b[ 2 ]\n const b32 = b[ 6 ]\n const b33 = b[ 10 ]\n const b34 = b[ 14 ]\n const b41 = b[ 3 ]\n const b42 = b[ 7 ]\n const b43 = b[ 11 ]\n const b44 = b[ 15 ]\n\n out[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41\n out[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42\n out[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43\n out[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44\n\n out[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41\n out[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42\n out[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43\n out[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44\n\n out[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41\n out[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42\n out[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43\n out[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44\n\n out[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41\n out[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42\n out[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43\n out[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44\n}\n\nexport function m4makeScale (out: Float32Array, x: number, y: number, z: number) {\n m4set(out,\n x, 0, 0, 0,\n 0, y, 0, 0,\n 0, 0, z, 0,\n 0, 0, 0, 1\n )\n}\n(m4makeScale as any).__deps = [ m4set ]\n\nexport function m4makeTranslation (out: Float32Array, x: number, y: number, z: number) {\n m4set(out,\n 1, 0, 0, x,\n 0, 1, 0, y,\n 0, 0, 1, z,\n 0, 0, 0, 1\n )\n}\n(m4makeTranslation as any).__deps = [ m4set ]\n\nexport function m4makeRotationY (out: Float32Array, theta: number) {\n const c = Math.cos(theta)\n const s = Math.sin(theta)\n m4set(out,\n c, 0, s, 0,\n 0, 1, 0, 0,\n -s, 0, c, 0,\n 0, 0, 0, 1\n )\n}\n(m4makeRotationY as any).__deps = [ m4set ]\n\n//\n\nexport function m3new () {\n return new Float32Array([\n 1, 0, 0,\n 0, 1, 0,\n 0, 0, 1\n ])\n}\n\nexport function m3makeNormal (out: Float32Array, m4: Float32Array) {\n const r0 = v3new([ m4[0], m4[1], m4[2] ])\n const r1 = v3new([ m4[4], m4[5], m4[6] ])\n const r2 = v3new([ m4[8], m4[9], m4[10] ])\n const cp = v3new()\n // [ r0 ] [ r1 x r2 ]\n // M3x3 = [ r1 ] N = [ r2 x r0 ]\n // [ r2 ] [ r0 x r1 ]\n v3cross(cp, r1, r2)\n out[ 0 ] = cp[ 0 ]\n out[ 1 ] = cp[ 1 ]\n out[ 2 ] = cp[ 2 ]\n v3cross(cp, r2, r0)\n out[ 3 ] = cp[ 0 ]\n out[ 4 ] = cp[ 1 ]\n out[ 5 ] = cp[ 2 ]\n v3cross(cp, r0, r1)\n out[ 6 ] = cp[ 0 ]\n out[ 7 ] = cp[ 1 ]\n out[ 8 ] = cp[ 2 ]\n}\n(m3makeNormal as any).__deps = [ v3new, v3cross ]\n","/**\n * @file Surface Utils\n * @author Alexander Rose \n * @private\n */\n\nimport { degToRad } from '../math/math-utils'\nimport {\n m4new, m4multiply, m4makeTranslation, m4makeScale, m4makeRotationY\n} from '../math/matrix-utils'\nimport {\n v3addScalar, v3subScalar, v3divideScalar, v3multiplyScalar,\n v3floor, v3ceil, v3sub, v3negate,\n v3cross, v3fromArray, normalizeVector3array\n} from '../math/vector-utils'\nimport { NumberArray } from '../types'\n\nfunction laplacianSmooth (verts: Float32Array, faces: Float32Array, numiter: number, inflate: boolean) {\n // based on D. Xu, Y. Zhang (2009) Generating Triangulated Macromolecular\n // Surfaces by Euclidean Distance Transform. PLoS ONE 4(12): e8140.\n //\n // Permission to use, copy, modify, and distribute this program for\n // any purpose, with or without fee, is hereby granted, provided that\n // the notices on the head, the reference information, and this\n // copyright notice appear in all copies or substantial portions of\n // the Software. It is provided \"as is\" without express or implied\n // warranty.\n //\n // ported to JavaScript and adapted to NGL by Alexander Rose\n\n numiter = numiter || 1\n inflate = inflate || true\n\n const nv = verts.length / 3\n const nf = faces.length / 3\n let norms: Float32Array | undefined = undefined\n\n if (inflate) {\n norms = new Float32Array(nv * 3)\n }\n\n const tps = new Float32Array(nv * 3)\n\n let i\n const ndeg = 20\n const vertdeg = new Array(ndeg)\n\n for (i = 0; i < ndeg; ++i) {\n vertdeg[ i ] = new Uint32Array(nv)\n }\n\n for (i = 0; i < nv; ++i) {\n vertdeg[ 0 ][ i ] = 0\n }\n\n let j, jl\n let flagvert: boolean\n\n // for each face\n\n for (i = 0; i < nf; ++i) {\n var ao = i * 3\n var bo = i * 3 + 1\n var co = i * 3 + 2\n\n // vertex a\n\n flagvert = true\n for (j = 0, jl = vertdeg[ 0 ][ faces[ao] ]; j < jl; ++j) {\n if (faces[ bo ] === vertdeg[ j + 1 ][ faces[ ao ] ]) {\n flagvert = false\n break\n }\n }\n if (flagvert) {\n vertdeg[ 0 ][ faces[ ao ] ]++\n vertdeg[ vertdeg[ 0 ][ faces[ ao ] ] ][ faces[ ao ] ] = faces[ bo ]\n }\n\n flagvert = true\n for (j = 0, jl = vertdeg[ 0 ][ faces[ ao ] ]; j < jl; ++j) {\n if (faces[ co ] === vertdeg[ j + 1 ][ faces[ ao ] ]) {\n flagvert = false\n break\n }\n }\n if (flagvert) {\n vertdeg[ 0 ][ faces[ ao ] ]++\n vertdeg[ vertdeg[ 0 ][ faces[ ao ] ] ][ faces[ ao ] ] = faces[ co ]\n }\n\n // vertex b\n\n flagvert = true\n for (j = 0, jl = vertdeg[ 0 ][ faces[ bo ] ]; j < jl; ++j) {\n if (faces[ ao ] === vertdeg[ j + 1 ][ faces[ bo ] ]) {\n flagvert = false\n break\n }\n }\n if (flagvert) {\n vertdeg[ 0 ][ faces[ bo ] ]++\n vertdeg[ vertdeg[ 0 ][ faces[ bo ] ] ][ faces[ bo ] ] = faces[ ao ]\n }\n\n flagvert = true\n for (j = 0, jl = vertdeg[ 0 ][ faces[ bo ] ]; j < jl; ++j) {\n if (faces[ co ] === vertdeg[ j + 1 ][ faces[ bo ] ]) {\n flagvert = false\n break\n }\n }\n if (flagvert) {\n vertdeg[ 0 ][ faces[ bo ] ]++\n vertdeg[ vertdeg[ 0 ][ faces[ bo ] ] ][ faces[ bo ] ] = faces[ co ]\n }\n\n // vertex c\n\n flagvert = true\n for (j = 0; j < vertdeg[ 0 ][ faces[ co ] ]; ++j) {\n if (faces[ ao ] === vertdeg[ j + 1 ][ faces[ co ] ]) {\n flagvert = false\n break\n }\n }\n if (flagvert) {\n vertdeg[ 0 ][ faces[ co ] ]++\n vertdeg[ vertdeg[ 0 ][ faces[ co ] ] ][ faces[ co ] ] = faces[ ao ]\n }\n\n flagvert = true\n for (j = 0, jl = vertdeg[ 0 ][ faces[ co ] ]; j < jl; ++j) {\n if (faces[ bo ] === vertdeg[ j + 1 ][ faces[ co ] ]) {\n flagvert = false\n break\n }\n }\n if (flagvert) {\n vertdeg[ 0 ][ faces[ co ] ]++\n vertdeg[ vertdeg[ 0 ][ faces[ co ] ] ][ faces[ co ] ] = faces[ bo ]\n }\n }\n\n var wt = 1.0\n var wt2 = 0.5\n var i3, vi3, vdi, wtvi, wt2vi\n var ssign = -1\n var scaleFactor = 1\n var outwt = 0.75 / (scaleFactor + 3.5) // area-preserving\n\n // smoothing iterations\n\n for (var k = 0; k < numiter; ++k) {\n // for each vertex\n\n for (i = 0; i < nv; ++i) {\n i3 = i * 3\n vdi = vertdeg[ 0 ][ i ]\n\n if (vdi < 3) {\n tps[ i3 ] = verts[ i3 ]\n tps[ i3 + 1 ] = verts[ i3 + 1 ]\n tps[ i3 + 2 ] = verts[ i3 + 2 ]\n } else if (vdi === 3 || vdi === 4) {\n tps[ i3 ] = 0\n tps[ i3 + 1 ] = 0\n tps[ i3 + 2 ] = 0\n\n for (j = 0; j < vdi; ++j) {\n vi3 = vertdeg[ j + 1 ][ i ] * 3\n tps[ i3 ] += verts[ vi3 ]\n tps[ i3 + 1 ] += verts[ vi3 + 1 ]\n tps[ i3 + 2 ] += verts[ vi3 + 2 ]\n }\n\n tps[ i3 ] += wt2 * verts[ i3 ]\n tps[ i3 + 1 ] += wt2 * verts[ i3 + 1 ]\n tps[ i3 + 2 ] += wt2 * verts[ i3 + 2 ]\n\n wt2vi = wt2 + vdi\n tps[ i3 ] /= wt2vi\n tps[ i3 + 1 ] /= wt2vi\n tps[ i3 + 2 ] /= wt2vi\n } else {\n tps[ i3 ] = 0\n tps[ i3 + 1 ] = 0\n tps[ i3 + 2 ] = 0\n\n for (j = 0; j < vdi; ++j) {\n vi3 = vertdeg[ j + 1 ][ i ] * 3\n tps[ i3 ] += verts[ vi3 ]\n tps[ i3 + 1 ] += verts[ vi3 + 1 ]\n tps[ i3 + 2 ] += verts[ vi3 + 2 ]\n }\n\n tps[ i3 ] += wt * verts[ i3 ]\n tps[ i3 + 1 ] += wt * verts[ i3 + 1 ]\n tps[ i3 + 2 ] += wt * verts[ i3 + 2 ]\n\n wtvi = wt + vdi\n tps[ i3 ] /= wtvi\n tps[ i3 + 1 ] /= wtvi\n tps[ i3 + 2 ] /= wtvi\n }\n }\n\n verts.set(tps) // copy smoothed positions\n\n if (inflate) {\n computeVertexNormals(verts, faces, norms)\n var nv3 = nv * 3\n\n for (i3 = 0; i3 < nv3; i3 += 3) {\n // if(verts[i].inout) ssign=1;\n // else ssign=-1;\n\n verts[ i3 ] += ssign * outwt * norms![ i3 ]\n verts[ i3 + 1 ] += ssign * outwt * norms![ i3 + 1 ]\n verts[ i3 + 2 ] += ssign * outwt * norms![ i3 + 2 ]\n }\n }\n }\n}\nObject.assign(laplacianSmooth, {__deps: [ computeVertexNormals ]})\n\nfunction computeVertexNormals (position: Float32Array, index?: NumberArray, normal?: Float32Array) {\n var i, il\n\n if (normal === undefined) {\n normal = new Float32Array(position.length)\n } else {\n // reset existing normals to zero\n for (i = 0, il = normal.length; i < il; i++) {\n normal[ i ] = 0\n }\n }\n\n var a = new Float32Array(3)\n var b = new Float32Array(3)\n var c = new Float32Array(3)\n var cb = new Float32Array(3)\n var ab = new Float32Array(3)\n\n if (index) {\n // indexed elements\n for (i = 0, il = index.length; i < il; i += 3) {\n var ai = index[ i ] * 3\n var bi = index[ i + 1 ] * 3\n var ci = index[ i + 2 ] * 3\n\n v3fromArray(a, position, ai)\n v3fromArray(b, position, bi)\n v3fromArray(c, position, ci)\n\n v3sub(cb, c, b)\n v3sub(ab, a, b)\n v3cross(cb, cb, ab)\n\n normal[ ai ] += cb[ 0 ]\n normal[ ai + 1 ] += cb[ 1 ]\n normal[ ai + 2 ] += cb[ 2 ]\n\n normal[ bi ] += cb[ 0 ]\n normal[ bi + 1 ] += cb[ 1 ]\n normal[ bi + 2 ] += cb[ 2 ]\n\n normal[ ci ] += cb[ 0 ]\n normal[ ci + 1 ] += cb[ 1 ]\n normal[ ci + 2 ] += cb[ 2 ]\n }\n } else {\n // non-indexed elements (unconnected triangle soup)\n for (i = 0, il = position.length; i < il; i += 9) {\n v3fromArray(a, position, i)\n v3fromArray(b, position, i + 3)\n v3fromArray(c, position, i + 6)\n\n v3sub(cb, c, b)\n v3sub(ab, a, b)\n v3cross(cb, cb, ab)\n\n normal[ i ] = cb[ 0 ]\n normal[ i + 1 ] = cb[ 1 ]\n normal[ i + 2 ] = cb[ 2 ]\n\n normal[ i + 3 ] = cb[ 0 ]\n normal[ i + 4 ] = cb[ 1 ]\n normal[ i + 5 ] = cb[ 2 ]\n\n normal[ i + 6 ] = cb[ 0 ]\n normal[ i + 7 ] = cb[ 1 ]\n normal[ i + 8 ] = cb[ 2 ]\n }\n }\n\n normalizeVector3array(normal)\n\n return normal\n}\nObject.assign(computeVertexNormals, {__deps: [\n v3sub, v3cross, v3fromArray, normalizeVector3array\n]})\n\nfunction getRadiusDict (radiusList: number[]) {\n var radiusDict: {[k: number]: boolean} = {}\n for (var i = 0, il = radiusList.length; i < il; ++i) {\n radiusDict[ radiusList[ i ] ] = true\n }\n return radiusDict\n}\n\nfunction getSurfaceGrid (min: Float32Array, max: Float32Array, maxRadius: number, scaleFactor: number, extraMargin: number) {\n // need margin to avoid boundary/round off effects\n var margin = (1 / scaleFactor) * 3\n margin += maxRadius\n\n v3subScalar(min, min, extraMargin + margin)\n v3addScalar(max, max, extraMargin + margin)\n\n v3multiplyScalar(min, min, scaleFactor)\n v3floor(min, min)\n v3divideScalar(min, min, scaleFactor)\n\n v3multiplyScalar(max, max, scaleFactor)\n v3ceil(max, max)\n v3divideScalar(max, max, scaleFactor)\n\n var dim = new Float32Array(3)\n v3sub(dim, max, min)\n v3multiplyScalar(dim, dim, scaleFactor)\n v3ceil(dim, dim)\n v3addScalar(dim, dim, 1)\n\n var maxSize = Math.pow(10, 6) * 256\n var tmpSize = dim[ 0 ] * dim[ 1 ] * dim[ 2 ] * 3\n\n if (maxSize <= tmpSize) {\n scaleFactor *= Math.pow(maxSize / tmpSize, 1 / 3)\n\n v3multiplyScalar(min, min, scaleFactor)\n v3floor(min, min)\n v3divideScalar(min, min, scaleFactor)\n\n v3multiplyScalar(max, max, scaleFactor)\n v3ceil(max, max)\n v3divideScalar(max, max, scaleFactor)\n\n v3sub(dim, max, min)\n v3multiplyScalar(dim, dim, scaleFactor)\n v3ceil(dim, dim)\n v3addScalar(dim, dim, 1)\n }\n\n var tran = new Float32Array(min)\n v3negate(tran, tran)\n\n // coordinate transformation matrix\n var matrix = m4new()\n var mroty = m4new()\n m4makeRotationY(mroty, degToRad(90))\n m4multiply(matrix, matrix, mroty)\n\n var mscale = m4new()\n m4makeScale(\n mscale,\n -1 / scaleFactor,\n 1 / scaleFactor,\n 1 / scaleFactor\n )\n m4multiply(matrix, matrix, mscale)\n\n var mtrans = m4new()\n m4makeTranslation(\n mtrans,\n -scaleFactor * tran[2],\n -scaleFactor * tran[1],\n -scaleFactor * tran[0]\n )\n m4multiply(matrix, matrix, mtrans)\n\n return {\n dim: dim,\n tran: tran,\n matrix: matrix,\n scaleFactor: scaleFactor\n }\n}\nObject.assign(getSurfaceGrid, {__deps: [\n degToRad,\n v3subScalar, v3addScalar, v3divideScalar, v3multiplyScalar,\n v3floor, v3ceil, v3sub, v3negate,\n m4new, m4multiply, m4makeTranslation, m4makeScale, m4makeRotationY\n]})\n\nexport {\n laplacianSmooth,\n computeVertexNormals,\n getRadiusDict,\n getSurfaceGrid\n}\n","/**\n * @file Surface\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3, Box3, BufferGeometry, Group, Color } from 'three'\n\nimport { Debug, Log, ColormakerRegistry } from '../globals'\nimport { getUintArray } from '../utils'\nimport { AtomPicker, SurfacePicker } from '../utils/picker'\nimport { uniformArray, uniformArray3, serialArray } from '../math/array-utils'\nimport Selection from '../selection/selection'\nimport { ColormakerParameters } from '../color/colormaker';\nimport { Structure, Volume } from '../ngl';\n\nexport interface SurfaceData {\n position: Float32Array\n index: Uint32Array|Uint16Array|undefined\n normal: Float32Array\n color: Float32Array\n atomindex: Int32Array\n contour: boolean\n}\n/**\n * Surface\n */\nclass Surface {\n name: string\n path: string\n position: Float32Array\n index: Uint32Array|Uint16Array|undefined\n normal: Float32Array|undefined\n color: Float32Array|undefined\n atomindex: Int32Array|undefined\n contour: boolean\n center: Vector3\n boundingBox: Box3\n size: number\n info: {\n type?: string\n probeRadius?: number\n scaleFactor?: number\n smooth?: number\n cutoff?: number\n isolevel?: number\n volume?: Volume\n }\n\n /**\n * @param {String} name - surface name\n * @param {String} path - source path\n * @param {Object} data - surface data\n * @param {Float32Array} data.position - surface positions\n * @param {Int32Array} data.index - surface indices\n * @param {Float32Array} data.normal - surface normals\n * @param {Float32Array} data.color - surface colors\n * @param {Int32Array} data.atomindex - atom indices\n * @param {boolean} data.contour - contour mode flag\n */\n constructor (name: string, path: string, data?: SurfaceData) {\n this.name = name || ''\n this.path = path || ''\n this.info = {}\n\n this.center = new Vector3()\n this.boundingBox = new Box3()\n\n if (data instanceof BufferGeometry ||\n data instanceof Group\n ) {\n // to be removed\n this.fromGeometry(data)\n } else if (data) {\n this.set(\n data.position,\n data.index,\n data.normal,\n data.color,\n data.atomindex,\n data.contour\n )\n\n this.boundingBox.setFromArray(data.position)\n this.boundingBox.getCenter(this.center)\n }\n }\n\n get type () { return 'Surface' }\n\n /**\n * set surface data\n * @param {Float32Array} position - surface positions\n * @param {Int32Array} index - surface indices\n * @param {Float32Array} normal - surface normals\n * @param {Float32Array} color - surface colors\n * @param {Int32Array} atomindex - atom indices\n * @param {boolean} contour - contour mode flag\n * @return {undefined}\n */\n set (position: Float32Array,\n index: Uint32Array|Uint16Array|undefined,\n normal: Float32Array|undefined,\n color: Float32Array|undefined,\n atomindex: Int32Array|undefined,\n contour: boolean = false) {\n /**\n * @type {Float32Array}\n */\n this.position = position\n /**\n * @type {Uint32Array|Uint16Array|undefined}\n */\n this.index = index\n /**\n * @type {Float32Array|undefined}\n */\n this.normal = normal\n /**\n * @type {Float32Array|undefined}\n */\n this.color = color\n /**\n * @type {Int32Array|undefined}\n */\n this.atomindex = atomindex\n\n this.size = position.length / 3\n this.contour = contour\n }\n\n fromGeometry (geometry: BufferGeometry|Group) {\n if (Debug) Log.time('GeometrySurface.fromGeometry')\n\n let geo\n\n if (geometry instanceof BufferGeometry) {\n geo = geometry\n } else {\n geo = (geometry as any)[ 0 ]\n }\n\n if (!geo.boundingBox) geo.computeBoundingBox()\n\n this.boundingBox.copy(geo.boundingBox)\n this.boundingBox.getCenter(this.center)\n\n let position, color, index, normal\n\n if (geo instanceof BufferGeometry) {\n const attr = geo.attributes\n const an = (attr as any).normal ? (attr as any).normal.array : false\n\n // assume there are no normals if the first is zero\n if (!an || (an[ 0 ] === 0 && an[ 1 ] === 0 && an[ 2 ] === 0)) {\n geo.computeVertexNormals()\n }\n\n position = (attr).position.array\n index = (attr).index ? (attr).index.array : null\n normal = (attr).normal.array\n }\n\n this.set(position, index, normal, color, undefined)\n\n if (Debug) Log.timeEnd('GeometrySurface.setGeometry')\n }\n\n getPosition () {\n return this.position\n }\n\n getColor (params: ColormakerParameters&{ scheme: string}) {\n const p = params || {}\n p.surface = this\n\n const n = this.size\n const array = new Float32Array(n * 3)\n const colormaker = ColormakerRegistry.getScheme(p)\n\n if (colormaker.volumeColor || p.scheme === 'random') {\n for (let i = 0; i < n; ++i) {\n colormaker.volumeColorToArray(i, array, i * 3)\n }\n } else if (colormaker.positionColor) {\n const v = new Vector3()\n const pos = this.position\n\n for (let i = 0; i < n; ++i) {\n var i3 = i * 3\n v.set(pos[ i3 ], pos[ i3 + 1 ], pos[ i3 + 2 ])\n colormaker.positionColorToArray(v, array, i3)\n }\n } else if (colormaker.atomColor && this.atomindex) {\n const atomProxy = p.structure!.getAtomProxy()\n const atomindex = this.atomindex\n\n for (let i = 0; i < n; ++i) {\n atomProxy.index = atomindex[ i ]\n colormaker.atomColorToArray(atomProxy, array, i * 3)\n }\n } else {\n const tc = new Color(p.value)\n uniformArray3(n, tc.r, tc.g, tc.b, array)\n }\n\n return array\n }\n\n getPicking (structure?: Structure) {\n if (this.atomindex && structure) {\n return new AtomPicker(this.atomindex as any, structure)\n } else {\n return new SurfacePicker(serialArray(this.size), this)\n }\n }\n\n getNormal () {\n return this.normal\n }\n\n getSize (size: number, scale: number) {\n return uniformArray(this.size, size * scale)\n }\n\n getIndex () {\n return this.index\n }\n\n getFilteredIndex (sele: string, structure: Structure) {\n if (sele && this.atomindex) {\n const selection = new Selection(sele)\n const atomSet = structure.getAtomSet(selection)\n const filteredIndex = []\n\n const atomindex = this.atomindex\n const index = this.index\n const n = index!.length\n const elementSize = this.contour ? 2 : 3\n\n let j = 0\n\n for (let i = 0; i < n; i += elementSize) {\n let include = true\n\n for (let a = 0; a < elementSize; a++) {\n const idx = index![ i + a ]\n const ai = atomindex[ idx ]\n if (!atomSet.get(ai)) {\n include = false\n break\n }\n }\n\n if (!include) { continue }\n\n for (let a = 0; a < elementSize; a++, j++) {\n filteredIndex[ j ] = index![ i + a ]\n }\n }\n\n return getUintArray(filteredIndex, this.position.length / 3)\n } else {\n return this.index\n }\n }\n\n getAtomindex () {\n return this.atomindex\n }\n\n dispose () {\n\n //\n\n }\n}\n\nexport default Surface\n","/**\n * @file Volume\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3, Box3, Matrix3, Matrix4 } from 'three'\n\nimport { WorkerRegistry, ColormakerRegistry } from '../globals'\nimport { defaults } from '../utils'\nimport WorkerPool from '../worker/worker-pool'\nimport { VolumePicker } from '../utils/picker'\nimport {\n uniformArray, serialArray,\n arrayMin, arrayMax, arraySum, arrayMean, arrayRms\n} from '../math/array-utils'\nimport MarchingCubes from './marching-cubes'\nimport { laplacianSmooth, computeVertexNormals } from './surface-utils'\nimport {\n applyMatrix4toVector3array, applyMatrix3toVector3array\n} from '../math/vector-utils'\nimport { m3new, m3makeNormal } from '../math/matrix-utils'\nimport Surface from './surface'\nimport { NumberArray } from '../types';\nimport { ColormakerParameters } from '../color/colormaker';\n\nexport interface VolumeSurface {\n new (data: NumberArray, nx: number, ny: number, nz: number, atomindex: NumberArray): void\n getSurface: (isolevel: number, smooth: boolean|number, box: number[][]|undefined, matrix: Float32Array, contour: boolean, wrap?: boolean) => {\n position: Float32Array\n normal: undefined|Float32Array\n index: Uint32Array|Uint16Array\n atomindex: Int32Array|undefined\n contour: boolean\n }\n}\nexport function VolumeSurface (this: VolumeSurface,data: NumberArray, nx: number, ny: number, nz: number, atomindex: NumberArray) {\n var mc = new (MarchingCubes as any)(data, nx, ny, nz, atomindex) as MarchingCubes\n\n function getSurface (isolevel: number, smooth: boolean|number, box: number[][]|undefined, matrix: Float32Array, contour: boolean, wrap: boolean = false) {\n const sd = mc.triangulate(isolevel, smooth as boolean, box, contour, wrap)\n if (smooth && !contour) {\n laplacianSmooth(sd.position, sd.index as any, smooth as number, true)\n sd.normal = computeVertexNormals(sd.position, sd.index as any)\n }\n if (matrix) {\n applyMatrix4toVector3array(matrix, sd.position)\n if (sd.normal) {\n const normalMatrix = m3new()\n m3makeNormal(normalMatrix, matrix)\n applyMatrix3toVector3array(normalMatrix, sd.normal)\n }\n }\n return sd\n }\n\n this.getSurface = getSurface\n}\nObject.assign(VolumeSurface, {__deps: [\n laplacianSmooth, computeVertexNormals, MarchingCubes,\n applyMatrix4toVector3array, applyMatrix3toVector3array,\n m3new, m3makeNormal\n]})\n\nWorkerRegistry.add('surf', function func (e: any, callback: (data: any, transferList: any) => void) {\n const a = e.data.args\n const p = e.data.params\n if (a) {\n /* global self */\n (self as any).volsurf = new (VolumeSurface as any)(a[0], a[1], a[2], a[3], a[4]) as VolumeSurface\n }\n if (p) {\n const sd = ((self as any).volsurf as VolumeSurface).getSurface(\n p.isolevel, p.smooth, p.box, p.matrix, p.contour, p.wrap\n )\n const transferList = [ sd.position.buffer, sd.index.buffer ]\n if (sd.normal) transferList.push(sd.normal.buffer)\n if (sd.atomindex) transferList.push(sd.atomindex.buffer)\n const data = {\n sd: sd,\n p: p\n }\n callback(data, transferList)\n }\n}, [ VolumeSurface ])\n\nexport type VolumeSize = 'value'|'abs-value'|'value-min'|'deviation'\n/**\n * Volume\n */\nclass Volume {\n name: string\n path: string\n\n matrix: Matrix4\n normalMatrix: Matrix3\n inverseMatrix: Matrix4\n center: Vector3\n boundingBox: Box3\n\n nx: number\n ny: number\n nz: number\n data: Float32Array\n\n worker: Worker\n workerPool: WorkerPool\n _position: Float32Array|undefined\n _min: number|undefined\n _max: number|undefined\n _mean: number|undefined\n _rms: number|undefined\n _sum: number|undefined\n __box: Box3|undefined\n\n atomindex: Int32Array|undefined\n volsurf: VolumeSurface|undefined\n header: any\n /**\n * Make Volume instance\n * @param {String} name - volume name\n * @param {String} path - source path\n * @param {Float32array} data - volume 3d grid\n * @param {Integer} nx - x dimension of the 3d volume\n * @param {Integer} ny - y dimension of the 3d volume\n * @param {Integer} nz - z dimension of the 3d volume\n * @param {Int32Array} atomindex - atom indices corresponding to the cells in the 3d grid\n */\n constructor (name: string, path: string, data?: Float32Array, nx?: number, ny?: number, nz?: number, atomindex?: Int32Array) {\n this.name = name\n this.path = path\n\n this.matrix = new Matrix4()\n this.normalMatrix = new Matrix3()\n this.inverseMatrix = new Matrix4()\n this.center = new Vector3()\n this.boundingBox = new Box3()\n\n this.setData(data, nx, ny, nz, atomindex)\n }\n\n get type () { return 'Volume' }\n\n /**\n * set volume data\n * @param {Float32array} data - volume 3d grid\n * @param {Integer} nx - x dimension of the 3d volume\n * @param {Integer} ny - y dimension of the 3d volume\n * @param {Integer} nz - z dimension of the 3d volume\n * @param {Int32Array} atomindex - atom indices corresponding to the cells in the 3d grid\n * @return {undefined}\n */\n setData (data?: Float32Array, nx?: number, ny?: number, nz?: number, atomindex?: Int32Array) {\n this.nx = nx || 1\n this.ny = ny || 1\n this.nz = nz || 1\n\n this.data = data || new Float32Array(1)\n this.setAtomindex(atomindex)\n\n delete this._position\n\n delete this._min\n delete this._max\n delete this._mean\n delete this._rms\n\n if (this.worker) this.worker.terminate()\n }\n\n /**\n * Set statistics, which can be different from the data in this volume,\n * if this volume is a slice of a bigger volume\n * @param {Number|undefined} min - minimum value of the whole data set\n * @param {Number|undefined} max - maximum value of the whole data set\n * @param {Number|undefined} mean - average value of the whole data set\n * @param {Number|undefined} rms - sigma value of the whole data set\n */\n setStats (min: number|undefined, max: number|undefined, mean: number|undefined, rms: number|undefined) {\n this._min = min\n this._max = max\n this._mean = mean\n this._rms = rms\n }\n\n /**\n * set transformation matrix\n * @param {Matrix4} matrix - 4x4 transformation matrix\n * @return {undefined}\n */\n setMatrix (matrix: Matrix4) {\n this.matrix.copy(matrix)\n\n const bb = this.boundingBox\n const v = this.center // temporary re-purposing\n\n const x = this.nx - 1\n const y = this.ny - 1\n const z = this.nz - 1\n\n bb.makeEmpty()\n\n bb.expandByPoint(v.set(x, y, z))\n bb.expandByPoint(v.set(x, y, 0))\n bb.expandByPoint(v.set(x, 0, z))\n bb.expandByPoint(v.set(x, 0, 0))\n bb.expandByPoint(v.set(0, y, z))\n bb.expandByPoint(v.set(0, 0, z))\n bb.expandByPoint(v.set(0, y, 0))\n bb.expandByPoint(v.set(0, 0, 0))\n\n bb.applyMatrix4(this.matrix)\n bb.getCenter(this.center)\n\n // make normal matrix\n\n const me = this.matrix.elements\n const r0 = new Vector3(me[0], me[1], me[2])\n const r1 = new Vector3(me[4], me[5], me[6])\n const r2 = new Vector3(me[8], me[9], me[10])\n const cp = new Vector3()\n // [ r0 ] [ r1 x r2 ]\n // M3x3 = [ r1 ] N = [ r2 x r0 ]\n // [ r2 ] [ r0 x r1 ]\n const ne = this.normalMatrix.elements\n cp.crossVectors(r1, r2)\n ne[ 0 ] = cp.x\n ne[ 1 ] = cp.y\n ne[ 2 ] = cp.z\n cp.crossVectors(r2, r0)\n ne[ 3 ] = cp.x\n ne[ 4 ] = cp.y\n ne[ 5 ] = cp.z\n cp.crossVectors(r0, r1)\n ne[ 6 ] = cp.x\n ne[ 7 ] = cp.y\n ne[ 8 ] = cp.z\n\n this.inverseMatrix.copy(this.matrix).invert()\n }\n\n /**\n * set atom indices\n * @param {Int32Array} atomindex - atom indices corresponding to the cells in the 3d grid\n * @return {undefined}\n */\n setAtomindex (atomindex?: Int32Array) {\n this.atomindex = atomindex\n }\n\n getBox (center: Vector3, size: number, target: Box3) {\n if (!target) target = new Box3()\n\n target.set(center, center)\n target.expandByScalar(size)\n target.applyMatrix4(this.inverseMatrix)\n\n target.min.round()\n target.max.round()\n\n return target\n }\n\n _getBox (center: Vector3|undefined, size: number) {\n if (!center || !size) return\n\n if (!this.__box) this.__box = new Box3()\n const box = this.getBox(center, size, this.__box)\n return [ box.min.toArray(), box.max.toArray() ]\n }\n\n _makeSurface (sd: any, isolevel: number, smooth: number) {\n const name = this.name + '@' + isolevel.toPrecision(2)\n const surface = new Surface(name, '', sd)\n surface.info.isolevel = isolevel\n surface.info.smooth = smooth\n surface.info.volume = this\n\n return surface\n }\n\n getSurface (isolevel: number, smooth: number, center: Vector3, size: number, contour: boolean, wrap: boolean = false) {\n isolevel = isNaN(isolevel) ? this.getValueForSigma(2) : isolevel\n smooth = defaults(smooth, 0)\n\n //\n\n if (this.volsurf === undefined) {\n this.volsurf = new (VolumeSurface as any)(\n this.data, this.nx, this.ny, this.nz, this.atomindex\n ) as VolumeSurface\n }\n\n const box = this._getBox(center, size)\n const sd = this.volsurf.getSurface(\n isolevel, smooth, box!, this.matrix.elements as unknown as Float32Array, contour, wrap\n )\n\n return this._makeSurface(sd, isolevel, smooth)\n }\n\n getSurfaceWorker (isolevel: number, smooth: number, center: Vector3, size: number, contour: boolean, wrap: boolean, callback: (s: Surface) => void) {\n isolevel = isNaN(isolevel) ? this.getValueForSigma(2) : isolevel\n smooth = smooth || 0\n\n //\n\n if (window.hasOwnProperty('Worker')) {\n if (this.workerPool === undefined) {\n this.workerPool = new WorkerPool('surf', 2)\n }\n\n const msg = {}\n const worker = this.workerPool.getNextWorker()\n\n if (worker!.postCount === 0) {\n Object.assign(msg, {\n args: [\n this.data, this.nx, this.ny, this.nz, this.atomindex\n ]\n })\n }\n\n Object.assign(msg, {\n params: {\n isolevel: isolevel,\n smooth: smooth,\n box: this._getBox(center, size),\n matrix: this.matrix.elements,\n contour: contour,\n wrap: wrap\n }\n })\n\n worker!.post(msg, undefined,\n (e: any) => {\n const sd = e.data.sd\n const p = e.data.p\n callback(this._makeSurface(sd, p.isolevel, p.smooth))\n },\n (e : string) => {\n console.warn(\n 'Volume.getSurfaceWorker error - trying without worker', e\n )\n const surface = this.getSurface(isolevel, smooth, center, size, contour, wrap)\n callback(surface)\n }\n )\n } else {\n const surface = this.getSurface(isolevel, smooth, center, size, contour, wrap)\n callback(surface)\n }\n }\n\n getValueForSigma (sigma: number) {\n return this.mean + defaults(sigma, 2) * this.rms\n }\n\n getSigmaForValue (value: number) {\n return (defaults(value, 0) - this.mean) / this.rms\n }\n\n get position () {\n if (!this._position) {\n const nz = this.nz\n const ny = this.ny\n const nx = this.nx\n const position = new Float32Array(nx * ny * nz * 3)\n\n let p = 0\n for (let z = 0; z < nz; ++z) {\n for (let y = 0; y < ny; ++y) {\n for (let x = 0; x < nx; ++x) {\n position[ p + 0 ] = x\n position[ p + 1 ] = y\n position[ p + 2 ] = z\n p += 3\n }\n }\n }\n\n applyMatrix4toVector3array(this.matrix.elements as unknown as Float32Array, position)\n this._position = position\n }\n\n return this._position\n }\n\n getDataAtomindex () {\n return this.atomindex\n }\n\n getDataPosition () {\n return this.position\n }\n\n getDataColor (params: ColormakerParameters & {scheme: string}) {\n const p = params || {}\n p.volume = this\n p.scale = p.scale || 'Spectral'\n p.domain = p.domain || [ this.min, this.max ]\n\n const colormaker = ColormakerRegistry.getScheme(p)\n\n const n = this.position.length / 3\n const array = new Float32Array(n * 3)\n\n // var atoms = p.structure.atoms;\n // var atomindex = this.atomindex;\n\n for (let i = 0; i < n; ++i) {\n colormaker.volumeColorToArray(i, array, i * 3)\n // a = atoms[ atomindex[ i ] ];\n // if( a ) colormaker.atomColorToArray( a, array, i * 3 );\n }\n\n return array\n }\n\n getDataPicking () {\n const picking = serialArray(this.position.length / 3)\n return new VolumePicker(picking, this)\n }\n\n getDataSize (size: VolumeSize|number, scale: number) {\n const data = this.data\n const n = this.position.length / 3\n let array\n\n switch (size) {\n case 'value':\n array = new Float32Array(data)\n break\n\n case 'abs-value':\n array = new Float32Array(data)\n for (let i = 0; i < n; ++i) {\n array[ i ] = Math.abs(array[ i ])\n }\n break\n\n case 'value-min': {\n array = new Float32Array(data)\n const min = this.min\n for (let i = 0; i < n; ++i) {\n array[ i ] -= min\n }\n break\n }\n\n case 'deviation':\n array = new Float32Array(data)\n break\n\n default:\n array = uniformArray(n, size)\n break\n }\n\n if (scale !== 1.0) {\n for (let i = 0; i < n; ++i) {\n array[ i ] *= scale\n }\n }\n\n return array\n }\n\n get min () {\n if (this._min === undefined) {\n this._min = arrayMin(this.data)\n }\n return this._min\n }\n\n get max () {\n if (this._max === undefined) {\n this._max = arrayMax(this.data)\n }\n return this._max\n }\n\n get sum () {\n if (this._sum === undefined) {\n this._sum = arraySum(this.data)\n }\n return this._sum\n }\n\n get mean () {\n if (this._mean === undefined) {\n this._mean = arrayMean(this.data)\n }\n return this._mean\n }\n\n get rms () {\n if (this._rms === undefined) {\n this._rms = arrayRms(this.data)\n }\n return this._rms\n }\n\n clone () {\n const vol = new Volume(\n this.name,\n this.path,\n\n this.data,\n\n this.nx,\n this.ny,\n this.nz,\n\n this.atomindex\n )\n\n vol.matrix.copy(this.matrix)\n vol.header = Object.assign({}, this.header)\n\n return vol\n }\n\n dispose () {\n if (this.workerPool) this.workerPool.terminate()\n }\n}\n\nexport default Volume\n","/**\n * @file Buffer\n * @author Alexander Rose \n * @private\n */\n\nimport {\n Color, Vector3, Matrix4,\n FrontSide, BackSide, DoubleSide,\n // VertexColors,\n NoBlending,\n BufferGeometry, BufferAttribute,\n UniformsUtils, UniformsLib, Uniform,\n Group, LineSegments, Points, Mesh, Object3D,\n ShaderMaterial,\n DynamicDrawUsage,\n StaticDrawUsage\n} from 'three'\n\nimport { Log } from '../globals'\nimport { createParams, getTypedArray, getUintArray } from '../utils'\nimport { GenericColor, TypedArray } from '../types'\nimport { getShader, ShaderDefines } from '../shader/shader-utils'\nimport { serialArray } from '../math/array-utils'\nimport { Picker } from '../utils/picker'\n\nexport type BufferSide = 'front'|'back'|'double'\n\nfunction getThreeSide (side: BufferSide) {\n if (side === 'front') {\n return FrontSide\n } else if (side === 'back') {\n return BackSide\n } else if (side === 'double') {\n return DoubleSide\n } else {\n return DoubleSide\n }\n}\n\nconst itemSize = {\n 'f': 1, 'v2': 2, 'v3': 3, 'c': 3\n}\n\nfunction setObjectMatrix (object: Object3D, matrix: Matrix4) {\n object.matrix.copy(matrix)\n object.matrix.decompose(object.position, object.quaternion, object.scale)\n object.matrixWorldNeedsUpdate = true\n}\n\nexport type BufferTypes = 'picking'|'background'\nexport type BufferMaterials = 'material'|'wireframeMaterial'|'pickingMaterial'\n\nexport interface _BufferAttribute {\n type: 'f'|'v2'|'v3'|'c'\n value?: TypedArray\n}\n\nexport type Uniforms = { [k: string]: Uniform|{ value: any } }\n\nexport const BufferDefaultParameters = {\n opaqueBack: false,\n side: 'double' as BufferSide, // which triangle sides to render\n opacity: 1.0, // translucency: 1 is fully opaque, 0 is fully transparent\n depthWrite: true,\n clipNear: 0, // position of camera near/front clipping plane in percent of scene bounding box\n clipRadius: 0,\n clipCenter: new Vector3(),\n flatShaded: false, // render flat shaded\n wireframe: false, // render as wireframe\n roughness: 0.4, // how rough the material is, between 0 and 1\n metalness: 0.0, // how metallic the material is, between 0 and 1\n diffuse: 0xffffff, // diffuse color for lighting\n diffuseInterior: false,\n useInteriorColor: false, // render back-side with interior color\n interiorColor: 0xdddddd, // interior color\n interiorDarkening: 0, // interior darkening factor\n forceTransparent: false, // force the material to allow transparency\n matrix: new Matrix4(), // additional transformation matrix\n disablePicking: false, // disable picking\n sortParticles: false,\n background: false\n}\nexport type BufferParameters = Omit & { diffuse: GenericColor; interiorColor: GenericColor }\n\nexport const BufferParameterTypes = {\n opaqueBack: { updateShader: true },\n side: { updateShader: true, property: true },\n opacity: { uniform: true },\n depthWrite: { property: true },\n clipNear: { updateShader: true, property: true },\n clipRadius: { updateShader: true, uniform: true },\n clipCenter: { uniform: true },\n flatShaded: { updateShader: true },\n background: { updateShader: true },\n wireframe: { updateVisibility: true },\n roughness: { uniform: true },\n metalness: { uniform: true },\n diffuse: { uniform: true },\n diffuseInterior: { updateShader: true },\n useInteriorColor: { updateShader: true },\n interiorColor: { uniform: true },\n interiorDarkening: { uniform: true },\n matrix: {}\n}\n\nexport interface BufferData {\n position?: Float32Array\n position1?: Float32Array // TODO\n color?: Float32Array\n index?: Uint32Array|Uint16Array\n normal?: Float32Array\n\n picking?: Picker\n primitiveId?: Float32Array\n}\n\n/**\n * Buffer class. Base class for buffers.\n * @interface\n */\nclass Buffer {\n parameterTypes = BufferParameterTypes\n get defaultParameters() { return BufferDefaultParameters }\n parameters: BufferParameters\n uniforms: Uniforms\n pickingUniforms: Uniforms\n\n private _positionDataSize: number\n\n geometry = new BufferGeometry()\n indexVersion = 0\n wireframeIndexVersion = -1\n group = new Group()\n wireframeGroup = new Group()\n pickingGroup = new Group()\n\n vertexShader = ''\n fragmentShader = ''\n isImpostor = false\n isText = false\n isSurface = false\n isPoint = false\n isLine = false\n dynamic = true\n visible = true\n\n picking?: Picker\n\n material: ShaderMaterial\n wireframeMaterial: ShaderMaterial\n pickingMaterial: ShaderMaterial\n\n wireframeIndex?: Uint32Array|Uint16Array\n wireframeIndexCount = 0\n wireframeGeometry?: BufferGeometry\n\n /**\n * @param {Object} data - attribute object\n * @param {Float32Array} data.position - positions\n * @param {Float32Array} data.color - colors\n * @param {Uint32Array|Uint16Array} data.index - triangle indices\n * @param {Picker} [data.picking] - picking ids\n * @param {BufferParameters} params - parameters object\n */\n constructor (data: BufferData, params: Partial = {}) {\n this.parameters = createParams(params, this.defaultParameters)\n\n this.uniforms = UniformsUtils.merge([\n UniformsLib.common,\n {\n fogColor: { value: new Color(0x000000) },\n fogNear: { value: 0.0 },\n fogFar: { value: 0.0 },\n opacity: { value: this.parameters.opacity },\n clipNear: { value: 0.0 },\n clipRadius: { value: this.parameters.clipRadius },\n clipCenter: { value: this.parameters.clipCenter }\n },\n {\n emissive: { value: new Color(0x000000) },\n roughness: { value: this.parameters.roughness },\n metalness: { value: this.parameters.metalness },\n interiorColor: { value: new Color(this.parameters.interiorColor) },\n interiorDarkening: { value: this.parameters.interiorDarkening },\n },\n UniformsLib.lights\n ])\n\n this.uniforms.diffuse.value.set(this.parameters.diffuse)\n\n this.pickingUniforms = {\n clipNear: { value: 0.0 },\n objectId: { value: 0 },\n opacity: { value: this.parameters.opacity }\n }\n\n //\n\n const position = data.position || data.position1\n this._positionDataSize = position ? position.length / 3 : 0\n\n if (!data.primitiveId) {\n data.primitiveId = serialArray(this._positionDataSize)\n }\n\n this.addAttributes({\n position: { type: 'v3', value: data.position },\n color: { type: 'c', value: data.color },\n primitiveId: { type: 'f', value: data.primitiveId }\n })\n\n if (params.matrix) {\n this.matrix = params.matrix\n }\n\n if (data.index) {\n this.initIndex(data.index)\n }\n this.picking = data.picking\n\n this.makeWireframeGeometry()\n }\n\n set matrix (m) {\n this.setMatrix(m)\n }\n get matrix () {\n return this.group.matrix.clone()\n }\n\n get transparent () {\n return this.parameters.opacity < 1 || this.parameters.forceTransparent\n }\n\n get size () {\n return this._positionDataSize\n }\n\n get attributeSize () {\n return this.size\n }\n\n get pickable () {\n return !!this.picking && !this.parameters.disablePicking\n }\n\n setMatrix (m: Matrix4) {\n setObjectMatrix(this.group, m)\n setObjectMatrix(this.wireframeGroup, m)\n setObjectMatrix(this.pickingGroup, m)\n }\n\n initIndex (index: Uint32Array|Uint16Array) {\n this.geometry.setIndex(\n new BufferAttribute(index, 1)\n )\n const nindex = this.geometry.getIndex();\n if (!nindex) { Log.error('Index is null'); return; }\n nindex.setUsage(this.dynamic ? DynamicDrawUsage : StaticDrawUsage)\n }\n\n makeMaterial () {\n const side = getThreeSide(this.parameters.side)\n\n const m = new ShaderMaterial({\n uniforms: this.uniforms,\n vertexShader: '',\n fragmentShader: '',\n depthTest: true,\n transparent: this.transparent,\n depthWrite: this.parameters.depthWrite,\n lights: true,\n fog: true,\n side: side\n })\n m.vertexColors = true\n m.extensions.derivatives = true\n m.extensions.fragDepth = this.isImpostor\n\n const wm = new ShaderMaterial({\n uniforms: this.uniforms,\n vertexShader: '',\n fragmentShader: '',\n depthTest: true,\n transparent: this.transparent,\n depthWrite: this.parameters.depthWrite,\n lights: false,\n fog: true,\n side: side\n })\n wm.vertexColors = true\n\n const pm = new ShaderMaterial({\n uniforms: this.pickingUniforms,\n vertexShader: '',\n fragmentShader: '',\n depthTest: true,\n transparent: false,\n depthWrite: this.parameters.depthWrite,\n lights: false,\n fog: false,\n side: side,\n blending: NoBlending\n })\n pm.vertexColors = true\n pm.extensions.fragDepth = this.isImpostor\n\n ;(m as any).clipNear = this.parameters.clipNear\n ;(wm as any).clipNear = this.parameters.clipNear\n ;(pm as any).clipNear = this.parameters.clipNear\n\n this.material = m\n this.wireframeMaterial = wm\n this.pickingMaterial = pm\n\n // also sets vertexShader/fragmentShader\n this.updateShader()\n }\n\n makeWireframeGeometry () {\n this.makeWireframeIndex()\n\n const geometry = this.geometry\n const wireframeIndex = this.wireframeIndex\n const wireframeGeometry = new BufferGeometry()\n\n wireframeGeometry.attributes = geometry.attributes\n if (wireframeIndex) {\n wireframeGeometry.setIndex(\n new BufferAttribute(wireframeIndex, 1).setUsage(this.dynamic ? DynamicDrawUsage : StaticDrawUsage)\n )\n wireframeGeometry.setDrawRange(0, this.wireframeIndexCount)\n }\n\n this.wireframeGeometry = wireframeGeometry\n }\n\n makeWireframeIndex () {\n const edges: number[][] = []\n\n function checkEdge (a: number, b: number) {\n if (a > b) {\n const tmp = a\n a = b\n b = tmp\n }\n\n const list = edges[ a ]\n\n if (list === undefined) {\n edges[ a ] = [ b ]\n return true\n } else if (!list.includes(b)) {\n list.push(b)\n return true\n }\n\n return false\n }\n\n const geometry = this.geometry\n const index = geometry.index\n\n if (!this.parameters.wireframe) {\n this.wireframeIndex = new Uint16Array(0)\n this.wireframeIndexCount = 0\n } else if (index) {\n const array = index.array\n let n = array.length\n if (geometry.drawRange.count !== Infinity) {\n n = geometry.drawRange.count\n }\n let wireframeIndex\n if (this.wireframeIndex && this.wireframeIndex.length > n * 2) {\n wireframeIndex = this.wireframeIndex\n } else {\n const count = (geometry.attributes as any).position.count // TODO\n wireframeIndex = getUintArray(n * 2, count)\n }\n\n let j = 0\n edges.length = 0\n\n for (let i = 0; i < n; i += 3) {\n const a = array[ i + 0 ]\n const b = array[ i + 1 ]\n const c = array[ i + 2 ]\n\n if (checkEdge(a, b)) {\n wireframeIndex[ j + 0 ] = a\n wireframeIndex[ j + 1 ] = b\n j += 2\n }\n if (checkEdge(b, c)) {\n wireframeIndex[ j + 0 ] = b\n wireframeIndex[ j + 1 ] = c\n j += 2\n }\n if (checkEdge(c, a)) {\n wireframeIndex[ j + 0 ] = c\n wireframeIndex[ j + 1 ] = a\n j += 2\n }\n }\n\n this.wireframeIndex = wireframeIndex\n this.wireframeIndexCount = j\n this.wireframeIndexVersion = this.indexVersion\n } else {\n const n = (geometry.attributes as any).position.count // TODO\n\n let wireframeIndex\n if (this.wireframeIndex && this.wireframeIndex.length > n * 2) {\n wireframeIndex = this.wireframeIndex\n } else {\n wireframeIndex = getUintArray(n * 2, n)\n }\n\n for (let i = 0, j = 0; i < n; i += 3) {\n wireframeIndex[ j + 0 ] = i\n wireframeIndex[ j + 1 ] = i + 1\n wireframeIndex[ j + 2 ] = i + 1\n wireframeIndex[ j + 3 ] = i + 2\n wireframeIndex[ j + 4 ] = i + 2\n wireframeIndex[ j + 5 ] = i\n\n j += 6\n }\n\n this.wireframeIndex = wireframeIndex\n this.wireframeIndexCount = n * 2\n this.wireframeIndexVersion = this.indexVersion\n }\n }\n\n updateWireframeIndex () {\n if (!this.wireframeGeometry || !this.wireframeIndex) return\n\n this.wireframeGeometry.setDrawRange(0, Infinity)\n if (this.wireframeIndexVersion < this.indexVersion) this.makeWireframeIndex()\n\n if (this.wireframeGeometry.index &&\n this.wireframeIndex.length > this.wireframeGeometry.index.array.length) {\n this.wireframeGeometry.setIndex(\n new BufferAttribute(this.wireframeIndex, 1).setUsage(this.dynamic ? DynamicDrawUsage : StaticDrawUsage)\n )\n } else {\n const index = this.wireframeGeometry.getIndex()\n if (!index) { Log.error('Index is null'); return; }\n index.set(this.wireframeIndex)\n index.needsUpdate = this.wireframeIndexCount > 0\n index.updateRange.count = this.wireframeIndexCount\n }\n\n this.wireframeGeometry.setDrawRange(0, this.wireframeIndexCount)\n }\n\n getRenderOrder () {\n let renderOrder = 0\n\n if (this.isText) {\n renderOrder = 1\n } else if (this.transparent) {\n if (this.isSurface) {\n renderOrder = 3\n } else {\n renderOrder = 2\n }\n }\n\n return renderOrder\n }\n\n _getMesh (materialName: BufferMaterials) {\n if (!this.material) this.makeMaterial()\n\n const g = this.geometry\n const m = this[ materialName ]\n\n let mesh\n\n if (this.isLine) {\n mesh = new LineSegments(g, m)\n } else if (this.isPoint) {\n mesh = new Points(g, m)\n } else {\n mesh = new Mesh(g, m)\n }\n\n mesh.frustumCulled = false\n mesh.renderOrder = this.getRenderOrder()\n\n return mesh\n }\n\n getMesh () {\n return this._getMesh('material')\n }\n\n getWireframeMesh () {\n let mesh\n\n if (!this.material) this.makeMaterial()\n if (!this.wireframeGeometry) this.makeWireframeGeometry()\n\n mesh = new LineSegments(\n this.wireframeGeometry, this.wireframeMaterial\n )\n\n mesh.frustumCulled = false\n mesh.renderOrder = this.getRenderOrder()\n\n return mesh\n }\n\n getPickingMesh () {\n return this._getMesh('pickingMaterial')\n }\n\n getShader (name: string, type?: BufferTypes) {\n return getShader(name, this.getDefines(type))\n }\n\n getVertexShader (type?: BufferTypes) {\n return this.getShader(this.vertexShader, type)\n }\n\n getFragmentShader (type?: BufferTypes) {\n return this.getShader(this.fragmentShader, type)\n }\n\n getDefines (type?: BufferTypes) {\n const defines: ShaderDefines = {}\n\n if (this.parameters.clipNear) {\n defines.NEAR_CLIP = 1\n }\n\n if (this.parameters.clipRadius) {\n defines.RADIUS_CLIP = 1\n }\n\n if (type === 'picking') {\n defines.PICKING = 1\n } else {\n if (type === 'background' || this.parameters.background) {\n defines.NOLIGHT = 1\n }\n if (this.parameters.flatShaded) {\n defines.FLAT_SHADED = 1\n }\n if (this.parameters.opaqueBack) {\n defines.OPAQUE_BACK = 1\n }\n if (this.parameters.diffuseInterior) {\n defines.DIFFUSE_INTERIOR = 1\n }\n if (this.parameters.useInteriorColor) {\n defines.USE_INTERIOR_COLOR = 1\n }\n }\n\n return defines\n }\n\n getParameters () {\n return this.parameters\n }\n\n addUniforms (uniforms: Uniforms) {\n this.uniforms = UniformsUtils.merge(\n [ this.uniforms, uniforms ]\n )\n\n this.pickingUniforms = UniformsUtils.merge(\n [ this.pickingUniforms, uniforms ]\n )\n }\n\n addAttributes (attributes: { [k: string]: _BufferAttribute }) {\n for (let name in attributes) {\n let buf\n const a = attributes[ name ]\n const arraySize = this.attributeSize * itemSize[ a.type ]\n\n if (a.value) {\n if (arraySize !== a.value.length) {\n Log.error('attribute value has wrong length', name)\n }\n buf = a.value\n } else {\n buf = getTypedArray('float32', arraySize)\n }\n\n this.geometry.setAttribute(\n name,\n new BufferAttribute(buf, itemSize[ a.type ]).setUsage(this.dynamic ? DynamicDrawUsage : StaticDrawUsage)\n )\n }\n }\n\n updateRenderOrder () {\n const renderOrder = this.getRenderOrder()\n function setRenderOrder (mesh: Object3D) {\n mesh.renderOrder = renderOrder\n }\n\n this.group.children.forEach(setRenderOrder)\n if (this.pickingGroup) {\n this.pickingGroup.children.forEach(setRenderOrder)\n }\n }\n\n updateShader () {\n const m = this.material\n const wm = this.wireframeMaterial\n const pm = this.pickingMaterial\n\n m.vertexShader = this.getVertexShader()\n m.fragmentShader = this.getFragmentShader()\n m.needsUpdate = true\n\n wm.vertexShader = this.getShader('Line.vert')\n wm.fragmentShader = this.getShader('Line.frag')\n wm.needsUpdate = true\n\n pm.vertexShader = this.getVertexShader('picking')\n pm.fragmentShader = this.getFragmentShader('picking')\n pm.needsUpdate = true\n }\n\n /**\n * Set buffer parameters\n * @param {BufferParameters} params - buffer parameters object\n * @return {undefined}\n */\n setParameters (params: Partial) {\n const p = params as any\n const pt = this.parameterTypes as any\n const pv = this.parameters as any\n\n const propertyData: { [k: string]: any } = {}\n const uniformData: { [k: string]: any } = {}\n let doShaderUpdate = false\n let doVisibilityUpdate = false\n\n for (const name in p) {\n const value = p[ name ]\n\n if (value === undefined) continue\n pv[ name ] = value\n\n if (pt[ name ] === undefined) continue\n\n if (pt[ name ].property) {\n if (pt[ name ].property !== true) {\n propertyData[ pt[ name ].property as any ] = value\n } else {\n propertyData[ name ] = value\n }\n }\n\n if (pt[ name ].uniform) {\n if (pt[ name ].uniform !== true) {\n uniformData[ pt[ name ].uniform as any ] = value\n } else {\n uniformData[ name ] = value\n }\n }\n\n if (pt[ name ].updateShader) {\n doShaderUpdate = true\n }\n\n if (pt[ name ].updateVisibility) {\n doVisibilityUpdate = true\n }\n\n if (this.dynamic && name === 'wireframe' && value === true) {\n this.updateWireframeIndex()\n }\n\n if (name === 'forceTransparent') {\n propertyData.transparent = this.transparent\n }\n\n if (name === 'matrix') {\n this.matrix = value\n }\n }\n\n this.setProperties(propertyData)\n this.setUniforms(uniformData)\n if (doShaderUpdate) this.updateShader()\n if (doVisibilityUpdate) this.setVisibility(this.visible)\n }\n\n /**\n * Sets buffer attributes\n * @param {Object} data - An object where the keys are the attribute names\n * and the values are the attribute data.\n * @example\n * var buffer = new Buffer();\n * buffer.setAttributes({ attrName: attrData });\n */\n setAttributes (data: any) { // TODO\n const geometry = this.geometry\n const attributes = geometry.attributes as any // TODO\n\n for (const name in data) {\n if (name === 'picking') continue\n\n const array = data[ name ]\n const length = array.length\n\n if (name === 'index') {\n const index = geometry.getIndex()\n if (!index) { Log.error('Index is null'); continue; }\n geometry.setDrawRange(0, Infinity)\n\n if (length > index.array.length) {\n geometry.setIndex(\n new BufferAttribute(array, 1)\n .setUsage(this.dynamic ? DynamicDrawUsage : StaticDrawUsage)\n )\n } else {\n index.set(array)\n index.needsUpdate = length > 0\n index.updateRange.count = length\n geometry.setDrawRange(0, length)\n }\n\n this.indexVersion++\n if (this.parameters.wireframe) this.updateWireframeIndex()\n } else {\n const attribute = attributes[ name ]\n\n if (length > attribute.array.length) {\n geometry.setAttribute(\n name,\n new BufferAttribute(array, attribute.itemSize)\n .setUsage(this.dynamic ? DynamicDrawUsage : StaticDrawUsage)\n )\n } else {\n attributes[ name ].set(array)\n attributes[ name ].needsUpdate = length > 0\n attributes[ name ].updateRange.count = length\n }\n }\n }\n }\n\n setUniforms (data: any) { // TODO\n if (!data) return\n\n const u = this.material.uniforms\n const wu = this.wireframeMaterial.uniforms\n const pu = this.pickingMaterial.uniforms\n\n for (let name in data) {\n if (name === 'opacity') {\n this.setProperties({ transparent: this.transparent })\n }\n\n if (u[ name ] !== undefined) {\n if (u[ name ].value.isVector3) {\n u[ name ].value.copy(data[ name ])\n } else if (u[ name ].value.set) {\n u[ name ].value.set(data[ name ])\n } else {\n u[ name ].value = data[ name ]\n }\n }\n\n if (wu[ name ] !== undefined) {\n if (wu[ name ].value.isVector3) {\n wu[ name ].value.copy(data[ name ])\n } else if (wu[ name ].value.set) {\n wu[ name ].value.set(data[ name ])\n } else {\n wu[ name ].value = data[ name ]\n }\n }\n\n if (pu[ name ] !== undefined) {\n if (pu[ name ].value.isVector3) {\n pu[ name ].value.copy(data[ name ])\n } else if (pu[ name ].value.set) {\n pu[ name ].value.set(data[ name ])\n } else {\n pu[ name ].value = data[ name ]\n }\n }\n }\n }\n\n setProperties (data: any) { // TODO\n if (!data) return\n\n const m = this.material\n const wm = this.wireframeMaterial\n const pm = this.pickingMaterial\n\n for (const _name in data) {\n const name = _name as 'side'|'transparent' // TODO\n\n let value = data[ name ]\n\n if (name === 'transparent') {\n this.updateRenderOrder()\n } else if (name === 'side') {\n value = getThreeSide(value)\n }\n\n (m[ name ] as any) = value;\n (wm[ name ] as any) = value;\n (pm[ name ] as any) = value\n }\n\n m.needsUpdate = true\n wm.needsUpdate = true\n pm.needsUpdate = true\n }\n\n /**\n * Set buffer visibility\n * @param {Boolean} value - visibility value\n * @return {undefined}\n */\n setVisibility (value: boolean) {\n this.visible = value\n\n if (this.parameters.wireframe) {\n this.group.visible = false\n this.wireframeGroup.visible = value\n if (this.pickable) {\n this.pickingGroup.visible = false\n }\n } else {\n this.group.visible = value\n this.wireframeGroup.visible = false\n if (this.pickable) {\n this.pickingGroup.visible = value\n }\n }\n }\n\n /**\n * Free buffer resources\n * @return {undefined}\n */\n dispose () {\n if (this.material) this.material.dispose()\n if (this.wireframeMaterial) this.wireframeMaterial.dispose()\n if (this.pickingMaterial) this.pickingMaterial.dispose()\n\n this.geometry.dispose()\n if (this.wireframeGeometry) this.wireframeGeometry.dispose()\n }\n\n /**\n * Customize JSON serialization to avoid circular references\n */\n toJSON () {\n var result: any = {};\n for (var x in this) {\n if (x !== \"group\" && x !== \"wireframeGroup\" && x != \"pickingGroup\"\n && x !== \"picking\") {\n result[x] = this[x];\n }\n }\n return result;\n }\n}\n\nexport default Buffer\n","/**\n * @file Mesh Buffer\n * @author Alexander Rose \n * @private\n */\n\nimport '../shader/Mesh.vert'\nimport '../shader/Mesh.frag'\n\nimport Buffer, { BufferParameters, BufferData } from './buffer'\n\n/**\n * Mesh buffer. Draws a triangle mesh.\n *\n * @example\n * var meshBuffer = new MeshBuffer({\n * position: new Float32Array(\n * [ 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1 ]\n * ),\n * color: new Float32Array(\n * [ 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0 ]\n * )\n * });\n */\nclass MeshBuffer extends Buffer {\n vertexShader = 'Mesh.vert'\n fragmentShader = 'Mesh.frag'\n\n /**\n * @param {Object} data - attribute object\n * @param {Float32Array} data.position - positions\n * @param {Float32Array} data.color - colors\n * @param {Float32Array} [data.index] - triangle indices\n * @param {Float32Array} [data.normal] - radii\n * @param {BufferParameters} params - parameter object\n */\n constructor (data: BufferData, params: Partial = {}) {\n super(data, params)\n\n this.addAttributes({\n 'normal': { type: 'v3', value: data.normal }\n })\n\n if (data.normal === undefined) {\n this.geometry.computeVertexNormals()\n }\n }\n}\n\nexport default MeshBuffer\n","/**\n * @file Surface Buffer\n * @author Alexander Rose \n * @private\n */\n\nimport MeshBuffer from './mesh-buffer'\n\n/**\n * Surface buffer. Like a {@link MeshBuffer}, but with `.isSurface` set to `true`.\n */\nclass SurfaceBuffer extends MeshBuffer {\n isSurface = true\n}\n\nexport default SurfaceBuffer\n","/**\n * @file Double Sided Buffer\n * @author Alexander Rose \n * @private\n */\n\n// @ts-ignore: unused import Vector3, Matrix4 required for declaration only\nimport { Group, BufferGeometry, Object3D, Mesh, LineSegments, Vector3, Matrix4 } from 'three'\n\nimport Buffer, { BufferSide } from './buffer'\nimport { Picker } from '../utils/picker'\n\nfunction setVisibilityTrue (m: Object3D) { m.visible = true }\nfunction setVisibilityFalse (m: Object3D) { m.visible = false }\n\n/**\n * A double-sided mesh buffer. Takes a buffer and renders the front and\n * the back as seperate objects to avoid some artifacts when rendering\n * transparent meshes. Also allows to render the back of a mesh opaque\n * while the front is transparent.\n * @implements {Buffer}\n *\n * @example\n * var sphereGeometryBuffer = new SphereGeometryBuffer({\n * position: new Float32Array([ 0, 0, 0 ]),\n * color: new Float32Array([ 1, 0, 0 ]),\n * radius: new Float32Array([ 1 ])\n * });\n * var doubleSidedBuffer = new DoubleSidedBuffer(sphereGeometryBuffer);\n */\nclass DoubleSidedBuffer {\n size: number\n side: BufferSide\n visible: boolean\n wireframe: boolean\n geometry: BufferGeometry\n\n picking?: Picker\n\n group = new Group()\n wireframeGroup = new Group()\n pickingGroup = new Group()\n\n frontMeshes: (Mesh|LineSegments)[] = []\n backMeshes: (Mesh|LineSegments)[] = []\n\n buffer: Buffer\n frontBuffer: Buffer\n backBuffer: Buffer\n\n /**\n * Create a double sided buffer\n * @param {Buffer} buffer - the buffer to be rendered double-sided\n */\n constructor (buffer: Buffer) {\n this.size = buffer.size\n this.side = buffer.parameters.side\n this.visible = buffer.visible\n this.geometry = buffer.geometry\n this.picking = buffer.picking\n\n this.group = new Group()\n this.wireframeGroup = new Group()\n this.pickingGroup = new Group()\n\n // requires Group objects to be present\n this.matrix = buffer.matrix\n\n const frontBuffer = buffer\n const backBuffer = new (buffer as any).constructor({ // TODO\n position: new Float32Array(0)\n }) as Buffer\n\n frontBuffer.makeMaterial()\n backBuffer.makeMaterial()\n\n backBuffer.picking = buffer.picking\n backBuffer.geometry = buffer.geometry\n backBuffer.wireframeGeometry = buffer.wireframeGeometry\n backBuffer.setParameters(buffer.getParameters())\n backBuffer.updateShader()\n\n frontBuffer.setParameters({\n side: 'front'\n })\n backBuffer.setParameters({\n side: 'back',\n opacity: backBuffer.parameters.opacity\n })\n\n this.buffer = buffer\n this.frontBuffer = frontBuffer\n this.backBuffer = backBuffer\n }\n\n set matrix (m) {\n Buffer.prototype.setMatrix.call(this, m)\n }\n get matrix () {\n return this.group.matrix.clone()\n }\n\n get pickable () {\n return !!this.picking && !this.parameters.disablePicking\n }\n\n get parameters () {\n return this.buffer.parameters\n }\n\n getParameters () {\n const p = Object.assign({}, this.buffer.parameters)\n p.side = this.side\n return p\n }\n\n getMesh (picking: boolean) {\n let front, back\n\n if (picking) {\n back = this.backBuffer.getPickingMesh()\n front = this.frontBuffer.getPickingMesh()\n } else {\n back = this.backBuffer.getMesh()\n front = this.frontBuffer.getMesh()\n }\n\n this.frontMeshes.push(front)\n this.backMeshes.push(back)\n\n this.setParameters({ side: this.side })\n\n return new Group().add(back, front)\n }\n\n getWireframeMesh () {\n return this.buffer.getWireframeMesh()\n }\n\n getPickingMesh () {\n return this.getMesh(true)\n }\n\n setAttributes (data: any) { // TODO\n this.buffer.setAttributes(data)\n }\n\n setParameters (data: any) { // TODO\n data = Object.assign({}, data)\n\n if (data.side === 'front') {\n this.frontMeshes.forEach(setVisibilityTrue)\n this.backMeshes.forEach(setVisibilityFalse)\n } else if (data.side === 'back') {\n this.frontMeshes.forEach(setVisibilityFalse)\n this.backMeshes.forEach(setVisibilityTrue)\n } else if (data.side === 'double') {\n this.frontMeshes.forEach(setVisibilityTrue)\n this.backMeshes.forEach(setVisibilityTrue)\n }\n\n if (data.side !== undefined) {\n this.side = data.side\n }\n delete data.side\n\n if (data.matrix !== undefined) {\n this.matrix = data.matrix\n }\n delete data.matrix\n\n this.frontBuffer.setParameters(data)\n\n if (data.wireframe !== undefined) {\n this.wireframe = data.wireframe\n this.setVisibility(this.visible)\n }\n delete data.wireframe\n\n this.backBuffer.setParameters(data)\n }\n\n setVisibility (value: boolean) {\n this.visible = value\n\n if (this.parameters.wireframe) {\n this.group.visible = false\n this.wireframeGroup.visible = value\n if (this.pickable) {\n this.pickingGroup.visible = false\n }\n } else {\n this.group.visible = value\n this.wireframeGroup.visible = false\n if (this.pickable) {\n this.pickingGroup.visible = value\n }\n }\n }\n\n dispose () {\n this.frontBuffer.dispose()\n this.backBuffer.dispose()\n }\n\n /**\n * Customize JSON serialization to avoid circular references.\n * Only export simple params which could be useful.\n */\n toJSON () {\n var result: any = {};\n for (var x in this) {\n if (['side', 'size', 'visible', 'matrix', 'parameters'].includes(x)) {\n result[x] = this[x];\n }\n }\n return result;\n }\n}\n\nexport default DoubleSidedBuffer\n","/**\n * @file Contour Buffer\n * @author Fred ludlow \n * @private\n */\n\nimport '../shader/Line.vert'\nimport '../shader/Line.frag'\n\nimport Buffer from './buffer'\n\n/**\n * Contour buffer. A buffer that draws lines (instead of triangle meshes).\n */\nclass ContourBuffer extends Buffer {\n isLine = true\n vertexShader = 'Line.vert'\n fragmentShader = 'Line.frag'\n}\n\nexport default ContourBuffer\n","/**\n * @file Surface Representation\n * @author Alexander Rose \n * @private\n */\n\nimport { Matrix4, Vector3, Box3 } from 'three'\n\nimport { defaults } from '../utils'\nimport Representation, { RepresentationParameters } from './representation'\nimport Volume from '../surface/volume'\nimport SurfaceBuffer from '../buffer/surface-buffer'\nimport DoubleSidedBuffer from '../buffer/doublesided-buffer'\nimport ContourBuffer from '../buffer/contour-buffer'\nimport Surface from '../surface/surface';\nimport Viewer from '../viewer/viewer';\nimport {SurfaceData} from '../surface/surface'\n// @ts-ignore: unused import ColormakerParameters required for declaration only\nimport { ColormakerParameters } from '../color/colormaker';\nexport type SurfaceDataFields = {position: boolean, color: boolean, index: boolean, normal: boolean, radius: boolean}\n\n/**\n * Surface representation parameter object. Extends {@link RepresentationParameters}\n *\n * @typedef {Object} SurfaceRepresentationParameters - surface representation parameters\n *\n * @property {String} isolevelType - Meaning of the isolevel value. Either *value* for the literal value or *sigma* as a factor of the sigma of the data. For volume data only.\n * @property {Float} isolevel - The value at which to create the isosurface. For volume data only.\n * @property {Boolean} negateIsolevel - For volume data only.\n * @property {Boolean} isolevelScroll - For volume data only\n * @property {Integer} smooth - How many iterations of laplacian smoothing after surface triangulation. For volume data only.\n * @property {Boolean} background - Render the surface in the background, unlit.\n * @property {Boolean} opaqueBack - Render the back-faces (where normals point away from the camera) of the surface opaque, ignoring the transparency parameter.\n * @property {Integer} boxSize - Size of the box to triangulate volume data in. Set to zero to triangulate the whole volume. For volume data only.\n * @property {Boolean} useWorker - Weather or not to triangulate the volume asynchronously in a Web Worker. For volume data only.\n * @property {Boolean} wrap - Wrap volume data around the edges; use in conjuction with boxSize but not larger than the volume dimension. For volume data only.\n */\nexport interface SurfaceRepresentationParameters extends RepresentationParameters {\n isolevelType: 'value'|'sigma'\n isolevel: number\n smooth: number\n background: boolean\n opaqueBack: boolean\n boxSize: number\n useWorker: boolean\n wrap: boolean\n}\n/**\n * Surface representation\n */\n/**\n * Create Surface representation object\n * @param {Surface|Volume} surface - the surface or volume to be represented\n * @param {Viewer} viewer - a viewer object\n * @param {SurfaceRepresentationParameters} params - surface representation parameters\n */\nclass SurfaceRepresentation extends Representation {\n protected surface: Surface|Volume|undefined\n protected volume: Volume|undefined\n protected boxCenter: Vector3\n protected __boxCenter: Vector3\n protected box: Box3\n protected __box: Box3\n protected _position: Vector3\n protected isolevelType: 'value'|'sigma'\n protected isolevel: number\n protected negateIsolevel: boolean\n protected isolevelScroll: boolean\n protected smooth: number\n protected background: boolean\n protected opaqueBack: boolean\n protected boxSize: number\n protected inverseMatrix: Matrix4\n protected colorVolume: Volume\n protected contour: boolean\n protected useWorker: boolean\n protected wrap: boolean\n\n protected __isolevel: number\n protected __smooth: number\n protected __contour: boolean\n protected __wrap: boolean\n protected __boxSize: number\n\n setBox: () => void\n\n constructor (surface: Surface, viewer: Viewer, params: Partial) {\n super(surface, viewer, params)\n\n this.type = 'surface'\n\n this.parameters = Object.assign({\n\n isolevelType: {\n type: 'select',\n options: {\n 'value': 'value', 'sigma': 'sigma'\n }\n },\n isolevel: {\n type: 'number', precision: 2, max: 1000, min: -1000\n },\n negateIsolevel: {\n type: 'boolean'\n },\n isolevelScroll: {\n type: 'boolean'\n },\n smooth: {\n type: 'integer', precision: 1, max: 10, min: 0\n },\n background: {\n type: 'boolean', rebuild: true // FIXME\n },\n opaqueBack: {\n type: 'boolean', buffer: true\n },\n boxSize: {\n type: 'integer', precision: 1, max: 100, min: 0\n },\n colorVolume: {\n type: 'hidden'\n },\n contour: {\n type: 'boolean', rebuild: true\n },\n useWorker: {\n type: 'boolean', rebuild: true\n },\n wrap: {\n type: 'boolean', rebuild: true\n }\n\n }, this.parameters)\n\n if (surface instanceof Volume) {\n this.surface = undefined\n this.volume = surface\n } else {\n this.surface = surface\n this.volume = undefined\n }\n\n this.boxCenter = new Vector3()\n this.__boxCenter = new Vector3()\n this.box = new Box3()\n this.__box = new Box3()\n\n this._position = new Vector3()\n this.inverseMatrix = new Matrix4()\n\n this.setBox = function setBox () {\n this._position.copy(viewer.translationGroup.position).negate()\n this._position.applyMatrix4(this.inverseMatrix)\n if (!this._position.equals(this.boxCenter)) {\n this.setParameters({ 'boxCenter': this._position })\n }\n }\n\n this.toBePrepared = true\n\n this.viewer.signals.ticked.add(this.setBox, this)\n\n this.init(params)\n }\n\n init (params: Partial) {\n const p = params || {}\n p.colorScheme = defaults(p.colorScheme, 'uniform')\n p.colorValue = defaults(p.colorValue, 0xDDDDDD)\n\n this.isolevelType = defaults(p.isolevelType, 'sigma')\n this.isolevel = defaults(p.isolevel, 2.0)\n this.negateIsolevel = defaults(p.negateIsolevel, false)\n this.isolevelScroll = defaults(p.isolevelScroll, false)\n this.smooth = defaults(p.smooth, 0)\n this.background = defaults(p.background, false)\n this.opaqueBack = defaults(p.opaqueBack, true)\n this.boxSize = defaults(p.boxSize, 0)\n this.colorVolume = defaults(p.colorVolume, undefined)\n this.contour = defaults(p.contour, false)\n this.useWorker = defaults(p.useWorker, true)\n this.wrap = defaults(p.wrap, false)\n\n super.init(p)\n\n this.inverseMatrix.copy(this.matrix).invert()\n\n this.build()\n }\n\n attach (callback: () => void) {\n this.bufferList.forEach(buffer => {\n this.viewer.add(buffer)\n })\n\n this.setVisibility(this.visible)\n\n callback()\n }\n\n prepare (callback: () => void) {\n if (this.volume) {\n let isolevel\n\n if (this.isolevelType === 'sigma') {\n isolevel = this.volume.getValueForSigma(this.isolevel)\n } else {\n isolevel = this.isolevel\n }\n if (this.negateIsolevel) isolevel *= -1\n\n if (!this.surface ||\n this.__isolevel !== isolevel ||\n this.__smooth !== this.smooth ||\n this.__contour !== this.contour ||\n this.__wrap !== this.wrap ||\n this.__boxSize !== this.boxSize ||\n (this.boxSize > 0 &&\n !this.__boxCenter.equals(this.boxCenter))\n ) {\n this.__isolevel = isolevel\n this.__smooth = this.smooth\n this.__contour = this.contour\n this.__wrap = this.wrap\n this.__boxSize = this.boxSize\n this.__boxCenter.copy(this.boxCenter)\n this.__box.copy(this.box)\n\n const onSurfaceFinish = (surface: Surface) => {\n this.surface = surface\n callback()\n }\n\n if (this.useWorker) {\n this.volume.getSurfaceWorker(\n isolevel, this.smooth, this.boxCenter, this.boxSize,\n this.contour, this.wrap, onSurfaceFinish\n )\n } else {\n onSurfaceFinish(\n this.volume.getSurface(\n isolevel, this.smooth, this.boxCenter, this.boxSize,\n this.contour, this.wrap\n )\n )\n }\n } else {\n callback()\n }\n } else {\n callback()\n }\n }\n\n create () {\n const sd = {\n position: (this.surface as Surface).getPosition(),\n color: (this.surface as Surface).getColor(this.getColorParams()),\n index: (this.surface as Surface).getIndex()\n }\n\n let buffer\n\n if (this.contour) {\n buffer = new ContourBuffer(\n sd,\n this.getBufferParams({ wireframe: false })\n )\n } else {\n Object.assign(sd, {\n normal: (this.surface as Surface).getNormal(),\n picking: (this.surface as Surface).getPicking()\n })\n\n const surfaceBuffer = new SurfaceBuffer(\n sd,\n this.getBufferParams({\n background: this.background,\n opaqueBack: this.opaqueBack,\n dullInterior: false\n })\n )\n\n buffer = new DoubleSidedBuffer(surfaceBuffer)\n }\n\n this.bufferList.push(buffer as ContourBuffer)\n }\n\n update (what: SurfaceDataFields) {\n if (this.bufferList.length === 0) return\n\n what = what || {}\n\n const surfaceData: Partial = {}\n\n if (what.position) {\n surfaceData.position = (this.surface as Surface).getPosition()\n }\n\n if (what.color) {\n surfaceData.color = (this.surface as Surface).getColor(\n this.getColorParams()\n )\n }\n\n if (what.index) {\n surfaceData.index = (this.surface as Surface).getIndex()\n }\n\n if (what.normal) {\n surfaceData.normal = (this.surface as Surface).getNormal()\n }\n\n this.bufferList.forEach(function (buffer) {\n buffer.setAttributes(surfaceData)\n })\n }\n\n /**\n * Set representation parameters\n * @alias SurfaceRepresentation#setParameters\n * @param {SurfaceRepresentationParameters} params - surface parameter object\n * @param {Object} [what] - buffer data attributes to be updated,\n * note that this needs to be implemented in the\n * derived classes. Generally it allows more\n * fine-grained control over updating than\n * forcing a rebuild.\n * @param {Boolean} what.position - update position data\n * @param {Boolean} what.color - update color data\n * @param {Boolean} [rebuild] - whether or not to rebuild the representation\n * @return {SurfaceRepresentation} this object\n */\n setParameters (params: Partial, what?: SurfaceDataFields, rebuild?: boolean) {\n if (params && params.isolevelType !== undefined &&\n this.volume\n ) {\n if (this.isolevelType === 'value' &&\n params.isolevelType === 'sigma'\n ) {\n this.isolevel = this.volume.getSigmaForValue(this.isolevel)\n } else if (this.isolevelType === 'sigma' &&\n params.isolevelType === 'value'\n ) {\n this.isolevel = this.volume.getValueForSigma(this.isolevel)\n }\n\n this.isolevelType = params.isolevelType\n }\n\n if (params && params.boxCenter) {\n this.boxCenter.copy(params.boxCenter)\n delete params.boxCenter\n }\n\n // Forbid wireframe && contour as in molsurface\n if (params && params.wireframe && (\n params.contour || (params.contour === undefined && this.contour)\n )) {\n params.wireframe = false\n }\n\n super.setParameters(params, what, rebuild)\n\n if (params.matrix) {\n this.inverseMatrix.copy(params.matrix).invert()\n }\n\n if (this.volume) {\n this.volume.getBox(this.boxCenter, this.boxSize, this.box)\n }\n\n if (params && params.colorVolume !== undefined) {\n if (what) what.color = true\n }\n\n if (this.surface && (\n params.isolevel !== undefined ||\n params.negateIsolevel !== undefined ||\n params.smooth !== undefined ||\n params.wrap !== undefined ||\n params.boxSize !== undefined ||\n (this.boxSize > 0 &&\n !this.__box.equals(this.box))\n )) {\n this.build({\n 'position': true,\n 'color': true,\n 'index': true,\n 'normal': !this.contour\n })\n }\n\n return this\n }\n\n getColorParams () {\n const p = super.getColorParams()\n\n p.volume = this.colorVolume\n\n return p\n }\n\n dispose () {\n this.viewer.signals.ticked.remove(this.setBox, this)\n\n super.dispose()\n }\n}\n\nexport default SurfaceRepresentation\n","/**\n * @file Mouse Actions\n * @author Alexander Rose \n * @private\n */\n\nimport PickingProxy from './picking-proxy'\nimport { almostIdentity } from '../math/math-utils'\nimport Stage from '../stage/stage'\nimport StructureComponent from '../component/structure-component'\nimport SurfaceRepresentation from '../representation/surface-representation'\n\nexport type ScrollCallback = (stage: Stage, delta: number) => void\nexport type DragCallback = (stage: Stage, dx: number, dy: number) => void\nexport type PickCallback = (stage: Stage, pickingProxy: PickingProxy) => void\nexport type MouseActionCallback = ScrollCallback | DragCallback | PickCallback\n\n/**\n * Mouse actions provided as static methods\n */\nclass MouseActions {\n /**\n * Zoom scene based on scroll-delta\n * @param {Stage} stage - the stage\n * @param {Number} delta - amount to zoom\n * @return {undefined}\n */\n static zoomScroll (stage: Stage, delta: number) {\n stage.trackballControls.zoom(delta)\n }\n\n /**\n * Move near clipping plane based on scroll-delta\n * @param {Stage} stage - the stage\n * @param {Number} delta - amount to move clipping plane\n * @return {undefined}\n */\n static clipNearScroll (stage: Stage, delta: number) {\n const sp = stage.getParameters()\n stage.setParameters({ clipNear: sp.clipNear + delta / 10 })\n }\n\n /**\n * Move clipping planes based on scroll-delta.\n * @param {Stage} stage - the stage\n * @param {Number} delta - direction to move planes\n * @return {undefined}\n */\n static focusScroll (stage: Stage, delta: number) {\n const focus = stage.getFocus()\n const sign = Math.sign(delta)\n const step = sign * almostIdentity((100 - focus) / 10, 5, 0.2)\n stage.setFocus(focus + step)\n }\n\n /**\n * Zoom scene based on scroll-delta and\n * move focus planes based on camera position (zoom)\n * @param {Stage} stage - the stage\n * @param {Number} delta - amount to move focus planes and zoom\n * @return {undefined}\n */\n static zoomFocusScroll (stage: Stage, delta: number) {\n stage.trackballControls.zoom(delta)\n const z = stage.viewer.camera.position.z\n stage.setFocus(100 - Math.abs(z / 8))\n }\n\n /**\n * Change isolevel of volume surfaces based on scroll-delta\n * @param {Stage} stage - the stage\n * @param {Number} delta - amount to change isolevel\n * @return {undefined}\n */\n static isolevelScroll (stage: Stage, delta: number) {\n const d = Math.sign(delta) / 10\n stage.eachRepresentation((reprElem, comp) => {\n if (reprElem.repr instanceof SurfaceRepresentation) {\n const p = reprElem.getParameters() as any // TODO\n if (p.isolevelScroll) {\n reprElem.setParameters({ isolevel: p.isolevel + d })\n }\n }\n })\n }\n\n /**\n * Pan scene based on mouse coordinate changes\n * @param {Stage} stage - the stage\n * @param {Number} dx - amount to pan in x direction\n * @param {Number} dy - amount to pan in y direction\n * @return {undefined}\n */\n static panDrag (stage: Stage, dx: number, dy: number) {\n stage.trackballControls.pan(dx, dy)\n }\n\n /**\n * Rotate scene based on mouse coordinate changes\n * @param {Stage} stage - the stage\n * @param {Number} dx - amount to rotate in x direction\n * @param {Number} dy - amount to rotate in y direction\n * @return {undefined}\n */\n static rotateDrag (stage: Stage, dx: number, dy: number) {\n stage.trackballControls.rotate(dx, dy)\n }\n\n /**\n * Rotate scene around z axis based on mouse coordinate changes\n * @param {Stage} stage - the stage\n * @param {Number} dx - amount to rotate in x direction\n * @param {Number} dy - amount to rotate in y direction\n * @return {undefined}\n */\n static zRotateDrag (stage: Stage, dx: number, dy: number) {\n stage.trackballControls.zRotate(dx, dy)\n }\n\n /**\n * Zoom scene based on mouse coordinate changes\n * @param {Stage} stage - the stage\n * @param {Number} dx - amount to zoom\n * @param {Number} dy - amount to zoom\n * @return {undefined}\n */\n static zoomDrag (stage: Stage, dx: number, dy: number) {\n stage.trackballControls.zoom((dx + dy) / -2)\n }\n\n /**\n * Zoom scene based on mouse coordinate changes and\n * move focus planes based on camera position (zoom)\n * @param {Stage} stage - the stage\n * @param {Number} dx - amount to zoom and focus\n * @param {Number} dy - amount to zoom and focus\n * @return {undefined}\n */\n static zoomFocusDrag (stage: Stage, dx: number, dy: number) {\n stage.trackballControls.zoom((dx + dy) / -2)\n const z = stage.viewer.camera.position.z\n stage.setFocus(100 - Math.abs(z / 8))\n }\n\n /**\n * Pan picked component based on mouse coordinate changes\n * @param {Stage} stage - the stage\n * @param {Number} dx - amount to pan in x direction\n * @param {Number} dy - amount to pan in y direction\n * @return {undefined}\n */\n static panComponentDrag (stage: Stage, dx: number, dy: number) {\n stage.trackballControls.panComponent(dx, dy)\n }\n\n /**\n * Pan picked atom based on mouse coordinate changes\n * @param {Stage} stage - the stage\n * @param {Number} dx - amount to pan in x direction\n * @param {Number} dy - amount to pan in y direction\n * @return {undefined}\n */\n static panAtomDrag (stage: Stage, dx: number, dy: number) {\n stage.trackballControls.panAtom(dx, dy)\n }\n\n /**\n * Rotate picked component based on mouse coordinate changes\n * @param {Stage} stage - the stage\n * @param {Number} dx - amount to rotate in x direction\n * @param {Number} dy - amount to rotate in y direction\n * @return {undefined}\n */\n static rotateComponentDrag (stage: Stage, dx: number, dy: number) {\n stage.trackballControls.rotateComponent(dx, dy)\n }\n\n /**\n * Move picked element to the center of the screen\n * @param {Stage} stage - the stage\n * @param {PickingProxy} pickingProxy - the picking data object\n * @return {undefined}\n */\n static movePick (stage: Stage, pickingProxy: PickingProxy) {\n if (pickingProxy) {\n stage.animationControls.move(pickingProxy.position.clone())\n }\n }\n\n /**\n * Show tooltip with information of picked element\n * @param {Stage} stage - the stage\n * @param {PickingProxy} pickingProxy - the picking data object\n * @return {undefined}\n */\n static tooltipPick (stage: Stage, pickingProxy: PickingProxy) {\n const tt = stage.tooltip\n const sp = stage.getParameters() as any\n if (sp.tooltip && pickingProxy) {\n const mp = pickingProxy.mouse.position\n tt.innerText = pickingProxy.getLabel()\n tt.style.bottom = (window.innerHeight - mp.y + 3) + 'px'\n tt.style.left = (mp.x + 3) + 'px'\n tt.style.display = 'block'\n } else {\n tt.style.display = 'none'\n }\n }\n\n static measurePick (stage: Stage, pickingProxy: PickingProxy) {\n if (pickingProxy && (pickingProxy.atom || pickingProxy.bond)) {\n const atom = pickingProxy.atom || pickingProxy.closestBondAtom\n const sc = pickingProxy.component as StructureComponent\n sc.measurePick(atom)\n } else {\n stage.measureClear()\n }\n }\n}\n\ntype MouseActionPreset = [ string, MouseActionCallback ][]\nexport const MouseActionPresets = {\n default: [\n [ 'scroll', MouseActions.zoomScroll ],\n [ 'scroll-shift', MouseActions.focusScroll ],\n [ 'scroll-ctrl', MouseActions.isolevelScroll ],\n [ 'scroll-shift-ctrl', MouseActions.zoomFocusScroll ],\n\n [ 'drag-left', MouseActions.rotateDrag ],\n [ 'drag-right', MouseActions.panDrag ],\n [ 'drag-ctrl-left', MouseActions.panDrag ],\n [ 'drag-ctrl-right', MouseActions.zRotateDrag ],\n [ 'drag-shift-left', MouseActions.zoomDrag ],\n [ 'drag-middle', MouseActions.zoomFocusDrag ],\n\n [ 'drag-ctrl-shift-right', MouseActions.panComponentDrag ],\n [ 'drag-ctrl-shift-left', MouseActions.rotateComponentDrag ],\n\n [ 'clickPick-right', MouseActions.measurePick ],\n [ 'clickPick-ctrl-left', MouseActions.measurePick ],\n [ 'clickPick-middle', MouseActions.movePick ],\n [ 'clickPick-left', MouseActions.movePick ],\n [ 'hoverPick', MouseActions.tooltipPick ]\n ] as MouseActionPreset,\n pymol: [\n [ 'drag-left', MouseActions.rotateDrag ],\n [ 'drag-middle', MouseActions.panDrag ],\n [ 'drag-right', MouseActions.zoomDrag ],\n [ 'scroll', MouseActions.focusScroll ],\n [ 'drag-shift-right', MouseActions.focusScroll ],\n\n [ 'clickPick-ctrl+shift-middle', MouseActions.movePick ],\n [ 'hoverPick', MouseActions.tooltipPick ]\n ] as MouseActionPreset,\n coot: [\n [ 'scroll', MouseActions.isolevelScroll ],\n\n [ 'drag-left', MouseActions.rotateDrag ],\n [ 'drag-middle', MouseActions.panDrag ],\n [ 'drag-ctrl-left', MouseActions.panDrag ],\n [ 'drag-right', MouseActions.zoomFocusDrag ],\n [ 'drag-ctrl-right', MouseActions.focusScroll ],\n\n [ 'clickPick-middle', MouseActions.movePick ],\n [ 'hoverPick', MouseActions.tooltipPick ]\n ] as MouseActionPreset,\n astexviewer: [\n [ 'drag-left', MouseActions.rotateDrag ],\n [ 'drag-ctrl-left', MouseActions.panDrag ],\n [ 'drag-shift-left', MouseActions.zoomDrag ],\n [ 'scroll', MouseActions.focusScroll ],\n [ 'clickPick-middle', MouseActions.movePick ],\n [ 'hoverPick', MouseActions.tooltipPick ]\n ] as MouseActionPreset\n}\n\nexport default MouseActions\n","/**\n * @file Mouse Controls\n * @author Alexander Rose \n * @private\n */\n\nimport { MouseActionPresets, MouseActionCallback } from './mouse-actions'\nimport Stage from '../stage/stage'\nimport MouseObserver from '../stage/mouse-observer'\n\nexport type MouseControlPreset = keyof typeof MouseActionPresets\nexport interface MouseControlsParams {\n preset?: MouseControlPreset\n disabled?: boolean\n}\n\nexport type MouseActionType = ''|'scroll'|'drag'|'click'|'doubleClick'|'hover'|'clickPick'|'hoverPick'\nexport interface MouseAction {\n type: MouseActionType\n key: number\n button: number\n callback: MouseActionCallback\n}\n\n/**\n * Strings to describe mouse events (including optional keyboard modifiers).\n * Must contain an event type: \"scroll\", \"drag\", \"click\", \"doubleClick\",\n * \"hover\", \"clickPick\" or \"hoverPick\". Optionally contain one or more\n * (seperated by plus signs) keyboard modifiers: \"alt\", \"ctrl\", \"meta\" or\n * \"shift\". Can contain the mouse button performing the event: \"left\",\n * \"middle\" or \"right\". The type, key and button parts must be seperated by\n * dashes.\n *\n * @example\n * // triggered on scroll event (no key or button)\n * \"scroll\"\n *\n * @example\n * // triggered on scroll event while shift key is pressed\n * \"scroll-shift\"\n *\n * @example\n * // triggered on drag event with left mouse button\n * \"drag-left\"\n *\n * @example\n * // triggered on drag event with right mouse button\n * // while ctrl and shift keys are pressed\n * \"drag-right-ctrl+shift\"\n *\n * @typedef {String} TriggerString\n */\n\n/**\n * Get event type, key and button\n * @param {TriggerString} str - input trigger string\n * @return {Array} event type, key and button\n */\nfunction triggerFromString (str: string) {\n const tokens = str.split(/[-+]/)\n\n let type = ''\n if (tokens.includes('scroll')) type = 'scroll'\n if (tokens.includes('drag')) type = 'drag'\n if (tokens.includes('click')) type = 'click'\n if (tokens.includes('doubleClick')) type = 'doubleClick'\n if (tokens.includes('hover')) type = 'hover'\n if (tokens.includes('clickPick')) type = 'clickPick'\n if (tokens.includes('hoverPick')) type = 'hoverPick'\n\n let key = 0\n if (tokens.includes('alt')) key += 1\n if (tokens.includes('ctrl')) key += 2\n if (tokens.includes('meta')) key += 4\n if (tokens.includes('shift')) key += 8\n\n let button = 0\n if (tokens.includes('left')) button += 1\n if (tokens.includes('right')) button += 2\n if (tokens.includes('middle')) button += 4\n\n return [ type, key, button ] as [ MouseActionType, number, number ]\n}\n\n/**\n * Mouse controls\n */\nclass MouseControls {\n actionList: MouseAction[] = []\n mouse: MouseObserver\n\n disabled: boolean // Flag to disable all actions\n\n /**\n * @param {Stage} stage - the stage object\n * @param {Object} [params] - the parameters\n * @param {String} params.preset - one of \"default\", \"pymol\", \"coot\"\n * @param {String} params.disabled - flag to disable all actions\n */\n constructor (readonly stage: Stage, params: MouseControlsParams = {}) {\n this.mouse = stage.mouseObserver\n this.disabled = params.disabled || false\n this.preset(params.preset || 'default')\n }\n\n run (type: MouseActionType, ...args: any[]) {\n if (this.disabled) return\n\n const key = this.mouse.key || 0\n const button = this.mouse.buttons || 0\n\n this.actionList.forEach(a => {\n if (a.type === type && a.key === key && a.button === button) {\n (a.callback as any)(this.stage, ...args) // TODO\n }\n })\n }\n\n /**\n * Add a new mouse action triggered by an event, key and button combination.\n * The {@link MouseActions} class provides a number of static methods for\n * use as callback functions.\n *\n * @example\n * // change ambient light intensity on mouse scroll\n * // while the ctrl and shift keys are pressed\n * stage.mouseControls.add( \"scroll-ctrl+shift\", function( stage, delta ){\n * var ai = stage.getParameters().ambientIntensity;\n * stage.setParameters( { ambientIntensity: Math.max( 0, ai + delta / 50 ) } );\n * } );\n *\n * @example\n * // Call the MouseActions.zoomDrag method on mouse drag events\n * // with left and right mouse buttons simultaneous\n * stage.mouseControls.add( \"drag-left+right\", MouseActions.zoomDrag );\n *\n * @param {TriggerString} triggerStr - the trigger for the action\n * @param {function(stage: Stage, ...args: Any)} callback - the callback function for the action\n * @return {undefined}\n */\n add (triggerStr: string, callback: MouseActionCallback) {\n const [ type, key, button ] = triggerFromString(triggerStr)\n\n this.actionList.push({ type, key, button, callback })\n }\n\n /**\n * Remove a mouse action. The trigger string can contain an asterix (*)\n * as a wildcard for any key or mouse button. When the callback function\n * is given, only actions that call that function are removed.\n *\n * @example\n * // remove actions triggered solely by a scroll event\n * stage.mouseControls.remove( \"scroll\" );\n *\n * @example\n * // remove actions triggered by a scroll event, including\n * // those requiring a key pressed or mouse button used\n * stage.mouseControls.remove( \"scroll-*\" );\n *\n * @example\n * // remove actions triggered by a scroll event\n * // while the shift key is pressed\n * stage.mouseControls.remove( \"scroll-shift\" );\n *\n * @param {TriggerString} triggerStr - the trigger for the action\n * @param {Function} [callback] - the callback function for the action\n * @return {undefined}\n */\n remove (triggerStr: string, callback?: MouseActionCallback) {\n const wildcard = triggerStr.includes('*')\n const [ type, key, button ] = triggerFromString(triggerStr)\n\n const actionList = this.actionList.filter(function (a) {\n return !(\n (a.type === type || (wildcard && type === '')) &&\n (a.key === key || (wildcard && key === 0)) &&\n (a.button === button || (wildcard && button === 0)) &&\n (a.callback === callback || callback === undefined)\n )\n })\n\n this.actionList = actionList\n }\n\n /**\n * Set mouse action preset\n * @param {String} name - one of \"default\", \"pymol\", \"coot\"\n * @return {undefined}\n */\n preset (name: MouseControlPreset) {\n this.clear()\n\n const list = MouseActionPresets[ name ] || []\n\n list.forEach(action => this.add(action[0], action[1]))\n }\n\n /**\n * Remove all mouse actions\n * @return {undefined}\n */\n clear () {\n this.actionList.length = 0\n }\n}\n\nexport default MouseControls\n","/**\n * @file Key Actions\n * @author Alexander Rose \n * @private\n */\n\nimport Stage from '../stage/stage'\n\nexport type KeyActionCallback = (stage: Stage) => void\n\n/**\n * Key actions provided as static methods\n */\nclass KeyActions {\n /**\n * Stage auto view\n */\n static autoView (stage: Stage) {\n stage.autoView(1000)\n }\n\n /**\n * Toggle stage animations\n */\n static toggleAnimations (stage: Stage) {\n stage.animationControls.toggle()\n }\n\n /**\n * Toggle stage rocking\n */\n static toggleRock (stage: Stage) {\n stage.toggleRock()\n }\n\n /**\n * Toggle stage spinning\n */\n static toggleSpin (stage: Stage) {\n stage.toggleSpin()\n }\n\n /**\n * Toggle anti-aliasing\n */\n static toggleAntialiasing (stage: Stage) {\n const p = stage.getParameters()\n stage.setParameters({ sampleLevel: p.sampleLevel === -1 ? 0 : -1 })\n }\n}\n\ntype KeyActionPreset = [ string, KeyActionCallback ][]\nexport const KeyActionPresets = {\n default: [\n [ 'i', KeyActions.toggleSpin ],\n [ 'k', KeyActions.toggleRock ],\n [ 'p', KeyActions.toggleAnimations ],\n [ 'a', KeyActions.toggleAntialiasing ],\n [ 'r', KeyActions.autoView ]\n ] as KeyActionPreset\n}\n\nexport default KeyActions\n","/**\n * @file Key Controls\n * @author Alexander Rose \n * @private\n */\n\nimport { KeyActionPresets, KeyActionCallback } from './key-actions'\nimport Stage from '../stage/stage'\n\nexport type KeyControlPreset = keyof typeof KeyActionPresets\nexport interface KeyControlsParams {\n preset?: KeyControlPreset\n disabled?: boolean\n}\n\nexport interface KeyAction {\n key: string,\n callback: KeyActionCallback\n}\n\n/**\n * Mouse controls\n */\nclass KeyControls {\n actionList: KeyAction[] = []\n\n disabled: boolean // Flag to disable all actions\n\n /**\n * @param {Stage} stage - the stage object\n * @param {Object} [params] - the parameters\n * @param {String} params.preset - one of \"default\"\n * @param {String} params.disabled - flag to disable all actions\n */\n constructor (readonly stage: Stage, params: KeyControlsParams = {}) {\n this.disabled = params.disabled || false\n this.preset(params.preset || 'default')\n }\n\n run (key: string) {\n if (this.disabled) return\n\n this.actionList.forEach(a => {\n if (a.key === key) {\n a.callback(this.stage)\n }\n })\n }\n\n /**\n * Add a key action triggered by pressing the given character.\n * The {@link KeyActions} class provides a number of static methods for\n * use as callback functions.\n *\n * @example\n * // call KeyActions.toggleRock when \"k\" is pressed\n * stage.keyControls.remove( \"k\", KeyActions.toggleRock );\n *\n * @param {Char} char - the key/character\n * @param {Function} callback - the callback function for the action\n * @return {undefined}\n */\n add (char: string, callback: KeyActionCallback) {\n this.actionList.push({ key: char, callback })\n }\n\n /**\n * Remove a key action. When the callback function\n * is given, only actions that call that function are removed.\n *\n * @example\n * // remove all actions triggered by pressing \"k\"\n * stage.keyControls.remove( \"k\" );\n *\n * @example\n * // remove action `toggleRock` triggered by pressing \"k\"\n * stage.keyControls.remove( \"k\", toggleRock );\n *\n * @param {Char} char - the key/character\n * @param {Function} [callback] - the callback function for the action\n * @return {undefined}\n */\n remove (char: string, callback: KeyActionCallback) {\n\n const actionList = this.actionList.filter(function (a) {\n return !(\n (a.key === char) &&\n (a.callback === callback || callback === undefined)\n )\n })\n\n this.actionList = actionList\n }\n\n /**\n * Set key action preset\n * @param {String} name - one of \"default\"\n * @return {undefined}\n */\n preset (name: KeyControlPreset) {\n this.clear()\n\n const list = KeyActionPresets[ name ] || []\n\n list.forEach(action => this.add(action[0], action[1]))\n }\n\n /**\n * Remove all key actions\n * @return {undefined}\n */\n clear () {\n this.actionList.length = 0\n }\n}\n\nexport default KeyControls\n","/**\n * @file Picking Behavior\n * @author Alexander Rose \n * @private\n */\n\nimport Stage from './stage'\nimport MouseObserver from './mouse-observer'\nimport Viewer from '../viewer/viewer'\nimport MouseControls from '../controls/mouse-controls'\n\nclass PickingBehavior {\n viewer: Viewer\n mouse: MouseObserver\n controls: MouseControls\n\n constructor (readonly stage: Stage) {\n this.stage = stage\n this.mouse = stage.mouseObserver\n this.controls = stage.mouseControls\n\n this.mouse.signals.clicked.add(this._onClick, this)\n this.mouse.signals.hovered.add(this._onHover, this)\n }\n\n _onClick (x: number, y: number) {\n const pickingProxy = this.stage.pickingControls.pick(x, y)\n this.stage.signals.clicked.dispatch(pickingProxy)\n this.controls.run('clickPick', pickingProxy)\n }\n\n _onHover (x: number, y: number) {\n const pickingProxy = this.stage.pickingControls.pick(x, y)\n if (pickingProxy && this.mouse.down.equals(this.mouse.position)) {\n this.stage.transformComponent = pickingProxy.component\n this.stage.transformAtom = pickingProxy.atom\n }\n this.stage.signals.hovered.dispatch(pickingProxy)\n this.controls.run('hoverPick', pickingProxy)\n }\n\n dispose () {\n this.mouse.signals.clicked.remove(this._onClick, this)\n this.mouse.signals.hovered.remove(this._onHover, this)\n }\n}\n\nexport default PickingBehavior\n","/**\n * @file Mouse Behavior\n * @author Alexander Rose \n * @private\n */\n\nimport Stage from './stage'\nimport MouseObserver from './mouse-observer'\nimport Viewer from '../viewer/viewer'\nimport MouseControls from '../controls/mouse-controls'\n\nclass MouseBehavior {\n viewer: Viewer\n mouse: MouseObserver\n controls: MouseControls\n domElement: HTMLCanvasElement\n\n constructor (readonly stage: Stage) {\n this.stage = stage\n this.mouse = stage.mouseObserver\n this.controls = stage.mouseControls\n\n this.mouse.signals.moved.add(this._onMove, this)\n this.mouse.signals.scrolled.add(this._onScroll, this)\n this.mouse.signals.dragged.add(this._onDrag, this)\n this.mouse.signals.clicked.add(this._onClick, this)\n this.mouse.signals.hovered.add(this._onHover, this)\n this.mouse.signals.doubleClicked.add(this._onDblclick, this)\n }\n\n _onMove (/* x, y */) {\n this.stage.tooltip.style.display = 'none'\n }\n\n _onScroll (delta: number) {\n this.controls.run('scroll', delta)\n }\n\n _onDrag (dx: number, dy: number) {\n this.controls.run('drag', dx, dy)\n }\n\n _onClick (x: number, y: number) {\n this.controls.run('click', x, y)\n }\n\n _onDblclick (x: number, y: number) {\n this.controls.run('doubleClick', x, y)\n }\n\n _onHover (x: number, y: number) {\n this.controls.run('hover', x, y)\n }\n\n dispose () {\n this.mouse.signals.moved.remove(this._onMove, this)\n this.mouse.signals.scrolled.remove(this._onScroll, this)\n this.mouse.signals.dragged.remove(this._onDrag, this)\n this.mouse.signals.clicked.remove(this._onClick, this)\n this.mouse.signals.hovered.remove(this._onHover, this)\n }\n}\n\nexport default MouseBehavior\n","/**\n * @file Animation Behavior\n * @author Alexander Rose \n * @private\n */\n\nimport Stage from './stage'\nimport Viewer from '../viewer/viewer'\nimport Stats from '../viewer/stats'\nimport AnimationControls from '../controls/animation-controls'\n\nclass AnimationBehavior {\n viewer: Viewer\n animationControls: AnimationControls\n\n constructor (readonly stage: Stage) {\n this.viewer = stage.viewer\n this.animationControls = stage.animationControls\n\n this.viewer.signals.ticked.add(this._onTick, this)\n }\n\n _onTick (stats: Stats) {\n this.animationControls.run(stats)\n }\n\n dispose () {\n this.viewer.signals.ticked.remove(this._onTick, this)\n }\n}\n\nexport default AnimationBehavior\n","/**\n * @file Key Behavior\n * @author Alexander Rose \n * @private\n */\n\nimport { SupportsPassiveEventHandler } from '../globals'\nimport Stage from './stage'\nimport Viewer from '../viewer/viewer'\nimport KeyControls from '../controls/key-controls'\n\nconst passive = SupportsPassiveEventHandler ? { passive: true } : false\n\nclass KeyBehavior {\n viewer: Viewer\n controls: KeyControls\n domElement: HTMLCanvasElement\n\n /**\n * @param {Stage} stage - the stage object\n */\n constructor (readonly stage: Stage) {\n this.stage = stage\n this.controls = stage.keyControls\n this.domElement = stage.viewer.renderer.domElement\n\n // ensure the domElement is focusable\n this.domElement.setAttribute('tabIndex', '-1')\n this.domElement.style.outline = 'none'\n\n this._focusDomElement = this._focusDomElement.bind(this)\n this._onKeydown = this._onKeydown.bind(this)\n this._onKeyup = this._onKeyup.bind(this)\n this._onKeypress = this._onKeypress.bind(this)\n\n this.domElement.addEventListener('mousedown', this._focusDomElement)\n this.domElement.addEventListener('touchstart', this._focusDomElement, passive as any) // TODO\n this.domElement.addEventListener('keydown', this._onKeydown)\n this.domElement.addEventListener('keyup', this._onKeyup)\n this.domElement.addEventListener('keypress', this._onKeypress)\n }\n\n /**\n * handle key down\n * @param {Event} event - key event\n * @return {undefined}\n */\n _onKeydown (/* event */) {\n // console.log( \"down\", event.keyCode, String.fromCharCode( event.keyCode ) );\n }\n\n /**\n * handle key up\n * @param {Event} event - key event\n * @return {undefined}\n */\n _onKeyup (/* event */) {\n // console.log( \"up\", event.keyCode, String.fromCharCode( event.keyCode ) );\n }\n\n /**\n * handle key press\n * @param {Event} event - key event\n * @return {undefined}\n */\n _onKeypress (event: KeyboardEvent) {\n // console.log( \"press\", event.keyCode, String.fromCharCode( event.keyCode ) );\n let pressedKey: string;\n if (\"key\" in KeyboardEvent.prototype) {\n pressedKey = event.key;\n // some mobile browsers don't support this attribute\n } else {\n pressedKey = String.fromCharCode(event.which || event.keyCode);\n }\n this.controls.run(pressedKey);\n }\n\n _focusDomElement () {\n this.domElement.focus()\n }\n\n dispose () {\n this.domElement.removeEventListener('mousedown', this._focusDomElement)\n this.domElement.removeEventListener('touchstart', this._focusDomElement, passive as any) // TODO\n this.domElement.removeEventListener('keydown', this._onKeypress)\n this.domElement.removeEventListener('keyup', this._onKeypress)\n this.domElement.removeEventListener('keypress', this._onKeypress)\n }\n}\n\nexport default KeyBehavior\n","/**\n * @file Annotation\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector2, Vector3 } from 'three'\n\nimport { defaults } from '../utils'\nimport { smoothstep } from '../math/math-utils'\nimport Stage from '../stage/stage'\nimport Viewer from '../viewer/viewer'\nimport Component from './component'\n\nexport interface AnnotationParams {\n offsetX?: number\n offsetY?: number\n visible?: boolean\n}\n\n/**\n * Annotation HTML element floating on top of a position rendered in 3d\n */\nexport default class Annotation {\n offsetX: number\n offsetY: number\n visible: boolean\n\n stage: Stage\n viewer: Viewer\n element: HTMLElement\n\n private _viewerPosition: Vector3\n private _canvasPosition: Vector2\n private _cameraPosition: Vector3\n private _clientRect: ClientRect\n\n /**\n * @param {Component} component - the associated component\n * @param {Vector3} position - position in 3d\n * @param {String|Element} content - HTML content\n * @param {Object} [params] - parameters\n * @param {Integer} params.offsetX - 2d offset in x direction\n * @param {Integer} params.offsetY - 2d offset in y direction\n * @param {Boolean} params.visible - visibility flag\n */\n constructor (readonly component: Component, readonly position: Vector3, content: string|HTMLElement, params: AnnotationParams = {}) {\n this.offsetX = defaults(params.offsetX, 0)\n this.offsetY = defaults(params.offsetY, 0)\n this.visible = defaults(params.visible, true)\n\n this.stage = component.stage\n this.viewer = component.stage.viewer\n\n this._viewerPosition = new Vector3()\n this._updateViewerPosition()\n this._canvasPosition = new Vector2()\n this._cameraPosition = new Vector3()\n\n this.element = document.createElement('div')\n Object.assign(this.element.style, {\n display: 'block',\n position: 'absolute',\n pointerEvents: 'none',\n whiteSpace: 'nowrap',\n left: '-10000px'\n })\n\n this.viewer.wrapper.appendChild(this.element)\n this.setContent(content)\n this.updateVisibility()\n this.viewer.signals.rendered.add(this._update, this)\n this.component.signals.matrixChanged.add(this._updateViewerPosition, this)\n }\n\n /**\n * Set HTML content of the annotation\n * @param {String|Element} value - HTML content\n * @return {undefined}\n */\n setContent (value: string|HTMLElement) {\n const displayValue = this.element.style.display\n if (displayValue === 'none') {\n this.element.style.left = '-10000px'\n this.element.style.display = 'block'\n }\n\n if (value instanceof HTMLElement) {\n this.element.appendChild(value)\n } else {\n const content = document.createElement('div')\n content.innerText = value\n Object.assign(content.style, {\n backgroundColor: 'rgba( 0, 0, 0, 0.6 )',\n color: 'lightgrey',\n padding: '8px',\n fontFamily: 'sans-serif',\n })\n this.element.appendChild(content)\n }\n\n this._clientRect = this.element.getBoundingClientRect()\n\n if (displayValue === 'none') {\n this.element.style.display = displayValue\n }\n }\n\n /**\n * Set visibility of the annotation\n * @param {Boolean} value - visibility flag\n * @return {undefined}\n */\n setVisibility (value: boolean) {\n this.visible = value\n this.updateVisibility()\n }\n\n getVisibility () {\n return this.visible && this.component.parameters.visible\n }\n\n updateVisibility () {\n this.element.style.display = this.getVisibility() ? 'block' : 'none'\n }\n\n _updateViewerPosition () {\n this._viewerPosition\n .copy(this.position)\n .applyMatrix4(this.component.matrix)\n }\n\n _update () {\n if (!this.getVisibility()) return\n\n const s = this.element.style\n const cp = this._canvasPosition\n const vp = this._viewerPosition\n const cr = this._clientRect\n\n this._cameraPosition.copy(vp)\n .add(this.viewer.translationGroup.position)\n .applyMatrix4(this.viewer.rotationGroup.matrix)\n .sub(this.viewer.camera.position)\n\n if (this._cameraPosition.z < 0) {\n s.display = 'none'\n return\n } else {\n s.display = 'block'\n }\n\n const depth = this._cameraPosition.length()\n const fog = this.viewer.scene.fog as any // TODO\n\n s.opacity = (1 - smoothstep(fog.near, fog.far, depth)).toString()\n s.zIndex = (Math.round((fog.far - depth) * 100)).toString()\n\n this.stage.viewerControls.getPositionOnCanvas(vp, cp)\n\n s.bottom = (this.offsetX + cp.y + cr.height / 2) + 'px'\n s.left = (this.offsetY + cp.x - cr.width / 2) + 'px'\n }\n\n /**\n * Safely remove the annotation\n * @return {undefined}\n */\n dispose () {\n this.viewer.wrapper.removeChild(this.element)\n this.viewer.signals.ticked.remove(this._update, this)\n this.component.signals.matrixChanged.remove(this._updateViewerPosition, this)\n }\n}","/**\n * @file Component Controls\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3, Matrix4, Quaternion } from 'three'\nimport * as signalsWrapper from 'signals'\n\nimport { ensureVector3 } from '../utils'\nimport Component from '../component/component'\nimport Stage from '../stage/stage'\nimport Viewer from '../viewer/viewer'\n\nconst tmpRotateMatrix = new Matrix4()\nconst tmpRotateVector = new Vector3()\nconst tmpRotateQuaternion = new Quaternion()\n\n/**\n * Component controls\n */\nclass ComponentControls {\n signals = {\n changed: new signalsWrapper.Signal()\n }\n\n stage: Stage\n viewer: Viewer\n\n /**\n * @param {Component} component - the component object\n */\n constructor (readonly component: Component) {\n this.stage = component.stage\n this.viewer = component.stage.viewer\n }\n\n /**\n * component center position\n * @type {Vector3}\n */\n get position () {\n return this.component.position\n }\n\n /**\n * component rotation\n * @type {Quaternion}\n */\n get rotation () {\n return this.component.quaternion\n }\n\n /**\n * Trigger render and emit changed event\n * @emits {ComponentControls.signals.changed}\n * @return {undefined}\n */\n changed () {\n this.component.updateMatrix()\n this.viewer.requestRender()\n this.signals.changed.dispatch()\n }\n\n /**\n * spin component on axis\n * @param {Vector3|Array} axis - rotation axis\n * @param {Number} angle - amount to spin\n * @return {undefined}\n */\n spin (axis: Vector3, angle: number) {\n tmpRotateMatrix.copy(this.viewer.rotationGroup.matrix).invert()\n tmpRotateVector\n .copy(ensureVector3(axis)).applyMatrix4(tmpRotateMatrix)\n\n tmpRotateMatrix.extractRotation(this.component.transform)\n tmpRotateMatrix.premultiply(this.viewer.rotationGroup.matrix)\n tmpRotateMatrix.invert()\n\n tmpRotateVector.copy(ensureVector3(axis))\n tmpRotateVector.applyMatrix4(tmpRotateMatrix)\n tmpRotateMatrix.makeRotationAxis(tmpRotateVector, angle)\n tmpRotateQuaternion.setFromRotationMatrix(tmpRotateMatrix)\n\n this.component.quaternion.premultiply(tmpRotateQuaternion)\n this.changed()\n }\n}\n\nexport default ComponentControls\n","/**\n * @file Radius Factory\n * @author Alexander Rose \n * @private\n */\n\nimport { defaults } from '../utils'\nimport { NucleicBackboneAtoms } from '../structure/structure-constants'\nimport AtomProxy from '../proxy/atom-proxy'\n\nexport const RadiusFactoryTypes = {\n '': '',\n 'vdw': 'by vdW radius',\n 'covalent': 'by covalent radius',\n 'sstruc': 'by secondary structure',\n 'bfactor': 'by bfactor',\n 'size': 'size',\n 'data': 'data',\n 'explicit' : 'explicit'\n}\nexport type RadiusType = keyof typeof RadiusFactoryTypes\n\nexport interface RadiusParams {\n type?: RadiusType\n scale?: number\n size?: number\n data?: { [k: number]: number }\n}\n\nclass RadiusFactory {\n max = 10\n\n static types = RadiusFactoryTypes\n\n readonly type: RadiusType\n readonly scale: number\n readonly size: number\n readonly data: { [k: number]: number }\n\n constructor (params: RadiusParams = {}) {\n this.type = defaults(params.type, 'size')\n this.scale = defaults(params.scale, 1)\n this.size = defaults(params.size, 1)\n this.data = defaults(params.data, {})\n }\n\n atomRadius (a: AtomProxy) {\n let r\n\n switch (this.type) {\n case 'vdw':\n r = a.vdw\n break\n\n case 'covalent':\n r = a.covalent\n break\n\n case 'bfactor':\n r = a.bfactor || 1.0\n break\n\n case 'sstruc':\n const sstruc = a.sstruc\n if (sstruc === 'h') {\n r = 0.25\n } else if (sstruc === 'g') {\n r = 0.25\n } else if (sstruc === 'i') {\n r = 0.25\n } else if (sstruc === 'e') {\n r = 0.25\n } else if (sstruc === 'b') {\n r = 0.25\n } else if (NucleicBackboneAtoms.includes(a.atomname)) {\n r = 0.4\n } else {\n r = 0.1\n }\n break\n\n case 'data':\n r = defaults(this.data[ a.index ], 1.0)\n break\n\n case 'explicit':\n // defaults is inappropriate as AtomProxy.radius returns\n // null for missing radii\n r = a.radius\n if (r === null) r = this.size\n break\n\n default:\n r = this.size\n break\n }\n\n return Math.min(r * this.scale, this.max)\n }\n\n}\n\nexport default RadiusFactory\n","/**\n * @file Principal Axes\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3, Matrix4, Quaternion } from 'three'\n\nimport {\n Matrix, meanRows, subRows, transpose, multiplyABt, svd\n} from './matrix-utils'\nimport { projectPointOnVector } from './vector-utils'\nimport Structure from '../structure/structure'\nimport AtomProxy from '../proxy/atom-proxy'\n\nconst negateVector = new Vector3(-1, -1, -1)\nconst tmpMatrix = new Matrix4()\n\n/**\n * Principal axes\n */\nclass PrincipalAxes {\n begA: Vector3\n endA: Vector3\n begB: Vector3\n endB: Vector3\n begC: Vector3\n endC: Vector3\n\n center: Vector3\n\n vecA: Vector3\n vecB: Vector3\n vecC: Vector3\n\n normVecA: Vector3\n normVecB: Vector3\n normVecC: Vector3\n\n /**\n * @param {Matrix} points - 3 by N matrix\n */\n constructor (points: Matrix) {\n // console.time( \"PrincipalAxes\" );\n\n const n = points.rows\n const n3 = n / 3\n const pointsT = new Matrix(n, 3)\n const A = new Matrix(3, 3)\n const W = new Matrix(1, 3)\n const U = new Matrix(3, 3)\n const V = new Matrix(3, 3)\n\n // calculate\n const mean = meanRows(points)\n subRows(points, mean)\n transpose(pointsT, points)\n multiplyABt(A, pointsT, pointsT)\n svd(A, W, U, V)\n\n // console.log( points, pointsT, mean )\n // console.log( n, A, W, U, V );\n\n // center\n const vm = new Vector3(mean[0], mean[1], mean[2])\n\n // normalized\n const van = new Vector3(U.data[0], U.data[3], U.data[6])\n const vbn = new Vector3(U.data[1], U.data[4], U.data[7])\n const vcn = new Vector3(U.data[2], U.data[5], U.data[8])\n\n // scaled\n const va = van.clone().multiplyScalar(Math.sqrt(W.data[0] / n3))\n const vb = vbn.clone().multiplyScalar(Math.sqrt(W.data[1] / n3))\n const vc = vcn.clone().multiplyScalar(Math.sqrt(W.data[2] / n3))\n\n // points\n this.begA = vm.clone().sub(va)\n this.endA = vm.clone().add(va)\n this.begB = vm.clone().sub(vb)\n this.endB = vm.clone().add(vb)\n this.begC = vm.clone().sub(vc)\n this.endC = vm.clone().add(vc)\n\n //\n\n this.center = vm\n\n this.vecA = va\n this.vecB = vb\n this.vecC = vc\n\n this.normVecA = van\n this.normVecB = vbn\n this.normVecC = vcn\n\n // console.timeEnd( \"PrincipalAxes\" );\n }\n\n /**\n * Get the basis matrix descriping the axes\n * @param {Matrix4} [optionalTarget] - target object\n * @return {Matrix4} the basis\n */\n getBasisMatrix (optionalTarget = new Matrix4()) {\n const basis = optionalTarget\n\n basis.makeBasis(this.normVecB, this.normVecA, this.normVecC)\n if (basis.determinant() < 0) {\n basis.scale(negateVector)\n }\n\n return basis\n }\n\n /**\n * Get a quaternion descriping the axes rotation\n * @param {Quaternion} [optionalTarget] - target object\n * @return {Quaternion} the rotation\n */\n getRotationQuaternion (optionalTarget = new Quaternion()) {\n const q = optionalTarget\n q.setFromRotationMatrix(this.getBasisMatrix(tmpMatrix))\n\n return q.invert()\n }\n\n /**\n * Get the scale/length for each dimension for a box around the axes\n * to enclose the atoms of a structure\n * @param {Structure|StructureView} structure - the structure\n * @return {{d1a: Number, d2a: Number, d3a: Number, d1b: Number, d2b: Number, d3b: Number}} scale\n */\n getProjectedScaleForAtoms (structure: Structure) {\n let d1a = -Infinity\n let d1b = -Infinity\n let d2a = -Infinity\n let d2b = -Infinity\n let d3a = -Infinity\n let d3b = -Infinity\n\n const p = new Vector3()\n const t = new Vector3()\n\n const center = this.center\n const ax1 = this.normVecA\n const ax2 = this.normVecB\n const ax3 = this.normVecC\n\n structure.eachAtom(function (ap: AtomProxy) {\n projectPointOnVector(p.copy(ap as any), ax1, center) // TODO\n const dp1 = t.subVectors(p, center).normalize().dot(ax1)\n const dt1 = p.distanceTo(center)\n if (dp1 > 0) {\n if (dt1 > d1a) d1a = dt1\n } else {\n if (dt1 > d1b) d1b = dt1\n }\n\n projectPointOnVector(p.copy(ap as any), ax2, center)\n const dp2 = t.subVectors(p, center).normalize().dot(ax2)\n const dt2 = p.distanceTo(center)\n if (dp2 > 0) {\n if (dt2 > d2a) d2a = dt2\n } else {\n if (dt2 > d2b) d2b = dt2\n }\n\n projectPointOnVector(p.copy(ap as any), ax3, center)\n const dp3 = t.subVectors(p, center).normalize().dot(ax3)\n const dt3 = p.distanceTo(center)\n if (dp3 > 0) {\n if (dt3 > d3a) d3a = dt3\n } else {\n if (dt3 > d3b) d3b = dt3\n }\n })\n\n return {\n d1a: d1a,\n d2a: d2a,\n d3a: d3a,\n d1b: -d1b,\n d2b: -d2b,\n d3b: -d3b\n }\n }\n}\n\nexport default PrincipalAxes\n","/**\n * @file Filtered Volume\n * @author Alexander Rose \n * @private\n */\n\nimport { defaults } from '../utils'\nimport Volume from './volume'\nimport { Box3, Matrix4, Matrix3, Vector3 } from 'three';\n\nclass FilteredVolume {\n volume: Volume\n data: Float32Array\n position: Float32Array\n atomindex: Int32Array\n _filterHash: string\n _dataBuffer: ArrayBuffer\n _positionBuffer: ArrayBuffer\n _atomindexBuffer: ArrayBuffer\n getValueForSigma: typeof Volume.prototype.getValueForSigma\n getSigmaForValue: typeof Volume.prototype.getSigmaForValue\n getDataAtomindex: typeof Volume.prototype.getDataAtomindex\n getDataPosition: typeof Volume.prototype.getDataPosition\n getDataColor: typeof Volume.prototype.getDataColor\n getDataPicking: typeof Volume.prototype.getDataPicking\n getDataSize: typeof Volume.prototype.getDataSize\n\n\n constructor (volume: Volume, minValue?: number, maxValue?: number, outside?: boolean) {\n this.volume = volume\n this.setFilter(minValue, maxValue, outside)\n }\n\n get header () { return this.volume.header }\n get matrix (): Matrix4 { return this.volume.matrix }\n get normalMatrix (): Matrix3 { return this.volume.normalMatrix }\n get inverseMatrix (): Matrix4 { return this.volume.inverseMatrix }\n get center (): Vector3 { return this.volume.center }\n get boundingBox (): Box3 { return this.volume.boundingBox }\n get min () { return this.volume.min }\n get max () { return this.volume.max }\n get mean () { return this.volume.mean }\n get rms () { return this.volume.rms }\n\n _getFilterHash (minValue: number, maxValue: number, outside: boolean) {\n return JSON.stringify([ minValue, maxValue, outside ])\n }\n\n setFilter (minValue: number|undefined, maxValue: number|undefined, outside: boolean|undefined) {\n if (isNaN(minValue) && this.header) {\n minValue = this.header.DMEAN + 2.0 * this.header.ARMS\n }\n\n minValue = (minValue !== undefined && !isNaN(minValue)) ? minValue : -Infinity\n maxValue = defaults(maxValue, Infinity) as number\n outside = defaults(outside, false) as boolean\n\n const data = this.volume.data\n const position = this.volume.position\n const atomindex = this.volume.atomindex\n\n const filterHash = this._getFilterHash(minValue, maxValue, outside)\n\n if (filterHash === this._filterHash) {\n // already filtered\n return\n } else if (minValue === -Infinity && maxValue === Infinity) {\n this.data = data\n this.position = position\n this.atomindex = atomindex!\n } else {\n const n = data.length\n\n if (!this._dataBuffer) {\n // ArrayBuffer for re-use as Float32Array backend\n\n this._dataBuffer = new ArrayBuffer(n * 4)\n this._positionBuffer = new ArrayBuffer(n * 3 * 4)\n if (atomindex) this._atomindexBuffer = new ArrayBuffer(n * 4)\n }\n\n const filteredData = new Float32Array(this._dataBuffer)\n const filteredPosition = new Float32Array(this._positionBuffer)\n let filteredAtomindex\n if (atomindex) filteredAtomindex = new Uint32Array(this._atomindexBuffer)\n\n let j = 0\n\n for (let i = 0; i < n; ++i) {\n const i3 = i * 3\n const v = data[ i ]\n\n if ((!outside && v >= minValue && v <= maxValue) ||\n (outside && (v < minValue || v > maxValue))\n ) {\n const j3 = j * 3\n\n filteredData[ j ] = v\n\n filteredPosition[ j3 + 0 ] = position[ i3 + 0 ]\n filteredPosition[ j3 + 1 ] = position[ i3 + 1 ]\n filteredPosition[ j3 + 2 ] = position[ i3 + 2 ]\n\n if (atomindex && filteredAtomindex) filteredAtomindex[ j ] = atomindex[ i ]\n\n j += 1\n }\n }\n\n // set views\n\n this.data = new Float32Array(this._dataBuffer, 0, j)\n this.position = new Float32Array(this._positionBuffer, 0, j * 3)\n if (atomindex) this.atomindex = new Int32Array(this._atomindexBuffer, 0, j)\n }\n\n this._filterHash = filterHash\n }\n}\n\nFilteredVolume.prototype.getValueForSigma = Volume.prototype.getValueForSigma\nFilteredVolume.prototype.getSigmaForValue = Volume.prototype.getSigmaForValue\n\nFilteredVolume.prototype.getDataAtomindex = Volume.prototype.getDataAtomindex\nFilteredVolume.prototype.getDataPosition = Volume.prototype.getDataPosition\nFilteredVolume.prototype.getDataColor = Volume.prototype.getDataColor\nFilteredVolume.prototype.getDataPicking = Volume.prototype.getDataPicking\nFilteredVolume.prototype.getDataSize = Volume.prototype.getDataSize\n\nexport default FilteredVolume\n","/**\n * @file Bond Hash\n * @author Alexander Rose \n * @private\n */\n\nimport BondStore from './bond-store'\nimport { createAdjacencyList } from '../utils/adjacency-list'\n\nclass BondHash {\n countArray: Uint8Array\n offsetArray: Int32Array\n indexArray: Int32Array\n\n constructor (bondStore: BondStore, atomCount: number) {\n const al = createAdjacencyList({\n nodeArray1: bondStore.atomIndex1,\n nodeArray2: bondStore.atomIndex2,\n edgeCount: bondStore.count,\n nodeCount: atomCount\n })\n\n this.countArray = al.countArray\n this.offsetArray = al.offsetArray\n this.indexArray = al.indexArray\n }\n}\n\nexport default BondHash\n","/**\n * @file Bond Store\n * @author Alexander Rose \n * @private\n */\n\nimport Store, { StoreField } from './store'\nimport AtomProxy from '../proxy/atom-proxy'\n\n/**\n * Bond store\n */\nexport default class BondStore extends Store {\n atomIndex1: Uint32Array\n atomIndex2: Uint32Array\n bondOrder: Uint8Array\n\n get _defaultFields () {\n return [\n [ 'atomIndex1', 1, 'int32' ],\n [ 'atomIndex2', 1, 'int32' ],\n [ 'bondOrder', 1, 'int8' ]\n ] as StoreField[]\n }\n\n addBond (atom1: AtomProxy, atom2: AtomProxy, bondOrder?: number) {\n this.growIfFull()\n\n const i = this.count\n const ai1 = atom1.index\n const ai2 = atom2.index\n\n if (ai1 < ai2) {\n this.atomIndex1[ i ] = ai1\n this.atomIndex2[ i ] = ai2\n } else {\n this.atomIndex2[ i ] = ai1\n this.atomIndex1[ i ] = ai2\n }\n if (bondOrder) this.bondOrder[ i ] = bondOrder\n\n this.count += 1\n }\n\n addBondIfConnected (atom1: AtomProxy, atom2: AtomProxy, bondOrder?: number) {\n if (atom1.connectedTo(atom2)) {\n this.addBond(atom1, atom2, bondOrder)\n return true\n }\n\n return false\n }\n}","/**\n * @file Atom Store\n * @author Alexander Rose \n * @private\n */\n\nimport Store, { StoreField } from './store'\n\n/**\n * Atom store\n */\nexport default class AtomStore extends Store {\n residueIndex: Uint32Array\n atomTypeId: Uint16Array\n\n x: Float32Array\n y: Float32Array\n z: Float32Array\n serial: Int32Array\n bfactor: Float32Array\n altloc: Uint8Array\n occupancy: Float32Array\n\n partialCharge?: Float32Array\n formalCharge?: Int8Array\n\n get _defaultFields () {\n return [\n [ 'residueIndex', 1, 'uint32' ],\n [ 'atomTypeId', 1, 'uint16' ],\n\n [ 'x', 1, 'float32' ],\n [ 'y', 1, 'float32' ],\n [ 'z', 1, 'float32' ],\n [ 'serial', 1, 'int32' ],\n [ 'bfactor', 1, 'float32' ],\n [ 'altloc', 1, 'uint8' ],\n [ 'occupancy', 1, 'float32' ]\n ] as StoreField[]\n }\n\n setAltloc (i: number, str: string) {\n this.altloc[ i ] = str.charCodeAt(0)\n }\n\n getAltloc (i: number) {\n const code = this.altloc[ i ]\n return code ? String.fromCharCode(code) : ''\n }\n}","/**\n * @file Residue Store\n * @author Alexander Rose \n * @private\n */\n\nimport Store, { StoreField } from './store'\n\n/**\n * Residue store\n */\nexport default class ResidueStore extends Store {\n chainIndex: Uint32Array\n atomOffset: Uint32Array\n atomCount: Uint32Array\n residueTypeId: Uint16Array\n\n resno: Uint32Array\n sstruc: Uint8Array\n inscode: Uint8Array\n\n get _defaultFields () {\n return [\n [ 'chainIndex', 1, 'uint32' ],\n [ 'atomOffset', 1, 'uint32' ],\n [ 'atomCount', 1, 'uint32' ],\n [ 'residueTypeId', 1, 'uint16' ],\n\n [ 'resno', 1, 'int32' ],\n [ 'sstruc', 1, 'uint8' ],\n [ 'inscode', 1, 'uint8' ]\n ] as StoreField[]\n }\n\n setSstruc (i: number, str: string) {\n this.sstruc[ i ] = str.charCodeAt(0)\n }\n\n getSstruc (i: number) {\n const code = this.sstruc[ i ]\n return code ? String.fromCharCode(code) : ''\n }\n\n setInscode (i: number, str: string) {\n this.inscode[ i ] = str.charCodeAt(0)\n }\n\n getInscode (i: number) {\n const code = this.inscode[ i ]\n return code ? String.fromCharCode(code) : ''\n }\n}","/**\n * @file Chain Store\n * @author Alexander Rose \n * @private\n */\n\nimport Store, { StoreField } from './store'\n\n/**\n * Chain store\n */\nexport default class ChainStore extends Store {\n entityIndex: Uint16Array\n modelIndex: Uint16Array\n residueOffset: Uint32Array\n residueCount: Uint32Array\n\n chainname: Uint8Array\n chainid: Uint8Array\n\n get _defaultFields () {\n return [\n [ 'entityIndex', 1, 'uint16' ],\n [ 'modelIndex', 1, 'uint16' ],\n [ 'residueOffset', 1, 'uint32' ],\n [ 'residueCount', 1, 'uint32' ],\n\n [ 'chainname', 4, 'uint8' ],\n [ 'chainid', 4, 'uint8' ]\n ] as StoreField[]\n }\n\n setChainname (i: number, str: string) {\n const j = 4 * i\n this.chainname[ j ] = str.charCodeAt(0)\n this.chainname[ j + 1 ] = str.charCodeAt(1)\n this.chainname[ j + 2 ] = str.charCodeAt(2)\n this.chainname[ j + 3 ] = str.charCodeAt(3)\n }\n\n getChainname (i: number) {\n let chainname = ''\n for (let k = 0; k < 4; ++k) {\n const code = this.chainname[ 4 * i + k ]\n if (code) {\n chainname += String.fromCharCode(code)\n } else {\n break\n }\n }\n return chainname\n }\n\n setChainid (i: number, str: string) {\n const j = 4 * i\n this.chainid[ j ] = str.charCodeAt(0)\n this.chainid[ j + 1 ] = str.charCodeAt(1)\n this.chainid[ j + 2 ] = str.charCodeAt(2)\n this.chainid[ j + 3 ] = str.charCodeAt(3)\n }\n\n getChainid (i: number) {\n let chainid = ''\n for (let k = 0; k < 4; ++k) {\n const code = this.chainid[ 4 * i + k ]\n if (code) {\n chainid += String.fromCharCode(code)\n } else {\n break\n }\n }\n return chainid\n }\n}\n","/**\n * @file Model Store\n * @author Alexander Rose \n * @private\n */\n\nimport Store, { StoreField } from './store'\n\n/**\n * Model store\n */\nexport default class ModelStore extends Store {\n\tchainOffset: Uint32Array\n\tchainCount: Uint32Array\n\n get _defaultFields () {\n return [\n [ 'chainOffset', 1, 'uint32' ],\n [ 'chainCount', 1, 'uint32' ]\n ] as StoreField[]\n }\n}","/**\n * @file Helixorient\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3 } from 'three'\n\nimport { ColormakerRegistry } from '../globals'\nimport { ColormakerParameters } from '../color/colormaker'\nimport { AtomPicker } from '../utils/picker'\nimport RadiusFactory, { RadiusParams } from '../utils/radius-factory'\nimport { copyArray } from '../math/array-utils'\nimport { projectPointOnVector } from '../math/vector-utils'\nimport Polymer from '../proxy/polymer'\n\nexport interface HelixIterator {\n size: number\n next: () => Vector3\n get: (idx: number) => Vector3\n reset: () => void\n}\n\nexport interface HelixPosition {\n center: Float32Array\n axis: Float32Array\n bending: Float32Array\n radius: Float32Array\n rise: Float32Array\n twist: Float32Array\n resdir: Float32Array\n}\n\nclass Helixorient {\n size: number\n\n constructor (readonly polymer: Polymer) {\n this.size = polymer.residueCount\n }\n\n getCenterIterator (smooth = 0): HelixIterator {\n const center = this.getPosition().center\n const size = center.length / 3\n\n let i = 0\n let j = -1\n\n const cache = [\n new Vector3(),\n new Vector3(),\n new Vector3(),\n new Vector3()\n ]\n\n function next (this: HelixIterator) {\n const vector = this.get(j)\n j += 1\n return vector\n }\n\n function get (idx: number) {\n idx = Math.min(size - 1, Math.max(0, idx))\n const v = cache[ i % 4 ]\n const idx3 = 3 * idx\n v.fromArray(center as any, idx3) // TODO\n if (smooth) {\n const w = Math.min(smooth, idx, size - idx - 1)\n for (let k = 1; k <= w; ++k) {\n const l = k * 3\n const t = (w + 1 - k) / (w + 1)\n v.x += t * center[ idx3 - l + 0 ] + t * center[ idx3 + l + 0 ]\n v.y += t * center[ idx3 - l + 1 ] + t * center[ idx3 + l + 1 ]\n v.z += t * center[ idx3 - l + 2 ] + t * center[ idx3 + l + 2 ]\n }\n v.x /= w + 1\n v.y /= w + 1\n v.z /= w + 1\n }\n i += 1\n return v\n }\n\n function reset () {\n i = 0\n j = -1\n }\n\n return { size, next, get, reset }\n }\n\n getColor (params: { scheme: string } & ColormakerParameters) {\n const polymer = this.polymer\n const structure = polymer.structure\n const n = polymer.residueCount\n const residueIndexStart = polymer.residueIndexStart\n\n const col = new Float32Array(n * 3)\n\n const p = params || {}\n p.structure = structure\n\n const colormaker = ColormakerRegistry.getScheme(p)\n\n const rp = structure.getResidueProxy()\n const ap = structure.getAtomProxy()\n\n for (let i = 0; i < n; ++i) {\n rp.index = residueIndexStart + i\n ap.index = rp.traceAtomIndex\n\n colormaker.atomColorToArray(ap, col, i * 3)\n }\n\n return {\n 'color': col\n }\n }\n\n getPicking () {\n const polymer = this.polymer\n const structure = polymer.structure\n const n = polymer.residueCount\n const residueIndexStart = polymer.residueIndexStart\n\n const pick = new Float32Array(n)\n const rp = structure.getResidueProxy()\n\n for (let i = 0; i < n; ++i) {\n rp.index = residueIndexStart + i\n pick[ i ] = rp.traceAtomIndex\n }\n\n return {\n 'picking': new AtomPicker(pick, structure)\n }\n }\n\n getSize (params: RadiusParams) {\n const polymer = this.polymer\n const structure = polymer.structure\n const n = polymer.residueCount\n const residueIndexStart = polymer.residueIndexStart\n\n const size = new Float32Array(n)\n const radiusFactory = new RadiusFactory(params)\n\n const rp = structure.getResidueProxy()\n const ap = structure.getAtomProxy()\n\n for (let i = 0; i < n; ++i) {\n rp.index = residueIndexStart + i\n ap.index = rp.traceAtomIndex\n size[ i ] = radiusFactory.atomRadius(ap)\n }\n\n return { size }\n }\n\n getPosition (): HelixPosition {\n const polymer = this.polymer\n const structure = polymer.structure\n const n = polymer.residueCount\n const n3 = n - 3\n\n const center = new Float32Array(3 * n)\n const axis = new Float32Array(3 * n)\n const diff = new Float32Array(n)\n const radius = new Float32Array(n)\n const rise = new Float32Array(n)\n const twist = new Float32Array(n)\n const resdir = new Float32Array(3 * n)\n\n const r12 = new Vector3()\n const r23 = new Vector3()\n const r34 = new Vector3()\n\n const diff13 = new Vector3()\n const diff24 = new Vector3()\n\n const v1 = new Vector3()\n const v2 = new Vector3()\n const vt = new Vector3()\n\n const _axis = new Vector3()\n const _prevAxis = new Vector3()\n\n const _resdir = new Vector3()\n const _center = new Vector3(0, 0, 0)\n\n const type = 'trace'\n const a1 = structure.getAtomProxy()\n const a2 = structure.getAtomProxy(polymer.getAtomIndexByType(0, type))\n const a3 = structure.getAtomProxy(polymer.getAtomIndexByType(1, type))\n const a4 = structure.getAtomProxy(polymer.getAtomIndexByType(2, type))\n\n for (let i = 0; i < n3; ++i) {\n a1.index = a2.index\n a2.index = a3.index\n a3.index = a4.index\n a4.index = polymer.getAtomIndexByType(i + 3, type)! // TODO\n\n const j = 3 * i\n\n // ported from GROMACS src/tools/gmx_helixorient.c\n\n r12.subVectors(a2 as any, a1 as any) // TODO\n r23.subVectors(a3 as any, a2 as any) // TODO\n r34.subVectors(a4 as any, a3 as any) // TODO\n\n diff13.subVectors(r12, r23)\n diff24.subVectors(r23, r34)\n\n _axis.crossVectors(diff13, diff24).normalize()\n _axis.toArray(axis as any, j) // TODO\n\n if (i > 0) {\n diff[ i ] = _axis.angleTo(_prevAxis)\n }\n\n const tmp = Math.cos(diff13.angleTo(diff24))\n twist[ i ] = 180.0 / Math.PI * Math.acos(tmp)\n\n const diff13Length = diff13.length()\n const diff24Length = diff24.length()\n\n radius[ i ] = (\n Math.sqrt(diff24Length * diff13Length) /\n // clamp, to avoid instabilities for when\n // angle between diff13 and diff24 is near 0\n Math.max(2.0, 2.0 * (1.0 - tmp))\n )\n\n rise[ i ] = Math.abs(r23.dot(_axis))\n\n //\n\n v1.copy(diff13).multiplyScalar(radius[ i ] / diff13Length)\n v2.copy(diff24).multiplyScalar(radius[ i ] / diff24Length)\n\n v1.subVectors(a2 as any, v1) // TODO\n v2.subVectors(a3 as any, v2) // TODO\n\n v1.toArray(center as any, j + 3) // TODO\n v2.toArray(center as any, j + 6) // TODO\n\n //\n\n _resdir.subVectors(a1 as any, _center) // TODO\n _resdir.toArray(resdir as any, j) // TODO\n\n _prevAxis.copy(_axis)\n _center.copy(v1)\n }\n\n //\n\n // calc axis as dir of second and third center pos\n // project first traceAtom onto axis to get first center pos\n v1.fromArray(center as any, 3) // TODO\n v2.fromArray(center as any, 6) // TODO\n _axis.subVectors(v1, v2).normalize()\n // _center.copy( res[ 0 ].getTraceAtom() );\n a1.index = polymer.getAtomIndexByType(0, type)! // TODO\n _center.copy(a1 as any) // TODO\n vt.copy(a1 as any) // TODO\n projectPointOnVector(vt, _axis, v1)\n vt.toArray(center as any, 0) // TODO\n\n // calc first resdir\n _resdir.subVectors(_center, v1)\n _resdir.toArray(resdir as any, 0) // TODO\n\n // calc axis as dir of n-1 and n-2 center pos\n // project last traceAtom onto axis to get last center pos\n v1.fromArray(center as any, 3 * n - 6) // TODO\n v2.fromArray(center as any, 3 * n - 9) // TODO\n _axis.subVectors(v1, v2).normalize()\n // _center.copy( res[ n - 1 ].getTraceAtom() );\n a1.index = polymer.getAtomIndexByType(n - 1, type)! // TODO\n _center.copy(a1 as any) // TODO\n vt.copy(a1 as any) // TODO\n projectPointOnVector(vt, _axis, v1)\n vt.toArray(center as any, 3 * n - 3) // TODO\n\n // calc last three resdir\n for (let i = n - 3; i < n; ++i) {\n v1.fromArray(center as any, 3 * i) // TODO\n // _center.copy( res[ i ].getTraceAtom() );\n a1.index = polymer.getAtomIndexByType(i, type)! // TODO\n _center.copy(a1 as any) // TODO\n\n _resdir.subVectors(_center, v1)\n _resdir.toArray(resdir as any, 3 * i) // TODO\n }\n\n // average measures to define them on the residues\n\n const resRadius = new Float32Array(n)\n const resTwist = new Float32Array(n)\n const resRise = new Float32Array(n)\n const resBending = new Float32Array(n)\n\n resRadius[ 1 ] = radius[ 0 ]\n resTwist[ 1 ] = twist[ 0 ]\n resRise[ 1 ] = radius[ 0 ]\n\n for (let i = 2; i < n - 2; ++i) {\n resRadius[ i ] = 0.5 * (radius[ i - 2 ] + radius[ i - 1 ])\n resTwist[ i ] = 0.5 * (twist[ i - 2 ] + twist[ i - 1 ])\n resRise[ i ] = 0.5 * (rise[ i - 2 ] + rise[ i - 1 ])\n\n v1.fromArray(axis as any, 3 * (i - 2)) // TODO\n v2.fromArray(axis as any, 3 * (i - 1)) // TODO\n resBending[ i ] = 180.0 / Math.PI * Math.acos(Math.cos(v1.angleTo(v2)))\n }\n\n resRadius[ n - 2 ] = radius[ n - 4 ]\n resTwist[ n - 2 ] = twist[ n - 4 ]\n resRise[ n - 2 ] = rise[ n - 4 ]\n\n // average helix axes to define them on the residues\n\n const resAxis = new Float32Array(3 * n)\n\n copyArray(axis, resAxis, 0, 0, 3)\n copyArray(axis, resAxis, 0, 3, 3)\n\n for (let i = 2; i < n - 2; ++i) {\n v1.fromArray(axis as any, 3 * (i - 2)) // TODO\n v2.fromArray(axis as any, 3 * (i - 1)) // TODO\n\n _axis.addVectors(v2, v1).multiplyScalar(0.5).normalize()\n _axis.toArray(resAxis as any, 3 * i) // TODO\n }\n\n copyArray(axis, resAxis, 3 * n - 12, 3 * n - 6, 3)\n copyArray(axis, resAxis, 3 * n - 12, 3 * n - 3, 3)\n\n return {\n center,\n axis: resAxis,\n bending: resBending,\n radius: resRadius,\n rise: resRise,\n twist: resTwist,\n resdir: resdir\n }\n }\n\n}\n\nexport default Helixorient\n","/**\n * @file Helixbundle\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3 } from 'three'\n\nimport { ColormakerRegistry } from '../globals'\nimport { AtomPicker } from '../utils/picker'\nimport RadiusFactory, { RadiusParams } from '../utils/radius-factory'\nimport Helixorient, { HelixPosition } from './helixorient'\nimport { calculateMeanVector3, projectPointOnVector } from '../math/vector-utils'\nimport Polymer from '../proxy/polymer'\nimport { ColormakerParameters } from '../color/colormaker';\n\nexport interface Axis {\n axis: Float32Array\n center: Float32Array\n begin: Float32Array\n end: Float32Array\n color: Float32Array\n picking: AtomPicker\n size: Float32Array\n residueOffset: number[]\n residueCount: number[]\n}\n\nclass Helixbundle {\n helixorient: Helixorient;\n position: HelixPosition;\n\n constructor (readonly polymer: Polymer) {\n\n this.helixorient = new Helixorient(polymer)\n this.position = this.helixorient.getPosition()\n }\n\n getAxis (localAngle: number, centerDist: number, ssBorder: boolean, colorParams: { scheme: string} & ColormakerParameters, radiusParams: RadiusParams): Axis {\n localAngle = localAngle || 30\n centerDist = centerDist || 2.5\n ssBorder = ssBorder === undefined ? false : ssBorder\n\n const polymer = this.polymer\n const structure = polymer.structure\n const n = polymer.residueCount\n const residueIndexStart = polymer.residueIndexStart\n\n const pos = this.position\n\n const cp = colorParams || {}\n cp.structure = structure\n\n const colormaker = ColormakerRegistry.getScheme(cp)\n\n const radiusFactory = new RadiusFactory(radiusParams)\n\n let j = 0\n let k = 0\n\n const axis: number[] = []\n const center: number[] = []\n const beg: number[] = []\n const end: number[] = []\n const col: number[] = []\n const pick = []\n const size = []\n const residueOffset = []\n const residueCount = []\n\n let tmpAxis = new Float32Array(n * 3)\n let tmpCenter = new Float32Array(n * 3)\n\n let _axis, _center\n const _beg = new Vector3()\n const _end = new Vector3()\n\n const rp1 = structure.getResidueProxy()\n const rp2 = structure.getResidueProxy()\n const ap = structure.getAtomProxy()\n\n const c1 = new Vector3()\n const c2 = new Vector3()\n\n let split = false\n\n for (let i = 0; i < n; ++i) {\n rp1.index = residueIndexStart + i\n c1.fromArray(pos.center as any, i * 3)\n\n if (i === n - 1) {\n split = true\n } else {\n rp2.index = residueIndexStart + i + 1\n c2.fromArray(pos.center as any, i * 3 + 3)\n\n if (ssBorder && rp1.sstruc !== rp2.sstruc) {\n split = true\n } else if (c1.distanceTo(c2) > centerDist) {\n split = true\n } else if (pos.bending[ i ] > localAngle) {\n split = true\n }\n }\n\n if (split) {\n if (i - j < 4) {\n j = i\n split = false\n continue\n }\n\n ap.index = rp1.traceAtomIndex\n\n // ignore first and last axis\n tmpAxis = pos.axis.subarray(j * 3 + 3, i * 3)\n tmpCenter = pos.center.subarray(j * 3, i * 3 + 3)\n\n _axis = calculateMeanVector3(tmpAxis).normalize()\n _center = calculateMeanVector3(tmpCenter)\n\n _beg.fromArray(tmpCenter as any)\n projectPointOnVector(_beg, _axis, _center)\n\n _end.fromArray(tmpCenter as any, tmpCenter.length - 3)\n projectPointOnVector(_end, _axis, _center)\n\n _axis.subVectors(_end, _beg)\n\n _axis.toArray(axis as any, k)\n _center.toArray(center as any, k)\n _beg.toArray(beg as any, k)\n _end.toArray(end as any, k)\n\n colormaker.atomColorToArray(ap, col, k)\n\n pick.push(ap.index)\n\n size.push(radiusFactory.atomRadius(ap))\n\n residueOffset.push(residueIndexStart + j)\n residueCount.push(residueIndexStart + i + 1 - j)\n\n k += 3\n j = i\n split = false\n }\n }\n\n const picking = new Float32Array(pick)\n\n return {\n axis: new Float32Array(axis),\n center: new Float32Array(center),\n begin: new Float32Array(beg),\n end: new Float32Array(end),\n color: new Float32Array(col),\n picking: new AtomPicker(picking, structure),\n size: new Float32Array(size),\n residueOffset: residueOffset,\n residueCount: residueCount\n }\n }\n}\n\nexport default Helixbundle\n","/**\n * @file Binary Heap\n * @author Alexander Rose \n * @private\n */\n\n/**\n * Binary heap implementation\n * @class\n * @author http://eloquentjavascript.net/appendix2.htm\n * @param {Function} scoreFunction - the heap scoring function\n */\nclass BinaryHeap {\n content: T[] = []\n\n constructor(readonly scoreFunction: (x: T) => number) {\n\n this.scoreFunction = scoreFunction\n }\n\n push (element: T) {\n // Add the new element to the end of the array.\n this.content.push(element)\n\n // Allow it to bubble up.\n this.bubbleUp(this.content.length - 1)\n }\n\n pop () {\n // Store the first element so we can return it later.\n const result = this.content[ 0 ]\n\n // Get the element at the end of the array.\n const end = this.content.pop()\n\n // If there are any elements left, put the end element at the\n // start, and let it sink down.\n if (end && this.content.length > 0) {\n this.content[ 0 ] = end\n this.sinkDown(0)\n }\n\n return result\n }\n\n peek () {\n return this.content[ 0 ]\n }\n\n remove (element: T) {\n const len = this.content.length\n\n // To remove a value, we must search through the array to find it.\n for (let i = 0; i < len; i++) {\n if (this.content[ i ] === element) {\n // When it is found, the process seen in 'pop' is repeated\n // to fill up the hole.\n const end = this.content.pop()\n\n if (end && i !== len - 1) {\n this.content[ i ] = end\n\n if (this.scoreFunction(end) < this.scoreFunction(element)) {\n this.bubbleUp(i)\n } else {\n this.sinkDown(i)\n }\n }\n\n return\n }\n }\n\n throw new Error('Node not found.')\n }\n\n size () {\n return this.content.length\n }\n\n bubbleUp (n: number) {\n // Fetch the element that has to be moved.\n const element = this.content[ n ]\n\n // When at 0, an element can not go up any further.\n while (n > 0) {\n // Compute the parent element's index, and fetch it.\n const parentN = Math.floor((n + 1) / 2) - 1\n const parent = this.content[ parentN ]\n\n // Swap the elements if the parent is greater.\n if (this.scoreFunction(element) < this.scoreFunction(parent)) {\n this.content[ parentN ] = element\n this.content[ n ] = parent\n\n // Update 'n' to continue at the new position.\n n = parentN\n } else {\n // Found a parent that is less, no need to move it further.\n break\n }\n }\n }\n\n sinkDown (n: number) {\n // Look up the target element and its score.\n const length = this.content.length\n const element = this.content[ n ]\n const elemScore = this.scoreFunction(element)\n\n let child1Score = 0\n let child2Score = 0\n\n while (true) {\n // Compute the indices of the child elements.\n const child2N = (n + 1) * 2\n const child1N = child2N - 1\n\n // This is used to store the new position of the element, if any.\n let swap = null\n\n // If the first child exists (is inside the array)...\n if (child1N < length) {\n // Look it up and compute its score.\n const child1 = this.content[ child1N ]\n child1Score = this.scoreFunction(child1)\n\n // If the score is less than our element's, we need to swap.\n if (child1Score < elemScore) swap = child1N\n }\n\n // Do the same checks for the other child.\n if (child2N < length) {\n const child2 = this.content[ child2N ]\n child2Score = this.scoreFunction(child2)\n\n if (child2Score < (swap === null ? elemScore : child1Score)) swap = child2N\n }\n\n // If the element needs to be moved, swap it, and continue.\n if (swap !== null) {\n this.content[ n ] = this.content[ swap ]\n this.content[ swap ] = element\n n = swap\n } else {\n // Otherwise, we are done.\n break\n }\n }\n }\n\n}\n\nexport default BinaryHeap\n","/**\n * @file Kdtree\n * @author Alexander Rose \n * @private\n */\n\nimport { NumberArray } from '../types'\nimport BinaryHeap from './binary-heap'\n\n/**\n * Kdtree\n * @class\n * @author Alexander Rose , 2016\n * @author Roman Bolzern , 2013\n * @author I4DS http://www.fhnw.ch/i4ds, 2013\n * @license MIT License \n * @description\n * k-d Tree for typed arrays of 3d points (e.g. for Float32Array), in-place\n * provides fast nearest neighbour search\n *\n * Based on https://github.com/ubilabs/kd-tree-javascript by Ubilabs\n *\n * Further information (including mathematical properties)\n * http://en.wikipedia.org/wiki/Binary_tree\n * http://en.wikipedia.org/wiki/K-d_tree\n *\n * @example\n * points: [x, y, z, x, y, z, x, y, z, ...]\n * metric: function(a, b){\n * return Math.pow(a[0]-b[0], 2) + Math.pow(a[1]-b[1], 2) + Math.pow(a[2]-b[2], 2);\n * }\n *\n * @param {Float32Array} points - points\n * @param {Function} metric - metric\n */\nclass Kdtree {\n indices: Uint32Array\n nodes: Int32Array\n rootIndex: number\n\n maxDepth = 0\n currentNode = 0\n\n constructor(readonly points: NumberArray, readonly metric: (a: NumberArray, b: NumberArray) => number) {\n const n = points.length / 3\n\n const indices = new Uint32Array(n)\n for (let i = 0; i < n; ++i) {\n indices[ i ] = i\n }\n this.indices = indices\n this.nodes = new Int32Array(n * 4)\n this.rootIndex = this.buildTree(0, -1, 0, n)\n }\n\n buildTree (depth: number, parent: number, arrBegin: number, arrEnd: number) {\n if (depth > this.maxDepth) this.maxDepth = depth\n\n const plength = arrEnd - arrBegin\n if (plength === 0) {\n return -1\n }\n\n const nodeIndex = this.currentNode * 4\n const nodes = this.nodes\n\n this.currentNode += 1\n if (plength === 1) {\n nodes[ nodeIndex ] = arrBegin\n nodes[ nodeIndex + 1 ] = -1\n nodes[ nodeIndex + 2 ] = -1\n nodes[ nodeIndex + 3 ] = parent\n return nodeIndex\n }\n // if(plength <= 32){\n // return nodeIndex;\n // }\n\n const indices = this.indices\n const points = this.points\n\n const arrMedian = arrBegin + Math.floor(plength / 2)\n const currentDim = depth % 3\n\n // inlined quickselect function\n let j, tmp, pivotIndex, pivotValue, storeIndex\n let left = arrBegin\n let right = arrEnd - 1\n while (right > left) {\n pivotIndex = (left + right) >> 1\n pivotValue = points[ indices[ pivotIndex ] * 3 + currentDim ]\n // swap( pivotIndex, right );\n tmp = indices[ pivotIndex ]\n indices[ pivotIndex ] = indices[ right ]\n indices[ right ] = tmp\n storeIndex = left\n for (j = left; j < right; ++j) {\n if (points[ indices[ j ] * 3 + currentDim ] < pivotValue) {\n // swap( storeIndex, j );\n tmp = indices[ storeIndex ]\n indices[ storeIndex ] = indices[ j ]\n indices[ j ] = tmp\n ++storeIndex\n }\n }\n // swap( right, storeIndex );\n tmp = indices[ right ]\n indices[ right ] = indices[ storeIndex ]\n indices[ storeIndex ] = tmp\n pivotIndex = storeIndex\n if (arrMedian === pivotIndex) {\n break\n } else if (arrMedian < pivotIndex) {\n right = pivotIndex - 1\n } else {\n left = pivotIndex + 1\n }\n }\n\n nodes[ nodeIndex ] = arrMedian\n nodes[ nodeIndex + 1 ] = this.buildTree(depth + 1, nodeIndex, arrBegin, arrMedian)\n nodes[ nodeIndex + 2 ] = this.buildTree(depth + 1, nodeIndex, arrMedian + 1, arrEnd)\n nodes[ nodeIndex + 3 ] = parent\n\n return nodeIndex\n }\n\n getNodeDepth (nodeIndex: number): number {\n const parentIndex = this.nodes[ nodeIndex + 3 ]\n return (parentIndex === -1) ? 0 : this.getNodeDepth(parentIndex) + 1\n }\n\n // TODO\n // function getNodePos (node) {}\n\n /**\n * find nearest points\n * @param {Array} point - array of size 3\n * @param {Integer} maxNodes - max amount of nodes to return\n * @param {Float} maxDistance - maximum distance of point to result nodes\n * @return {Array} array of point, distance pairs\n */\n nearest (point: NumberArray, maxNodes: number, maxDistance: number) {\n const bestNodes = new BinaryHeap<[number, number]>(e => -e[ 1 ])\n\n const nodes = this.nodes\n const points = this.points\n const indices = this.indices\n\n const nearestSearch = (nodeIndex: number) => {\n let bestChild, otherChild\n const dimension = this.getNodeDepth(nodeIndex) % 3\n const pointIndex = indices[ nodes[ nodeIndex ] ] * 3\n const ownPoint = [\n points[ pointIndex + 0 ],\n points[ pointIndex + 1 ],\n points[ pointIndex + 2 ]\n ]\n const ownDistance = this.metric(point, ownPoint)\n\n function saveNode (nodeIndex: number, distance: number) {\n bestNodes.push([ nodeIndex, distance ])\n if (bestNodes.size() > maxNodes) {\n bestNodes.pop()\n }\n }\n\n const leftIndex = nodes[ nodeIndex + 1 ]\n const rightIndex = nodes[ nodeIndex + 2 ]\n\n // if it's a leaf\n if (rightIndex === -1 && leftIndex === -1) {\n if ((bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[ 1 ]) &&\n ownDistance <= maxDistance\n ) {\n saveNode(nodeIndex, ownDistance)\n }\n return\n }\n\n if (rightIndex === -1) {\n bestChild = leftIndex\n } else if (leftIndex === -1) {\n bestChild = rightIndex\n } else {\n if (point[ dimension ] <= points[ pointIndex + dimension ]) {\n bestChild = leftIndex\n } else {\n bestChild = rightIndex\n }\n }\n\n // recursive search\n nearestSearch(bestChild)\n\n if ((bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[ 1 ]) &&\n ownDistance <= maxDistance\n ) {\n saveNode(nodeIndex, ownDistance)\n }\n\n // if there's still room or the current distance is nearer than the best distance\n const linearPoint = []\n for (let i = 0; i < 3; i += 1) {\n if (i === dimension) {\n linearPoint[ i ] = point[ i ]\n } else {\n linearPoint[ i ] = points[ pointIndex + i ]\n }\n }\n const linearDistance = this.metric(linearPoint, ownPoint)\n\n if ((bestNodes.size() < maxNodes || Math.abs(linearDistance) < bestNodes.peek()[ 1 ]) &&\n Math.abs(linearDistance) <= maxDistance\n ) {\n if (bestChild === leftIndex) {\n otherChild = rightIndex\n } else {\n otherChild = leftIndex\n }\n if (otherChild !== -1) {\n nearestSearch(otherChild)\n }\n }\n }\n\n nearestSearch(this.rootIndex)\n\n const result = []\n for (let i = 0, il = Math.min(bestNodes.size(), maxNodes); i < il; i += 1) {\n result.push(bestNodes.content[ i ])\n }\n\n return result\n }\n\n verify (nodeIndex?: number, depth = 0) {\n let count = 1\n\n if (nodeIndex === undefined) {\n nodeIndex = this.rootIndex\n }\n\n if (nodeIndex === -1) {\n throw new Error('node is null')\n }\n\n const dim = depth % 3\n const nodes = this.nodes\n const points = this.points\n const indices = this.indices\n\n const leftIndex = nodes[ nodeIndex + 1 ]\n const rightIndex = nodes[ nodeIndex + 2 ]\n\n if (leftIndex !== -1) {\n if (points[ indices[ nodes[ leftIndex ] ] * 3 + dim ] >\n points[ indices[ nodes[ nodeIndex ] ] * 3 + dim ]\n ) {\n throw new Error('left child is > parent!')\n }\n count += this.verify(leftIndex, depth + 1)\n }\n\n if (rightIndex !== -1) {\n if (points[ indices[ nodes[ rightIndex ] ] * 3 + dim ] <\n points[ indices[ nodes[ nodeIndex ] ] * 3 + dim ]\n ) {\n throw new Error('right child is < parent!')\n }\n count += this.verify(rightIndex, depth + 1)\n }\n\n return count\n }\n}\n\nexport default Kdtree\n","/**\n * @file Atom Proxy\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3 } from 'three'\n\nimport { NumberArray } from '../types'\nimport {\n Elements,\n SecStrucHelix, SecStrucSheet, SecStrucTurn,\n ProteinType, RnaType, DnaType, WaterType, IonType, SaccharideType,\n CgProteinBackboneType, CgRnaBackboneType, CgDnaBackboneType\n} from '../structure/structure-constants'\n\nimport Structure from '../structure/structure'\n\nimport ChainStore from '../store/chain-store'\nimport ResidueStore from '../store/residue-store'\nimport AtomStore from '../store/atom-store'\n\nimport AtomMap from '../store/atom-map'\nimport ResidueMap from '../store/residue-map'\n\nimport BondProxy from '../proxy/bond-proxy'\nimport AtomType from '../store/atom-type';\nimport ResidueType from '../store/residue-type';\nimport ResidueProxy from './residue-proxy';\nimport Entity from '../structure/entity';\nimport BondHash from '../store/bond-hash';\n\n/**\n * Atom proxy\n */\nclass AtomProxy {\n index: number\n\n chainStore: ChainStore\n residueStore: ResidueStore\n atomStore: AtomStore\n\n residueMap: ResidueMap\n atomMap: AtomMap\n\n /**\n * @param {Structure} structure - the structure\n * @param {Integer} index - the index\n */\n constructor (readonly structure: Structure, index = 0) {\n this.index = index\n this.chainStore = structure.chainStore\n this.residueStore = structure.residueStore\n this.atomStore = structure.atomStore\n this.residueMap = structure.residueMap\n this.atomMap = structure.atomMap\n }\n\n /**\n * @type {BondHash}\n */\n get bondHash (): BondHash|undefined { return this.structure.bondHash }\n\n /**\n * Molecular enity\n * @type {Entity}\n */\n get entity (): Entity {\n return this.structure.entityList[ this.entityIndex ]\n }\n get entityIndex () {\n return this.chainStore.entityIndex[ this.chainIndex ]\n }\n get modelIndex () {\n return this.chainStore.modelIndex[ this.chainIndex ]\n }\n get chainIndex () {\n return this.residueStore.chainIndex[ this.residueIndex ]\n }\n /**\n * @type {ResidueProxy}\n */\n get residue (): ResidueProxy {\n console.warn('residue - might be expensive')\n return this.structure.getResidueProxy(this.residueIndex)\n }\n\n get residueIndex () {\n return this.atomStore.residueIndex[ this.index ]\n }\n set residueIndex (value) {\n this.atomStore.residueIndex[ this.index ] = value\n }\n\n //\n\n /**\n * Secondary structure code\n * @type {String}\n */\n get sstruc () {\n return this.residueStore.getSstruc(this.residueIndex)\n }\n /**\n * Insertion code\n * @type {String}\n */\n get inscode () {\n return this.residueStore.getInscode(this.residueIndex)\n }\n /**\n * Residue number/label\n * @type {Integer}\n */\n get resno () {\n return this.residueStore.resno[ this.residueIndex ]\n }\n /**\n * Chain name\n * @type {String}\n */\n get chainname () {\n return this.chainStore.getChainname(this.chainIndex)\n }\n /**\n * Chain id\n * @type {String}\n */\n get chainid () {\n return this.chainStore.getChainid(this.chainIndex)\n }\n\n //\n\n /**\n * @type {ResidueType}\n */\n get residueType (): ResidueType {\n return this.residueMap.get(this.residueStore.residueTypeId[ this.residueIndex ])\n }\n /**\n * @type {AtomType}\n */\n get atomType (): AtomType {\n return this.atomMap.get(this.atomStore.atomTypeId[ this.index ])\n }\n get residueAtomOffset () {\n return this.residueStore.atomOffset[ this.residueIndex ]\n }\n\n //\n\n /**\n * Residue name\n */\n get resname () {\n return this.residueType.resname\n }\n /**\n * Hetero flag\n */\n get hetero () {\n return this.residueType.hetero\n }\n\n //\n\n /**\n * Atom name\n */\n get atomname () {\n return this.atomType.atomname\n }\n /**\n * Atomic number\n */\n get number () {\n return this.atomType.number\n }\n /**\n * Element\n */\n get element () {\n return this.atomType.element\n }\n /**\n * Van-der-Waals radius\n */\n get vdw () {\n return this.atomType.vdw\n }\n /**\n * Covalent radius\n */\n get covalent () {\n return this.atomType.covalent\n }\n\n //\n\n /**\n * X coordinate\n */\n get x () {\n return this.atomStore.x[ this.index ]\n }\n set x (value) {\n this.atomStore.x[ this.index ] = value\n }\n\n /**\n * Y coordinate\n */\n get y () {\n return this.atomStore.y[ this.index ]\n }\n set y (value) {\n this.atomStore.y[ this.index ] = value\n }\n\n /**\n * Z coordinate\n */\n get z () {\n return this.atomStore.z[ this.index ]\n }\n set z (value) {\n this.atomStore.z[ this.index ] = value\n }\n\n /**\n * Serial number\n */\n get serial () {\n return this.atomStore.serial[ this.index ]\n }\n set serial (value) {\n this.atomStore.serial[ this.index ] = value\n }\n\n /**\n * B-factor value\n */\n get bfactor () {\n return this.atomStore.bfactor[ this.index ]\n }\n set bfactor (value) {\n this.atomStore.bfactor[ this.index ] = value\n }\n\n /**\n * Occupancy value\n */\n get occupancy () {\n return this.atomStore.occupancy[ this.index ]\n }\n set occupancy (value) {\n this.atomStore.occupancy[ this.index ] = value\n }\n\n /**\n * Alternate location identifier\n */\n get altloc () {\n return this.atomStore.getAltloc(this.index)\n }\n set altloc (value) {\n this.atomStore.setAltloc(this.index, value)\n }\n\n /**\n * Partial charge\n */\n get partialCharge () {\n return this.atomStore.partialCharge ? this.atomStore.partialCharge[ this.index ] : null\n }\n set partialCharge (value) {\n if (this.atomStore.partialCharge) {\n this.atomStore.partialCharge[ this.index ] = value as number\n }\n }\n\n /**\n * Explicit radius\n */\n get radius () {\n return this.atomStore.radius ? this.atomStore.radius[ this.index ] : null\n }\n set radius (value) {\n if (this.atomStore.radius) {\n this.atomStore.radius[ this.index ] = value as number\n }\n }\n\n /**\n * Formal charge\n */\n get formalCharge () {\n return this.atomStore.formalCharge ? this.atomStore.formalCharge[ this.index ] : null\n }\n set formalCharge (value) {\n if (this.atomStore.formalCharge) {\n this.atomStore.formalCharge[ this.index ] = value as number\n }\n }\n\n /**\n * Aromaticity flag\n */\n get aromatic () {\n if (this.atomStore.aromatic) {\n return this.atomStore.aromatic[ this.index ] as number\n } else {\n return this.residueType.isAromatic(this) ? 1 : 0\n }\n }\n set aromatic (value) {\n if (this.atomStore.aromatic) {\n this.atomStore.aromatic[ this.index ] = value as number\n }\n }\n\n //\n\n get bondCount () {\n return this.bondHash!.countArray[ this.index ] // TODO\n }\n\n //\n\n /**\n * Iterate over each bond\n * @param {function(bond: BondProxy)} callback - iterator callback function\n * @param {BondProxy} [bp] - optional target bond proxy for use in the callback\n * @return {undefined}\n */\n eachBond (callback: (bp: BondProxy) => void, bp?: BondProxy) {\n bp = bp || this.structure._bp\n const idx = this.index\n const bondHash = this.bondHash! // TODO\n const indexArray = bondHash.indexArray\n const n = bondHash.countArray[ idx ]\n const offset = bondHash.offsetArray[ idx ]\n\n for (let i = 0; i < n; ++i) {\n bp.index = indexArray[ offset + i ]\n callback(bp)\n }\n }\n\n /**\n * Iterate over each bonded atom\n * @param {function(atom: AtomProxy)} callback - iterator callback function\n * @param {AtomProxy} [ap] - optional target atom proxy for use in the callback\n * @return {undefined}\n */\n eachBondedAtom (callback: (ap: AtomProxy) => void, _ap?: AtomProxy) {\n const ap = _ap ? _ap : this.structure._ap\n const idx = this.index\n\n this.eachBond(function (bp) {\n ap.index = idx !== bp.atomIndex1 ? bp.atomIndex1 : bp.atomIndex2\n callback(ap)\n })\n this.index = idx\n }\n\n /**\n * Check if this atom is bonded to the given atom,\n * assumes both atoms are from the same structure\n * @param {AtomProxy} ap - the given atom\n * @return {Boolean} whether a bond exists or not\n */\n hasBondTo (ap: AtomProxy) {\n let flag = false\n this.eachBondedAtom(function (bap) {\n if (ap.index === bap.index) flag = true\n })\n return flag\n }\n\n bondToElementCount (element: Elements) {\n let count = 0\n const idx = this.index // Avoid reentrancy problems\n this.eachBondedAtom(function (bap) {\n if (bap.number === element) count += 1\n })\n this.index = idx\n return count\n }\n\n hasBondToElement (element: Elements) {\n return this.bondToElementCount(element) > 0\n }\n\n //\n\n /**\n * If atom is part of a backbone\n * @return {Boolean} flag\n */\n isBackbone () {\n const backboneIndexList = this.residueType.backboneIndexList\n if (backboneIndexList.length > 0) {\n return backboneIndexList.includes(this.index - this.residueAtomOffset)\n } else {\n return false\n }\n }\n\n /**\n * If atom is part of a polymer\n * @return {Boolean} flag\n */\n isPolymer () {\n if (this.structure.entityList.length > 0) {\n return this.entity.isPolymer()\n } else {\n const moleculeType = this.residueType.moleculeType\n return (\n moleculeType === ProteinType ||\n moleculeType === RnaType ||\n moleculeType === DnaType\n )\n }\n }\n\n /**\n * If atom is part of a sidechin\n * @return {Boolean} flag\n */\n isSidechain () {\n return this.isPolymer() && !this.isBackbone()\n }\n\n /**\n * If atom is part of a coarse-grain group\n * @return {Boolean} flag\n */\n isCg () {\n const backboneType = this.residueType.backboneType\n return (\n backboneType === CgProteinBackboneType ||\n backboneType === CgRnaBackboneType ||\n backboneType === CgDnaBackboneType\n )\n }\n\n isTrace () {\n return this.index === (this.residueType.traceAtomIndex + this.residueAtomOffset)\n }\n\n /**\n * If atom is part of a hetero group\n * @return {Boolean} flag\n */\n isHetero () {\n return this.residueType.hetero === 1\n }\n\n /**\n * If atom is part of a protein molecule\n * @return {Boolean} flag\n */\n isProtein () {\n return this.residueType.moleculeType === ProteinType\n }\n\n /**\n * If atom is part of a nucleic molecule\n * @return {Boolean} flag\n */\n isNucleic () {\n const moleculeType = this.residueType.moleculeType\n return moleculeType === RnaType || moleculeType === DnaType\n }\n\n /**\n * If atom is part of a rna\n * @return {Boolean} flag\n */\n isRna () {\n return this.residueType.moleculeType === RnaType\n }\n\n /**\n * If atom is part of a dna\n * @return {Boolean} flag\n */\n isDna () {\n return this.residueType.moleculeType === DnaType\n }\n\n /**\n * If atom is part of a water molecule\n * @return {Boolean} flag\n */\n isWater () {\n return this.residueType.moleculeType === WaterType\n }\n\n /**\n * If atom is part of an ion\n * @return {Boolean} flag\n */\n isIon () {\n return this.residueType.moleculeType === IonType\n }\n\n /**\n * If atom is part of a saccharide\n * @return {Boolean} flag\n */\n isSaccharide () {\n return this.residueType.moleculeType === SaccharideType\n }\n\n /**\n * If atom is part of a helix\n * @return {Boolean} flag\n */\n isHelix () {\n return SecStrucHelix.includes(this.sstruc)\n }\n\n /**\n * If atom is part of a sheet\n * @return {Boolean} flag\n */\n isSheet () {\n return SecStrucSheet.includes(this.sstruc)\n }\n\n /**\n * If atom is part of a turn\n * @return {Boolean} flag\n */\n isTurn () {\n return SecStrucTurn.includes(this.sstruc) && this.isProtein()\n }\n\n isBonded () {\n return this.bondHash!.countArray[ this.index ] !== 0 // TODO\n }\n\n /**\n * If atom is part of a ring\n * @return {Boolean} flag\n */\n isRing () {\n const atomRings = this.residueType.getRings()!.atomRings // TODO\n return atomRings[ this.index - this.residueAtomOffset ] !== undefined\n }\n\n isAromatic () {\n return this.aromatic === 1\n }\n\n isPolarHydrogen () {\n let result = false\n\n if (this.number !== 1) return result\n\n result = !this.hasBondToElement(Elements.C)\n\n return result\n }\n\n isMetal () { return this.atomType.isMetal() }\n isNonmetal () { return this.atomType.isNonmetal() }\n isMetalloid () { return this.atomType.isMetalloid() }\n isHalogen () { return this.atomType.isHalogen() }\n isDiatomicNonmetal () { return this.atomType.isDiatomicNonmetal() }\n isPolyatomicNonmetal () { return this.atomType.isPolyatomicNonmetal() }\n isAlkaliMetal () { return this.atomType.isAlkaliMetal() }\n isAlkalineEarthMetal () { return this.atomType.isAlkalineEarthMetal() }\n isNobleGas () { return this.atomType.isNobleGas() }\n isTransitionMetal () { return this.atomType.isTransitionMetal() }\n isPostTransitionMetal () { return this.atomType.isPostTransitionMetal() }\n isLanthanide () { return this.atomType.isLanthanide() }\n isActinide () { return this.atomType.isActinide() }\n\n getDefaultValence () { return this.atomType.getDefaultValence() }\n getValenceList () { return this.atomType.getValenceList() }\n getOuterShellElectronCount () { return this.atomType.getOuterShellElectronCount() }\n\n /**\n * Distance to another atom\n * @param {AtomProxy} atom - the other atom\n * @return {Number} the distance\n */\n distanceTo (atom: AtomProxy) {\n const taa = this.atomStore\n const aaa = atom.atomStore\n const ti = this.index\n const ai = atom.index\n const x = taa.x[ ti ] - aaa.x[ ai ]\n const y = taa.y[ ti ] - aaa.y[ ai ]\n const z = taa.z[ ti ] - aaa.z[ ai ]\n const distSquared = x * x + y * y + z * z\n return Math.sqrt(distSquared)\n }\n\n /**\n * If connected to another atom\n * @param {AtomProxy} atom - the other atom\n * @return {Boolean} flag\n */\n connectedTo (atom: AtomProxy) {\n const taa = this.atomStore\n const aaa = atom.atomStore\n const ti = this.index\n const ai = atom.index\n\n if (taa.altloc && aaa.altloc) {\n const ta = taa.altloc[ ti ] // use Uint8 value to compare\n const aa = aaa.altloc[ ai ] // no need to convert to char\n // 0 is the Null character, 32 is the space character\n if (!(ta === 0 || aa === 0 || ta === 32 || aa === 32 || (ta === aa))) return false\n }\n\n const x = taa.x[ ti ] - aaa.x[ ai ]\n const y = taa.y[ ti ] - aaa.y[ ai ]\n const z = taa.z[ ti ] - aaa.z[ ai ]\n\n const distSquared = x * x + y * y + z * z\n\n // if( this.isCg() ) console.log( this.qualifiedName(), Math.sqrt( distSquared ), distSquared )\n if (distSquared < 48.0 && this.isCg()) return true\n\n if (isNaN(distSquared)) return false\n\n const d = this.covalent + atom.covalent\n const d1 = d + 0.3\n const d2 = d - 0.5\n\n return distSquared < (d1 * d1) && distSquared > (d2 * d2)\n }\n\n /**\n * Set atom position from array\n * @param {Array|TypedArray} array - input array\n * @param {Integer} [offset] - the offset\n * @return {AtomProxy} this object\n */\n positionFromArray (array: NumberArray, offset = 0) {\n this.x = array[ offset + 0 ]\n this.y = array[ offset + 1 ]\n this.z = array[ offset + 2 ]\n\n return this\n }\n\n /**\n * Write atom position to array\n * @param {Array|TypedArray} [array] - target array\n * @param {Integer} [offset] - the offset\n * @return {Array|TypedArray} target array\n */\n positionToArray (array: NumberArray = [], offset = 0) {\n const index = this.index\n const atomStore = this.atomStore\n\n array[ offset + 0 ] = atomStore.x[ index ]\n array[ offset + 1 ] = atomStore.y[ index ]\n array[ offset + 2 ] = atomStore.z[ index ]\n\n return array\n }\n\n /**\n * Write atom position to vector\n * @param {Vector3} [v] - target vector\n * @return {Vector3} target vector\n */\n positionToVector3 (v?: Vector3) {\n if (v === undefined) v = new Vector3()\n\n v.x = this.x\n v.y = this.y\n v.z = this.z\n\n return v\n }\n\n /**\n * Set atom position from vector\n * @param {Vector3} v - input vector\n * @return {AtomProxy} this object\n */\n positionFromVector3 (v: Vector3) {\n this.x = v.x\n this.y = v.y\n this.z = v.z\n\n return this\n }\n\n /**\n * Add vector to atom position\n * @param {Vector3} v - input vector\n * @return {AtomProxy} this object\n */\n positionAdd (v: Vector3|AtomProxy) {\n this.x += v.x\n this.y += v.y\n this.z += v.z\n\n return this\n }\n\n /**\n * Subtract vector from atom position\n * @param {Vector3} v - input vector\n * @return {AtomProxy} this object\n */\n positionSub (v: Vector3|AtomProxy) {\n this.x -= v.x\n this.y -= v.y\n this.z -= v.z\n\n return this\n }\n\n /**\n * Get intra group/residue bonds\n * @param {Boolean} firstOnly - immediately return the first connected atomIndex\n * @return {Integer[]|Integer|undefined} connected atomIndices\n */\n getResidueBonds (firstOnly = false) {\n const residueAtomOffset = this.residueAtomOffset\n const relativeIndex = this.index - this.residueAtomOffset\n const bonds = this.residueType.getBonds()! // TODO\n const atomIndices1 = bonds.atomIndices1\n const atomIndices2 = bonds.atomIndices2\n let idx1, idx2, connectedAtomIndex\n let connectedAtomIndices: number[]|undefined\n\n if (!firstOnly) connectedAtomIndices = []\n\n idx1 = atomIndices1.indexOf(relativeIndex)\n while (idx1 !== -1) {\n connectedAtomIndex = atomIndices2[ idx1 ] + residueAtomOffset\n if (connectedAtomIndices) {\n connectedAtomIndices.push(connectedAtomIndex)\n idx1 = atomIndices1.indexOf(relativeIndex, idx1 + 1)\n } else {\n return connectedAtomIndex\n }\n }\n\n idx2 = atomIndices2.indexOf(relativeIndex)\n while (idx2 !== -1) {\n connectedAtomIndex = atomIndices1[ idx2 ] + residueAtomOffset\n if (connectedAtomIndices) {\n connectedAtomIndices.push(connectedAtomIndex)\n idx2 = atomIndices2.indexOf(relativeIndex, idx2 + 1)\n } else {\n return connectedAtomIndex\n }\n }\n\n return connectedAtomIndices\n }\n\n //\n\n qualifiedName (noResname = false) {\n var name = ''\n if (this.resname && !noResname) name += '[' + this.resname + ']'\n if (this.resno !== undefined) name += this.resno\n if (this.inscode) name += '^' + this.inscode\n if (this.chainname) name += ':' + this.chainname\n if (this.atomname) name += '.' + this.atomname\n if (this.altloc) name += '%' + this.altloc\n if (this.structure.modelStore.count > 1) name += '/' + this.modelIndex\n return name\n }\n\n /**\n * Clone object\n * @return {AtomProxy} cloned atom\n */\n clone () {\n return new AtomProxy(this.structure, this.index)\n }\n\n toObject () {\n return {\n index: this.index,\n residueIndex: this.residueIndex,\n\n resname: this.resname,\n x: this.x,\n y: this.y,\n z: this.z,\n element: this.element,\n chainname: this.chainname,\n resno: this.resno,\n serial: this.serial,\n vdw: this.vdw,\n covalent: this.covalent,\n hetero: this.hetero,\n bfactor: this.bfactor,\n altloc: this.altloc,\n atomname: this.atomname,\n modelIndex: this.modelIndex\n }\n }\n}\n\nexport default AtomProxy\n","/**\n * @file Kdtree\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3 } from 'three'\n\nimport { Debug, Log } from '../globals'\nimport _Kdtree from '../utils/kdtree'\nimport Structure from '../structure/structure'\nimport AtomProxy from '../proxy/atom-proxy'\nimport ResidueProxy from '../proxy/residue-proxy'\n\nfunction euclideanDistSq(a: number[], b: number[]) {\n const dx = a[0] - b[0]\n const dy = a[1] - b[1]\n const dz = a[2] - b[2]\n return dx * dx + dy * dy + dz * dz\n}\n\nfunction euclideanDist(a: number[], b: number[]) {\n return Math.sqrt(euclideanDistSq(a, b))\n}\n\nconst pointArray = new Float32Array(3)\n\nclass Kdtree {\n points: Float32Array\n atomIndices: Uint32Array\n kdtree: _Kdtree\n\n constructor(structure: Structure|ResidueProxy, useSquaredDist = false) {\n if (Debug) Log.time('Kdtree build')\n\n const metric = useSquaredDist ? euclideanDistSq : euclideanDist\n\n const points = new Float32Array(structure.atomCount * 3)\n const atomIndices = new Uint32Array(structure.atomCount)\n let i = 0\n\n structure.eachAtom(function (ap) {\n points[ i + 0 ] = ap.x\n points[ i + 1 ] = ap.y\n points[ i + 2 ] = ap.z\n atomIndices[ i / 3 ] = ap.index\n i += 3\n })\n\n this.atomIndices = atomIndices\n this.points = points\n this.kdtree = new _Kdtree(points, metric)\n\n if (Debug) Log.timeEnd('Kdtree build')\n\n // console.log(\"this.kdtree.verify()\", this.kdtree.verify())\n }\n\n nearest (point: number[]|Vector3, maxNodes: number, maxDistance: number) {\n // Log.time( \"Kdtree nearest\" );\n\n if (point instanceof Vector3) {\n point.toArray(pointArray as any)\n } else if (point instanceof AtomProxy) {\n point.positionToArray(pointArray)\n }\n\n const nodeList = this.kdtree.nearest(pointArray, maxNodes, maxDistance)\n\n const indices = this.kdtree.indices\n const nodes = this.kdtree.nodes\n const atomIndices = this.atomIndices\n const resultList = []\n\n for (let i = 0, n = nodeList.length; i < n; ++i) {\n const d = nodeList[ i ]\n const nodeIndex = d[ 0 ]\n const dist = d[ 1 ]\n\n resultList.push({\n index: atomIndices[ indices[ nodes[ nodeIndex ] ] ],\n distance: dist\n })\n }\n\n // Log.timeEnd( \"Kdtree nearest\" );\n\n return resultList\n }\n}\n\nexport default Kdtree\n","/**\n * @file Symmetry Constants\n * @author Alexander Rose \n * @private\n */\n\nexport const SymOpCode: { [k: string]: string } = {\n ' ': 'X',\n '!': 'Y',\n '#': 'Z',\n '$': '-X',\n '%': '-Y',\n '&': '-Z',\n \"'\": 'Y+1/2',\n '(': '1/2+X',\n ')': '1/2+Y',\n '*': '1/2-X',\n '+': '1/2+Z',\n ',': '1/2-Y',\n '-': '1/2-Z',\n '.': 'X+1/2',\n '/': 'Z+1/2',\n '0': '-X+1/2',\n '1': '-Y+1/2',\n '2': '-Z+1/2',\n '3': '1/4+X',\n '4': '1/4-Y',\n '5': '1/4+Z',\n '6': '1/4-X',\n '7': '1/4+Y',\n '8': '3/4-Y',\n '9': '3/4+Z',\n ':': '3/4+Y',\n ';': '3/4+X',\n '<': '3/4-X',\n '=': '1/4-Z',\n '>': '3/4-Z',\n '?': 'X-Y',\n '@': 'Y-X',\n 'A': 'Z+1/3',\n 'B': 'Z+2/3',\n 'C': 'X+2/3',\n 'D': 'Y+1/3',\n 'E': '-Y+2/3',\n 'F': 'X-Y+1/3',\n 'G': 'Y-X+2/3',\n 'H': '-X+1/3',\n 'I': 'X+1/3',\n 'J': 'Y+2/3',\n 'K': '-Y+1/3',\n 'L': 'X-Y+2/3',\n 'M': 'Y-X+1/3',\n 'N': '-X+2/3',\n 'O': '2/3+X',\n 'P': '1/3+Y',\n 'Q': '1/3+Z',\n 'R': '2/3-Y',\n 'S': '1/3+X-Y',\n 'T': '2/3+Y-X',\n 'U': '1/3-X',\n 'V': '2/3-X',\n 'W': '1/3-Y',\n 'X': '1/3-Z',\n 'Y': '2/3+Y',\n 'Z': '1/3+Y-X',\n '[': '2/3+X-Y',\n ']': '1/3+X',\n '^': '2/3+Z',\n '_': '2/3-Z',\n '`': '5/6+Z',\n 'a': '1/6+Z',\n 'b': '5/6-Z',\n 'c': '1/6-Z',\n 'd': 'Z+5/6',\n 'e': 'Z+1/6',\n 'f': 'Z+1/4',\n 'g': '+Y'\n}\n\n// encoded, originally from CCP4 symop.lib\nexport const EncodedSymOp: { [k: string]: string } = {\n 'P 1': ' !#',\n 'P -1': ' !#$%&',\n 'P 1 2 1': ' !#$!&',\n 'P 1 21 1': \" !#$'&\",\n 'C 1 2 1': ' !#$!&()#*)&',\n 'P 1 m 1': ' !# %#',\n 'P 1 c 1': ' !# %+',\n 'C 1 m 1': ' !# %#()#(,#',\n 'C 1 c 1': ' !# %+()#(,+',\n 'P 1 2/m 1': ' !# %#$!&$%&',\n 'P 1 21/m 1': ' !#$)&$%& ,#',\n 'C 1 2/m 1': ' !# %#$!&$%&()#(,#*)&*,&',\n 'P 1 2/c 1': ' !#$!-$%& %+',\n 'P 1 21/c 1': ' !#$%&$)- ,+',\n 'C 1 2/c 1': ' !#$!-$%& %+()#*)-*,&(,+',\n 'P 2 2 2': ' !#$%#$!& %&',\n 'P 2 2 21': ' !#$%+$!- %&',\n 'P 21 21 2': ' !#$%#*)&(,&',\n 'P 21 21 21': ' !#*%+$)-(,&',\n 'C 2 2 21': ' !#$%+$!- %&()#*,+*)-(,&',\n 'C 2 2 2': ' !#$%#$!& %&()#*,#*)&(,&',\n 'F 2 2 2': ' !#$%#$!& %& )+$,+$)- ,-(!+*%+*!-(%-()#*,#*)&(,&',\n 'I 2 2 2': \" !#$%# %&$!&.'/01/.120'2\",\n 'I 21 21 21': ' !#*%+$)-(,&()+$,#*!& %-',\n 'P m m 2': ' !#$%# %#$!#',\n 'P m c 21': ' !#$%+ %+$!#',\n 'P c c 2': ' !#$%# %+$!+',\n 'P m a 2': ' !#$%#(%#*!#',\n 'P c a 21': ' !#$%+(%#*!+',\n 'P n c 2': ' !#$%# ,+$)+',\n 'P m n 21': ' !#*%+(%+$!#',\n 'P b a 2': ' !#$%#(,#*)#',\n 'P n a 21': ' !#$%+(,#*)+',\n 'P n n 2': ' !#$%#(,+*)+',\n 'C m m 2': ' !#$%# %#$!#()#*,#(,#*)#',\n 'C m c 21': ' !#$%+ %+$!#()#*,+(,+*)#',\n 'C c c 2': ' !#$%# %+$!+()#*,#(,+*)+',\n 'A m m 2': ' !#$%# %#$!# )+$,+ ,+$)+',\n 'A b m 2': ' !#$%# ,#$)# )+$,+ %+$!+',\n 'A m a 2': ' !#$%#(%#*!# )+$,+(,+*)+',\n 'A b a 2': ' !#$%#(,#*)# )+$,+(%+*!+',\n 'F m m 2': ' !#$%# %#$!# )+$,+ ,+$)+(!+*%+(%+*!+()#*,#(,#*)#',\n 'F d d 2': ' !#$%#345675 )+$,+3896:9(!+*%+;49<79()#*,#;85<:5',\n 'I m m 2': ' !#$%# %#$!#()+*,+(,+*)+',\n 'I b a 2': ' !#$%#(,#*)#()+*,+ %+$!+',\n 'I m a 2': ' !#$%#(%#*!#()+*,+ ,+$)+',\n 'P 2/m 2/m 2/m': ' !#$%#$!& %&$%& !& %#$!#',\n 'P 2/n 2/n 2/n': ' !#$%#$!& %&*,-()-(,+*)+',\n 'P 2/c 2/c 2/m': ' !#$%#$!- %-$%& !& %+$!+',\n 'P 2/b 2/a 2/n': ' !#$%#$!& %&*,&()&(,#*)#',\n 'P 21/m 2/m 2/a': ' !#*%#$!&(%&$%&(!& %#*!#',\n 'P 2/n 21/n 2/a': ' !#*%#*)- ,-$%&(!&(,+$)+',\n 'P 2/m 2/n 21/a': ' !#*%+*!- %&$%&(!-(%+$!#',\n 'P 21/c 2/c 2/a': ' !#*%#$!-(%-$%&(!& %+*!+',\n 'P 21/b 21/a 2/m': ' !#$%#*)&(,&$%& !&(,#*)#',\n 'P 21/c 21/c 2/n': ' !#*,#$)-(%-$%&()& ,+*!+',\n 'P 2/b 21/c 21/m': ' !#$%+$)- ,&$%& !- ,+$)#',\n 'P 21/n 21/n 2/m': ' !#$%#*)-(,-$%& !&(,+*)+',\n 'P 21/m 21/m 2/n': \" !#$%#*'&.,&*,&.'& %#$!#\",\n 'P 21/b 2/c 21/n': ' !#*,+$!-(,&$%&()- %+*)#',\n 'P 21/b 21/c 21/a': ' !#*%+$)-(,&$%&(!- ,+*)#',\n 'P 21/n 21/m 21/a': \" !#0%/$'&.12$%&.!2 1#0'/\",\n 'C 2/m 2/c 21/m': ' !#$%+$!- %&$%& !- %+$!#()#*,+*)-(,&*,&()-(,+*)#',\n 'C 2/m 2/c 21/a': ' !#$,+$)- %&$%& )- ,+$!#()#*%+*!-(,&*,&(!-(%+*)#',\n 'C 2/m 2/m 2/m': ' !#$%#$!& %&$%& !& %#$!#()#*,#*)&(,&*,&()&(,#*)#',\n 'C 2/c 2/c 2/m': ' !#$%#$!- %-$%& !& %+$!+()#*,#*)-(,-*,&()&(,+*)+',\n 'C 2/m 2/m 2/a': ' !#$,#$)& %&$%& )& ,#$!#()#*%#*!&(,&*,&(!&(%#*)#',\n 'C 2/c 2/c 2/a': ' !#*,#$!&(,&$,-(!- ,+*!+()#$%#*)& %&*%- )-(%+$)+',\n 'F 2/m 2/m 2/m': ' !#$%#$!& %&$%& !& %#$!# )+$,+$)- ,-$,- )- ,+$)+(!+*%+*!-(%-*%-(!-(%+*!+()#*,#*)&(,&*,&()&(,#*)#',\n 'F 2/d 2/d 2/d': ' !#$%#$!& %&64=37=345675 )+$,+$)- ,-68>3:>3896:9(!+*%+*!-(%-<4>;7>;49<79()#*,#*)&(,&<8=;:=;85<:5',\n 'I 2/m 2/m 2/m': ' !#$%#$!& %&$%& !& %#$!#()+*,+*)-(,-*,-()-(,+*)+',\n 'I 2/b 2/a 2/m': ' !#$%#*)&(,&$%& !&(,#*)#()+*,+$!- %-*,-()- %+$!+',\n 'I 21/b 21/c 21/a': ' !#*%+$)-(,&$%&(!- ,+*)#()+$,#*!& %-*,- )&(%#$!+',\n 'I 21/m 21/m 21/a': ' !#$,#$)& %&$%& )& ,#$!#()+*%+*!-(,-*,-(!-(%+*)+',\n 'P 4': ' !#$%#% #!$#',\n 'P 41': ' !#$%+% 5!$9',\n 'P 42': ' !#$%#% +!$+',\n 'P 43': ' !#$%+% 9!$5',\n 'I 4': ' !#$%#% #!$#()+*,+,(+)*+',\n 'I 41': ' !#*,+%(5)$9()+$%#, 9!*5',\n 'P -4': ' !#$%#!$&% &',\n 'I -4': ' !#$%#!$&% &()+*,+)*-,(-',\n 'P 4/m': ' !#$%#% #!$#$%& !&!$&% &',\n 'P 42/m': ' !#$%#% +!$+$%& !&!$-% -',\n 'P 4/n': ' !#$%#,(#)*#*,&()&!$&% &',\n 'P 42/n': ' !#$%#,(+)*+*,-()-!$&% &',\n 'I 4/m': ' !#$%#% #!$#$%& !&!$&% &()+*,+,(+)*+*,-()-)*-,(-',\n 'I 41/a': ' !#*,+%(5)$9$,=(!>!$&,(-()+$%#, 9!*5*%> )=)*-% &',\n 'P 4 2 2': ' !#$%#% #!$#$!& %&! &%$&',\n 'P 4 21 2': ' !#$%#,(#)*#*)&(,&! &%$&',\n 'P 41 2 2': ' !#$%+% 5!$9$!& %-! >%$=',\n 'P 41 21 2': ' !#$%+,(5)*9*)=(,>! &%$-',\n 'P 42 2 2': ' !#$%#% +!$+$!& %&! -%$-',\n 'P 42 21 2': ' !#$%#,(+)*+*)-(,-! &%$&',\n 'P 43 2 2': ' !#$%+% 9!$5$!& %-! =%$>',\n 'P 43 21 2': ' !#$%+,(9)*5*)>(,=! &%$-',\n 'I 4 2 2': ' !#$%#% #!$#$!& %&! &%$&()+*,+,(+)*+*)-(,-)(-,*-',\n 'I 41 2 2': ' !#*,+%(5)$9*!> ,=)(-%$&()+$%#, 9!*5$)=(%>! &,*-',\n 'P 4 m m': ' !#$%#% #!$# %#$!#%$#! #',\n 'P 4 b m': ' !#$%#% #!$#(,#*)#,*#)(#',\n 'P 42 c m': ' !#$%#% +!$+ %+$!+%$#! #',\n 'P 42 n m': ' !#$%#,(+)*+(,+*)+%$#! #',\n 'P 4 c c': ' !#$%#% #!$# %+$!+%$+! +',\n 'P 4 n c': ' !#$%#% #!$#(,+*)+,*+)(+',\n 'P 42 m c': ' !#$%#% +!$+ %#$!#%$+! +',\n 'P 42 b c': ' !#$%#% +!$+(,#*)#,*+)(+',\n 'I 4 m m': ' !#$%#% #!$# %#$!#%$#! #()+*,+,(+)*+(,+*)+,*+)(+',\n 'I 4 c m': ' !#$%#% #!$# %+$!+%$+! +()+*,+,(+)*+(,#*)#,*#)(#',\n 'I 41 m d': ' !#*,+%(5)$9 %#*)+%*5) 9()+$%#, 9!*5(,+$!#,$9!(5',\n 'I 41 c d': ' !#*,+%(5)$9 %+*)#%*9) 5()+$%#, 9!*5(,#$!+,$5!(9',\n 'P -4 2 m': ' !#$%#% &!$&$!& %&%$#! #',\n 'P -4 2 c': ' !#$%#% &!$&$!- %-%$+! +',\n 'P -4 21 m': ' !#$%#% &!$&*)&(,&,*#)(#',\n 'P -4 21 c': ' !#$%#% &!$&*)-(,-,*+)(+',\n 'P -4 m 2': ' !#$%#!$&% & %#$!#! &%$&',\n 'P -4 c 2': ' !#$%#% &!$& %+$!+! -%$-',\n 'P -4 b 2': ' !#$%#% &!$&(,#*)#)(&,*&',\n 'P -4 n 2': ' !#$%#% &!$&(,+*)+)(-,*-',\n 'I -4 m 2': ' !#$%#% &!$& %#$!#! &%$&()+*,+,(-)*-(,+*)+)(-,*-',\n 'I -4 c 2': ' !#$%#% &!$& %+$!+! -%$-()+*,+,(-)*-(,#*)#)(&,*&',\n 'I -4 2 m': ' !#$%#% &!$&$!& %&%$#! #()+*,+,(-)*-*)-(,-,*+)(+',\n 'I -4 2 d': ' !#$%#% &!$&*!>(%>,$9) 9()+*,+,(-)*-$)= ,=%*5!(5',\n 'P 4/m 2/m 2/m': ' !#$%#% #!$#$!& %&! &%$&$%& !&!$&% & %#$!#%$#! #',\n 'P 4/m 2/c 2/c': ' !#$%#% #!$#$!- %-! -%$-$%& !&!$&% & %+$!+%$+! +',\n 'P 4/n 2/b 2/m': ' !#$%#% #!$#$!& %&! &%$&*,&()&)*&,(&(,#*)#,*#)(#',\n 'P 4/n 2/n 2/c': ' !#$%#% #!$#$!& %&! &%$&*,-()-)*-,(-(,+*)+,*+)(+',\n 'P 4/m 21/b 2/m': ' !#$%#% #!$#*)&(,&)(&,*&$%& !&!$&% &(,#*)#,*#)(#',\n 'P 4/m 21/n 2/c': ' !#$%#% #!$#*)-(,-)(-,*-$%& !&!$&% &(,+*)+,*+)(+',\n 'P 4/n 21/m 2/m': ' !#$%#,(#)*#*)&(,&! &%$&*,&()&!$&% & %#$!#,*#)(#',\n 'P 4/n 2/c 2/c': ' !#$%#,(#)*#*)-(,-! -%$-*,&()&!$&% & %+$!+,*+)(+',\n 'P 42/m 2/m 2/c': ' !#$%#% +!$+$!& %&! -%$-$%& !&!$-% - %#$!#%$+! +',\n 'P 42/m 2/c 2/m': ' !#$%#% +!$+$!- %-! &%$&$%& !&!$-% - %+$!+%$#! #',\n 'P 42/n 2/b 2/c': ' !#$%#,(+)*+$!- %-)(&,*&*,-()-!$&% &(,#*)#%$+! +',\n 'P 42/n 2/n 2/m': ' !#$%#,(+)*+$!& %&)(-,*-*,-()-!$&% &(,+*)+%$#! #',\n 'P 42/m 21/b 2/c': ' !#$%#% +!$+*)&(,&)(-,*-$%& !&!$-% -(,#*)#,*+)(+',\n 'P 42/m 21/n 2/m': \" !#$%#,./'*/*'-.,-! &%$&$%& !&'*-,.-.,/*'/%$#! #\",\n 'P 42/n 21/m 2/c': ' !#$%#,(+)*+*)-(,-! &%$&*,-()-!$&% & %#$!#,*+)(+',\n 'P 42/n 21/c 2/m': ' !#$%#,(+)*+*)&(,&! -%$-*,-()-!$&% & %+$!+,*#)(#',\n 'I 4/m 2/m 2/m': ' !#$%#% #!$#$!& %&! &%$&$%& !&!$&% & %#$!#%$#! #()+*,+,(+)*+*)-(,-)(-,*-*,-()-)*-,(-(,+*)+,*+)(+',\n 'I 4/m 2/c 2/m': ' !#$%#% #!$#$!- %-! -%$-$%& !&!$&% & %+$!+%$+! +()+*,+,(+)*+*)&(,&)(&,*&*,-()-)*-,(-(,#*)#,*#)(#',\n 'I 41/a 2/m 2/d': ' !#*,+%(5)$9*!> ,=)(-%$&$,=(!>!$&,(-(,+$!#,$9!(5()+$%#, 9!*5$)=(%>! &,*-*%> )=)*-% & %#*)+%*5) 9',\n 'I 41/a 2/c 2/d': ' !#*,+%(5)$9*!= ,>)(&%$-$,=(!>!$&,(-(,#$!+,$5!(9()+$%#, 9!*5$)>(%=! -,*&*%> )=)*-% & %+*)#%*9) 5',\n 'P 3': ' !#%?#@$#',\n 'P 31': ' !#%?A@$B',\n 'P 32': ' !#%?B@$A',\n 'H 3': ' !#%?#@$#CDAEFAGHAIJBKLBMNB',\n 'R 3': ' !## !!# ',\n 'P -3': ' !#%?#@$#$%&!@&? &',\n 'H -3': ' !#%?#@$#$%&!@&? &OPQRSQTUQVWXYZX[]X]Y^W[^ZV^UR_PT_SO_',\n 'R -3': ' !## !!# $%&&$%%&$',\n 'P 3 1 2': ' !#%?#@$#%$&@!& ?&',\n 'P 3 2 1': ' !#%?#@$#! &?%&$@&',\n 'P 31 1 2': ' !#%?Q@$^%$_@!X ?&',\n 'P 31 2 1': ' !#%?A@$B! &?%_$@X',\n 'P 32 1 2': ' !#%?^@$Q%$X@!_ ?&',\n 'P 32 2 1': ' !#%?B@$A! &?%X$@_',\n 'H 3 2': ' !#%?#@$#! &?%&$@&OPQRSQTUQY]X[WXVZX]Y^W[^ZV^PO_SR_UT_',\n 'R 3 2': ' !## !!# %$&$&%&%$',\n 'P 3 m 1': ' !#%?#@$#%$#@!# ?#',\n 'P 3 1 m': ' !#%?#@$#! #?%#$@#',\n 'P 3 c 1': ' !#%?#@$#%$+@!+ ?+',\n 'P 3 1 c': ' !#%?#@$#! +?%+$@+',\n 'H 3 m': ' !#%?#@$#%$#@!# ?#OPQRSQTUQRUQTPQOSQ]Y^W[^ZV^WV^ZY^][^',\n 'R 3 m': ' !## !!# ! # #!#! ',\n 'H 3 c': ' !#%?#@$#%$+@!+ ?+OPQRSQTUQRU`TP`OS`]Y^W[^ZV^WVaZYa][a',\n 'R 3 c': \" !## !!# '././'/'.\",\n 'P -3 1 2/m': ' !#%?#@$#%$&@!& ?&$%&!@&? &! #?%#$@#',\n 'P -3 1 2/c': ' !#%?#@$#%$-@!- ?-$%&!@&? &! +?%+$@+',\n 'P -3 2/m 1': ' !#%?#@$#! &?%&$@&$%&!@&? &%$#@!# ?#',\n 'P -3 2/c 1': ' !#%?#@$#! -?%-$@-$%&!@&? &%$+@!+ ?+',\n 'H -3 2/m': ' !#%?#@$#! &?%&$@&$%&!@&? &%$#@!# ?#OPQRSQTUQY]X[WXVZXVWXYZX[]XRUQTPQOSQ]Y^W[^ZV^PO_SR_UT_UR_PT_SO_WV^ZY^][^',\n 'R -3 2/m': ' !## !!# %$&$&%&%$$%&&$%%&$! # #!#! ',\n 'H -3 2/c': ' !#%?#@$#! -?%-$@-$%&!@&? &%$+@!+ ?+OPQRSQTUQY]b[WbVZbVWXYZX[]XRU`TP`OS`]Y^W[^ZV^POcSRcUTcUR_PT_SO_WVaZYa][a',\n 'R -3 2/c': \" !## !!# 102021210$%&&$%%&$'././'/'.\",\n 'P 6': ' !#%?#@$#$%#!@#? #',\n 'P 61': ' !#%?A@$B$%/!@d? e',\n 'P 65': ' !#%?B@$A$%/!@e? d',\n 'P 62': ' !#%?^@$Q$%#!@^? Q',\n 'P 64': ' !#%?Q@$^$%#!@Q? ^',\n 'P 63': ' !#%?#@$#$%+!@+? +',\n 'P -6': ' !#%?#@$# !&%?&@$&',\n 'P 6/m': ' !#%?#@$#$%#!@#? #$%&!@&? & !&%?&@$&',\n 'P 63/m': ' !#%?#@$#$%+!@+? +$%&!@&? & !-%?-@$-',\n 'P 6 2 2': ' !#%?#@$#$%#!@#? #! &?%&$@&%$&@!& ?&',\n 'P 61 2 2': ' !#%?Q@$^$%+!@`? a! X?%&$@_%$b@!- ?c',\n 'P 65 2 2': ' !#%?^@$Q$%+!@a? `! _?%&$@X%$c@!- ?b',\n 'P 62 2 2': ' !#%?^@$Q$%#!@^? Q! _?%&$@X%$_@!& ?X',\n 'P 64 2 2': ' !#%?Q@$^$%#!@Q? ^! X?%&$@_%$X@!& ?_',\n 'P 63 2 2': ' !#%?#@$#$%+!@+? +! &?%&$@&%$-@!- ?-',\n 'P 6 m m': ' !#%?#@$#$%#!@#? #%$#@!# ?#! #?%#$@#',\n 'P 6 c c': ' !#%?#@$#$%#!@#? #%$+@!+ ?+! +?%+$@+',\n 'P 63 c m': ' !#%?#@$#$%+!@+? +%$+@!+ ?+! #?%#$@#',\n 'P 63 m c': ' !#%?#@$#$%+!@+? +%$#@!# ?#! +?%+$@+',\n 'P -6 m 2': ' !#%?#@$# !&%?&@$&%$#@!# ?#%$&@!& ?&',\n 'P -6 c 2': ' !#%?#@$# !-%?-@$-%$+@!+ ?+%$&@!& ?&',\n 'P -6 2 m': ' !#%?#@$# !&%?&@$&! &?%&$@&! #?%#$@#',\n 'P -6 2 c': ' !#%?#@$# !-%?-@$-! &?%&$@&! +?%+$@+',\n 'P 6/m 2/m 2/m': ' !#%?#@$#$%#!@#? #! &?%&$@&%$&@!& ?&$%&!@&? & !&@$&%?&%$#@!# ?#! #?%#$@#',\n 'P 6/m 2/c 2/c': ' !#%?#@$#$%#!@#? #! -?%-$@-%$-@!- ?-$%&!@&? & !&@$&%?&%$+@!+ ?+! +?%+$@+',\n 'P 63/m 2/c 2/m': ' !#%?#@$#$%+!@+? +! -?%-$@-%$&@!& ?&$%&!@&? & !-@$-%?-%$+@!+ ?+! #?%#$@#',\n 'P 63/m 2/m 2/c': ' !#%?#@$#$%+!@+? +! &?%&$@&%$-@!- ?-$%&!@&? & !-@$-%?-%$#@!# ?#! +?%+$@+',\n 'P 2 3': ' !#$%#$!& %&# !#$%&$!& %!# %#$!&$%& ',\n 'F 2 3': ' !#$%#$!& %&# !#$%&$!& %!# %#$!&$%& )+$,+$)- ,-#()#*,&*)&(,!+(%+*!-*%-((!+*%+*!-(%-+ )+$,-$)- ,)#(,#*)&*,&(()#*,#*)&(,&+(!+*%-*!-(%)+ ,+$)-$,- ',\n 'I 2 3': ' !#$%#$!& %&# !#$%&$!& %!# %#$!&$%& ()+*,+*)-(,-+()+*,-*)-(,)+(,+*)-*,-(',\n 'P 21 3': ' !#*%+$)-(,&# !+*%-$)&(,!# %+*)-$,&(',\n 'I 21 3': ' !#*%+$)-(,&# !+*%-$)&(,!# %+*)-$,&(()+$,#*!& %-+()#$,&*!- %)+(,#$!&*%- ',\n 'P 2/m -3': ' !#$%#$!& %&# !#$%&$!& %!# %#$!&$%& $%& !& %#$!#&$%& !# %#$!%&$!& %# !#$',\n 'P 2/n -3': ' !#$%#$!& %&# !#$%&$!& %!# %#$!&$%& *,-()-(,+*)+-*,-()+(,+*),-*)-(,+()+*',\n 'F 2/m -3': ' !#$%#$!& %&# !#$%&$!& %!# %#$!&$%& $%& !& %#$!#&$%& !# %#$!%&$!& %# !#$ )+$,+$)- ,-#()#*,&*)&(,!+(%+*!-*%-($,- )- ,+$)+&*,&()#(,#*)%-*!-(%+(!+*(!+*%+*!-(%-+ )+$,-$)- ,)#(,#*)&*,&(*%-(!-(%+*!+-$,- )+ ,+$),&*)&(,#()#*()#*,#*)&(,&+(!+*%-*!-(%)+ ,+$)-$,- *,&()&(,#*)#-*%-(!+(%+*!,-$)- ,+ )+$',\n 'F 2/d -3': ' !#$%#$!& %&# !#$%&$!& %!# %#$!&$%& 64=37=345675=64=375345674=67=3453756 )+$,+$)- ,-#()#*,&*)&(,!+(%+*!-*%-(68>3:>3896:9=<8=;:5;85<:4><7>;49;79<(!+*%+*!-(%-+ )+$,-$)- ,)#(,#*)&*,&(<4>;7>;49<79>68>3:93896:8=<:=;85;:5<()#*,#*)&(,&+(!+*%-*!-(%)+ ,+$)-$,- <8=;:=;8f<:f><4>;79;49<78>6:>3893:96',\n 'I 2/m -3': ' !#$%#$!& %&# !#$%&$!& %!# %#$!&$%& $%& !& %#$!#&$%& !# %#$!%&$!& %# !#$()+*,+*)-(,-+()+*,-*)-(,)+(,+*)-*,-(*,-()-(,+*)+-*,-()+(,+*),-*)-(,+()+*',\n 'P 21/a -3': ' !#*%+$)-(,&# !+*%-$)&(,!# %+*)-$,&($%&(!- ,+*)#&$%-(!+ ,#*)%&$!-(,+ )#*',\n 'I 21/a -3': ' !#*%+$)-(,&# !+*%-$)&(,!# %+*)-$,&($%&(!- ,+*)#&$%-(!+ ,#*)%&$!-(,+ )#*()+$,#*g& %-+()#$,&*!- %)+(,#$!&*%- *,- )&(%#$!+-*,& )#(%+$!,-*)& %#(!+$',\n 'P 4 3 2': ' !#$%#$!& %&# !#$%&$!& %!# %#$!&$%& ! &%$&!$#% # #%$#!$&% &!#!$#% &! &%$',\n 'P 42 3 2': ' !#$%#$!& %&# !#$%&$!& %!# %#$!&$%& )(-,*-)*+,(+(+,*+)*-,(-)+)*+,(-)(-,*',\n 'F 4 3 2': ' !#$%#$!& %&# !#$%&$!& %!# %#$!&$%& ! &%$&!$#% # #%$#!$&% &!#!$#% &! &%$ )+$,+$)- ,-#()#*,&*)&(,!+(%+*!-*%-(!(-%*-!*+%(+ +,$+)$-, -)#)*#,(&)(&,*(!+*%+*!-(%-+ )+$,-$)- ,)#(,#*)&*,&() -,$-)$+, +(#,*#)*&,(&)+!*+%(-!(-%*()#*,#*)&(,&+(!+*%-*!-(%)+ ,+$)-$,- )(&,*&)*#,(#(+%*+!*-%(-!+)$+, -) -,$',\n 'F 41 3 2': ' !#$,+*)&(%-# !+$,&*)-(%!# ,+$)&*%-(:3>46=7<98;5;58<976=43>:97<58;>:3=46 )+$%#*!-(,&#()+*%&$!- ,!+(,#*)-$%& :;=4<>765839;94<5:6>83=79:6543>7;=8<(!+*,#$)- %&+ )#$%-*!&(,)#(%+*!&$,- 73=86>:<54;935469:<=8;>7576983=:;>4<()#*%+$!& ,-+(!#*,-$)& %)+ %#$!-*,&(7;>8<=:69435398657<>4;=:5:<94;=73>86',\n 'I 4 3 2': ' !#$%#$!& %&# !#$%&$!& %!# %#$!&$%& ! &%$&!$#% # #%$#!$&% &!#!$#% &! &%$()+*,+*)-(,-+()+*,-*)-(,)+(,+*)-*,-()(-,*-)*+,(+(+,*+)*-,(-)+)*+,(-)(-,*',\n 'P 43 3 2': ' !#*%+$)-(,&# !+*%-$)&(,!# %+*)-$,&(7;>46=:<5839398<5:6=4;>75:<983>7;=46',\n 'P 41 3 2': ' !#*%+$)-(,&# !+*%-$)&(,!# %+*)-$,&(:3=8<>7694;5;54697<>83=:97654;=:3>8<',\n 'I 41 3 2': ' !#*%+$)-(,&# !+*%-$)&(,!# %+*)-$,&(:3=8<>7694;5;54697<>83=:97654;=:3>8<()+$,#*!& %-+()#$,&*!- %)+(,#$!&*%- 7;>46=:<5839398<5:6=4;>75:<983>7;=46',\n 'P -4 3 m': ' !#$%#$!& %&# !#$%&$!& %!# %#$!&$%& ! #%$#!$&% & #!$#%$&! &%#! #%$&!$&% ',\n 'F -4 3 m': ' !#$%#$!& %&# !#$%&$!& %!# %#$!&$%& ! #%$#!$&% & #!$#%$&! &%#! #%$&!$&% )+$,+$)- ,-#()#*,&*)&(,!+(%+*!-*%-(!(+%*+!*-%(- +)$+,$-) -,#)(#,*&)*&,((!+*%+*!-(%-+ )+$,-$)- ,)#(,#*)&*,&() +,$+)$-, -(#)*#,*&)(&,+!(+%*-!*-%(()#*,#*)&(,&+(!+*%-*!-(%)+ ,+$)-$,- )(#,*#)*&,(&(+!*+%*-!(-%+) +,$-)$-, ',\n 'I -4 3 m': ' !#$%#$!& %&# !#$%&$!& %!# %#$!&$%& ! #%$#!$&% & #!$#%$&! &%#! #%$&!$&% ()+*,+*)-(,-+()+*,-*)-(,)+(,+*)-*,-()(+,*+)*-,(-(+)*+,*-)(-,+)(+,*-)*-,(',\n 'P -4 3 n': ' !#$%#$!& %&# !#$%&$!& %!# %#$!&$%& )(+,*+)*-,(-(+)*+,*-)(-,+)(+,*-)*-,(',\n 'F -4 3 c': ' !#$%#$!& %&# !#$%&$!& %!# %#$!&$%& )(+,*+)*-,(-(+)*+,*-)(-,+)(+,*-)*-,( )+$,+$)- ,-#()#*,&*)&(,!+(%+*!-*%-() #,$#)$&, &(#!*#%*&!(&%+! +%$-!$-% (!+*%+*!-(%-+ )+$,-$)- ,)#(,#*)&*,&(!(#%*#!*&%(& +!$+%$-! -%#) #,$&)$&, ()#*,#*)&(,&+(!+*%-*!-(%)+ ,+$)-$,- ! +%$+!$-% - #)$#,$&) &,#!(#%*&!*&%(',\n 'I -4 3 d': ' !#*%+$)-(,&# !+*%-$)&(,!# %+*)-$,&(7354<9:6>8;=357<946>:;=857394<>:6=8;()+$,#*!& %-+()#$,&*!- %)+(,#$!&*%- :;98657<=43>;9:658<=73>49:;586=7<>43',\n 'P 4/m -3 2/m': ' !#$%#$!& %&# !#$%&$!& %!# %#$!&$%& ! &%$&!$#% # #%$#!$&% &!#!$#% &! &%$$%& !& %#$!#&$%& !# %#$!%&$!& %# !#$%$#! #% &!$&$&! &% #!$#%&% &!$#%$#! ',\n 'P 4/n -3 2/n': ' !#$%#$!& %&# !#$%&$!& %!# %#$!&$%& ! &%$&!$#% # #%$#!$&% &!#!$#% &! &%$*,-()-(,+*)+-*,-()+(,+*),-*)-(,+()+*,*+)(+,(-)*-*-)(-,(+)*+,-,(-)*+,*+)(',\n 'P 42/m -3 2/n': ' !#$%#$!& %&# !#$%&$!& %!# %#$!&$%& )(-,*-)*+,(+(+,*+)*-,(-)+)*+,(-)(-,*$%& !& %#$!#&$%& !# %#$!%&$!& %# !#$,*+)(+,(-)*-*-)(-,(+)*+,-,(-)*+,*+)(',\n 'P 42/n -3 2/m': ' !#$%#$!& %&# !#$%&$!& %!# %#$!&$%& )(-,*-)*+,(+(+,*+)*-,(-)+)*+,(-)(-,**,-()-(,+*)+-*,-()+(,+*),-*)-(,+()+*%$#! #% &!$&$&! &% #!$#%&% &!$#%$#! ',\n 'F 4/m -3 2/m': ' !#$%#$!& %&# !#$%&$!& %!# %#$!&$%& ! &%$&!$#% # #%$#!$&% &!#!$#% &! &%$$%& !& %#$!#&$%& !# %#$!%&$!& %# !#$%$#! #% &!$&$&! &% #!$#%&% &!$#%$#! )+$,+$)- ,-#()#*,&*)&(,!+(%+*!-*%-(!(-%*-!*+%(+ +,$+)$-, -)#)*#,(&)(&,*$,- )- ,+$)+&*,&()#(,#*)%-*!-(%+(!+*%*+!(+%(-!*-$-) -, +)$+,&,(&)*#,*#)((!+*%+*!-(%-+ )+$,-$)- ,)#(,#*)&*,&() -,$-)$+, +(#,*#)*&,(&)+!*+%(-!(-%**%-(!-(%+*!+-$,- )+ ,+$),&*)&(,#()#*,$+) +, -)$-*&)(&,(#)*#,-%(-!*+%*+!(()#*,#*)&(,&+(!+*%-*!-(%)+ ,+$)-$,- )(&,*&)*#,(#(+%*+!*-%(-!+)$+, -) -,$*,&()&(,#*)#-*%-(!+(%+*!,-$)- ,+ )+$,*#)(#,(&)*&*-!(-%(+!*+%-, -)$+,$+) ',\n 'F 4/m -3 2/c': ' !#$%#$!& %&# !#$%&$!& %!# %#$!&$%& )(-,*-)*+,(+(+,*+)*-,(-)+)*+,(-)(-,*$%& !& %#$!#&$%& !# %#$!%&$!& %# !#$,*+)(+,(-)*-*-)(-,(+)*+,-,(-)*+,*+)( )+$,+$)- ,-#()#*,&*)&(,!+(%+*!-*%-() &,$&)$#, #(#%*#!*&%(&!+!$+% -! -%$$,- )- ,+$)+&*,&()#(,#*)%-*!-(%+(!+*,$#) #, &)$&*&!(&%(#!*#%-% -!$+%$+! (!+*%+*!-(%-+ )+$,-$)- ,)#(,#*)&*,&(!(&%*&!*#%(# +%$+!$-% -!#)$#, &) &,$*%-(!-(%+*!+-$,- )+ ,+$),&*)&(,#()#*%*#!(#%(&!*&$-! -% +!$+%&, &)$#,$#) ()#*,#*)&(,&+(!+*%-*!-(%)+ ,+$)-$,- ! -%$-!$+% + #,$#)$&, &)#!*#%(&!(&%**,&()&(,#*)#-*%-(!+(%+*!,-$)- ,+ )+$%$+! +% -!$-$&) &, #)$#,&%(&!*#%*#!(',\n 'F 41/d -3 2/m': ' !#$,+*)&(%-# !+$,&*)-(%!# ,+$)&*%-(:3>46=7<98;5;58<976=43>:97<58;>:3=4664=3:>;85<79=64>3:5;89<74=6:>385;79<,$+! #%(-)*&*&)(-% #!$+,-%(&)*+,$#! )+$%#*!-(,&#()+*%&$!- ,!+(,#*)-$%& :;=4<>765839;94<5:6>83=79:6543>7;=8<68>37=;49<:5=<8>;753496:4><:=;893756,*#!(+% &)$-*-!(&, +)$#%-, &!$+%*#)((!+*,#$)- %&+ )#$%-*!&(,)#(%+*!&$,- 73=86>:<54;935469:<=8;>7576983=:;>4<<4>;:=389675>68=379;45<:8=<7>;453:96%$#) +,(&!*-$&! -,(#)*+%&% -)$#,*+!(()#*%+$!& ,-+(!#*,-$)& %)+ %#$!-*,&(7;>8<=:69435398657<>4;=:5:<94;=73>86<8=;7>3456:9><4=;:9385678>67=349;:5<%*+)(#, -!$&$-) &%(+!*#,&,(-!*#%$+) ',\n 'F 41/d -3 2/c': ' !#$,+*)&(%-# !+$,&*)-(%!# ,+$)&*%-(:3>46=7<98;5;58<976=43>:97<58;>:3=46<8>;7=3496:5><8=;793456:8><7=;493:56%*#)(+, &!$-$-! &,(+)*#%&, -!$#%*+)( )+$%#*!-(,&#()+*%&$!- ,!+(,#*)-$%& :;=4<>765839;94<5:6>83=79:6543>7;=8<<4=;:>385679>64=3:9;85<78=67>345;:9<%$+) #,(-!*&$&) -%(#!*+,&%(-)*#,$+! (!+*,#$)- %&+ )#$%-*!&(,)#(%+*!&$,- 73=86>:<54;935469:<=8;>7576983=:;>4<68=37>;45<:9=<4>;:5389674>6:=389;75<,*+!(#% -)$&*-)(&% +!$#,-,(&!*+%$#) ()#*%+$!& ,-+(!#*,-$)& %)+ %#$!-*,&(7;>8<=:69435398657<>4;=:5:<94;=73>8664>3:=;89<75=68>375;49<:4=<:>;853796,$#! +%(&)*-*&!(-, #)$+%-% &)$+,*#!(',\n 'I 4/m -3 2/m': ' !#$%#$!& %&# !#$%&$!& %!# %#$!&$%& ! &%$&!$#% # #%$#!$&% &!#!$#% &! &%$$%& !& %#$!#&$%& !# %#$!%&$!& %# !#$%$#! #% &!$&$&! &% #!$#%&% &!$#%$#! ()+*,+*)-(,-+()+*,-*)-(,)+(,+*)-*,-()(-,*-)*+,(+(+,*+)*-,(-)+)*+,(-)(-,**,-()-(,+*)+-*,-()+(,+*),-*)-(,+()+*,*+)(+,(-)*-*-)(-,(+)*+,-,(-)*+,*+)(',\n 'I 41/a -3 2/d': ' !#*%+$)-(,&# !+*%-$)&(,!# %+*)-$,&(:3=8<>7694;5;54697<>83=:97654;=:3>8<$%&(!- ,+*)#&$%-(!+ ,#*)%&$!-(,+ )#*4<97358;=:6>6>:;=8357<94=8;>:694<573()+$,#*!& %-+()#$,&*!- %)+(,#$!&*%- 7;>46=:<5839398<5:6=4;>75:<983>7;=46*,- )&(%#$!+-*,& )#(%+$!,-*)& %#(!+$865:;943>7<=<=73>4;9:658>43=7<5869:;',\n 'P 1 1 2': ' !#$%#',\n 'P 1 1 21': ' !#$%+',\n 'B 1 1 2': ' !#$%#(g+*%+',\n 'A 1 2 1': ' !#$!& )+$)-',\n 'C 1 21 1': ' !#$)&()#*!&',\n 'I 1 2 1': \" !#$!&.'/0'2\",\n 'I 1 21 1': \" !#$)&.'/0!-\",\n 'P 1 1 m': ' !# !&',\n 'P 1 1 b': ' !# )&',\n 'B 1 1 m': ' !# !&(!+(!-',\n 'B 1 1 b': ' !# )&(!+()-',\n 'P 1 1 2/m': ' !# !&$%#$%&',\n 'P 1 1 21/m': ' !#$%+$%& !-',\n 'B 1 1 2/m': ' !# !&$%#$%&(!+(!-*%+*%-',\n 'P 1 1 2/b': ' !#$,#$%& )&',\n 'P 1 1 21/b': ' !#$%&$,+ )-',\n 'B 1 1 2/b': ' !#$,#$%& )&(!+*,+*%-()-',\n 'P 21 2 2': ' !#$!&(%&*%#',\n 'P 2 21 2': ' !# ,&$)&$%#',\n 'P 21 21 2 (a)': \" !#*,#.%&$'&\",\n 'P 21 2 21': ' !#$!&(%-*%+',\n 'P 2 21 21': ' !# %&$)-$,+',\n 'C 2 2 21a)': ' !#*%+(,&$)-()#$,+ %&*!-',\n 'C 2 2 2a': \" !#*,#.%&$'&()#$%# ,&*!&\",\n 'F 2 2 2a': \" !#*,#.%&$'& '/*%/.12$!2.!/$,/ %20'2.'#$%# 1&0!&\",\n 'I 2 2 2a': \" !#*,#.%&$'&()+$%+*!- ,-\",\n 'P 21/m 21/m 2/n a': \" !#*,#$)&(%&$%&.'& ,#*!#\",\n 'P 42 21 2a': \" !#*,#%.+'$+$'&.%&! -,*-\",\n 'I 2 3a': \" !#*,#.%&$'&!# ,- '&$%/$# !-*!/$%&.%()+$%+ ,-*!-)+(%&(!-*,#*+()&$)#*,- ,\"\n}\n","/**\n * @file Symmetry Utils\n * @author Alexander Rose \n * @private\n */\n\nimport { Matrix4 } from 'three'\n\nimport { Log } from '../globals'\nimport { EncodedSymOp, SymOpCode } from './symmetry-constants'\n\nconst reInteger = /^[1-9]$/\n\nexport function getSymmetryOperations (spacegroup: string) {\n const encodedSymopList = EncodedSymOp[ spacegroup ]\n const matrixDict: { [k: string]: Matrix4 } = {}\n\n if (encodedSymopList === undefined) {\n console.warn(`spacegroup '${spacegroup}' not found in symop library`)\n return matrixDict\n }\n\n const symopList = []\n for (let i = 0, il = encodedSymopList.length; i < il; i += 3) {\n const symop = []\n for (let j = 0; j < 3; ++j) {\n symop.push(SymOpCode[ encodedSymopList[ i + j ] ])\n }\n symopList.push(symop)\n }\n\n symopList.forEach(function (symop) {\n let row = 0\n const matrix = new Matrix4().set(\n 0, 0, 0, 0,\n 0, 0, 0, 0,\n 0, 0, 0, 0,\n 0, 0, 0, 1\n )\n const me = matrix.elements\n\n matrixDict[ symop.toString() ] = matrix\n\n symop.forEach(function (elm) {\n let negate = false\n let denominator = false\n\n for (let i = 0, n = elm.length; i < n; ++i) {\n const c = elm[ i ]\n\n if (c === '-') {\n negate = true\n } else if (c === '+') {\n negate = false\n } else if (c === '/') {\n denominator = true\n } else if (c === 'X') {\n me[ 0 + row ] = negate ? -1 : 1\n } else if (c === 'Y') {\n me[ 4 + row ] = negate ? -1 : 1\n } else if (c === 'Z') {\n me[ 8 + row ] = negate ? -1 : 1\n } else if (reInteger.test(c)) {\n const integer = parseInt(c)\n if (denominator) {\n me[ 12 + row ] /= integer\n } else {\n me[ 12 + row ] = integer\n }\n } else {\n Log.warn(`getSymmetryOperations: unknown token '${c}'`)\n }\n }\n\n row += 1\n })\n })\n\n return matrixDict\n}\n","/**\n * @file Assembly\n * @author Alexander Rose \n * @private\n */\n\nimport { Matrix4, Box3, Vector3 } from 'three'\n\nimport { uniqueArray } from '../utils'\nimport Selection from '../selection/selection'\nimport Structure from '../structure/structure'\nimport StructureView from '../structure/structure-view';\n\nfunction selectionFromChains (chainList: string[]) {\n let sele = ''\n if (chainList.length > 0) {\n sele = ':' + uniqueArray(chainList).join(' OR :')\n }\n return new Selection(sele)\n}\n\n/**\n * Assembly of transformed parts of a {@link Structure}\n */\nclass Assembly {\n partList: AssemblyPart[] = []\n\n /**\n * @param {String} name - assembly name\n */\n constructor (readonly name = '') {}\n\n get type () { return 'Assembly' }\n\n /**\n * Add transformed parts to the assembly\n * @example\n * var m1 = new NGL.Matrix4().set( ... );\n * var m2 = new NGL.Matrix4().set( ... );\n * var assembly = new NGL.Assembly( \"myAssembly\" );\n * // add part that transforms chain 'A' and 'B' using matrices `m1` and `m2`\n * assembly.addPart( [ m1, m2 ], [ \"A\", \"B\" ] )\n *\n * @param {Matrix4[]} matrixList - array of 4x4 transformation matrices\n * @param {String[]} chainList - array of chain names\n * @return {AssemblyPart} the added assembly part\n */\n addPart (matrixList?: Matrix4[], chainList?: string[]) {\n const part = new AssemblyPart(matrixList, chainList)\n this.partList.push(part)\n return part\n }\n\n /**\n * Get the number of atom for a given structure\n * @param {Structure} structure - the given structure\n * @return {Integer} number of atoms in the assembly\n */\n getAtomCount (structure: Structure) {\n return this.partList.reduce(\n (count, part) => count + part.getAtomCount(structure), 0\n )\n }\n\n /**\n * Get the number of residues for a given structure\n * @param {Structure} structure - the given structure\n * @return {Integer} number of residues in the assembly\n */\n getResidueCount (structure: Structure) {\n return this.partList.reduce(\n (count, part) => count + part.getResidueCount(structure), 0\n )\n }\n\n /**\n * Get number of instances the assembly will produce, i.e.\n * the number of transformations performed by the assembly\n * @return {Integer} number of instances\n */\n getInstanceCount () {\n let instanceCount = 0\n\n this.partList.forEach(function (part) {\n instanceCount += part.matrixList.length\n })\n\n return instanceCount\n }\n\n /**\n * Determine if the assembly is the full and untransformed structure\n * @param {Structure} structure - the given structure\n * @return {Boolean} whether the assembly is identical to the structure\n */\n isIdentity (structure: Structure) {\n if (this.partList.length !== 1) return false\n\n const part = this.partList[ 0 ]\n if (part.matrixList.length !== 1) return false\n\n const identityMatrix = new Matrix4()\n if (!identityMatrix.equals(part.matrixList[ 0 ])) return false\n\n let structureChainList: string[] = []\n structure.eachChain(function (cp) {\n structureChainList.push(cp.chainname)\n })\n structureChainList = uniqueArray(structureChainList)\n if (part.chainList.length !== structureChainList.length) return false\n\n return true\n }\n\n getBoundingBox (structure: Structure) {\n const boundingBox = new Box3()\n\n this.partList.forEach(function (part) {\n const partBox = part.getBoundingBox(structure)\n boundingBox.expandByPoint(partBox.min)\n boundingBox.expandByPoint(partBox.max)\n })\n\n return boundingBox\n }\n\n getCenter (structure: Structure) {\n return this.getBoundingBox(structure).getCenter(new Vector3())\n }\n\n getSelection () {\n let chainList: string[] = []\n this.partList.forEach(function (part) {\n chainList = chainList.concat(part.chainList)\n })\n return selectionFromChains(chainList)\n }\n}\n\nexport class AssemblyPart {\n constructor (readonly matrixList: Matrix4[] = [], readonly chainList: string[] = []) {}\n\n get type () { return 'AssemblyPart' }\n\n _getCount (structure: Structure, propertyName: 'atomCount'|'residueCount') {\n let count = 0\n\n structure.eachChain(cp => {\n if (this.chainList.length === 0 || this.chainList.includes(cp.chainname)) {\n count += cp[ propertyName ]\n }\n })\n\n return this.matrixList.length * count\n }\n\n getAtomCount (structure: Structure) {\n return this._getCount(structure, 'atomCount')\n }\n\n getResidueCount (structure: Structure) {\n return this._getCount(structure, 'residueCount')\n }\n\n getBoundingBox (structure: Structure) {\n const partBox = new Box3()\n const instanceBox = new Box3()\n\n const selection = this.getSelection()\n const structureBox = structure.getBoundingBox(selection)\n\n this.matrixList.forEach(function (matrix) {\n instanceBox.copy(structureBox).applyMatrix4(matrix)\n partBox.expandByPoint(instanceBox.min)\n partBox.expandByPoint(instanceBox.max)\n })\n\n return partBox\n }\n\n getSelection () {\n return selectionFromChains(this.chainList)\n }\n\n getView (structure: Structure): Structure | StructureView {\n const selection = this.getSelection()\n if (selection) {\n return structure.getView(selection)\n } else {\n return structure\n }\n }\n\n getInstanceList () {\n const instanceList = []\n for (let j = 0, jl = this.matrixList.length; j < jl; ++j) {\n instanceList.push({\n id: j + 1,\n name: j,\n matrix: this.matrixList[ j ]\n })\n }\n return instanceList\n }\n}\n\nexport default Assembly\n","/**\n * @file Structure Builder\n * @author Alexander Rose \n * @private\n */\n\nimport Structure from './structure'\n\nclass StructureBuilder {\n currentModelindex: number|null = null\n currentChainid: string|null = null\n currentResname: string|null = null\n currentResno: number|null = null\n currentInscode: string|undefined = undefined\n currentHetero: boolean|null = null\n\n previousResname: string|null = ''\n previousHetero: boolean|null = null\n\n ai = -1\n ri = -1\n ci = -1\n mi = -1\n\n constructor(readonly structure: Structure) {}\n\n addResidueType (ri: number) {\n const atomStore = this.structure.atomStore\n const residueStore = this.structure.residueStore\n const residueMap = this.structure.residueMap\n const cc = this.structure.chemCompMap?.dict[this.previousResname!]\n\n const count = residueStore.atomCount[ ri ]\n const offset = residueStore.atomOffset[ ri ]\n const atomTypeIdList = new Array(count)\n for (let i = 0; i < count; ++i) {\n atomTypeIdList[ i ] = atomStore.atomTypeId[ offset + i ]\n }\n const chemCompType = cc?.chemCompType\n const bonds = cc ? this.structure.chemCompMap?.getBonds(this.previousResname!, atomTypeIdList) : undefined\n residueStore.residueTypeId[ ri ] = residueMap.add(\n this.previousResname!, atomTypeIdList, this.previousHetero!, chemCompType, bonds\n )\n }\n\n addAtom (modelindex: number, chainname: string, chainid: string, resname: string, resno: number, hetero: boolean, sstruc?: string|undefined, inscode?: string|undefined) {\n const atomStore = this.structure.atomStore\n const residueStore = this.structure.residueStore\n const chainStore = this.structure.chainStore\n const modelStore = this.structure.modelStore\n\n let addModel = false\n let addChain = false\n let addResidue = false\n\n if (this.currentModelindex !== modelindex) {\n addModel = true\n addChain = true\n addResidue = true\n this.mi += 1\n this.ci += 1\n this.ri += 1\n } else if (this.currentChainid !== chainid) {\n addChain = true\n addResidue = true\n this.ci += 1\n this.ri += 1\n } else if (this.currentResno !== resno || this.currentResname !== resname || this.currentInscode !== inscode) {\n addResidue = true\n this.ri += 1\n }\n this.ai += 1\n\n if (addModel) {\n modelStore.growIfFull()\n modelStore.chainOffset[ this.mi ] = this.ci\n modelStore.chainCount[ this.mi ] = 0\n modelStore.count += 1\n chainStore.modelIndex[ this.ci ] = this.mi\n }\n\n if (addChain) {\n chainStore.growIfFull()\n chainStore.setChainname(this.ci, chainname)\n chainStore.setChainid(this.ci, chainid)\n chainStore.residueOffset[ this.ci ] = this.ri\n chainStore.residueCount[ this.ci ] = 0\n chainStore.count += 1\n chainStore.modelIndex[ this.ci ] = this.mi\n modelStore.chainCount[ this.mi ] += 1\n residueStore.chainIndex[ this.ri ] = this.ci\n }\n\n if (addResidue) {\n this.previousResname = this.currentResname\n this.previousHetero = this.currentHetero\n if (this.ri > 0) this.addResidueType(this.ri - 1)\n residueStore.growIfFull()\n residueStore.resno[ this.ri ] = resno\n if (sstruc !== undefined) {\n residueStore.sstruc[ this.ri ] = sstruc.charCodeAt(0)\n }\n if (inscode !== undefined) {\n residueStore.inscode[ this.ri ] = inscode.charCodeAt(0)\n }\n residueStore.atomOffset[ this.ri ] = this.ai\n residueStore.atomCount[ this.ri ] = 0\n residueStore.count += 1\n residueStore.chainIndex[ this.ri ] = this.ci\n chainStore.residueCount[ this.ci ] += 1\n }\n\n atomStore.count += 1\n atomStore.residueIndex[ this.ai ] = this.ri\n residueStore.atomCount[ this.ri ] += 1\n\n this.currentModelindex = modelindex\n this.currentChainid = chainid\n this.currentResname = resname\n this.currentResno = resno\n this.currentInscode = inscode\n this.currentHetero = hetero\n }\n\n finalize () {\n this.previousResname = this.currentResname\n this.previousHetero = this.currentHetero\n if (this.ri > -1) this.addResidueType(this.ri)\n }\n}\n\nexport default StructureBuilder\n","/**\n * @file Structure Utils\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3, Matrix4 } from 'three'\n\nimport { Debug, Log } from '../globals'\nimport { binarySearchIndexOf } from '../utils'\nimport Helixbundle from '../geometry/helixbundle'\nimport Kdtree from '../geometry/kdtree'\nimport { getSymmetryOperations } from '../symmetry/symmetry-utils'\nimport Assembly from '../symmetry/assembly'\nimport Structure from '../structure/structure'\nimport StructureBuilder from '../structure/structure-builder'\nimport Polymer from '../proxy/polymer'\nimport ResidueProxy from '../proxy/residue-proxy'\n\nimport { UnknownBackboneType, AA3, Bases, AtomicNumbers } from './structure-constants'\n\nexport function reorderAtoms (structure: Structure) {\n if (Debug) Log.time('reorderAtoms')\n\n var ap1 = structure.getAtomProxy()\n var ap2 = structure.getAtomProxy()\n\n function compareModelChainResno (index1: number, index2: number) {\n ap1.index = index1\n ap2.index = index2\n if (ap1.modelIndex < ap2.modelIndex) {\n return -1\n } else if (ap1.modelIndex > ap2.modelIndex) {\n return 1\n } else {\n if (ap1.chainname < ap2.chainname) {\n return -1\n } else if (ap1.chainname > ap2.chainname) {\n return 1\n } else {\n if (ap1.resno < ap2.resno) {\n return -1\n } else if (ap1.resno > ap2.resno) {\n return 1\n } else {\n return 0\n }\n }\n }\n }\n\n structure.atomStore.sort(compareModelChainResno)\n\n if (Debug) Log.timeEnd('reorderAtoms')\n}\n\nexport interface SecStruct {\n helices: [string, number, string, string, number, string, number][]\n sheets: [string, number, string, string, number, string][]\n}\n\nexport function assignSecondaryStructure (structure: Structure, secStruct: SecStruct) {\n if (!secStruct) return\n\n if (Debug) Log.time('assignSecondaryStructure')\n\n const chainnames: string[] = []\n structure.eachModel(function (mp) {\n mp.eachChain(function (cp) {\n chainnames.push(cp.chainname)\n })\n })\n\n const chainnamesSorted = chainnames.slice().sort()\n const chainnamesIndex: number[] = []\n chainnamesSorted.forEach(function (c) {\n chainnamesIndex.push(chainnames.indexOf(c))\n })\n\n // helix assignment\n\n const helices = secStruct.helices.filter(function (h) {\n return binarySearchIndexOf(chainnamesSorted, h[ 0 ]) >= 0\n })\n\n helices.sort(function (h1, h2) {\n const c1 = h1[ 0 ]\n const c2 = h2[ 0 ]\n const r1 = h1[ 1 ]\n const r2 = h2[ 1 ]\n\n if (c1 === c2) {\n if (r1 === r2) {\n return 0\n } else {\n return r1 < r2 ? -1 : 1\n }\n } else {\n const idx1 = binarySearchIndexOf(chainnamesSorted, c1)\n const idx2 = binarySearchIndexOf(chainnamesSorted, c2)\n return chainnamesIndex[ idx1 ] < chainnamesIndex[ idx2 ] ? -1 : 1\n }\n })\n\n const residueStore = structure.residueStore\n\n structure.eachModel(function (mp) {\n let i = 0\n const n = helices.length\n if (n === 0) return\n let helix = helices[ i ]\n let helixRun = false\n let done = false\n\n mp.eachChain(function (cp) {\n let chainChange = false\n\n if (cp.chainname === helix[ 0 ]) {\n const count = cp.residueCount\n const offset = cp.residueOffset\n const end = offset + count\n\n for (let j = offset; j < end; ++j) {\n if (residueStore.resno[ j ] === helix[ 1 ] && // resnoBeg\n residueStore.getInscode(j) === helix[ 2 ] // inscodeBeg\n ) {\n helixRun = true\n }\n\n if (helixRun) {\n residueStore.sstruc[ j ] = helix[ 6 ]\n\n if (residueStore.resno[ j ] === helix[ 4 ] && // resnoEnd\n residueStore.getInscode(j) === helix[ 5 ] // inscodeEnd\n ) {\n helixRun = false\n i += 1\n\n if (i < n) {\n // must look at previous residues as\n // residues may not be ordered by resno\n j = offset - 1\n helix = helices[ i ]\n chainChange = cp.chainname !== helix[ 0 ]\n } else {\n done = true\n }\n }\n }\n\n if (chainChange || done) return\n }\n }\n })\n })\n\n // sheet assignment\n\n const sheets = secStruct.sheets.filter(function (s) {\n return binarySearchIndexOf(chainnamesSorted, s[ 0 ]) >= 0\n })\n\n sheets.sort(function (s1, s2) {\n const c1 = s1[ 0 ]\n const c2 = s2[ 0 ]\n\n if (c1 === c2) return 0\n const idx1 = binarySearchIndexOf(chainnamesSorted, c1)\n const idx2 = binarySearchIndexOf(chainnamesSorted, c2)\n return chainnamesIndex[ idx1 ] < chainnamesIndex[ idx2 ] ? -1 : 1\n })\n\n const strandCharCode = 'e'.charCodeAt(0)\n structure.eachModel(function (mp) {\n let i = 0\n const n = sheets.length\n if (n === 0) return\n let sheet = sheets[ i ]\n let sheetRun = false\n let done = false\n\n mp.eachChain(function (cp) {\n let chainChange = false\n\n if (cp.chainname === sheet[ 0 ]) {\n const count = cp.residueCount\n const offset = cp.residueOffset\n const end = offset + count\n\n for (let j = offset; j < end; ++j) {\n if (residueStore.resno[ j ] === sheet[ 1 ] && // resnoBeg\n residueStore.getInscode(j) === sheet[ 2 ] // inscodeBeg\n ) {\n sheetRun = true\n }\n\n if (sheetRun) {\n residueStore.sstruc[ j ] = strandCharCode\n\n if (residueStore.resno[ j ] === sheet[ 4 ] && // resnoEnd\n residueStore.getInscode(j) === sheet[ 5 ] // inscodeEnd\n ) {\n sheetRun = false\n i += 1\n\n if (i < n) {\n // must look at previous residues as\n // residues may not be ordered by resno\n j = offset - 1\n sheet = sheets[ i ]\n chainChange = cp.chainname !== sheet[ 0 ]\n } else {\n done = true\n }\n }\n }\n\n if (chainChange || done) return\n }\n }\n })\n })\n\n if (Debug) Log.timeEnd('assignSecondaryStructure')\n}\n\nexport const calculateSecondaryStructure = (function () {\n // Implementation for proteins based on \"pv\"\n //\n // assigns secondary structure information based on a simple and very fast\n // algorithm published by Zhang and Skolnick in their TM-align paper.\n // Reference:\n //\n // TM-align: a protein structure alignment algorithm based on the Tm-score\n // (2005) NAR, 33(7) 2302-2309\n\n const zhangSkolnickSS = function (polymer: Polymer, i: number, distances: number[], delta: number) {\n const structure = polymer.structure\n const offset = polymer.residueIndexStart\n const rp1 = structure.getResidueProxy()\n const rp2 = structure.getResidueProxy()\n const ap1 = structure.getAtomProxy()\n const ap2 = structure.getAtomProxy()\n\n for (let j = Math.max(0, i - 2); j <= i; ++j) {\n for (let k = 2; k < 5; ++k) {\n if (j + k >= polymer.residueCount) {\n continue\n }\n\n rp1.index = offset + j\n rp2.index = offset + j + k\n ap1.index = rp1.traceAtomIndex\n ap2.index = rp2.traceAtomIndex\n\n const d = ap1.distanceTo(ap2)\n\n if (Math.abs(d - distances[ k - 2 ]) > delta) {\n return false\n }\n }\n }\n\n return true\n }\n\n const isHelical = function (polymer: Polymer, i: number) {\n const helixDistances = [ 5.45, 5.18, 6.37 ]\n const helixDelta = 2.1\n return zhangSkolnickSS(polymer, i, helixDistances, helixDelta)\n }\n\n const isSheet = function (polymer: Polymer, i: number) {\n const sheetDistances = [ 6.1, 10.4, 13.0 ]\n const sheetDelta = 1.42\n return zhangSkolnickSS(polymer, i, sheetDistances, sheetDelta)\n }\n\n const proteinPolymer = function (p: Polymer) {\n const residueStore = p.residueStore\n const offset = p.residueIndexStart\n for (let i = 0, il = p.residueCount; i < il; ++i) {\n let sstruc = 'c'\n if (isHelical(p, i)) {\n sstruc = 'h'\n } else if (isSheet(p, i)) {\n sstruc = 'e'\n }\n residueStore.sstruc[ offset + i ] = sstruc.charCodeAt(0)\n }\n }\n\n const cgPolymer = function (p: Polymer) {\n const localAngle = 20\n const centerDist = 2.0\n\n const residueStore = p.residueStore\n const offset = p.residueIndexStart\n\n const helixbundle = new Helixbundle(p)\n const pos = helixbundle.position\n\n const c1 = new Vector3()\n const c2 = new Vector3()\n\n for (let i = 0, il = p.residueCount; i < il; ++i) {\n c1.fromArray(pos.center as any, i * 3) // TODO\n c2.fromArray(pos.center as any, i * 3 + 3) // TODO\n const d = c1.distanceTo(c2)\n\n if (d < centerDist && d > 1.0 && pos.bending[ i ] < localAngle) {\n residueStore.sstruc[ offset + i ] = 'h'.charCodeAt(0)\n residueStore.sstruc[ offset + i + 1 ] = 'h'.charCodeAt(0)\n }\n }\n }\n\n return function calculateSecondaryStructure (structure: Structure) {\n if (Debug) Log.time('calculateSecondaryStructure')\n\n structure.eachPolymer(function (p) {\n // assign secondary structure\n if (p.residueCount < 4) return\n if (p.isCg()) {\n cgPolymer(p)\n } else if (p.isProtein()) {\n proteinPolymer(p)\n } else {\n return\n }\n\n // set lone secondary structure assignments to \"c\"\n let prevSstruc: string\n let sstrucCount = 0\n p.eachResidue(function (r: ResidueProxy) {\n if (r.sstruc === prevSstruc) {\n sstrucCount += 1\n } else {\n if (sstrucCount === 1) {\n r.index -= 1\n r.sstruc = 'c'\n }\n sstrucCount = 1\n prevSstruc = r.sstruc\n }\n })\n })\n\n if (Debug) Log.timeEnd('calculateSecondaryStructure')\n }\n}())\n\n// const ChainnameAlphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\" +\n// \"abcdefghijklmnopqrstuvwxyz\" +\n// \"0123456789\";\nconst ChainnameAlphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'\n\nexport function getChainname (index: number) {\n const n = ChainnameAlphabet.length\n let j = index\n let k = 0\n let chainname = ChainnameAlphabet[j % n]\n while (j >= n) {\n j = Math.floor(j / n)\n chainname += ChainnameAlphabet[j % n]\n k += 1\n }\n if (k >= 5) {\n Log.warn('chainname overflow')\n }\n return chainname\n}\n\ninterface ChainData {\n mIndex: number\n chainname: string\n rStart: number\n rCount: number\n}\n\n/**\n * When no chain names are set for the given structure, calculates\n * chains based on:\n * - polymer connectivity: when adjacent residues are not bonded, a new chain is created.\n * - non polymer chemical type: adjacent residues which are not polymers but are of the same\n * chemical type (e.g. water molecules) are grouped into the same chain.\n **/\nexport function calculateChainnames (structure: Structure, useExistingBonds = false) {\n if (Debug) Log.time('calculateChainnames')\n\n let doAutoChainName = true\n structure.eachChain(function (c) {\n if (c.chainname) doAutoChainName = false\n })\n\n if (doAutoChainName) {\n const modelStore = structure.modelStore\n const chainStore = structure.chainStore\n const residueStore = structure.residueStore\n\n const addChain = function (mIndex: number, chainname: string, rOffset: number, rCount: number) {\n const ci = chainStore.count\n for (let i = 0; i < rCount; ++i) {\n residueStore.chainIndex[ rOffset + i ] = ci\n }\n chainStore.growIfFull()\n chainStore.modelIndex[ ci ] = mIndex\n chainStore.setChainname(ci, chainname)\n chainStore.setChainid(ci, chainname)\n chainStore.residueOffset[ ci ] = rOffset\n chainStore.residueCount[ ci ] = rCount\n chainStore.count += 1\n modelStore.chainCount[ mIndex ] += 1\n }\n\n const ap1 = structure.getAtomProxy()\n const ap2 = structure.getAtomProxy()\n\n let i = 0\n let mi = 0\n let rStart = 0\n let rEnd = 0\n const chainData: ChainData[] = []\n\n if (residueStore.count === 1) {\n chainData.push({\n mIndex: 0,\n chainname: 'A',\n rStart: 0,\n rCount: 1\n })\n } else {\n structure.eachResidueN(2, function (rp1: ResidueProxy, rp2: ResidueProxy) {\n let newChain = false\n\n const bbType1 = rp1.backboneType\n const bbType2 = rp2.backboneType\n const bbTypeUnk = UnknownBackboneType\n\n rEnd = rp1.index\n\n if (rp1.modelIndex !== rp2.modelIndex) {\n newChain = true\n } else if (rp1.moleculeType !== rp2.moleculeType) {\n newChain = true\n } else if (bbType1 !== bbTypeUnk && bbType1 === bbType2) {\n ap1.index = rp1.backboneEndAtomIndex\n ap2.index = rp2.backboneStartAtomIndex\n if (useExistingBonds) {\n newChain = !ap1.hasBondTo(ap2)\n } else {\n newChain = !ap1.connectedTo(ap2)\n }\n }\n\n // current chain goes to end of the structure\n if (!newChain && rp2.index === residueStore.count - 1) {\n newChain = true\n rEnd = rp2.index\n }\n\n if (newChain) {\n chainData.push({\n mIndex: mi,\n chainname: getChainname(i),\n rStart: rStart,\n rCount: rEnd - rStart + 1\n })\n\n i += 1\n\n if (rp1.modelIndex !== rp2.modelIndex) {\n i = 0\n mi += 1\n }\n\n // new chain for the last residue of the structure\n if (rp2.index === residueStore.count - 1 && rEnd !== rp2.index) {\n chainData.push({\n mIndex: mi,\n chainname: getChainname(i),\n rStart: residueStore.count - 1,\n rCount: 1\n })\n }\n\n rStart = rp2.index\n rEnd = rp2.index\n }\n })\n }\n\n //\n\n chainStore.count = 0\n modelStore.chainCount.fill(0, 0, modelStore.count)\n modelStore.chainOffset.fill(0, 0, modelStore.count)\n chainData.forEach(function (d) {\n addChain(d.mIndex, d.chainname, d.rStart, d.rCount)\n })\n\n let chainOffset = 0\n structure.eachModel(function (mp) {\n modelStore.chainOffset[ mp.index ] = chainOffset\n chainOffset += modelStore.chainCount[ mp.index ]\n })\n }\n\n if (Debug) Log.timeEnd('calculateChainnames')\n}\n\nexport function calculateBonds (structure: Structure, inferBonds: InferBondsOptions='all') {\n if (inferBonds === 'none') return \n if (Debug) Log.time('calculateBonds')\n\n calculateBondsWithin(structure, false, inferBonds)\n calculateBondsBetween(structure)\n\n if (Debug) Log.timeEnd('calculateBonds')\n}\n\n/**\n * Should Bonds be inferred for `all` atoms, `none` or `auto`\n * If `auto`, any hetgroup residue with at least one CONECT record will \n * not have bonding inferred, and will rely on the CONECT records\n */\nexport type InferBondsOptions = 'all' | 'none' | 'auto'\n\nexport interface ResidueBonds {\n atomIndices1: number[]\n atomIndices2: number[]\n bondOrders: number[]\n}\n\n\nconst BondOrderTable: { [k: string]: number } = {\n 'HIS|CD2|CG': 2,\n 'HIS|CE1|ND1': 2,\n 'ARG|CZ|NH2': 2,\n 'PHE|CE1|CZ': 2,\n 'PHE|CD2|CE2': 2,\n 'PHE|CD1|CG': 2,\n 'TRP|CD1|CG': 2,\n 'TRP|CD2|CE2': 2,\n 'TRP|CE3|CZ3': 2,\n 'TRP|CH2|CZ2': 2,\n 'ASN|CG|OD1': 2,\n 'GLN|CD|OE1': 2,\n 'TYR|CD1|CG': 2,\n 'TYR|CD2|CE2': 2,\n 'TYR|CE1|CZ': 2,\n 'ASP|CG|OD1': 2,\n 'GLU|CD|OE1': 2,\n\n 'G|C8|N7': 2,\n 'G|C4|C5': 2,\n 'G|C2|N3': 2,\n 'G|C6|O6': 2,\n 'C|C4|N3': 2,\n 'C|C5|C6': 2,\n 'C|C2|O2': 2,\n 'A|C2|N3': 2,\n 'A|C6|N1': 2,\n 'A|C4|C5': 2,\n 'A|C8|N7': 2,\n 'U|C5|C6': 2,\n 'U|C2|O2': 2,\n 'U|C4|O4': 2,\n\n 'DG|C8|N7': 2,\n 'DG|C4|C5': 2,\n 'DG|C2|N3': 2,\n 'DG|C6|O6': 2,\n 'DC|C4|N3': 2,\n 'DC|C5|C6': 2,\n 'DC|C2|O2': 2,\n 'DA|C2|N3': 2,\n 'DA|C6|N1': 2,\n 'DA|C4|C5': 2,\n 'DA|C8|N7': 2,\n 'DT|C5|C6': 2,\n 'DT|C2|O2': 2,\n 'DT|C4|O4': 2\n}\nfunction getBondOrderFromTable (resname: string, atomname1: string, atomname2: string) {\n [ atomname1, atomname2 ] = atomname1 < atomname2 ? [ atomname1, atomname2 ] : [ atomname2, atomname1 ]\n if (AA3.includes(resname) && atomname1 === 'C' && atomname2 === 'O') return 2\n if (Bases.includes(resname) && atomname1 === 'OP1' && atomname2 === 'P') return 2\n return BondOrderTable[ `${resname}|${atomname1}|${atomname2}` ] || 1\n}\n\nexport function calculateResidueBonds (r: ResidueProxy) {\n const structure = r.structure\n const a1 = structure.getAtomProxy()\n const a2 = structure.getAtomProxy()\n\n const count = r.atomCount\n const offset = r.atomOffset\n const end = offset + count\n const end1 = end - 1\n\n const atomIndices1 = []\n const atomIndices2 = []\n const bondOrders = []\n\n if (count > 500) {\n if (Debug) Log.warn('more than 500 atoms, skip residue for auto-bonding', r.qualifiedName())\n } else {\n if (count > 50) {\n const kdtree = new Kdtree(r, true)\n const radius = r.isCg() ? 1.2 : 2.3\n\n for (let i = offset; i < end1; ++i) {\n a1.index = i\n const maxd = a1.covalent + radius + 0.3\n const nearestAtoms = kdtree.nearest(a1 as any, Infinity, maxd * maxd) // TODO\n const m = nearestAtoms.length\n for (let j = 0; j < m; ++j) {\n a2.index = nearestAtoms[ j ].index\n if (a1.index < a2.index) {\n if (a1.connectedTo(a2)) {\n atomIndices1.push(a1.index - offset)\n atomIndices2.push(a2.index - offset)\n bondOrders.push(getBondOrderFromTable(a1.resname, a1.atomname, a2.atomname))\n }\n }\n }\n }\n } else {\n for (let i = offset; i < end1; ++i) {\n a1.index = i\n for (let j = i + 1; j <= end1; ++j) {\n a2.index = j\n if (a1.connectedTo(a2)) {\n atomIndices1.push(i - offset)\n atomIndices2.push(j - offset)\n bondOrders.push(getBondOrderFromTable(a1.resname, a1.atomname, a2.atomname))\n }\n }\n }\n }\n }\n\n return {\n atomIndices1: atomIndices1,\n atomIndices2: atomIndices2,\n bondOrders: bondOrders\n }\n}\n\nexport function calculateAtomBondMap (structure: Structure) {\n if (Debug) Log.time('calculateAtomBondMap')\n\n var atomBondMap: number[][] = []\n\n structure.eachBond(function (bp) {\n var ai1 = bp.atomIndex1\n var ai2 = bp.atomIndex2\n if (atomBondMap[ ai1 ] === undefined) atomBondMap[ ai1 ] = []\n atomBondMap[ ai1 ][ ai2 ] = bp.index\n })\n\n if (Debug) Log.timeEnd('calculateAtomBondMap')\n\n return atomBondMap\n}\n\nexport function calculateBondsWithin (structure: Structure, onlyAddRung = false, inferBonds: InferBondsOptions='all') {\n if (Debug) Log.time('calculateBondsWithin')\n\n const bondStore = structure.bondStore\n const rungBondStore = structure.rungBondStore\n const rungAtomSet = structure.getAtomSet(false)\n const a1 = structure.getAtomProxy()\n const a2 = structure.getAtomProxy()\n const bp = structure.getBondProxy()\n const atomBondMap = onlyAddRung ? null : calculateAtomBondMap(structure)\n\n let bondedAtoms: Set\n if (!onlyAddRung && inferBonds === 'auto') {\n bondedAtoms = new Set()\n atomBondMap!.forEach((a, i) => {\n bondedAtoms.add(i)\n a.forEach(j => {bondedAtoms.add(j)})\n })\n }\n\n structure.eachResidue(function (r) {\n if (!onlyAddRung && atomBondMap) {\n const count = r.atomCount\n const offset = r.atomOffset\n\n if (count > 500) {\n Log.warn('more than 500 atoms, skip residue for auto-bonding', r.qualifiedName())\n return\n }\n\n if (inferBonds === 'auto' && r.hetero) {\n // Are bonds present on this residue?\n for (let rai=r.atomOffset; rai {\n ncsMatrixList.forEach(nm => {\n ncsUnitcellMatrixList.push(sm.clone().multiply(nm))\n })\n })\n unitcellAssembly.addPart(ncsUnitcellMatrixList)\n } else {\n unitcellAssembly.addPart(unitcellMatrixList)\n }\n\n const vec = new Vector3()\n const supercellAssembly = new Assembly('SUPERCELL')\n const supercellMatrixList = Array.prototype.concat.call(\n getMatrixList(vec.set(1, 0, 0)), // 655\n getMatrixList(vec.set(0, 1, 0)), // 565\n getMatrixList(vec.set(0, 0, 1)), // 556\n\n getMatrixList(vec.set(-1, 0, 0)), // 455\n getMatrixList(vec.set(0, -1, 0)), // 545\n getMatrixList(vec.set(0, 0, -1)), // 554\n\n getMatrixList(vec.set(1, 1, 0)), // 665\n getMatrixList(vec.set(1, 0, 1)), // 656\n getMatrixList(vec.set(0, 1, 1)), // 566\n\n getMatrixList(vec.set(-1, -1, 0)), // 445\n getMatrixList(vec.set(-1, 0, -1)), // 454\n getMatrixList(vec.set(0, -1, -1)), // 544\n\n getMatrixList(vec.set(1, -1, -1)), // 644\n getMatrixList(vec.set(1, 1, -1)), // 664\n getMatrixList(vec.set(1, -1, 1)), // 646\n getMatrixList(vec.set(-1, 1, 1)), // 466\n getMatrixList(vec.set(-1, -1, 1)), // 446\n getMatrixList(vec.set(-1, 1, -1)), // 464\n\n getMatrixList(vec.set(0, 1, -1)), // 564\n getMatrixList(vec.set(0, -1, 1)), // 546\n getMatrixList(vec.set(1, 0, -1)), // 654\n getMatrixList(vec.set(-1, 0, 1)), // 456\n getMatrixList(vec.set(1, -1, 0)), // 645\n getMatrixList(vec.set(-1, 1, 0)), // 465\n\n getMatrixList(), // 555\n getMatrixList(vec.set(1, 1, 1)), // 666\n getMatrixList(vec.set(-1, -1, -1)) // 444\n )\n if (structure.biomolDict.NCS) {\n const ncsSupercellMatrixList: Matrix4[] = []\n supercellMatrixList.forEach(function (sm: Matrix4) {\n ncsMatrixList.forEach(function (nm) {\n ncsSupercellMatrixList.push(sm.clone().multiply(nm))\n })\n })\n supercellAssembly.addPart(ncsSupercellMatrixList)\n } else {\n supercellAssembly.addPart(supercellMatrixList)\n }\n\n structure.biomolDict.UNITCELL = unitcellAssembly\n structure.biomolDict.SUPERCELL = supercellAssembly\n\n if (Debug) Log.timeEnd('buildUnitcellAssembly')\n}\n\nconst elm1 = [ 'H', 'C', 'O', 'N', 'S', 'P' ]\nconst elm2 = [ 'NA', 'CL', 'FE' ]\n\nexport function guessElement (atomName: string) {\n // Retain first group of letters in atomName\n let at = atomName.toUpperCase()\n let begin = 0, end = 0\n for (let i = 0; i < at.length ; i++) {\n if (at.charCodeAt(i) < 65) {\n if (end > 0) break\n ++begin\n }\n else end = i + 1\n }\n if (begin > 0 || end < at.length) at = at.substring(begin, end)\n \n const n = at.length\n\n if (n === 0) return ''\n if (n === 1) return at\n if (n === 2) {\n if (elm2.indexOf(at) !== -1) return at\n if (elm1.indexOf(at[0]) !== -1) return at[0]\n if (at in AtomicNumbers) return at\n }\n if (n >= 3) {\n if (elm1.indexOf(at[0]) !== -1) return at[0]\n }\n return ''\n}\n\n/**\n * Assigns ResidueType bonds.\n * @param {Structure} structure - the structure object\n * @return {undefined}\n */\nexport function assignResidueTypeBonds (structure: Structure) {\n // if( Debug ) Log.time( \"assignResidueTypeBonds\" )\n\n const bondHash = structure.bondHash! // TODO\n const countArray = bondHash.countArray\n const offsetArray = bondHash.offsetArray\n const indexArray = bondHash.indexArray\n const bp = structure.getBondProxy()\n\n structure.eachResidue(function (rp) {\n const residueType = rp.residueType\n if (residueType.bonds !== undefined) return\n\n var atomOffset = rp.atomOffset\n var atomIndices1: number[] = []\n var atomIndices2: number[] = []\n var bondOrders: number[] = []\n var bondDict: { [k: string]: boolean } = {}\n\n const nextAtomOffset = atomOffset + rp.atomCount\n\n rp.eachAtom(function (ap) {\n const index = ap.index\n const offset = offsetArray[ index ]\n const count = countArray[ index ]\n for (let i = 0, il = count; i < il; ++i) {\n bp.index = indexArray[ offset + i ]\n let idx1 = bp.atomIndex1\n if (idx1 < atomOffset || idx1 >= nextAtomOffset) {\n // Don't add bonds outside of this resiude\n continue\n }\n let idx2 = bp.atomIndex2\n if (idx2 < atomOffset || idx2 >= nextAtomOffset) {\n continue\n }\n\n if (idx1 > idx2) {\n const tmp = idx2\n idx2 = idx1\n idx1 = tmp\n }\n const hash = idx1 + '|' + idx2\n if (bondDict[ hash ] === undefined) {\n bondDict[ hash ] = true\n atomIndices1.push(idx1 - atomOffset)\n atomIndices2.push(idx2 - atomOffset)\n bondOrders.push(bp.bondOrder)\n }\n }\n })\n\n residueType.bonds = {\n atomIndices1: atomIndices1,\n atomIndices2: atomIndices2,\n bondOrders: bondOrders\n }\n })\n\n // if( Debug ) Log.timeEnd( \"assignResidueTypeBonds\" )\n}\n\nexport function concatStructures (name: string, ...structures: Structure[]) {\n if( Debug ) Log.time( \"concatStructures\" )\n\n const s = new Structure(name, '')\n const sb = new StructureBuilder(s)\n\n const atomStore = s.atomStore as any\n const atomMap = s.atomMap\n atomStore.addField('formalCharge', 1, 'int8')\n atomStore.addField('partialCharge', 1, 'float32')\n\n const atomIndexDict: { [k: number]: number } = {}\n\n let idx = 0\n let atomCount = 0\n let modelCount = 0\n structures.forEach(structure => {\n structure.eachAtom(a => {\n atomStore.growIfFull()\n atomStore.atomTypeId[ idx ] = atomMap.add(a.atomname, a.element)\n\n atomStore.x[ idx ] = a.x\n atomStore.y[ idx ] = a.y\n atomStore.z[ idx ] = a.z\n atomStore.serial[ idx ] = a.serial\n atomStore.formalCharge[ idx ] = a.formalCharge\n atomStore.partialCharge[ idx ] = a.partialCharge\n atomStore.altloc[ idx ] = a.altloc\n atomStore.occupancy[ idx ] = a.occupancy\n atomStore.bfactor[ idx ] = a.bfactor\n\n sb.addAtom(\n a.modelIndex + modelCount,\n a.chainname,\n a.chainid,\n a.resname,\n a.resno,\n a.hetero === 1,\n a.sstruc,\n a.inscode\n )\n\n atomIndexDict[a.index + atomCount] = idx\n idx += 1\n })\n atomCount += structure.atomStore.count\n modelCount += structure.modelStore.count\n })\n\n const bondStore = s.bondStore\n const a1 = s.getAtomProxy()\n const a2 = s.getAtomProxy()\n\n atomCount = 0\n structures.forEach(structure => {\n structure.eachBond(b => {\n a1.index = atomIndexDict[ b.atomIndex1 + atomCount ]\n a2.index = atomIndexDict[ b.atomIndex2 + atomCount ]\n bondStore.addBond(a1, a2, b.bondOrder)\n })\n atomCount += structure.atomStore.count\n })\n\n sb.finalize()\n\n calculateBondsBetween(s, true) // calculate backbone bonds\n calculateBondsWithin(s, true) // calculate rung bonds\n\n s.finalizeAtoms()\n s.finalizeBonds()\n assignResidueTypeBonds(s)\n\n if( Debug ) Log.timeEnd( \"concatStructures\" )\n\n return s\n}\n","/**\n * @file Atom Type\n * @author Alexander Rose \n * @private\n */\n\nimport { guessElement } from '../structure/structure-utils'\nimport {\n AtomicNumbers, DefaultAtomicNumber,\n VdwRadii, DefaultVdwRadius,\n CovalentRadii, DefaultCovalentRadius,\n Valences, DefaultValence,\n OuterShellElectronCounts, DefaultOuterShellElectronCount\n} from '../structure/structure-constants'\nimport Structure from '../structure/structure'\n\n// Li, Na, K, Rb, Cs Fr\nconst AlkaliMetals = [ 3, 11, 19, 37, 55, 87 ]\n\n// Be, Mg, Ca, Sr, Ba, Ra\nconst AlkalineEarthMetals = [ 4, 12, 20, 38, 56, 88 ]\n\n// C, P, S, Se\nconst PolyatomicNonmetals = [ 6, 15, 16, 34, ]\n\n// H, N, O, F, Cl, Br, I\nconst DiatomicNonmetals = [ 1, 7, 8, 9, 17, 35, 53 ]\n\n// He, Ne, Ar, Kr, Xe, Rn\nconst NobleGases = [ 2, 10, 18, 36, 54, 86 ]\n\n// Zn, Ga, Cd, In, Sn, Hg, Ti, Pb, Bi, Po, Cn\nconst PostTransitionMetals = [ 13, 30, 31, 48, 49, 50, 80, 81, 82, 83, 84, 85, 112 ]\n\n// B, Si, Ge, As, Sb, Te, At\nconst Metalloids = [ 5, 14, 32, 33, 51, 52, 85 ]\n\n// F, Cl, Br, I, At\nconst Halogens = [ 9, 17, 35, 53, 85 ]\n\n/**\n * Atom type\n */\nclass AtomType {\n element: string\n number: number\n vdw: number\n covalent: number\n\n /**\n * @param {Structure} structure - the structure object\n * @param {String} atomname - the name of the atom\n * @param {String} element - the chemical element\n */\n constructor (readonly structure: Structure, readonly atomname: string, element?: string) {\n element = element || guessElement(atomname)\n\n this.element = element\n this.number = AtomicNumbers[ element ] || DefaultAtomicNumber\n this.vdw = VdwRadii[ this.number ] || DefaultVdwRadius\n this.covalent = CovalentRadii[ this.number ] || DefaultCovalentRadius\n }\n\n getDefaultValence() {\n const vl = Valences[ this.number ]\n return vl ? vl[ 0 ] : DefaultValence\n }\n\n getValenceList () {\n return Valences[ this.number ] || []\n }\n\n getOuterShellElectronCount () {\n return OuterShellElectronCounts[ this.number ] || DefaultOuterShellElectronCount\n }\n\n isMetal () {\n return (\n this.isAlkaliMetal() ||\n this.isAlkalineEarthMetal() ||\n this.isLanthanide() ||\n this.isActinide() ||\n this.isTransitionMetal() ||\n this.isPostTransitionMetal()\n )\n }\n\n isNonmetal () {\n return (\n this.isDiatomicNonmetal() ||\n this.isPolyatomicNonmetal() ||\n this.isNobleGas()\n )\n }\n\n isMetalloid () {\n return Metalloids.includes(this.number)\n }\n\n isHalogen () {\n return Halogens.includes(this.number)\n }\n\n isDiatomicNonmetal () {\n return DiatomicNonmetals.includes(this.number)\n }\n\n isPolyatomicNonmetal () {\n return PolyatomicNonmetals.includes(this.number)\n }\n\n isAlkaliMetal () {\n return AlkaliMetals.includes(this.number)\n }\n\n isAlkalineEarthMetal () {\n return AlkalineEarthMetals.includes(this.number)\n }\n\n isNobleGas () {\n return NobleGases.includes(this.number)\n }\n\n isTransitionMetal () {\n const no = this.number\n return (\n (no >= 21 && no <= 29) ||\n (no >= 39 && no <= 47) ||\n (no >= 72 && no <= 79) ||\n (no >= 104 && no <= 108)\n )\n }\n\n isPostTransitionMetal () {\n return PostTransitionMetals.includes(this.number)\n }\n\n isLanthanide () {\n return this.number >= 57 && this.number <= 71\n }\n\n isActinide () {\n return this.number >= 89 && this.number <= 103\n }\n\n}\n\nexport default AtomType","/**\n * @file Atom Map\n * @author Alexander Rose \n * @private\n */\n\nimport AtomType from './atom-type'\nimport { guessElement } from '../structure/structure-utils'\nimport Structure from '../structure/structure'\n\nfunction getHash (atomname: string, element: string) {\n return atomname + '|' + element\n}\n\nclass AtomMap {\n dict: { [k: string]: number } = {}\n list: AtomType[] = []\n\n constructor (readonly structure: Structure) {\n this.structure = structure\n }\n\n add (atomname: string, element?: string) {\n atomname = atomname.toUpperCase()\n if (!element) {\n element = guessElement(atomname)\n } else {\n element = element.toUpperCase()\n }\n const hash = getHash(atomname, element)\n let id = this.dict[ hash ]\n if (id === undefined) {\n const atomType = new AtomType(this.structure, atomname, element)\n id = this.list.length\n this.dict[ hash ] = id\n this.list.push(atomType)\n }\n return id\n }\n\n get (id: number) {\n return this.list[ id ]\n }\n}\n\nexport default AtomMap\n","/**\n * @file Residue Type\n * @author Alexander Rose \n * @author Fred Ludlow\n * @private\n */\n\nimport { defaults } from '../utils'\nimport PrincipalAxes from '../math/principal-axes'\nimport { Matrix } from '../math/matrix-utils'\nimport { calculateResidueBonds, ResidueBonds } from '../structure/structure-utils'\nimport {\n Elements,\n ProteinType, RnaType, DnaType, WaterType, IonType, SaccharideType, UnknownType,\n ProteinBackboneType, RnaBackboneType, DnaBackboneType, UnknownBackboneType,\n CgProteinBackboneType, CgRnaBackboneType, CgDnaBackboneType,\n ChemCompProtein, ChemCompRna, ChemCompDna, ChemCompSaccharide,\n AA3, PurinBases, RnaBases, DnaBases, Bases, IonNames, WaterNames, SaccharideNames,\n ProteinBackboneAtoms, NucleicBackboneAtoms, ResidueTypeAtoms\n} from '../structure/structure-constants'\nimport Structure from '../structure/structure'\nimport ResidueProxy from '../proxy/residue-proxy'\nimport AtomProxy from '../proxy/atom-proxy'\n\nexport interface BondGraph {\n [k: number]: number[]\n}\n\nexport interface RingData {\n atomRings: number[][] // sparse array:\n // atomRings[atomIdx] -> array of ring indices\n rings: number[][] // rings as arrays of indices\n}\n\n/**\n * Residue type\n */\nexport default class ResidueType {\n resname: string\n atomTypeIdList: number[]\n hetero: number\n chemCompType: string\n bonds?: ResidueBonds\n rings?: RingData\n bondGraph?: BondGraph\n aromaticAtoms?: Uint8Array\n aromaticRings?: number[][]\n\n atomCount: number\n\n moleculeType: number\n backboneType: number\n backboneEndType: number\n backboneStartType: number\n backboneIndexList: number[]\n\n traceAtomIndex: number\n direction1AtomIndex: number\n direction2AtomIndex: number\n backboneStartAtomIndex: number\n backboneEndAtomIndex: number\n rungEndAtomIndex: number\n\n // Sparse array containing the reference atom index for each bond.\n bondReferenceAtomIndices: number[] = []\n\n /**\n * @param {Structure} structure - the structure object\n * @param {String} resname - name of the residue\n * @param {Array} atomTypeIdList - list of IDs of {@link AtomType}s corresponding\n * to the atoms of the residue\n * @param {Boolean} hetero - hetero flag\n * @param {String} chemCompType - chemical component type\n * @param {Object} [bonds] - TODO\n */\n constructor (readonly structure: Structure, resname: string, atomTypeIdList: number[], hetero: boolean, chemCompType: string, bonds?: ResidueBonds) {\n this.resname = resname\n this.atomTypeIdList = atomTypeIdList\n this.hetero = hetero ? 1 : 0\n this.chemCompType = chemCompType\n this.bonds = bonds\n this.atomCount = atomTypeIdList.length\n\n this.moleculeType = this.getMoleculeType()\n this.backboneType = this.getBackboneType(0)\n this.backboneEndType = this.getBackboneType(-1)\n this.backboneStartType = this.getBackboneType(1)\n this.backboneIndexList = this.getBackboneIndexList()\n\n const atomnames = ResidueTypeAtoms[ this.backboneType ]\n const atomnamesStart = ResidueTypeAtoms[ this.backboneStartType ]\n const atomnamesEnd = ResidueTypeAtoms[ this.backboneEndType ]\n\n const traceIndex = this.getAtomIndexByName(atomnames.trace)\n this.traceAtomIndex = defaults(traceIndex, -1)\n\n const dir1Index = this.getAtomIndexByName(atomnames.direction1)\n this.direction1AtomIndex = defaults(dir1Index, -1)\n\n const dir2Index = this.getAtomIndexByName(atomnames.direction2)\n this.direction2AtomIndex = defaults(dir2Index, -1)\n\n const bbStartIndex = this.getAtomIndexByName(atomnamesStart.backboneStart)\n this.backboneStartAtomIndex = defaults(bbStartIndex, -1)\n\n const bbEndIndex = this.getAtomIndexByName(atomnamesEnd.backboneEnd)\n this.backboneEndAtomIndex = defaults(bbEndIndex, -1)\n\n let rungEndIndex\n if (PurinBases.includes(resname)) {\n rungEndIndex = this.getAtomIndexByName('N1')\n } else {\n rungEndIndex = this.getAtomIndexByName('N3')\n }\n this.rungEndAtomIndex = defaults(rungEndIndex, -1)\n }\n\n getBackboneIndexList () {\n const backboneIndexList: number[] = []\n let atomnameList\n switch (this.moleculeType) {\n case ProteinType:\n atomnameList = ProteinBackboneAtoms\n break\n case RnaType:\n case DnaType:\n atomnameList = NucleicBackboneAtoms\n break\n default:\n return backboneIndexList\n }\n const atomMap = this.structure.atomMap\n const atomTypeIdList = this.atomTypeIdList\n for (let i = 0, il = this.atomCount; i < il; ++i) {\n const atomType = atomMap.get(atomTypeIdList[ i ])\n if (atomnameList.includes(atomType.atomname)) {\n backboneIndexList.push(i)\n }\n }\n return backboneIndexList\n }\n\n getMoleculeType () {\n if (this.isProtein()) {\n return ProteinType\n } else if (this.isRna()) {\n return RnaType\n } else if (this.isDna()) {\n return DnaType\n } else if (this.isWater()) {\n return WaterType\n } else if (this.isIon()) {\n return IonType\n } else if (this.isSaccharide()) {\n return SaccharideType\n } else {\n return UnknownType\n }\n }\n\n getBackboneType (position: number) {\n if (this.hasProteinBackbone(position)) {\n return ProteinBackboneType\n } else if (this.hasRnaBackbone(position)) {\n return RnaBackboneType\n } else if (this.hasDnaBackbone(position)) {\n return DnaBackboneType\n } else if (this.hasCgProteinBackbone(position)) {\n return CgProteinBackboneType\n } else if (this.hasCgRnaBackbone(position)) {\n return CgRnaBackboneType\n } else if (this.hasCgDnaBackbone(position)) {\n return CgDnaBackboneType\n } else {\n return UnknownBackboneType\n }\n }\n\n isProtein () {\n if (this.chemCompType) {\n return ChemCompProtein.includes(this.chemCompType)\n } else {\n return (\n this.hasAtomWithName('CA', 'C', 'N') ||\n AA3.includes(this.resname)\n )\n }\n }\n\n isCg () {\n const backboneType = this.backboneType\n return (\n backboneType === CgProteinBackboneType ||\n backboneType === CgRnaBackboneType ||\n backboneType === CgDnaBackboneType\n )\n }\n\n isNucleic () {\n return this.isRna() || this.isDna()\n }\n\n isRna () {\n if (this.chemCompType) {\n return ChemCompRna.includes(this.chemCompType)\n } else if (this.hetero === 1) {\n return false\n } else {\n return (\n this.hasAtomWithName(\n [ 'P', \"O3'\", 'O3*' ], [ \"C4'\", 'C4*' ], [ \"O2'\", 'O2*', \"F2'\", 'F2*' ]\n ) ||\n (RnaBases.includes(this.resname) &&\n (this.hasAtomWithName([ \"O2'\", 'O2*', \"F2'\", 'F2*' ])))\n )\n }\n }\n\n isDna () {\n if (this.chemCompType) {\n return ChemCompDna.includes(this.chemCompType)\n } else if (this.hetero === 1) {\n return false\n } else {\n return (\n (this.hasAtomWithName([ 'P', \"O3'\", 'O3*' ], [ \"C3'\", 'C3*' ]) &&\n !this.hasAtomWithName([ \"O2'\", 'O2*', \"F2'\", 'F2*' ])) ||\n DnaBases.includes(this.resname)\n )\n }\n }\n\n isHetero () {\n return this.hetero === 1\n }\n\n isIon () {\n return IonNames.includes(this.resname)\n }\n\n isWater () {\n return WaterNames.includes(this.resname)\n }\n\n isSaccharide () {\n if (this.chemCompType) {\n return ChemCompSaccharide.includes(this.chemCompType)\n } else {\n return SaccharideNames.includes(this.resname)\n }\n }\n\n isStandardAminoacid () {\n return AA3.includes(this.resname)\n }\n\n isStandardBase () {\n return Bases.includes(this.resname)\n }\n\n hasBackboneAtoms (position: number, type: number) {\n const atomnames = ResidueTypeAtoms[ type ]\n if (position === -1) {\n return this.hasAtomWithName(\n atomnames.trace,\n atomnames.backboneEnd,\n atomnames.direction1,\n atomnames.direction2\n )\n } else if (position === 0) {\n return this.hasAtomWithName(\n atomnames.trace,\n atomnames.direction1,\n atomnames.direction2\n )\n } else if (position === 1) {\n return this.hasAtomWithName(\n atomnames.trace,\n atomnames.backboneStart,\n atomnames.direction1,\n atomnames.direction2\n )\n } else {\n return this.hasAtomWithName(\n atomnames.trace,\n atomnames.backboneStart,\n atomnames.backboneEnd,\n atomnames.direction1,\n atomnames.direction2\n )\n }\n }\n\n hasProteinBackbone (position: number) {\n return (\n this.isProtein() &&\n this.hasBackboneAtoms(position, ProteinBackboneType)\n )\n }\n\n hasRnaBackbone (position: number) {\n return (\n this.isRna() &&\n this.hasBackboneAtoms(position, RnaBackboneType)\n )\n }\n\n hasDnaBackbone (position: number) {\n return (\n this.isDna() &&\n this.hasBackboneAtoms(position, DnaBackboneType)\n )\n }\n\n hasCgProteinBackbone (position: number) {\n return (\n this.atomCount < 7 &&\n this.isProtein() &&\n this.hasBackboneAtoms(position, CgProteinBackboneType)\n )\n }\n\n hasCgRnaBackbone (position: number) {\n return (\n this.atomCount < 11 &&\n this.isRna() &&\n this.hasBackboneAtoms(position, CgRnaBackboneType)\n )\n }\n\n hasCgDnaBackbone (position: number) {\n return (\n this.atomCount < 11 &&\n this.isDna() &&\n this.hasBackboneAtoms(position, CgDnaBackboneType)\n )\n }\n\n hasBackbone (position: number) {\n return (\n this.hasProteinBackbone(position) ||\n this.hasRnaBackbone(position) ||\n this.hasDnaBackbone(position) ||\n this.hasCgProteinBackbone(position) ||\n this.hasCgRnaBackbone(position) ||\n this.hasCgDnaBackbone(position)\n )\n }\n\n getAtomIndexByName (atomname: string|string[]) {\n const n = this.atomCount\n const atomMap = this.structure.atomMap\n const atomTypeIdList = this.atomTypeIdList\n if (Array.isArray(atomname)) {\n for (let i = 0; i < n; ++i) {\n const index = atomTypeIdList[ i ]\n if (atomname.includes(atomMap.get(index).atomname)) {\n return i\n }\n }\n } else {\n for (let i = 0; i < n; ++i) {\n const index = atomTypeIdList[ i ]\n if (atomname === atomMap.get(index).atomname) {\n return i\n }\n }\n }\n return undefined\n }\n\n hasAtomWithName (...atomnames: (string|string[])[]) {\n const n = atomnames.length\n for (let i = 0; i < n; ++i) {\n if (atomnames[ i ] === undefined) continue\n if (this.getAtomIndexByName(atomnames[ i ]) === undefined) {\n return false\n }\n }\n return true\n }\n\n getBonds (r?: ResidueProxy) {\n if (this.bonds === undefined) {\n this.bonds = calculateResidueBonds(r!) // TODO\n }\n return this.bonds\n }\n\n getRings () {\n if (this.rings === undefined) {\n this.calculateRings()\n }\n return this.rings\n }\n\n getBondGraph () {\n if (this.bondGraph === undefined) {\n this.calculateBondGraph()\n }\n return this.bondGraph\n }\n\n getAromatic (a?: AtomProxy) {\n if (this.aromaticAtoms === undefined) {\n this.calculateAromatic(this.structure.getResidueProxy((a!).residueIndex)) // TODO\n }\n return this.aromaticAtoms\n }\n\n getAromaticRings (r?: ResidueProxy) {\n if (this.aromaticRings === undefined) {\n this.calculateAromatic(r!) // TODO\n }\n return this.aromaticRings\n }\n\n /**\n * @return {Object} bondGraph - represents the bonding in this\n * residue: { ai1: [ ai2, ai3, ...], ...}\n */\n calculateBondGraph () {\n const bondGraph: BondGraph = this.bondGraph = {}\n const bonds = this.getBonds()\n const nb = bonds.atomIndices1.length\n const atomIndices1 = bonds.atomIndices1\n const atomIndices2 = bonds.atomIndices2\n\n for (let i = 0; i < nb; ++i) {\n const ai1 = atomIndices1[i]\n const ai2 = atomIndices2[i]\n\n const a1 = bondGraph[ ai1 ] = bondGraph[ ai1 ] || []\n a1.push(ai2)\n\n const a2 = bondGraph[ ai2 ] = bondGraph[ ai2 ] || []\n a2.push(ai1)\n }\n }\n\n /**\n * Find all rings up to 2 * RingFinderMaxDepth\n */\n calculateRings () {\n const bondGraph = this.getBondGraph()! // TODO\n const state = RingFinderState(bondGraph, this.atomCount)\n\n for (let i = 0; i < state.count; i++) {\n if (state.visited[i] >= 0) continue\n findRings(state, i)\n }\n\n this.rings = { atomRings: state.atomRings, rings: state.rings }\n }\n\n isAromatic (atom: AtomProxy) {\n this.aromaticAtoms = this.getAromatic(atom)! // TODO\n return this.aromaticAtoms[atom.index - atom.residueAtomOffset] === 1\n }\n\n calculateAromatic (r: ResidueProxy) {\n const aromaticAtoms = this.aromaticAtoms = new Uint8Array(this.atomCount)\n const rings = this.getRings()!.rings\n\n const aromaticRingFlags = rings.map(ring => {\n return isRingAromatic(ring.map(idx => {\n return this.structure.getAtomProxy(idx + r.atomOffset)\n }))\n })\n\n const aromaticRings: number[][] = this.aromaticRings = []\n rings.forEach((ring, i) => {\n if (aromaticRingFlags[i]) {\n aromaticRings.push(ring)\n ring.forEach(idx => aromaticAtoms[idx] = 1)\n }\n })\n }\n\n /**\n * For bonds with order > 1, pick a reference atom\n * @return {undefined}\n */\n assignBondReferenceAtomIndices () {\n const bondGraph = this.getBondGraph()! // TODO\n const rings = this.getRings()! // TODO\n const atomRings = rings.atomRings\n const ringData = rings.rings\n\n const bonds = this.bonds! // TODO\n const atomIndices1 = bonds.atomIndices1\n const atomIndices2 = bonds.atomIndices2\n const bondOrders = bonds.bondOrders\n const bondReferenceAtomIndices = this.bondReferenceAtomIndices\n\n const nb = bonds.atomIndices1.length\n\n bondReferenceAtomIndices.length = 0 // reset array\n\n for (let i = 0; i < nb; ++i) {\n // Not required for single bonds\n if (bondOrders[i] <= 1) continue\n\n let refRing\n\n const ai1 = atomIndices1[i]\n const ai2 = atomIndices2[i]\n\n const rings1 = atomRings[ ai1 ]\n const rings2 = atomRings[ ai2 ]\n // Are both atoms in a ring?\n if (rings1 && rings2) {\n // Are they in the same ring? (If not, ignore ring info)\n for (let ri1 = 0; ri1 < rings1.length; ri1++){\n if (rings2.indexOf(rings1[ ri1 ]) !== -1) {\n refRing = ringData[ rings1[ ri1 ] ]\n break\n }\n }\n }\n\n // Find the first neighbour.\n if (bondGraph[ ai1 ].length > 1) {\n for (let j = 0; j < bondGraph[ ai1 ].length; ++j) {\n const ai3 = bondGraph[ ai1 ][ j ]\n if (ai3 !== ai2) {\n if (refRing === undefined || refRing.indexOf(ai3) !== -1){\n bondReferenceAtomIndices[i] = ai3\n break\n }\n }\n }\n } else if (bondGraph[ ai2 ].length > 1) {\n for (let j = 0; j < bondGraph[ ai2 ].length; ++j) {\n const ai3 = bondGraph[ ai2 ][ j ]\n if (ai3 !== ai1) {\n if (refRing === undefined || refRing.indexOf(ai3) !== -1){\n bondReferenceAtomIndices[i] = ai3\n break\n }\n }\n }\n } // No reference atom could be found (e.g. diatomic molecule/fragment)\n }\n }\n\n getBondIndex (atomIndex1: number, atomIndex2: number) {\n const bonds = this.bonds! // TODO\n const atomIndices1 = bonds.atomIndices1\n const atomIndices2 = bonds.atomIndices2\n let idx1 = atomIndices1.indexOf(atomIndex1)\n let idx2 = atomIndices2.indexOf(atomIndex2)\n const _idx2 = idx2\n while (idx1 !== -1) {\n while (idx2 !== -1) {\n if (idx1 === idx2) return idx1\n idx2 = atomIndices2.indexOf(atomIndex2, idx2 + 1)\n }\n idx1 = atomIndices1.indexOf(atomIndex1, idx1 + 1)\n idx2 = _idx2\n }\n // returns undefined when no bond is found\n }\n\n getBondReferenceAtomIndex (atomIndex1: number, atomIndex2: number) {\n const bondIndex = this.getBondIndex(atomIndex1, atomIndex2)\n if (bondIndex === undefined) return undefined\n if (this.bondReferenceAtomIndices.length === 0) {\n this.assignBondReferenceAtomIndices()\n }\n return this.bondReferenceAtomIndices[ bondIndex ]\n }\n}\n\n//\n\nconst AromaticRingElements = [\n Elements.B, Elements.C, Elements.N, Elements.O,\n Elements.SI, Elements.P, Elements.S,\n Elements.GE, Elements.AS,\n Elements.SN, Elements.SB,\n Elements.BI\n]\nconst AromaticRingPlanarityThreshold = 0.05\n\nfunction isRingAromatic (ring: AtomProxy[]) {\n if (ring.some(a => !AromaticRingElements.includes(a.number))) return false\n\n let i = 0\n const coords = new Matrix(3, ring.length)\n const cd = coords.data\n\n ring.forEach(a => {\n cd[ i + 0 ] = a.x\n cd[ i + 1 ] = a.y\n cd[ i + 2 ] = a.z\n i += 3\n })\n\n const pa = new PrincipalAxes(coords)\n\n return pa.vecC.length() < AromaticRingPlanarityThreshold\n}\n\n//\n\n/**\n * Ring finding code below adapted from MolQL\n * Copyright (c) 2017 MolQL contributors, licensed under MIT\n * @author David Sehnal \n */\n\nfunction addRing(state: RingFinderState, a: number, b: number) {\n // only \"monotonous\" rings\n if (b < a) return\n\n const { pred, color, left, right } = state\n const nc = ++state.currentColor\n\n let current = a\n\n for (let t = 0; t < RingFinderMaxDepth; t++) {\n color[current] = nc\n current = pred[current]\n if (current < 0) break\n }\n\n let leftOffset = 0\n let rightOffset = 0\n\n let found = false\n let target = 0\n current = b\n for (let t = 0; t < RingFinderMaxDepth; t++) {\n if (color[current] === nc) {\n target = current\n found = true\n break\n }\n right[rightOffset++] = current\n current = pred[current]\n if (current < 0) break\n }\n if (!found) return\n\n current = a\n for (let t = 0; t < RingFinderMaxDepth; t++) {\n left[leftOffset++] = current\n if (target === current) break\n current = pred[current]\n if (current < 0) break\n }\n\n const rn = leftOffset + rightOffset\n const ring: number[] = new Array(rn)\n let ringOffset = 0;\n for (let t = 0; t < leftOffset; t++) {\n ring[ringOffset++] = left[t]\n }\n for (let t = rightOffset - 1; t >= 0; t--) {\n ring[ringOffset++] = right[t]\n }\n\n const ri = state.rings.length\n // set atomRing indices:\n for (let i = 0; i < rn; ++i) {\n const ai = ring[i]\n if (state.atomRings[ai]) {\n state.atomRings[ai].push(ri)\n } else {\n state.atomRings[ai] = [ri]\n }\n }\n\n state.rings.push(ring)\n}\n\nfunction findRings(state: RingFinderState, from: number) {\n const { bonds, visited, queue, pred } = state\n\n visited[from] = 1\n queue[0] = from\n\n let head = 0\n let size = 1\n\n while (head < size) {\n const top = queue[head++]\n const start = 0\n if (bonds[top] === undefined) {\n continue\n }\n const end = bonds[top].length\n\n for (let i = start; i < end; i++) {\n const other = bonds[top][i]\n\n if (visited[other] > 0) {\n if (pred[other] !== top && pred[top] !== other) {\n addRing(state, top, other)\n }\n continue\n }\n\n visited[other] = 1\n queue[size++] = other\n pred[other] = top\n }\n }\n}\n\nconst RingFinderMaxDepth = 4\n\ninterface RingFinderState {\n count: number,\n visited: Int32Array,\n queue: Int32Array,\n color: Int32Array,\n pred: Int32Array,\n\n left: Int32Array,\n right: Int32Array,\n\n currentColor: number,\n\n rings: number[][],\n atomRings: number[][],\n\n bonds: BondGraph\n}\n\nfunction RingFinderState(bonds: BondGraph, capacity: number): RingFinderState {\n const state = {\n count: capacity,\n visited: new Int32Array(capacity),\n queue: new Int32Array(capacity),\n pred: new Int32Array(capacity),\n left: new Int32Array(RingFinderMaxDepth),\n right: new Int32Array(RingFinderMaxDepth),\n color: new Int32Array(capacity),\n currentColor: 0,\n rings: [],\n atomRings: [],\n bonds\n }\n for (let i = 0; i < capacity; i++) {\n state.visited[i] = -1\n state.pred[i] = -1\n }\n return state\n}\n","/**\n * @file Residue Map\n * @author Alexander Rose \n * @private\n */\n\nimport Structure from '../structure/structure'\nimport { ResidueBonds } from '../structure/structure-utils'\nimport ResidueType from './residue-type'\n\nfunction getHash (resname: string, atomTypeIdList: number[], hetero: boolean, chemCompType = '') {\n return (\n resname + '|' +\n atomTypeIdList.join(',') + '|' +\n (hetero ? 1 : 0) + '|' +\n chemCompType\n )\n}\n\nclass ResidueMap {\n dict: { [k: string]: number } = {}\n list: ResidueType[] = []\n\n constructor (readonly structure: Structure) {}\n\n add (resname: string, atomTypeIdList: number[], hetero: boolean, chemCompType = '', bonds?: ResidueBonds) {\n resname = resname.toUpperCase()\n const hash = getHash(resname, atomTypeIdList, hetero, chemCompType)\n let id = this.dict[ hash ]\n if (id === undefined) {\n const residueType = new ResidueType(\n this.structure, resname, atomTypeIdList, hetero, chemCompType, bonds\n )\n id = this.list.length\n this.dict[ hash ] = id\n this.list.push(residueType)\n }\n return id\n }\n\n get (id: number) {\n return this.list[ id ]\n }\n}\n\nexport default ResidueMap\n","/**\n * @file Bond Proxy\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3 } from 'three'\n\nimport Structure from '../structure/structure'\nimport BondStore from '../store/bond-store'\nimport AtomProxy from '../proxy/atom-proxy'\n\n/**\n * Bond proxy\n */\nclass BondProxy {\n index: number\n\n bondStore: BondStore\n\n private _v12: Vector3\n private _v13: Vector3\n private _ap1: AtomProxy\n private _ap2: AtomProxy\n private _ap3: AtomProxy\n\n /**\n * @param {Structure} structure - the structure\n * @param {Integer} index - the index\n */\n constructor (readonly structure: Structure, index = 0) {\n this.index = index\n this.bondStore = structure.bondStore\n\n this._v12 = new Vector3()\n this._v13 = new Vector3()\n this._ap1 = this.structure.getAtomProxy()\n this._ap2 = this.structure.getAtomProxy()\n this._ap3 = this.structure.getAtomProxy()\n }\n\n /**\n * @type {AtomProxy}\n */\n get atom1 () {\n return this.structure.getAtomProxy(this.atomIndex1)\n }\n\n /**\n * @type {AtomProxy}\n */\n get atom2 () {\n return this.structure.getAtomProxy(this.atomIndex2)\n }\n\n /**\n * @type {Integer}\n */\n get atomIndex1 () {\n return this.bondStore.atomIndex1[ this.index ]\n }\n set atomIndex1 (value) {\n this.bondStore.atomIndex1[ this.index ] = value\n }\n\n /**\n * @type {Integer}\n */\n get atomIndex2 () {\n return this.bondStore.atomIndex2[ this.index ]\n }\n set atomIndex2 (value) {\n this.bondStore.atomIndex2[ this.index ] = value\n }\n\n /**\n * @type {Integer}\n */\n get bondOrder () {\n return this.bondStore.bondOrder[ this.index ]\n }\n set bondOrder (value) {\n this.bondStore.bondOrder[ this.index ] = value\n }\n\n getOtherAtomIndex (atomIndex: number) {\n return atomIndex === this.atomIndex1 ? this.atomIndex2 : this.atomIndex1\n }\n\n getOtherAtom (atom: AtomProxy) {\n return this.structure.getAtomProxy(this.getOtherAtomIndex(atom.index))\n }\n\n /**\n * Get reference atom index for the bond\n * @return {Integer|undefined} atom index, or `undefined` if unavailable\n */\n getReferenceAtomIndex () {\n const ap1 = this._ap1\n const ap2 = this._ap2\n ap1.index = this.atomIndex1\n ap2.index = this.atomIndex2\n if (ap1.residueIndex !== ap2.residueIndex) {\n return undefined // Bond between residues, for now ignore (could detect)\n }\n const typeAtomIndex1 = ap1.index - ap1.residueAtomOffset\n const typeAtomIndex2 = ap2.index - ap2.residueAtomOffset\n const residueType = ap1.residueType\n const ix = residueType.getBondReferenceAtomIndex(typeAtomIndex1, typeAtomIndex2)\n if (ix !== undefined) {\n return ix + ap1.residueAtomOffset\n } else {\n console.warn('No reference atom found', ap1.index, ap2.index)\n }\n }\n\n /**\n * calculate shift direction for displaying double/triple bonds\n * @param {Vector3} [v] pre-allocated output vector\n * @return {Vector3} the shift direction vector\n */\n calculateShiftDir (v = new Vector3()) {\n const ap1 = this._ap1\n const ap2 = this._ap2\n const ap3 = this._ap3\n const v12 = this._v12\n const v13 = this._v13\n\n ap1.index = this.atomIndex1\n ap2.index = this.atomIndex2\n const ai3 = this.getReferenceAtomIndex()\n\n v12.subVectors(ap1 as any, ap2 as any).normalize() // TODO\n if (ai3 !== undefined) {\n ap3.index = ai3\n v13.subVectors(ap1 as any, ap3 as any) // TODO\n } else {\n v13.copy(ap1 as any) // no reference point, use origin // TODO\n }\n v13.normalize()\n\n // make sure v13 and v12 are not colinear\n let dp = v12.dot(v13)\n if (1 - Math.abs(dp) < 1e-5) {\n v13.set(1, 0, 0)\n dp = v12.dot(v13)\n if (1 - Math.abs(dp) < 1e-5) {\n v13.set(0, 1, 0)\n dp = v12.dot(v13)\n }\n }\n\n return v.copy(v13.sub(v12.multiplyScalar(dp))).normalize()\n }\n\n qualifiedName () {\n return this.atomIndex1 + '=' + this.atomIndex2\n }\n\n /**\n * Clone object\n * @return {BondProxy} cloned bond\n */\n clone () {\n return new BondProxy(this.structure, this.index)\n }\n\n toObject () {\n return {\n atomIndex1: this.atomIndex1,\n atomIndex2: this.atomIndex2,\n bondOrder: this.bondOrder\n }\n }\n}\n\nexport default BondProxy\n","/**\n * @file Residue Proxy\n * @author Alexander Rose \n * @private\n */\n\nimport { NumberArray } from '../types'\nimport { defaults } from '../utils'\nimport {\n SecStrucHelix, SecStrucSheet, SecStrucTurn,\n ProteinType, RnaType, DnaType, WaterType, IonType, SaccharideType,\n CgProteinBackboneType, CgRnaBackboneType, CgDnaBackboneType,\n AA1\n} from '../structure/structure-constants'\n\nimport Structure from '../structure/structure'\nimport Selection from '../selection/selection'\n\nimport ChainStore from '../store/chain-store'\nimport ResidueStore from '../store/residue-store'\nimport AtomStore from '../store/atom-store'\n\nimport AtomMap from '../store/atom-map'\nimport ResidueMap from '../store/residue-map'\n\nimport AtomProxy from '../proxy/atom-proxy'\nimport ResidueType, { RingData } from '../store/residue-type';\nimport { ResidueBonds } from '../structure/structure-utils';\nimport AtomType from '../store/atom-type';\nimport ChainProxy from './chain-proxy';\nimport Entity from '../structure/entity';\n\n/**\n * Residue proxy\n */\nclass ResidueProxy {\n index: number\n\n chainStore: ChainStore\n residueStore: ResidueStore\n atomStore: AtomStore\n\n residueMap: ResidueMap\n atomMap: AtomMap\n\n /**\n * @param {Structure} structure - the structure\n * @param {Integer} index - the index\n */\n constructor (readonly structure: Structure, index = 0) {\n this.index = index\n this.chainStore = structure.chainStore\n this.residueStore = structure.residueStore\n this.atomStore = structure.atomStore\n this.residueMap = structure.residueMap\n this.atomMap = structure.atomMap\n }\n\n /**\n * Entity\n * @type {Entity}\n */\n get entity (): Entity {\n return this.structure.entityList[ this.entityIndex ]\n }\n get entityIndex () {\n return this.chainStore.entityIndex[ this.chainIndex ]\n }\n /**\n * Chain\n * @type {ChainProxy}\n */\n get chain (): ChainProxy {\n return this.structure.getChainProxy(this.chainIndex)\n }\n\n get chainIndex () {\n return this.residueStore.chainIndex[ this.index ]\n }\n set chainIndex (value) {\n this.residueStore.chainIndex[ this.index ] = value\n }\n\n get atomOffset () {\n return this.residueStore.atomOffset[ this.index ]\n }\n set atomOffset (value) {\n this.residueStore.atomOffset[ this.index ] = value\n }\n\n /**\n * Atom count\n * @type {Integer}\n */\n get atomCount () {\n return this.residueStore.atomCount[ this.index ]\n }\n set atomCount (value) {\n this.residueStore.atomCount[ this.index ] = value\n }\n\n get atomEnd () {\n return this.atomOffset + this.atomCount - 1\n }\n\n //\n\n get modelIndex () {\n return this.chainStore.modelIndex[ this.chainIndex ]\n }\n /**\n * Chain name\n * @type {String}\n */\n get chainname () {\n return this.chainStore.getChainname(this.chainIndex)\n }\n /**\n * Chain id\n * @type {String}\n */\n get chainid () {\n return this.chainStore.getChainid(this.chainIndex)\n }\n\n //\n\n /**\n * Residue number/label\n * @type {Integer}\n */\n get resno () {\n return this.residueStore.resno[ this.index ]\n }\n set resno (value) {\n this.residueStore.resno[ this.index ] = value\n }\n\n /**\n * Secondary structure code\n * @type {String}\n */\n get sstruc () {\n return this.residueStore.getSstruc(this.index)\n }\n set sstruc (value) {\n this.residueStore.setSstruc(this.index, value)\n }\n\n /**\n * Insertion code\n * @type {String}\n */\n get inscode () {\n return this.residueStore.getInscode(this.index)\n }\n set inscode (value) {\n this.residueStore.setInscode(this.index, value)\n }\n\n //\n\n get residueType (): ResidueType {\n return this.residueMap.get(this.residueStore.residueTypeId[ this.index ])\n }\n\n /**\n * Residue name\n * @type {String}\n */\n get resname () {\n return this.residueType.resname\n }\n /**\n * Hetero flag\n * @type {Boolean}\n */\n get hetero () {\n return this.residueType.hetero\n }\n get moleculeType () {\n return this.residueType.moleculeType\n }\n get backboneType () {\n return this.residueType.backboneType\n }\n get backboneStartType () {\n return this.residueType.backboneStartType\n }\n get backboneEndType () {\n return this.residueType.backboneEndType\n }\n get traceAtomIndex () {\n return this.residueType.traceAtomIndex + this.atomOffset\n }\n get direction1AtomIndex () {\n return this.residueType.direction1AtomIndex + this.atomOffset\n }\n get direction2AtomIndex () {\n return this.residueType.direction2AtomIndex + this.atomOffset\n }\n get backboneStartAtomIndex () {\n return this.residueType.backboneStartAtomIndex + this.atomOffset\n }\n get backboneEndAtomIndex () {\n return this.residueType.backboneEndAtomIndex + this.atomOffset\n }\n get rungEndAtomIndex () {\n return this.residueType.rungEndAtomIndex + this.atomOffset\n }\n\n //\n\n get x () {\n let x = 0\n for (let i = this.atomOffset; i <= this.atomEnd; ++i) {\n x += this.atomStore.x[ i ]\n }\n return x / this.atomCount\n }\n\n get y () {\n let y = 0\n for (let i = this.atomOffset; i <= this.atomEnd; ++i) {\n y += this.atomStore.y[ i ]\n }\n return y / this.atomCount\n }\n\n get z () {\n let z = 0\n for (let i = this.atomOffset; i <= this.atomEnd; ++i) {\n z += this.atomStore.z[ i ]\n }\n return z / this.atomCount\n }\n\n //\n\n /**\n * Atom iterator\n * @param {function(atom: AtomProxy)} callback - the callback\n * @param {Selection} [selection] - the selection\n * @return {undefined}\n */\n eachAtom (callback: (ap: AtomProxy) => void, selection?: Selection) {\n const count = this.atomCount\n const offset = this.atomOffset\n const ap = this.structure._ap\n const end = offset + count\n\n if (selection && selection.atomOnlyTest) {\n const atomOnlyTest = selection.atomOnlyTest\n for (let i = offset; i < end; ++i) {\n ap.index = i\n if (atomOnlyTest(ap)) callback(ap)\n }\n } else {\n for (let i = offset; i < end; ++i) {\n ap.index = i\n callback(ap)\n }\n }\n }\n\n //\n\n /**\n * Write residue center position to array\n * @param {Array|TypedArray} [array] - target array\n * @param {Integer} [offset] - the offset\n * @return {Array|TypedArray} target array\n */\n positionToArray (array: NumberArray = [], offset = 0) {\n array[ offset + 0 ] = this.x\n array[ offset + 1 ] = this.y\n array[ offset + 2 ] = this.z\n\n return array\n }\n\n //\n\n /**\n * If residue is from a protein\n * @return {Boolean} flag\n */\n isProtein () {\n return this.residueType.moleculeType === ProteinType\n }\n\n /**\n * If residue is nucleic\n * @return {Boolean} flag\n */\n isNucleic () {\n const moleculeType = this.residueType.moleculeType\n return moleculeType === RnaType || moleculeType === DnaType\n }\n\n /**\n * If residue is rna\n * @return {Boolean} flag\n */\n isRna () {\n return this.residueType.moleculeType === RnaType\n }\n\n /**\n * If residue is dna\n * @return {Boolean} flag\n */\n isDna () {\n return this.residueType.moleculeType === DnaType\n }\n\n /**\n * If residue is coarse-grain\n * @return {Boolean} flag\n */\n isCg () {\n const backboneType = this.residueType.backboneType\n return (\n backboneType === CgProteinBackboneType ||\n backboneType === CgRnaBackboneType ||\n backboneType === CgDnaBackboneType\n )\n }\n\n /**\n * If residue is from a polymer\n * @return {Boolean} flag\n */\n isPolymer () {\n if (this.structure.entityList.length > 0) {\n return this.entity.isPolymer()\n } else {\n const moleculeType = this.residueType.moleculeType\n return (\n moleculeType === ProteinType ||\n moleculeType === RnaType ||\n moleculeType === DnaType\n )\n }\n }\n\n /**\n * If residue is hetero\n * @return {Boolean} flag\n */\n isHetero () {\n return this.residueType.hetero === 1\n }\n\n /**\n * If residue is a water molecule\n * @return {Boolean} flag\n */\n isWater () {\n return this.residueType.moleculeType === WaterType\n }\n\n /**\n * If residue is an ion\n * @return {Boolean} flag\n */\n isIon () {\n return this.residueType.moleculeType === IonType\n }\n\n /**\n * If residue is a saccharide\n * @return {Boolean} flag\n */\n isSaccharide () {\n return this.residueType.moleculeType === SaccharideType\n }\n\n isStandardAminoacid () {\n return this.residueType.isStandardAminoacid()\n }\n\n isStandardBase () {\n return this.residueType.isStandardBase()\n }\n\n /**\n * If residue is part of a helix\n * @return {Boolean} flag\n */\n isHelix () {\n return SecStrucHelix.includes(this.sstruc)\n }\n\n /**\n * If residue is part of a sheet\n * @return {Boolean} flag\n */\n isSheet () {\n return SecStrucSheet.includes(this.sstruc)\n }\n\n /**\n * If residue is part of a turn\n * @return {Boolean} flag\n */\n isTurn () {\n return SecStrucTurn.includes(this.sstruc) && this.isProtein()\n }\n\n getAtomType (index: number): AtomType {\n return this.atomMap.get(this.atomStore.atomTypeId[ index ])\n }\n\n getResname1 () {\n // FIXME nucleic support\n return AA1[ this.resname.toUpperCase() ] || 'X'\n }\n\n getBackboneType (position: number) {\n switch (position) {\n case -1:\n return this.residueType.backboneStartType\n case 1:\n return this.residueType.backboneEndType\n default:\n return this.residueType.backboneType\n }\n }\n\n getAtomIndexByName (atomname: string) {\n let index = this.residueType.getAtomIndexByName(atomname)\n if (index !== undefined) {\n index += this.atomOffset\n }\n return index\n }\n\n hasAtomWithName (atomname: string) {\n return this.residueType.hasAtomWithName(atomname)\n }\n\n getAtomnameList () {\n console.warn('getAtomnameList - might be expensive')\n\n const n = this.atomCount\n const offset = this.atomOffset\n const list = new Array(n)\n for (let i = 0; i < n; ++i) {\n list[ i ] = this.getAtomType(offset + i).atomname\n }\n return list\n }\n\n /**\n * If residue is connected to another\n * @param {ResidueProxy} rNext - the other residue\n * @return {Boolean} - flag\n */\n connectedTo (rNext: ResidueProxy) {\n const bbAtomEnd = this.structure.getAtomProxy(this.backboneEndAtomIndex)\n const bbAtomStart = this.structure.getAtomProxy(rNext.backboneStartAtomIndex)\n if (bbAtomEnd && bbAtomStart) {\n return bbAtomEnd.connectedTo(bbAtomStart)\n } else {\n return false\n }\n }\n\n getNextConnectedResidue () {\n const rOffset = this.chainStore.residueOffset[ this.chainIndex ]\n const rCount = this.chainStore.residueCount[ this.chainIndex ]\n const nextIndex = this.index + 1\n if (nextIndex < rOffset + rCount) {\n const rpNext = this.structure.getResidueProxy(nextIndex)\n if (this.connectedTo(rpNext)) {\n return rpNext\n }\n } else if (nextIndex === rOffset + rCount) { // cyclic\n const rpFirst = this.structure.getResidueProxy(rOffset)\n if (this.connectedTo(rpFirst)) {\n return rpFirst\n }\n }\n return undefined\n }\n\n getPreviousConnectedResidue (residueProxy?: ResidueProxy) {\n const rOffset = this.chainStore.residueOffset[ this.chainIndex ]\n const prevIndex = this.index - 1\n if (prevIndex >= rOffset) {\n const rpPrev = defaults(residueProxy, this.structure.getResidueProxy())\n rpPrev.index = prevIndex\n if (rpPrev.connectedTo(this)) {\n return rpPrev\n }\n } else if (prevIndex === rOffset - 1) { // cyclic\n const rCount = this.chainStore.residueCount[ this.chainIndex ]\n const rpLast = defaults(residueProxy, this.structure.getResidueProxy())\n rpLast.index = rOffset + rCount - 1\n if (rpLast.connectedTo(this)) {\n return rpLast\n }\n }\n return undefined\n }\n\n getBonds (): ResidueBonds {\n return this.residueType.getBonds(this)\n }\n\n getRings (): RingData|undefined {\n return this.residueType.getRings()\n }\n\n getAromaticRings () {\n return this.residueType.getAromaticRings(this)\n }\n\n qualifiedName (noResname = false) {\n let name = ''\n if (this.resname && !noResname) name += '[' + this.resname + ']'\n if (this.resno !== undefined) name += this.resno\n if (this.inscode) name += '^' + this.inscode\n if (this.chain) name += ':' + this.chainname\n name += '/' + this.modelIndex\n return name\n }\n\n /**\n * Clone object\n * @return {ResidueProxy} cloned residue\n */\n clone () {\n return new ResidueProxy(this.structure, this.index)\n }\n\n toObject () {\n return {\n index: this.index,\n chainIndex: this.chainIndex,\n atomOffset: this.atomOffset,\n atomCount: this.atomCount,\n\n resno: this.resno,\n resname: this.resname,\n sstruc: this.sstruc\n }\n }\n}\n\nexport default ResidueProxy\n","/**\n * @file Polymer\n * @author Alexander Rose \n * @private\n */\n\n// import { Log } from '../globals'\n\nimport Structure from '../structure/structure'\nimport Selection from '../selection/selection'\n\nimport ChainStore from '../store/chain-store'\nimport ResidueStore from '../store/residue-store'\nimport AtomStore from '../store/atom-store'\n\nimport ResidueProxy from '../proxy/residue-proxy'\nimport AtomProxy from '../proxy/atom-proxy'\n\n/**\n * Polymer\n */\nclass Polymer {\n chainStore: ChainStore\n residueStore: ResidueStore\n atomStore: AtomStore\n\n residueCount: number\n\n isPrevConnected: boolean\n isNextConnected: boolean\n isNextNextConnected: boolean\n isCyclic: boolean\n\n private __residueProxy: ResidueProxy\n\n /**\n * @param {Structure} structure - the structure\n * @param {Integer} residueIndexStart - the index of the first residue\n * @param {Integer} residueIndexEnd - the index of the last residue\n */\n constructor (readonly structure: Structure, readonly residueIndexStart: number, readonly residueIndexEnd: number) {\n this.chainStore = structure.chainStore\n this.residueStore = structure.residueStore\n this.atomStore = structure.atomStore\n\n /**\n * @type {Integer}\n */\n this.residueCount = residueIndexEnd - residueIndexStart + 1\n\n const rpStart = this.structure.getResidueProxy(this.residueIndexStart)\n const rpEnd = this.structure.getResidueProxy(this.residueIndexEnd)\n this.isPrevConnected = rpStart.getPreviousConnectedResidue() !== undefined\n const rpNext = rpEnd.getNextConnectedResidue()\n this.isNextConnected = rpNext !== undefined\n this.isNextNextConnected = rpNext !== undefined && rpNext.getNextConnectedResidue() !== undefined\n this.isCyclic = rpEnd.connectedTo(rpStart)\n\n this.__residueProxy = this.structure.getResidueProxy()\n\n // console.log( this.qualifiedName(), this );\n }\n\n get chainIndex () {\n return this.residueStore.chainIndex[ this.residueIndexStart ]\n }\n get modelIndex () {\n return this.chainStore.modelIndex[ this.chainIndex ]\n }\n\n /**\n * @type {String}\n */\n get chainname () {\n return this.chainStore.getChainname(this.chainIndex)\n }\n\n //\n\n /**\n * If first residue is from aprotein\n * @return {Boolean} flag\n */\n isProtein () {\n this.__residueProxy.index = this.residueIndexStart\n return this.__residueProxy.isProtein()\n }\n\n /**\n * If atom is part of a coarse-grain group\n * @return {Boolean} flag\n */\n isCg () {\n this.__residueProxy.index = this.residueIndexStart\n return this.__residueProxy.isCg()\n }\n\n /**\n * If atom is part of a nucleic molecule\n * @return {Boolean} flag\n */\n isNucleic () {\n this.__residueProxy.index = this.residueIndexStart\n return this.__residueProxy.isNucleic()\n }\n\n getMoleculeType () {\n this.__residueProxy.index = this.residueIndexStart\n return this.__residueProxy.moleculeType\n }\n\n getBackboneType (position: number) {\n this.__residueProxy.index = this.residueIndexStart\n return this.__residueProxy.getBackboneType(position)\n }\n\n getAtomIndexByType (index: number, type: string) {\n // TODO pre-calculate, add to residueStore???\n\n if (this.isCyclic) {\n if (index === -1) {\n index = this.residueCount - 1\n } else if (index === this.residueCount) {\n index = 0\n }\n } else {\n if (index === -1 && !this.isPrevConnected) index += 1\n if (index === this.residueCount && !this.isNextNextConnected) index -= 1\n // if( index === this.residueCount - 1 && !this.isNextConnected ) index -= 1;\n }\n\n const rp = this.__residueProxy\n rp.index = this.residueIndexStart + index\n let aIndex\n\n switch (type) {\n case 'trace':\n aIndex = rp.traceAtomIndex\n break\n case 'direction1':\n aIndex = rp.direction1AtomIndex\n break\n case 'direction2':\n aIndex = rp.direction2AtomIndex\n break\n default:\n aIndex = rp.getAtomIndexByName(type)\n }\n\n // if (!ap){\n // console.log(this, type, rp.residueType)\n // // console.log(rp.qualifiedName(), rp.index, index, this.residueCount - 1)\n // // rp.index = this.residueIndexStart;\n // // console.log(rp.qualifiedName(), this.residueIndexStart)\n // // rp.index = this.residueIndexEnd;\n // // console.log(rp.qualifiedName(), this.residueIndexEnd)\n // }\n\n return aIndex\n }\n\n /**\n * Atom iterator\n * @param {function(atom: AtomProxy)} callback - the callback\n * @param {Selection} [selection] - the selection\n * @return {undefined}\n */\n eachAtom (callback: (ap: AtomProxy) => void, selection?: Selection) {\n this.eachResidue(function (rp) {\n rp.eachAtom(callback, selection)\n })\n }\n\n eachAtomN (n: number, callback: (...apArray: AtomProxy[]) => void, type: string) {\n const m = this.residueCount\n const array: AtomProxy[] = new Array(n)\n\n for (let i = 0; i < n; ++i) {\n array[ i ] = this.structure.getAtomProxy(this.getAtomIndexByType(i, type))\n }\n callback.apply(this, array)\n\n for (var j = n; j < m; ++j) {\n for (let i = 1; i < n; ++i) {\n array[ i - 1 ].index = array[ i ].index\n }\n array[ n - 1 ].index = this.getAtomIndexByType(j, type)! // TODO\n callback.apply(this, array)\n }\n }\n\n /**\n * Residue iterator\n * @param {function(residue: ResidueProxy)} callback - the callback\n * @return {undefined}\n */\n eachResidue (callback: (rp: ResidueProxy) => void) {\n const rp = this.structure.getResidueProxy()\n const n = this.residueCount\n const rStartIndex = this.residueIndexStart\n\n for (let i = 0; i < n; ++i) {\n rp.index = rStartIndex + i\n callback(rp)\n }\n }\n\n qualifiedName () {\n const rpStart = this.structure.getResidueProxy(this.residueIndexStart)\n const rpEnd = this.structure.getResidueProxy(this.residueIndexEnd)\n return rpStart.qualifiedName() + ' - ' + rpEnd.qualifiedName()\n }\n}\n\nexport default Polymer\n","/**\n * @file Chain Proxy\n * @author Alexander Rose \n * @private\n */\n\nimport { UnknownBackboneType } from '../structure/structure-constants'\n\nimport Structure from '../structure/structure'\nimport Selection from '../selection/selection'\n\nimport ChainStore from '../store/chain-store'\nimport ResidueStore from '../store/residue-store'\n\nimport Polymer from '../proxy/polymer'\nimport ResidueProxy from '../proxy/residue-proxy'\nimport AtomProxy from '../proxy/atom-proxy'\nimport ModelProxy from './model-proxy';\nimport Entity from '../structure/entity';\n\n/**\n * Chain proxy\n */\nclass ChainProxy {\n index: number\n\n chainStore: ChainStore\n residueStore: ResidueStore\n\n /**\n * @param {Structure} structure - the structure\n * @param {Integer} index - the index\n */\n constructor (readonly structure: Structure, index = 0) {\n this.index = index\n this.chainStore = structure.chainStore\n this.residueStore = structure.residueStore\n }\n\n /**\n * Entity\n * @type {Entity}\n */\n get entity (): Entity {\n return this.structure.entityList[ this.entityIndex ]\n }\n /**\n * Model\n * @type {ModelProxy}\n */\n get model (): ModelProxy {\n return this.structure.getModelProxy(this.modelIndex)\n }\n\n get entityIndex () {\n return this.chainStore.entityIndex[ this.index ]\n }\n set entityIndex (value) {\n this.chainStore.entityIndex[ this.index ] = value\n }\n\n get modelIndex () {\n return this.chainStore.modelIndex[ this.index ]\n }\n set modelIndex (value) {\n this.chainStore.modelIndex[ this.index ] = value\n }\n\n get residueOffset () {\n return this.chainStore.residueOffset[ this.index ]\n }\n set residueOffset (value) {\n this.chainStore.residueOffset[ this.index ] = value\n }\n\n /**\n * Residue count\n * @type {Integer}\n */\n get residueCount () {\n return this.chainStore.residueCount[ this.index ]\n }\n set residueCount (value) {\n this.chainStore.residueCount[ this.index ] = value\n }\n\n get residueEnd () {\n return this.residueOffset + this.residueCount - 1\n }\n\n get atomOffset () {\n return this.residueStore.atomOffset[ this.residueOffset ]\n }\n get atomEnd () {\n return (\n this.residueStore.atomOffset[ this.residueEnd ] +\n this.residueStore.atomCount[ this.residueEnd ] - 1\n )\n }\n /**\n * Atom count\n * @type {Integer}\n */\n get atomCount () {\n if (this.residueCount === 0) {\n return 0\n } else {\n return this.atomEnd - this.atomOffset + 1\n }\n }\n\n //\n\n /**\n * Chain name\n * @type {String}\n */\n get chainname () {\n return this.chainStore.getChainname(this.index)\n }\n set chainname (value) {\n this.chainStore.setChainname(this.index, value)\n }\n\n /**\n * Chain id\n * @type {String}\n */\n get chainid () {\n return this.chainStore.getChainid(this.index)\n }\n set chainid (value) {\n this.chainStore.setChainid(this.index, value)\n }\n\n //\n\n /**\n * Atom iterator\n * @param {function(atom: AtomProxy)} callback - the callback\n * @param {Selection} [selection] - the selection\n * @return {undefined}\n */\n eachAtom (callback: (ap: AtomProxy) => void, selection?: Selection) {\n this.eachResidue(function (rp) {\n rp.eachAtom(callback, selection)\n }, selection)\n }\n\n /**\n * Residue iterator\n * @param {function(residue: ResidueProxy)} callback - the callback\n * @param {Selection} [selection] - the selection\n * @return {undefined}\n */\n eachResidue (callback: (rp: ResidueProxy) => void, selection?: Selection) {\n const count = this.residueCount\n const offset = this.residueOffset\n const rp = this.structure._rp\n const end = offset + count\n\n if (selection && selection.test) {\n const residueOnlyTest = selection.residueOnlyTest\n if (residueOnlyTest) {\n for (let i = offset; i < end; ++i) {\n rp.index = i\n if (residueOnlyTest(rp)) {\n callback(rp)\n }\n }\n } else {\n for (let i = offset; i < end; ++i) {\n rp.index = i\n callback(rp)\n }\n }\n } else {\n for (let i = offset; i < end; ++i) {\n rp.index = i\n callback(rp)\n }\n }\n }\n\n /**\n * Multi-residue iterator\n * @param {Integer} n - window size\n * @param {function(residueList: ResidueProxy[])} callback - the callback\n * @return {undefined}\n */\n eachResidueN (n: number, callback: (...rpArray: ResidueProxy[]) => void) {\n const count = this.residueCount\n const offset = this.residueOffset\n const end = offset + count\n if (count < n) return\n const array: ResidueProxy[] = new Array(n)\n\n for (let i = 0; i < n; ++i) {\n array[ i ] = this.structure.getResidueProxy(offset + i)\n }\n callback.apply(this, array)\n\n for (let j = offset + n; j < end; ++j) {\n for (let i = 0; i < n; ++i) {\n array[ i ].index += 1\n }\n callback.apply(this, array)\n }\n }\n\n /**\n * Polymer iterator\n * @param {function(polymer: Polymer)} callback - the callback\n * @param {Selection} [selection] - the selection\n * @return {undefined}\n */\n eachPolymer (callback: (p: Polymer) => void, selection?: Selection) {\n let rStartIndex = 0\n let rNextIndex = 0\n const test = selection ? selection.residueOnlyTest : undefined\n const structure = this.model.structure\n\n const count = this.residueCount\n const offset = this.residueOffset\n const end = offset + count\n\n const rp1 = this.structure.getResidueProxy()\n const rp2 = this.structure.getResidueProxy(offset)\n\n const ap1 = this.structure.getAtomProxy()\n const ap2 = this.structure.getAtomProxy()\n\n let first = true\n\n for (let i = offset + 1; i < end; ++i) {\n rp1.index = rp2.index\n rp2.index = i\n\n const bbType1 = first ? rp1.backboneEndType : rp1.backboneType\n const bbType2 = rp2.backboneType\n\n if (first) {\n rStartIndex = rp1.index\n first = false\n }\n rNextIndex = rp2.index\n\n if (bbType1 !== UnknownBackboneType && bbType1 === bbType2) {\n ap1.index = rp1.backboneEndAtomIndex\n ap2.index = rp2.backboneStartAtomIndex\n } else {\n if (bbType1 !== UnknownBackboneType) {\n if (rp1.index - rStartIndex > 1) {\n // console.log(\"FOO1\",rStartIndex, rp1.index)\n callback(new Polymer(structure, rStartIndex, rp1.index))\n }\n }\n rStartIndex = rNextIndex\n\n continue\n }\n\n if (!ap1 || !ap2 || !ap1.connectedTo(ap2) ||\n (test && (!test(rp1) || !test(rp2)))\n ) {\n if (rp1.index - rStartIndex > 1) {\n // console.log(\"FOO2\",rStartIndex, rp1.index)\n callback(new Polymer(structure, rStartIndex, rp1.index))\n }\n rStartIndex = rNextIndex\n }\n }\n\n if (rNextIndex - rStartIndex > 1) {\n if (this.structure.getResidueProxy(rStartIndex).backboneEndType) {\n // console.log(\"FOO3\",rStartIndex, rNextIndex)\n callback(new Polymer(structure, rStartIndex, rNextIndex))\n }\n }\n }\n\n //\n\n qualifiedName () {\n var name = ':' + this.chainname + '/' + this.modelIndex\n return name\n }\n\n /**\n * Clone object\n * @return {ChainProxy} cloned chain\n */\n clone () {\n return new ChainProxy(this.structure, this.index)\n }\n\n toObject () {\n return {\n index: this.index,\n residueOffset: this.residueOffset,\n residueCount: this.residueCount,\n\n chainname: this.chainname\n }\n }\n}\n\nexport default ChainProxy\n","/**\n * @file Model Proxy\n * @author Alexander Rose \n * @private\n */\n\nimport Structure from '../structure/structure'\nimport Selection from '../selection/selection'\n\nimport ModelStore from '../store/model-store'\nimport ChainStore from '../store/chain-store'\nimport ResidueStore from '../store/residue-store'\n\nimport ChainProxy from '../proxy/chain-proxy'\nimport Polymer from '../proxy/polymer'\nimport ResidueProxy from '../proxy/residue-proxy'\nimport AtomProxy from '../proxy/atom-proxy'\n\n/**\n * Model proxy\n */\nclass ModelProxy {\n index: number\n\n modelStore: ModelStore\n chainStore: ChainStore\n residueStore: ResidueStore\n\n /**\n * @param {Structure} structure - the structure\n * @param {Integer} index - the index\n */\n constructor (readonly structure: Structure, index = 0) {\n this.index = index\n this.modelStore = structure.modelStore\n this.chainStore = structure.chainStore\n this.residueStore = structure.residueStore\n }\n\n get chainOffset () {\n return this.modelStore.chainOffset[ this.index ]\n }\n set chainOffset (value) {\n this.modelStore.chainOffset[ this.index ] = value\n }\n\n get chainCount () {\n return this.modelStore.chainCount[ this.index ]\n }\n set chainCount (value) {\n this.modelStore.chainCount[ this.index ] = value\n }\n\n get residueOffset () {\n return this.chainStore.residueOffset[ this.chainOffset ]\n }\n get atomOffset () {\n return this.residueStore.atomOffset[ this.residueOffset ]\n }\n\n get chainEnd () {\n return this.chainOffset + this.chainCount - 1\n }\n get residueEnd () {\n return (\n this.chainStore.residueOffset[ this.chainEnd ] +\n this.chainStore.residueCount[ this.chainEnd ] - 1\n )\n }\n get atomEnd () {\n return (\n this.residueStore.atomOffset[ this.residueEnd ] +\n this.residueStore.atomCount[ this.residueEnd ] - 1\n )\n }\n\n /**\n * Residue count\n * @type {Integer}\n */\n get residueCount () {\n if (this.chainCount === 0) {\n return 0\n } else {\n return this.residueEnd - this.residueOffset + 1\n }\n }\n\n /**\n * Atom count\n * @type {Integer}\n */\n get atomCount () {\n if (this.residueCount === 0) {\n return 0\n } else {\n return this.atomEnd - this.atomOffset + 1\n }\n }\n\n //\n\n /**\n * Atom iterator\n * @param {function(atom: AtomProxy)} callback - the callback\n * @param {Selection} [selection] - the selection\n * @return {undefined}\n */\n eachAtom (callback: (ap: AtomProxy) => void, selection?: Selection) {\n this.eachChain(function (cp) {\n cp.eachAtom(callback, selection)\n }, selection)\n }\n\n /**\n * Residue iterator\n * @param {function(residue: ResidueProxy)} callback - the callback\n * @param {Selection} [selection] - the selection\n * @return {undefined}\n */\n eachResidue (callback: (rp: ResidueProxy) => void, selection?: Selection) {\n this.eachChain(function (cp) {\n cp.eachResidue(callback, selection)\n }, selection)\n }\n\n /**\n * Polymer iterator\n * @param {function(polymer: Polymer)} callback - the callback\n * @param {Selection} [selection] - the selection\n * @return {undefined}\n */\n eachPolymer (callback: (p: Polymer) => void, selection?: Selection) {\n if (selection && selection.chainOnlyTest) {\n const chainOnlyTest = selection.chainOnlyTest\n\n this.eachChain(function (cp) {\n if (chainOnlyTest(cp)) {\n cp.eachPolymer(callback, selection)\n }\n })\n } else {\n this.eachChain(function (cp) {\n cp.eachPolymer(callback, selection)\n })\n }\n }\n\n /**\n * Chain iterator\n * @param {function(chain: ChainProxy)} callback - the callback\n * @param {Selection} [selection] - the selection\n * @return {undefined}\n */\n eachChain (callback: (cp: ChainProxy) => void, selection?: Selection) {\n const count = this.chainCount\n const offset = this.chainOffset\n const cp = this.structure._cp\n const end = offset + count\n\n if (selection && selection.test) {\n const chainOnlyTest = selection.chainOnlyTest\n if (chainOnlyTest) {\n for (let i = offset; i < end; ++i) {\n cp.index = i\n if (chainOnlyTest(cp)) {\n callback(cp)\n }\n }\n } else {\n for (let i = offset; i < end; ++i) {\n cp.index = i\n callback(cp)\n }\n }\n } else {\n for (let i = offset; i < end; ++i) {\n cp.index = i\n callback(cp)\n }\n }\n }\n\n //\n\n qualifiedName () {\n const name = '/' + this.index\n return name\n }\n\n /**\n * Clone object\n * @return {ModelProxy} cloned model\n */\n clone () {\n return new ModelProxy(this.structure, this.index)\n }\n\n toObject () {\n return {\n index: this.index,\n chainOffset: this.chainOffset,\n chainCount: this.chainCount\n }\n }\n}\n\nexport default ModelProxy\n","/**\n * @file Structure\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3, Box3 } from 'three'\nimport { Signal } from 'signals'\nimport { CifBlock } from 'molstar/lib/mol-io/reader/cif'\n\nimport { Debug, Log, ColormakerRegistry } from '../globals'\nimport { defaults } from '../utils'\nimport { AtomPicker, BondPicker } from '../utils/picker'\nimport { copyWithin, arrayMin, arrayMax } from '../math/array-utils'\nimport BitArray from '../utils/bitarray'\nimport RadiusFactory, { RadiusParams } from '../utils/radius-factory'\nimport { Matrix } from '../math/matrix-utils'\nimport PrincipalAxes from '../math/principal-axes'\nimport SpatialHash from '../geometry/spatial-hash'\nimport FilteredVolume from '../surface/filtered-volume'\nimport StructureView from './structure-view'\nimport { AtomDataParams, AtomData, BondDataParams, BondData } from './structure-data'\nimport { Data, createData } from './data'\n\nimport Entity from './entity'\nimport Unitcell from '../symmetry/unitcell'\nimport Validation from './validation'\nimport Selection from '../selection/selection'\nimport Assembly from '../symmetry/assembly'\nimport Volume from '../surface/volume'\nimport Polymer from '../proxy/polymer'\n\nimport BondHash from '../store/bond-hash'\nimport BondStore from '../store/bond-store'\nimport AtomStore from '../store/atom-store'\nimport ResidueStore from '../store/residue-store'\nimport ChainStore from '../store/chain-store'\nimport ModelStore from '../store/model-store'\n\nimport AtomMap from '../store/atom-map'\nimport ResidueMap from '../store/residue-map'\n\nimport BondProxy from '../proxy/bond-proxy'\nimport AtomProxy from '../proxy/atom-proxy'\nimport ResidueProxy from '../proxy/residue-proxy'\nimport ChainProxy from '../proxy/chain-proxy'\nimport ModelProxy from '../proxy/model-proxy'\nimport ChemCompMap from '../store/chemcomp-map'\n\ninterface Structure {\n signals: StructureSignals\n\n name: string\n path: string\n title: string\n id: string\n\n data: Data\n\n atomCount: number\n bondCount: number\n\n header: StructureHeader\n extraData: StructureExtraData\n\n atomSetCache: { [k: string]: BitArray }\n atomSetDict: { [k: string]: BitArray }\n biomolDict: { [k: string]: Assembly }\n\n entityList: Entity[]\n unitcell?: Unitcell\n\n frames: Float32Array[]\n boxes: Float32Array[]\n\n validation?: Validation\n\n bondStore: BondStore\n backboneBondStore: BondStore\n rungBondStore: BondStore\n atomStore: AtomStore\n residueStore: ResidueStore\n chainStore: ChainStore\n modelStore: ModelStore\n\n atomMap: AtomMap\n residueMap: ResidueMap\n chemCompMap?: ChemCompMap\n\n bondHash?: BondHash\n spatialHash?: SpatialHash\n\n atomSet?: BitArray\n bondSet?: BitArray\n\n center: Vector3\n boundingBox: Box3\n\n trajectory?: {\n name: string\n frame: number\n }\n\n getView(selection: Selection): StructureView\n\n _hasCoords?: boolean\n\n _bp: BondProxy\n _ap: AtomProxy\n _rp: ResidueProxy\n _cp: ChainProxy\n}\n\nexport type StructureHeader = {\n releaseDate?: string\n depositionDate?: string\n resolution?: number\n rFree?: number\n rWork?: number\n experimentalMethods?: string[]\n}\n\nexport type StructureExtraData = {\n cif?: CifBlock\n sdf?: object[]\n}\n\nexport type StructureSignals = {\n refreshed: Signal\n}\n\n/**\n * Structure\n */\nclass Structure implements Structure{\n signals: StructureSignals = {\n refreshed: new Signal()\n }\n\n /**\n * @param {String} name - structure name\n * @param {String} path - source path\n */\n constructor (name = '', path = '') {\n this.init(name, path)\n }\n\n init (name: string, path: string) {\n this.name = name\n this.path = path\n this.title = ''\n this.id = ''\n\n this.data = createData(this)\n\n this.header = {}\n this.extraData = {}\n\n this.atomSetCache = {}\n this.atomSetDict = {}\n this.biomolDict = {}\n\n this.entityList = []\n this.unitcell = undefined\n\n this.frames = []\n this.boxes = []\n\n this.validation = undefined\n\n this.bondStore = new BondStore(0)\n this.backboneBondStore = new BondStore(0)\n this.rungBondStore = new BondStore(0)\n this.atomStore = new AtomStore(0)\n this.residueStore = new ResidueStore(0)\n this.chainStore = new ChainStore(0)\n this.modelStore = new ModelStore(0)\n\n this.atomMap = new AtomMap(this)\n this.residueMap = new ResidueMap(this)\n this.chemCompMap = undefined\n\n this.bondHash = undefined\n this.spatialHash = undefined\n\n this.atomSet = undefined\n this.bondSet = undefined\n\n this.center = new Vector3()\n this.boundingBox = new Box3()\n\n this._bp = this.getBondProxy()\n this._ap = this.getAtomProxy()\n this._rp = this.getResidueProxy()\n this._cp = this.getChainProxy()\n }\n\n get type () { return 'Structure' }\n\n finalizeAtoms () {\n this.atomSet = this.getAtomSet()\n this.atomCount = this.atomStore.count\n this.boundingBox = this.getBoundingBox(undefined, this.boundingBox)\n this.center = this.boundingBox.getCenter(new Vector3())\n this.spatialHash = new SpatialHash(this.atomStore, this.boundingBox)\n }\n\n finalizeBonds () {\n this.bondSet = this.getBondSet()\n this.bondCount = this.bondStore.count\n this.bondHash = new BondHash(this.bondStore, this.atomStore.count)\n\n this.atomSetCache = {}\n if (!this.atomSetDict.rung) {\n this.atomSetDict.rung = this.getAtomSet(false)\n }\n\n for (let name in this.atomSetDict) {\n this.atomSetCache[ '__' + name ] = this.atomSetDict[ name ].clone()\n }\n }\n\n //\n\n getBondProxy (index?: number) {\n return new BondProxy(this, index)\n }\n\n getAtomProxy (index?: number) {\n return new AtomProxy(this, index)\n }\n\n getResidueProxy (index?: number) {\n return new ResidueProxy(this, index)\n }\n\n getChainProxy (index?: number) {\n return new ChainProxy(this, index)\n }\n\n getModelProxy (index?: number) {\n return new ModelProxy(this, index)\n }\n\n //\n\n getBondSet (/* selection */) {\n // TODO implement selection parameter\n\n const n = this.bondStore.count\n const bondSet = new BitArray(n)\n const atomSet = this.atomSet\n\n if (atomSet) {\n if (atomSet.isAllSet()) {\n bondSet.setAll()\n } else if (atomSet.isAllClear()) {\n bondSet.clearAll()\n } else {\n const bp = this.getBondProxy()\n\n for (let i = 0; i < n; ++i) {\n bp.index = i\n if (atomSet.isSet(bp.atomIndex1, bp.atomIndex2)) {\n bondSet.set(bp.index)\n }\n }\n }\n } else {\n bondSet.setAll()\n }\n\n return bondSet\n }\n\n getBackboneBondSet (/* selection */) {\n // TODO implement selection parameter\n\n const n = this.backboneBondStore.count\n const backboneBondSet = new BitArray(n)\n const backboneAtomSet = this.atomSetCache.__backbone\n\n if (backboneAtomSet) {\n const bp = this.getBondProxy()\n bp.bondStore = this.backboneBondStore\n\n for (let i = 0; i < n; ++i) {\n bp.index = i\n if (backboneAtomSet.isSet(bp.atomIndex1, bp.atomIndex2)) {\n backboneBondSet.set(bp.index)\n }\n }\n } else {\n backboneBondSet.setAll()\n }\n\n return backboneBondSet\n }\n\n getRungBondSet (/* selection */) {\n // TODO implement selection parameter\n\n const n = this.rungBondStore.count\n const rungBondSet = new BitArray(n)\n const rungAtomSet = this.atomSetCache.__rung\n\n if (rungAtomSet) {\n const bp = this.getBondProxy()\n bp.bondStore = this.rungBondStore\n\n for (let i = 0; i < n; ++i) {\n bp.index = i\n if (rungAtomSet.isSet(bp.atomIndex1, bp.atomIndex2)) {\n rungBondSet.set(bp.index)\n }\n }\n } else {\n rungBondSet.setAll()\n }\n\n return rungBondSet\n }\n\n /**\n * Get a set of atoms\n * @param {Boolean|Selection|BitArray} selection - object defining how to\n * initialize the atom set.\n * Boolean: init with value;\n * Selection: init with selection;\n * BitArray: return bit array\n * @return {BitArray} set of atoms\n */\n getAtomSet (selection?: boolean|Selection|BitArray) {\n const n = this.atomStore.count\n\n if (selection === undefined) {\n return new BitArray(n, true)\n } else if (selection instanceof BitArray) {\n return selection\n } else if (selection === true) {\n return new BitArray(n, true)\n } else if (selection && selection.test) {\n const seleString = selection.string\n if (seleString in this.atomSetCache) {\n return this.atomSetCache[ seleString ]\n } else {\n if (seleString === '') {\n return new BitArray(n, true)\n } else {\n const atomSet = new BitArray(n)\n this.eachAtom(function (ap: AtomProxy) {\n atomSet.set(ap.index)\n }, selection)\n this.atomSetCache[ seleString ] = atomSet\n return atomSet\n }\n }\n } else if (selection === false) {\n return new BitArray(n)\n }\n\n return new BitArray(n, true)\n }\n\n /**\n * Get set of atoms around a set of atoms from a selection\n * @param {Selection} selection - the selection object\n * @param {Number} radius - radius to select within\n * @return {BitArray} set of atoms\n */\n getAtomSetWithinSelection (selection: boolean|Selection|BitArray, radius: number) {\n const spatialHash = this.spatialHash\n const atomSet = this.getAtomSet(false)\n const ap = this.getAtomProxy()\n\n if (!spatialHash) return atomSet\n\n this.getAtomSet(selection).forEach(function (idx: number) {\n ap.index = idx\n spatialHash.within(ap.x, ap.y, ap.z, radius).forEach(function (idx2: number) {\n atomSet.set(idx2)\n })\n })\n\n return atomSet\n }\n\n /**\n * Get set of atoms around a point\n * @param {Vector3|AtomProxy} point - the point\n * @param {Number} radius - radius to select within\n * @return {BitArray} set of atoms\n */\n getAtomSetWithinPoint (point: Vector3|AtomProxy, radius: number) {\n const p = point\n const atomSet = this.getAtomSet(false)\n\n if (!this.spatialHash) return atomSet\n\n this.spatialHash.within(p.x, p.y, p.z, radius).forEach(function (idx: number) {\n atomSet.set(idx)\n })\n\n return atomSet\n }\n\n /**\n * Get set of atoms within a volume\n * @param {Volume} volume - the volume\n * @param {Number} radius - radius to select within\n * @param {[type]} minValue - minimum value to be considered as within the volume\n * @param {[type]} maxValue - maximum value to be considered as within the volume\n * @param {[type]} outside - use only values falling outside of the min/max values\n * @return {BitArray} set of atoms\n */\n getAtomSetWithinVolume (volume: Volume, radius: number, minValue: number, maxValue: number, outside: boolean) {\n const fv = new FilteredVolume(volume, minValue, maxValue, outside) as any // TODO\n\n const dp = fv.getDataPosition()\n const n = dp.length\n const r = fv.matrix.getMaxScaleOnAxis()\n const atomSet = this.getAtomSet(false)\n\n if (!this.spatialHash) return atomSet\n\n for (let i = 0; i < n; i += 3) {\n this.spatialHash.within(dp[ i ], dp[ i + 1 ], dp[ i + 2 ], r).forEach(function (idx) {\n atomSet.set(idx)\n })\n }\n\n return atomSet\n }\n\n /**\n * Get set of all atoms within the groups of a selection\n * @param {Selection} selection - the selection object\n * @return {BitArray} set of atoms\n */\n getAtomSetWithinGroup (selection: boolean|Selection|BitArray) {\n const atomResidueIndex = this.atomStore.residueIndex\n const atomSet = this.getAtomSet(false)\n const rp = this.getResidueProxy()\n\n this.getAtomSet(selection).forEach(function (idx) {\n rp.index = atomResidueIndex[ idx ]\n for (let idx2 = rp.atomOffset; idx2 <= rp.atomEnd; ++idx2) {\n atomSet.set(idx2)\n }\n })\n\n return atomSet\n }\n\n //\n\n getSelection (): undefined|Selection {\n return\n }\n\n getStructure (): Structure|StructureView {\n return this\n }\n\n /**\n * Entity iterator\n * @param {function(entity: Entity)} callback - the callback\n * @param {EntityType} type - entity type\n * @return {undefined}\n */\n eachEntity (callback: (entity: Entity) => void, type: number) {\n this.entityList.forEach(function (entity) {\n if (type === undefined || entity.getEntityType() === type) {\n callback(entity)\n }\n })\n }\n\n /**\n * Bond iterator\n * @param {function(bond: BondProxy)} callback - the callback\n * @param {Selection} [selection] - the selection\n * @return {undefined}\n */\n eachBond (callback: (entity: BondProxy) => void, selection?: Selection) {\n const bp = this.getBondProxy()\n let bondSet\n\n if (selection && selection.test) {\n bondSet = this.getBondSet(/*selection*/)\n if (this.bondSet) {\n bondSet.intersection(this.bondSet)\n }\n }\n\n if (bondSet) {\n bondSet.forEach(function (index) {\n bp.index = index\n callback(bp)\n })\n } else {\n const n = this.bondStore.count\n for (let i = 0; i < n; ++i) {\n bp.index = i\n callback(bp)\n }\n }\n }\n\n /**\n * Atom iterator\n * @param {function(atom: AtomProxy)} callback - the callback\n * @param {Selection} [selection] - the selection\n * @return {undefined}\n */\n eachAtom (callback: (entity: AtomProxy) => void, selection?: Selection) {\n if (selection && selection.test) {\n this.eachModel(function (mp) {\n mp.eachAtom(callback, selection)\n }, selection)\n } else {\n const an = this.atomStore.count\n const ap = this.getAtomProxy()\n for (let i = 0; i < an; ++i) {\n ap.index = i\n callback(ap)\n }\n }\n }\n\n /**\n * Residue iterator\n * @param {function(residue: ResidueProxy)} callback - the callback\n * @param {Selection} [selection] - the selection\n * @return {undefined}\n */\n eachResidue (callback: (entity: ResidueProxy) => void, selection?: Selection) {\n if (selection && selection.test) {\n const mn = this.modelStore.count\n const mp = this.getModelProxy()\n const modelOnlyTest = selection.modelOnlyTest\n if (modelOnlyTest) {\n for (let i = 0; i < mn; ++i) {\n mp.index = i\n if (modelOnlyTest(mp)) {\n mp.eachResidue(callback, selection)\n }\n }\n } else {\n for (let i = 0; i < mn; ++i) {\n mp.index = i\n mp.eachResidue(callback, selection)\n }\n }\n } else {\n const rn = this.residueStore.count\n const rp = this.getResidueProxy()\n for (let i = 0; i < rn; ++i) {\n rp.index = i\n callback(rp)\n }\n }\n }\n\n /**\n * Multi-residue iterator\n * @param {Integer} n - window size\n * @param {function(residueList: ResidueProxy[])} callback - the callback\n * @return {undefined}\n */\n eachResidueN (n: number, callback: (...entityArray: ResidueProxy[]) => void) {\n const rn = this.residueStore.count\n if (rn < n) return\n const array: ResidueProxy[] = new Array(n)\n\n for (let i = 0; i < n; ++i) {\n array[ i ] = this.getResidueProxy(i)\n }\n callback.apply(this, array)\n\n for (let j = n; j < rn; ++j) {\n for (let i = 0; i < n; ++i) {\n array[ i ].index += 1\n }\n callback.apply(this, array)\n }\n }\n\n /**\n * Polymer iterator\n * @param {function(polymer: Polymer)} callback - the callback\n * @param {Selection} [selection] - the selection\n * @return {undefined}\n */\n eachPolymer (callback: (entity: Polymer) => void, selection?: Selection) {\n if (selection && selection.modelOnlyTest) {\n const modelOnlyTest = selection.modelOnlyTest\n\n this.eachModel(function (mp) {\n if (modelOnlyTest(mp)) {\n mp.eachPolymer(callback, selection)\n }\n })\n } else {\n this.eachModel(function (mp) {\n mp.eachPolymer(callback, selection)\n })\n }\n }\n\n /**\n * Chain iterator\n * @param {function(chain: ChainProxy)} callback - the callback\n * @param {Selection} [selection] - the selection\n * @return {undefined}\n */\n eachChain (callback: (entity: ChainProxy) => void, selection?: Selection) {\n if (selection && selection.test) {\n this.eachModel(function (mp) {\n mp.eachChain(callback, selection)\n })\n } else {\n const cn = this.chainStore.count\n const cp = this.getChainProxy()\n for (let i = 0; i < cn; ++i) {\n cp.index = i\n callback(cp)\n }\n }\n }\n\n /**\n * Model iterator\n * @param {function(model: ModelProxy)} callback - the callback\n * @param {Selection} [selection] - the selection\n * @return {undefined}\n */\n eachModel (callback: (entity: ModelProxy) => void, selection?: Selection) {\n const n = this.modelStore.count\n const mp = this.getModelProxy()\n\n if (selection && selection.test) {\n const modelOnlyTest = selection.modelOnlyTest\n if (modelOnlyTest) {\n for (let i = 0; i < n; ++i) {\n mp.index = i\n if (modelOnlyTest(mp)) {\n callback(mp)\n }\n }\n } else {\n for (let i = 0; i < n; ++i) {\n mp.index = i\n callback(mp)\n }\n }\n } else {\n for (let i = 0; i < n; ++i) {\n mp.index = i\n callback(mp)\n }\n }\n }\n\n //\n\n getAtomData (params: AtomDataParams) {\n const p = Object.assign({}, params)\n if (p.colorParams) p.colorParams.structure = this.getStructure()\n\n const what = p.what\n const atomSet = defaults(p.atomSet, this.atomSet)\n\n let radiusFactory: any // TODO\n let colormaker: any // TODO\n\n const atomData: AtomData = {}\n const ap = this.getAtomProxy()\n const atomCount = atomSet.getSize()\n\n if (!what || what.position) {\n atomData.position = new Float32Array(atomCount * 3)\n }\n if ((!what || what.color) && p.colorParams) {\n atomData.color = new Float32Array(atomCount * 3)\n colormaker = ColormakerRegistry.getScheme(p.colorParams)\n }\n if (!what || what.picking) {\n atomData.picking = new AtomPicker(new Float32Array(atomCount), this.getStructure())\n }\n if (!what || what.radius) {\n atomData.radius = new Float32Array(atomCount)\n radiusFactory = new RadiusFactory(p.radiusParams as RadiusParams)\n }\n if (!what || what.index) {\n atomData.index = new Uint32Array(atomCount)\n }\n\n const {position, color, picking, radius, index} = atomData\n\n atomSet.forEach((idx: number, i: number) => {\n const i3 = i * 3\n ap.index = idx\n if (position) {\n ap.positionToArray(position, i3)\n }\n if (color) {\n colormaker.atomColorToArray(ap, color, i3)\n }\n if (picking) {\n picking.array![ i ] = idx\n }\n if (radius) {\n radius[ i ] = radiusFactory.atomRadius(ap)\n }\n if (index) {\n index[ i ] = idx\n }\n })\n return atomData\n }\n\n getBondData (params: BondDataParams) {\n const p = Object.assign({}, params)\n if (p.colorParams) p.colorParams.structure = this.getStructure()\n\n const what = p.what\n const bondSet = defaults(p.bondSet, this.bondSet)\n const multipleBond = defaults(p.multipleBond, 'off')\n const isMulti = multipleBond !== 'off'\n const isOffset = multipleBond === 'offset'\n const bondScale = defaults(p.bondScale, 0.4)\n const bondSpacing = defaults(p.bondSpacing, 1.0)\n\n let radiusFactory: any // TODO\n let colormaker: any // TODO\n\n const bondData: BondData = {}\n const bp = this.getBondProxy()\n if (p.bondStore) bp.bondStore = p.bondStore\n const ap1 = this.getAtomProxy()\n const ap2 = this.getAtomProxy()\n\n let bondCount: number\n if (isMulti) {\n const storeBondOrder = bp.bondStore.bondOrder\n bondCount = 0\n bondSet.forEach(function (index: number) {\n bondCount += storeBondOrder[ index ]\n })\n } else {\n bondCount = bondSet.getSize()\n }\n\n if (!what || what.position) {\n bondData.position1 = new Float32Array(bondCount * 3)\n bondData.position2 = new Float32Array(bondCount * 3)\n }\n if ((!what || what.color) && p.colorParams) {\n bondData.color = new Float32Array(bondCount * 3)\n bondData.color2 = new Float32Array(bondCount * 3)\n colormaker = ColormakerRegistry.getScheme(p.colorParams)\n }\n if (!what || what.picking) {\n bondData.picking = new BondPicker(new Float32Array(bondCount), this.getStructure(), p.bondStore)\n }\n if (!what || what.radius || (isMulti && what.position)) {\n radiusFactory = new RadiusFactory(p.radiusParams as RadiusParams)\n }\n if (!what || what.radius) {\n bondData.radius = new Float32Array(bondCount)\n if (p.radius2) {\n bondData.radius2 = new Float32Array(bondCount)\n }\n }\n\n const {position1, position2, color, color2, picking, radius, radius2} = bondData\n\n let i = 0\n let j, i3, k, bondOrder, absOffset\n let multiRadius\n\n const vt = new Vector3()\n const vShortening = new Vector3()\n const vShift = new Vector3()\n\n bondSet.forEach((index: number) => {\n i3 = i * 3\n bp.index = index\n ap1.index = bp.atomIndex1\n ap2.index = bp.atomIndex2\n bondOrder = bp.bondOrder\n if (position1) {\n if (isMulti && bondOrder > 1) {\n const atomRadius = radiusFactory.atomRadius(ap1)\n multiRadius = atomRadius * bondScale / (0.5 * bondOrder)\n\n bp.calculateShiftDir(vShift)\n\n if (isOffset) {\n absOffset = 2 * bondSpacing * atomRadius\n vShift.multiplyScalar(absOffset)\n vShift.negate()\n\n // Shortening is calculated so that neighbouring double\n // bonds on tetrahedral geometry (e.g. sulphonamide)\n // are not quite touching (arccos(1.9 / 2) ~ 109deg)\n // but don't shorten beyond 10% each end or it looks odd\n vShortening.subVectors(ap2 as any, ap1 as any).multiplyScalar( // TODO\n Math.max(0.1, absOffset / 1.88)\n )\n ap1.positionToArray(position1, i3)\n ap2.positionToArray(position2, i3)\n\n if (bondOrder >= 2) {\n vt.addVectors(ap1 as any, vShift).add(vShortening).toArray(position1 as any, i3 + 3) // TODO\n vt.addVectors(ap2 as any, vShift).sub(vShortening).toArray(position2 as any, i3 + 3) // TODO\n\n if (bondOrder >= 3) {\n vt.subVectors(ap1 as any, vShift).add(vShortening).toArray(position1 as any, i3 + 6) // TODO\n vt.subVectors(ap2 as any, vShift).sub(vShortening).toArray(position2 as any, i3 + 6) // TODO\n }\n }\n } else {\n absOffset = (bondSpacing - bondScale) * atomRadius\n vShift.multiplyScalar(absOffset)\n\n if (bondOrder === 2) {\n vt.addVectors(ap1 as any, vShift).toArray(position1 as any, i3) // TODO\n vt.subVectors(ap1 as any, vShift).toArray(position1 as any, i3 + 3) // TODO\n vt.addVectors(ap2 as any, vShift).toArray(position2 as any, i3) // TODO\n vt.subVectors(ap2 as any, vShift).toArray(position2 as any, i3 + 3) // TODO\n } else if (bondOrder === 3) {\n ap1.positionToArray(position1, i3)\n vt.addVectors(ap1 as any, vShift).toArray(position1 as any, i3 + 3) // TODO\n vt.subVectors(ap1 as any, vShift).toArray(position1 as any, i3 + 6) // TODO\n ap2.positionToArray(position2, i3)\n vt.addVectors(ap2 as any, vShift).toArray(position2 as any, i3 + 3) // TODO\n vt.subVectors(ap2 as any, vShift).toArray(position2 as any, i3 + 6) // TODO\n } else {\n // todo, better fallback\n ap1.positionToArray(position1, i3)\n ap2.positionToArray(position2, i3)\n }\n }\n } else {\n ap1.positionToArray(position1, i3)\n ap2.positionToArray(position2, i3)\n }\n }\n if (color && color2) {\n colormaker.bondColorToArray(bp, 1, color, i3)\n colormaker.bondColorToArray(bp, 0, color2, i3)\n if (isMulti && bondOrder > 1) {\n for (j = 1; j < bondOrder; ++j) {\n k = j * 3 + i3\n copyWithin(color, i3, k, 3)\n copyWithin(color2, i3, k, 3)\n }\n }\n }\n if (picking && picking.array) {\n picking.array[ i ] = index\n if (isMulti && bondOrder > 1) {\n for (j = 1; j < bondOrder; ++j) {\n picking.array[ i + j ] = index\n }\n }\n }\n if (radius) {\n radius[ i ] = radiusFactory.atomRadius(ap1)\n if (isMulti && bondOrder > 1) {\n multiRadius = radius[ i ] * bondScale / (isOffset ? 1 : (0.5 * bondOrder))\n for (j = isOffset ? 1 : 0; j < bondOrder; ++j) {\n radius[ i + j ] = multiRadius\n }\n }\n }\n if (radius2) {\n radius2[ i ] = radiusFactory.atomRadius(ap2)\n if (isMulti && bondOrder > 1) {\n multiRadius = radius2[ i ] * bondScale / (isOffset ? 1 : (0.5 * bondOrder))\n for (j = isOffset ? 1 : 0; j < bondOrder; ++j) {\n radius2[ i + j ] = multiRadius\n }\n }\n }\n i += isMulti ? bondOrder : 1\n })\n\n return bondData\n }\n\n getBackboneAtomData (params: AtomDataParams) {\n params = Object.assign({\n atomSet: this.atomSetCache.__backbone\n }, params)\n\n return this.getAtomData(params)\n }\n\n getBackboneBondData (params: BondDataParams) {\n params = Object.assign({\n bondSet: this.getBackboneBondSet(),\n bondStore: this.backboneBondStore\n }, params)\n\n return this.getBondData(params)\n }\n\n getRungAtomData (params: AtomDataParams) {\n params = Object.assign({\n atomSet: this.atomSetCache.__rung\n }, params)\n\n return this.getAtomData(params)\n }\n\n getRungBondData (params: BondDataParams) {\n params = Object.assign({\n bondSet: this.getRungBondSet(),\n bondStore: this.rungBondStore\n }, params)\n\n return this.getBondData(params)\n }\n\n //\n\n /**\n * Gets the bounding box of the (selected) structure atoms\n * @param {Selection} [selection] - the selection\n * @param {Box3} [box] - optional target\n * @return {Vector3} the box\n */\n getBoundingBox (selection?: Selection, box?: Box3) {\n if (Debug) Log.time('getBoundingBox')\n\n box = box || new Box3()\n\n let minX = +Infinity\n let minY = +Infinity\n let minZ = +Infinity\n\n let maxX = -Infinity\n let maxY = -Infinity\n let maxZ = -Infinity\n\n this.eachAtom(ap => {\n const x = ap.x\n const y = ap.y\n const z = ap.z\n\n if (x < minX) minX = x\n if (y < minY) minY = y\n if (z < minZ) minZ = z\n\n if (x > maxX) maxX = x\n if (y > maxY) maxY = y\n if (z > maxZ) maxZ = z\n }, selection)\n\n box.min.set(minX, minY, minZ)\n box.max.set(maxX, maxY, maxZ)\n\n if (Debug) Log.timeEnd('getBoundingBox')\n\n return box\n }\n\n /**\n * Gets the principal axes of the (selected) structure atoms\n * @param {Selection} [selection] - the selection\n * @return {PrincipalAxes} the principal axes\n */\n getPrincipalAxes (selection?: Selection) {\n if (Debug) Log.time('getPrincipalAxes')\n\n let i = 0\n const coords = new Matrix(3, this.atomCount)\n const cd = coords.data\n\n this.eachAtom(a => {\n cd[ i + 0 ] = a.x\n cd[ i + 1 ] = a.y\n cd[ i + 2 ] = a.z\n i += 3\n }, selection)\n\n if (Debug) Log.timeEnd('getPrincipalAxes')\n\n return new PrincipalAxes(coords)\n }\n\n /**\n * Gets the center of the (selected) structure atoms\n * @param {Selection} [selection] - the selection\n * @return {Vector3} the center\n */\n atomCenter (selection?: Selection) {\n if (selection) {\n return this.getBoundingBox(selection).getCenter(new Vector3())\n } else {\n return this.center.clone()\n }\n }\n\n hasCoords () {\n if (this._hasCoords === undefined) {\n const atomStore = this.atomStore\n this._hasCoords = (\n arrayMin(atomStore.x) !== 0 || arrayMax(atomStore.x) !== 0 ||\n arrayMin(atomStore.y) !== 0 || arrayMax(atomStore.y) !== 0 ||\n arrayMin(atomStore.z) !== 0 || arrayMax(atomStore.z) !== 0\n ) || (\n // allow models with a single atom at the origin\n atomStore.count / this.modelStore.count === 1\n )\n }\n return this._hasCoords;\n }\n\n getSequence (selection?: Selection) {\n const seq: string[] = []\n const rp = this.getResidueProxy()\n\n this.eachAtom(function (ap: AtomProxy) {\n rp.index = ap.residueIndex\n if (ap.index === rp.traceAtomIndex) {\n seq.push(rp.getResname1())\n }\n }, selection)\n\n return seq\n }\n\n getAtomIndices (selection?: Selection) {\n if (selection && selection.string) {\n const indices: number[] = []\n this.eachAtom(function (ap: AtomProxy) {\n indices.push(ap.index)\n }, selection)\n return new Uint32Array(indices)\n } else {\n const p = { what: { index: true } }\n return this.getAtomData(p).index\n }\n }\n\n /**\n * Get number of unique chainnames\n * @param {Selection} selection - limit count to selection\n * @return {Integer} count\n */\n getChainnameCount (selection?: Selection) {\n const chainnames = new Set()\n this.eachChain(function (cp: ChainProxy) {\n if (cp.residueCount) {\n chainnames.add(cp.chainname)\n }\n }, selection)\n\n return chainnames.size\n }\n\n /**\n * Update atomic positions\n * @param position - Array to copy positions from\n * @param refresh - Whether or not to issue a full refresh (automatically\n * triggers re-calculation of bounding boxes, spatial hash,\n * representations etc etc). This provides compatibility with\n * the old behaviour\n */\n updatePosition (position: Float32Array|number[], refresh: boolean = true) {\n let i = 0\n\n this.eachAtom(function (ap: AtomProxy) {\n ap.positionFromArray(position, i)\n i += 3\n }, undefined)\n\n this._hasCoords = undefined // to trigger recalculation (of the _hasCoords value)\n\n if (refresh) { \n this.refreshPosition() // Recalculate bounds - structure-component listener will \n // trigger representation rebuild\n }\n\n }\n\n refreshPosition () {\n this.getBoundingBox(undefined, this.boundingBox)\n this.boundingBox.getCenter(this.center)\n this.spatialHash = new SpatialHash(this.atomStore, this.boundingBox)\n\n this.signals.refreshed.dispatch(this)\n }\n\n /**\n * Calls dispose() method of property objects.\n * Unsets properties to help garbage collection.\n * @return {undefined}\n */\n dispose () {\n if (this.frames) this.frames.length = 0\n if (this.boxes) this.boxes.length = 0\n\n this.bondStore.dispose()\n this.backboneBondStore.dispose()\n this.rungBondStore.dispose()\n this.atomStore.dispose()\n this.residueStore.dispose()\n this.chainStore.dispose()\n this.modelStore.dispose()\n\n // can't delete non-optional properties as of TS 4\n // and since we've already disposed them, don't need to.\n\n delete this.bondSet\n delete this.atomSet\n }\n}\n\nexport default Structure\n","/**\n * @file Shape\n * @author Alexander Rose \n * @private\n */\n\n// @ts-ignore: unused import Matrix4 required for declaration only\nimport { Box3, Vector3, Color, Matrix4 } from 'three'\n\nimport { createParams, ensureFloat32Array, getUintArray } from '../utils'\nimport {\n ArrowPrimitive, BoxPrimitive, ConePrimitive, CylinderPrimitive, EllipsoidPrimitive,\n OctahedronPrimitive, SpherePrimitive, TetrahedronPrimitive, TextPrimitive,\n TorusPrimitive, PointPrimitive, WidelinePrimitive\n} from './primitive'\nimport { MeshPicker } from '../utils/picker'\nimport Buffer from '../buffer/buffer'\nimport MeshBuffer from '../buffer/mesh-buffer'\nimport { TextBufferParameters } from '../buffer/text-buffer'\n\nconst tmpBox = new Box3()\n\nconst Primitives = [\n ArrowPrimitive, BoxPrimitive, ConePrimitive, CylinderPrimitive,\n EllipsoidPrimitive, OctahedronPrimitive, SpherePrimitive, TetrahedronPrimitive,\n TextPrimitive, TorusPrimitive, PointPrimitive, WidelinePrimitive\n]\n\nexport const ShapeDefaultParameters = {\n aspectRatio: 1.5,\n sphereDetail: 2,\n radialSegments: 50,\n disableImpostor: false,\n openEnded: false,\n dashedCylinder: false,\n labelParams: {} as Partial,\n pointSize: 2,\n sizeAttenuation: false,\n useTexture: true,\n linewidth: 2\n}\nexport type ShapeParameters = typeof ShapeDefaultParameters\n\n/**\n * Class for building custom shapes.\n *\n * @example\n * var shape = new NGL.Shape('shape', { disableImpostor: true });\n * shape.addSphere([ 0, 0, 9 ], [ 1, 0, 0 ], 1.5 );\n * shape.addEllipsoid([ 6, 0, 0 ], [ 1, 0, 0 ], 1.5, [ 3, 0, 0 ], [ 0, 2, 0 ]);\n * shape.addCylinder([ 0, 2, 7 ], [ 0, 0, 9 ], [ 1, 1, 0 ], 0.5);\n * shape.addCone([ 0, 2, 7 ], [ 0, 3, 3 ], [ 1, 1, 0 ], 1.5);\n * shape.addArrow([ 1, 2, 7 ], [ 30, 3, 3 ], [ 1, 0, 1 ], 1.0);\n * shape.addBox([ 0, 3, 0 ], [ 1, 0, 1 ], 2, [ 0, 1, 1 ], [ 1, 0, 1 ]);\n * var shapeComp = stage.addComponentFromObject(shape);\n * geoComp.addRepresentation('buffer');\n */\nclass Shape {\n name: string\n parameters: ShapeParameters\n\n boundingBox = new Box3()\n bufferList: Buffer[] = []\n meshCount = 0\n\n _center?: Vector3\n _primitiveData: { [k: string]: any } = {}\n\n /**\n * @param {String} name - name\n * @param {Object} params - parameter object\n * @param {Integer} params.aspectRatio - arrow aspect ratio, used for cylinder radius and cone length\n * @param {Integer} params.sphereDetail - sphere quality (icosahedron subdivisions)\n * @param {Integer} params.radialSegments - cylinder quality (number of segments)\n * @param {Boolean} params.disableImpostor - disable use of raycasted impostors for rendering\n * @param {Boolean} params.openEnded - capped or not\n * @param {TextBufferParameters} params.labelParams - label parameters\n */\n constructor (name = 'shape', params: Partial = {}) {\n this.name = name\n\n this.parameters = createParams(params, ShapeDefaultParameters)\n\n Primitives.forEach(P => {\n Object.keys(P.fields).forEach(name => {\n this._primitiveData[ P.getShapeKey(name) ] = []\n })\n this._primitiveData[ P.getShapeKey('name') ] = []\n })\n }\n\n /**\n * Add a buffer\n * @param {Buffer} buffer - buffer object\n * @return {Shape} this object\n */\n addBuffer (buffer: Buffer) {\n this.bufferList.push(buffer)\n\n const geometry = (buffer as any).geometry // TODO\n if (!geometry.boundingBox) {\n geometry.computeBoundingBox()\n }\n this.boundingBox.union(geometry.boundingBox)\n\n return this\n }\n\n /**\n * Add a mesh\n * @example\n * shape.addMesh(\n * [ 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1 ],\n * [ 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0 ]\n * );\n *\n * @param {Float32Array|Array} position - positions\n * @param {Float32Array|Array} color - colors\n * @param {Uint32Array|Uint16Array|Array} [index] - indices\n * @param {Float32Array|Array} [normal] - normals\n * @param {String} [name] - text\n * @return {Shape} this object\n */\n addMesh (position: Float32Array|number[], color:Float32Array|number[], index: Uint32Array|Uint16Array|number[], normal?: Float32Array|number[], name?: string) {\n position = ensureFloat32Array(position)\n color = ensureFloat32Array(color)\n\n if (Array.isArray(index)) {\n index = getUintArray(index, position.length)\n }\n if (normal) {\n normal = ensureFloat32Array(normal)\n }\n\n let data\n if (normal === undefined || normal.length == 0 ) {\n data = { position, color, index }\n } else {\n data = { position, color, index, normal }\n }\n //const data = { position, color, index, normal }\n const picking = new MeshPicker(\n this, Object.assign({ serial: this.meshCount, name }, data)\n )\n const meshBuffer = new MeshBuffer(\n Object.assign({ picking }, data) as any\n )\n this.bufferList.push(meshBuffer)\n\n tmpBox.setFromArray(position)\n this.boundingBox.union(tmpBox)\n this.meshCount += 1\n\n return this\n }\n\n /**\n * Add a sphere\n * @example\n * shape.addSphere([ 0, 0, 9 ], [ 1, 0, 0 ], 1.5);\n *\n * @param {Vector3|Array} position - position vector or array\n * @param {Color|Array} color - color object or array\n * @param {Float} radius - radius value\n * @param {String} [name] - text\n * @return {Shape} this object\n */\n addSphere (position: Vector3|[number, number, number], color: Color|[number, number, number], radius: number, name: string) {\n SpherePrimitive.objectToShape(\n this, { position, color, radius, name }\n )\n return this\n }\n\n /**\n * Add an ellipsoid\n * @example\n * shape.addEllipsoid([ 6, 0, 0 ], [ 1, 0, 0 ], 1.5, [ 3, 0, 0 ], [ 0, 2, 0 ]);\n *\n * @param {Vector3|Array} position - position vector or array\n * @param {Color|Array} color - color object or array\n * @param {Float} radius - radius value\n * @param {Vector3|Array} majorAxis - major axis vector or array\n * @param {Vector3|Array} minorAxis - minor axis vector or array\n * @param {String} [name] - text\n * @return {Shape} this object\n */\n addEllipsoid (position: Vector3|[number, number, number], color: Color|[number, number, number], radius: number, majorAxis: Vector3|[number, number, number], minorAxis: Vector3|[number, number, number], name: string) {\n EllipsoidPrimitive.objectToShape(\n this, { position, color, radius, majorAxis, minorAxis, name }\n )\n return this\n }\n\n /**\n * Add a torus\n * @example\n * shape.addTorus([ 6, 0, 0 ], [ 1, 0, 0 ], 1.5, [ 3, 0, 0 ], [ 0, 2, 0 ]);\n *\n * @param {Vector3|Array} position - position vector or array\n * @param {Color|Array} color - color object or array\n * @param {Float} radius - radius value\n * @param {Vector3|Array} majorAxis - major axis vector or array\n * @param {Vector3|Array} minorAxis - minor axis vector or array\n * @param {String} [name] - text\n * @return {Shape} this object\n */\n addTorus (position: Vector3|[number, number, number], color: Color|[number, number, number], radius: number, majorAxis: Vector3|[number, number, number], minorAxis: Vector3|[number, number, number], name: string) {\n TorusPrimitive.objectToShape(\n this, { position, color, radius, majorAxis, minorAxis, name }\n )\n return this\n }\n\n /**\n * Add a cylinder\n * @example\n * shape.addCylinder([ 0, 2, 7 ], [ 0, 0, 9 ], [ 1, 1, 0 ], 0.5);\n *\n * @param {Vector3|Array} position1 - from position vector or array\n * @param {Vector3|Array} position2 - to position vector or array\n * @param {Color|Array} color - color object or array\n * @param {Float} radius - radius value\n * @param {String} [name] - text\n * @return {Shape} this object\n */\n addCylinder (position1: Vector3|[number, number, number], position2: Vector3|[number, number, number], color: Color|[number, number, number], radius: number, name: string) {\n CylinderPrimitive.objectToShape(\n this, { position1, position2, color, radius, name }\n )\n return this\n }\n\n /**\n * Add a cone\n * @example\n * shape.addCone([ 0, 2, 7 ], [ 0, 3, 3 ], [ 1, 1, 0 ], 1.5);\n *\n * @param {Vector3|Array} position1 - from position vector or array\n * @param {Vector3|Array} position2 - to position vector or array\n * @param {Color|Array} color - color object or array\n * @param {Float} radius - radius value\n * @param {String} [name] - text\n * @return {Shape} this object\n */\n addCone (position1: Vector3|[number, number, number], position2: Vector3|[number, number, number], color: Color|[number, number, number], radius: number, name: string) {\n ConePrimitive.objectToShape(\n this, { position1, position2, color, radius, name }\n )\n return this\n }\n\n /**\n * Add an arrow\n * @example\n * shape.addArrow([ 0, 2, 7 ], [ 0, 0, 9 ], [ 1, 1, 0 ], 0.5);\n *\n * @param {Vector3|Array} position1 - from position vector or array\n * @param {Vector3|Array} position2 - to position vector or array\n * @param {Color|Array} color - color object or array\n * @param {Float} radius - radius value\n * @param {String} [name] - text\n * @return {Shape} this object\n */\n addArrow (position1: Vector3|[number, number, number], position2: Vector3|[number, number, number], color: Color|[number, number, number], radius: number, name: string) {\n ArrowPrimitive.objectToShape(\n this, { position1, position2, color, radius, name }\n )\n return this\n }\n\n /**\n * Add a box\n * @example\n * shape.addBox([ 0, 3, 0 ], [ 1, 0, 1 ], 2, [ 0, 1, 1 ], [ 1, 0, 1 ]);\n *\n * @param {Vector3|Array} position - position vector or array\n * @param {Color|Array} color - color object or array\n * @param {Float} size - size value\n * @param {Vector3|Array} heightAxis - height axis vector or array\n * @param {Vector3|Array} depthAxis - depth axis vector or array\n * @param {String} [name] - text\n * @return {Shape} this object\n */\n addBox (position: Vector3|[number, number, number], color: Color|[number, number, number], size: number, heightAxis: Vector3|[number, number, number], depthAxis: Vector3|[number, number, number], name: string) {\n BoxPrimitive.objectToShape(\n this, { position, color, size, heightAxis, depthAxis, name }\n )\n return this\n }\n\n /**\n * Add an octahedron\n * @example\n * shape.addOctahedron([ 0, 3, 0 ], [ 1, 0, 1 ], 2, [ 0, 1, 1 ], [ 1, 0, 1 ]);\n *\n * @param {Vector3|Array} position - position vector or array\n * @param {Color|Array} color - color object or array\n * @param {Float} size - size value\n * @param {Vector3|Array} heightAxis - height axis vector or array\n * @param {Vector3|Array} depthAxis - depth axis vector or array\n * @param {String} [name] - text\n * @return {Shape} this object\n */\n addOctahedron (position: Vector3|[number, number, number], color: Color|[number, number, number], size: number, heightAxis: Vector3|[number, number, number], depthAxis: Vector3|[number, number, number], name: string) {\n OctahedronPrimitive.objectToShape(\n this, { position, color, size, heightAxis, depthAxis, name }\n )\n return this\n }\n\n /**\n * Add a tetrahedron\n * @example\n * shape.addTetrahedron([ 0, 3, 0 ], [ 1, 0, 1 ], 2, [ 0, 1, 1 ], [ 1, 0, 1 ]);\n *\n * @param {Vector3|Array} position - position vector or array\n * @param {Color|Array} color - color object or array\n * @param {Float} size - size value\n * @param {Vector3|Array} heightAxis - height axis vector or array\n * @param {Vector3|Array} depthAxis - depth axis vector or array\n * @param {String} [name] - text\n * @return {Shape} this object\n */\n addTetrahedron (position: Vector3|[number, number, number], color: Color|[number, number, number], size: number, heightAxis: Vector3|[number, number, number], depthAxis: Vector3|[number, number, number], name: string) {\n TetrahedronPrimitive.objectToShape(\n this, { position, color, size, heightAxis, depthAxis, name }\n )\n return this\n }\n\n /**\n * Add text\n * @example\n * shape.addText([ 10, -2, 4 ], [ 0.2, 0.5, 0.8 ], 0.5, \"Hello\");\n *\n * @param {Vector3|Array} position - position vector or array\n * @param {Color|Array} color - color object or array\n * @param {Float} size - size value\n * @param {String} text - text value\n * @return {Shape} this object\n */\n addText (position: Vector3|[number, number, number], color: Color|[number, number, number], size: number, text: string) {\n TextPrimitive.objectToShape(\n this, { position, color, size, text }\n )\n return this\n }\n\n /**\n * Add point\n * @example\n * shape.addPoint([ 10, -2, 4 ], [ 0.2, 0.5, 0.8 ]);\n *\n * @param {Vector3|Array} position - position vector or array\n * @param {Color|Array} color - color object or array\n * @param {String} [name] - text\n * @return {Shape} this object\n */\n addPoint (position: Vector3|[number, number, number], color: Color|[number, number, number], name: string) {\n PointPrimitive.objectToShape(\n this, { position, color, name }\n )\n return this\n }\n\n /**\n * Add a wideline\n * @example\n * shape.addWideline([ 0, 2, 7 ], [ 0, 0, 9 ], [ 1, 1, 0 ]);\n *\n * @param {Vector3|Array} position1 - from position vector or array\n * @param {Vector3|Array} position2 - to position vector or array\n * @param {Color|Array} color - color object or array\n * @param {String} [name] - text\n * @return {Shape} this object\n */\n addWideline (position1: Vector3|[number, number, number], position2: Vector3|[number, number, number], color: Color|[number, number, number], linewidth: number, name: string) {\n this.parameters.linewidth = linewidth\n WidelinePrimitive.objectToShape(\n this, { position1, position2, color, name }\n )\n return this\n }\n\n /**\n * Deprecated, use `.addText`\n */\n addLabel (position: Vector3|[number, number, number], color: Color|[number, number, number], size: number, text: string) {\n console.warn('Shape.addLabel is deprecated, use .addText instead')\n return this.addText(position, color, size, text)\n }\n\n getBufferList () {\n const buffers: Buffer[] = []\n\n Primitives.forEach(P => {\n if (this._primitiveData[ P.getShapeKey('color') ].length) {\n buffers.push(P.bufferFromShape(this, this.parameters))\n }\n })\n\n return this.bufferList.concat(buffers)\n }\n\n dispose () {\n this.bufferList.forEach(function (buffer) {\n buffer.dispose()\n })\n this.bufferList.length = 0\n\n Primitives.forEach(P => {\n Object.keys(P.fields).forEach(name => {\n this._primitiveData[ P.getShapeKey(name) ].length = 0\n })\n this._primitiveData[ P.getShapeKey('name') ].length = 0\n })\n }\n\n get center () {\n if (!this._center) {\n this._center = this.boundingBox.getCenter(new Vector3())\n }\n return this._center\n }\n\n get type () { return 'Shape' }\n}\n\nexport default Shape\n","/**\n * @file Buffer Representation\n * @author Alexander Rose \n * @private\n */\n\nimport Representation, { RepresentationParameters } from './representation'\nimport Viewer from '../viewer/viewer';\n\n/**\n * Representation for showing buffer objects. Good for efficiently showing\n * large amounts of geometric primitives e.g. spheres via {@link SphereBuffer}.\n * Smaller numbers of geometric primitives are more easily shown with help\n * from the {@link Shape} class.\n *\n * __Name:__ _buffer_\n *\n * @example\n * // add a single red sphere from a buffer to a shape instance\n * var shape = new NGL.Shape( \"shape\" );\n * var sphereBuffer = new NGL.SphereBuffer( {\n * position: new Float32Array( [ 0, 0, 0 ] ),\n * color: new Float32Array( [ 1, 0, 0 ] ),\n * radius: new Float32Array( [ 1 ] )\n * } );\n * shape.addBuffer( sphereBuffer );\n * var shapeComp = stage.addComponentFromObject( shape );\n * shapeComp.addRepresentation( \"buffer\" );\n *\n * @example\n * // add a single red sphere from a buffer to a structure component instance\n * stage.loadFile( \"rcsb://1crn\" ).then( function( o ){\n * var sphereBuffer = new NGL.SphereBuffer( {\n * position: new Float32Array( [ 0, 0, 0 ] ),\n * color: new Float32Array( [ 1, 0, 0 ] ),\n * radius: new Float32Array( [ 1 ] )\n * } );\n * o.addBufferRepresentation( sphereBuffer, { opacity: 0.5 } );\n * } );\n */\nclass BufferRepresentation extends Representation {\n buffer: Buffer[]\n /**\n * Create Buffer representation\n * @param {Buffer} buffer - a buffer object\n * @param {Viewer} viewer - a viewer object\n * @param {RepresentationParameters} params - representation parameters\n */\n constructor (buffer: Buffer|Buffer[], viewer: Viewer, params: Partial) {\n if (!Array.isArray(buffer)) {\n buffer = [ buffer ]\n }\n\n super(buffer, viewer, params)\n\n this.type = 'buffer'\n\n this.parameters = Object.assign({\n\n }, this.parameters, {\n\n colorScheme: null,\n colorScale: null,\n colorValue: null,\n colorDomain: null,\n colorMode: null\n\n })\n\n this.buffer = buffer\n\n this.init(params)\n }\n\n init (params: Partial) {\n super.init(params)\n\n this.build()\n }\n\n create () {\n this.bufferList.push.apply(this.bufferList, this.buffer)\n }\n\n attach (callback: ()=> void) {\n this.bufferList.forEach(buffer => {\n this.viewer.add(buffer)\n buffer.setParameters(this.getBufferParams())\n })\n this.setVisibility(this.visible)\n\n callback()\n }\n}\n\nexport default BufferRepresentation\n","/**\n * @file Geometry Buffer\n * @author Alexander Rose \n * @private\n */\n\n// @ts-ignore: unused import Vector3 required for declaration only\n import { Vector3, Matrix4, Matrix3, BufferGeometry } from 'three'\n\nimport { getUintArray } from '../utils'\nimport { serialBlockArray } from '../math/array-utils'\nimport { applyMatrix3toVector3array, applyMatrix4toVector3array } from '../math/vector-utils'\nimport MeshBuffer from './mesh-buffer'\nimport { BufferParameters, BufferData } from './buffer'\nimport {Log} from \"../globals\";\n\nconst matrix = new Matrix4()\nconst normalMatrix = new Matrix3()\n\nfunction getData(data: BufferData, geo: BufferGeometry){\n const geoPosition = (geo.attributes as any).position.array\n const geoIndex = geo.index ? geo.index.array : undefined\n\n const n = data.position!.length / 3\n const m = geoPosition.length / 3\n\n const size = n * m\n\n const meshPosition = new Float32Array(size * 3)\n const meshNormal = new Float32Array(size * 3)\n const meshColor = new Float32Array(size * 3)\n\n let meshIndex\n if (geoIndex) {\n meshIndex = getUintArray(n * geoIndex.length, size)\n }\n\n return {\n position: meshPosition,\n color: meshColor,\n index: meshIndex,\n normal: meshNormal,\n primitiveId: data.primitiveId || serialBlockArray(n, m) as Float32Array,\n picking: data.picking\n }\n}\n\n/**\n * Geometry buffer. Base class for geometry-based buffers. Used to draw\n * geometry primitives given a mesh.\n * @interface\n */\nabstract class GeometryBuffer extends MeshBuffer {\n updateNormals = false\n\n geoPosition: Float32Array\n geoNormal: Float32Array\n geoIndex?: Uint32Array|Uint16Array\n\n positionCount: number\n geoPositionCount: number\n\n transformedGeoPosition: Float32Array\n transformedGeoNormal: Float32Array\n\n meshPosition: Float32Array\n meshColor: Float32Array\n meshIndex: Uint32Array|Uint16Array\n meshNormal: Float32Array\n\n /**\n * @param {Object} data - buffer data\n * @param {Float32Array} data.position - positions\n * @param {Float32Array} data.color - colors\n * @param {Float32Array} data.radius - radii\n * @param {Picker} [data.picking] - picking ids\n * @param {BufferParameters} [params] - parameters object\n * @param {BufferGeometry} geo - geometry object\n */\n constructor (data: BufferData, params: Partial = {}, geo: BufferGeometry) {\n super(getData(data, geo), params)\n\n const geoPosition = (geo.attributes as any).position.array\n const geoNormal = (geo.attributes as any).normal.array\n const geoIndex = geo.index ? (geo.index.array as Uint32Array|Uint16Array) : undefined\n\n this.geoPosition = geoPosition\n this.geoNormal = geoNormal\n this.geoIndex = geoIndex\n\n this.positionCount = data.position!.length / 3\n this.geoPositionCount = geoPosition.length / 3\n\n this.transformedGeoPosition = new Float32Array(this.geoPositionCount * 3)\n this.transformedGeoNormal = new Float32Array(this.geoPositionCount * 3)\n\n const attributes = this.geometry.attributes as any // TODO\n this.meshPosition = attributes.position.array\n this.meshColor = attributes.color.array\n this.meshNormal = attributes.normal.array\n\n this.setAttributes(data)\n\n if (geoIndex) {\n const index = this.geometry.getIndex()\n if (!index) { Log.error('Index is null'); return; }\n this.meshIndex = index.array as Uint32Array|Uint16Array\n this.makeIndex()\n }\n }\n\n abstract applyPositionTransform (matrix: Matrix4, i: number, i3?: number): void\n\n setAttributes (data: Partial = {}, initNormals = false) {\n const attributes = this.geometry.attributes as any // TODO\n\n let position, color\n let geoPosition, geoNormal\n let transformedGeoPosition, transformedGeoNormal\n let meshPosition, meshColor, meshNormal\n\n const updateNormals = this.updateNormals\n\n if (data.position) {\n position = data.position\n geoPosition = this.geoPosition\n meshPosition = this.meshPosition\n transformedGeoPosition = this.transformedGeoPosition\n attributes.position.needsUpdate = true\n if (updateNormals || initNormals) {\n geoNormal = this.geoNormal\n meshNormal = this.meshNormal\n transformedGeoNormal = this.transformedGeoNormal\n attributes.normal.needsUpdate = true\n }\n }\n\n if (data.color) {\n color = data.color\n meshColor = this.meshColor\n attributes.color.needsUpdate = true\n }\n\n const n = this.positionCount\n const m = this.geoPositionCount\n\n for (let i = 0; i < n; ++i) {\n let j, l\n const k = i * m * 3\n const i3 = i * 3\n\n if (position && transformedGeoPosition && meshPosition && meshNormal && geoPosition && geoNormal) {\n transformedGeoPosition.set(geoPosition)\n matrix.makeTranslation(\n position[ i3 ], position[ i3 + 1 ], position[ i3 + 2 ]\n )\n this.applyPositionTransform(matrix, i, i3)\n applyMatrix4toVector3array(matrix.elements as unknown as Float32Array,\n transformedGeoPosition)\n\n meshPosition.set(transformedGeoPosition, k)\n\n if (updateNormals && transformedGeoNormal) {\n transformedGeoNormal.set(geoNormal)\n normalMatrix.getNormalMatrix(matrix)\n applyMatrix3toVector3array(normalMatrix.elements as unknown as Float32Array,\n transformedGeoNormal)\n\n meshNormal.set(transformedGeoNormal, k)\n } else if (initNormals) {\n meshNormal.set(geoNormal, k)\n }\n }\n\n if (color && meshColor) {\n for (j = 0; j < m; ++j) {\n l = k + 3 * j\n\n meshColor[ l ] = color[ i3 ]\n meshColor[ l + 1 ] = color[ i3 + 1 ]\n meshColor[ l + 2 ] = color[ i3 + 2 ]\n }\n }\n }\n }\n\n makeIndex () {\n const geoIndex = this.geoIndex\n const meshIndex = this.meshIndex\n\n if (!geoIndex) return\n\n const n = this.positionCount\n const m = this.geoPositionCount\n const o = geoIndex.length / 3\n\n const o3 = o * 3\n\n for (let i = 0; i < n; ++i) {\n const j = i * o3\n const q = j + o3\n\n meshIndex.set(geoIndex, j)\n for (let p = j; p < q; ++p) meshIndex[ p ] += i * m\n }\n }\n}\n\nexport default GeometryBuffer\n","/**\n * @file Sphere Geometry Buffer\n * @author Alexander Rose \n * @private\n */\n\nimport { IcosahedronGeometry, Vector3, Matrix4 } from 'three'\nimport { defaults } from '../utils'\nimport GeometryBuffer from './geometry-buffer'\nimport { SphereBufferData } from './sphere-buffer'\nimport { BufferDefaultParameters, BufferParameters } from './buffer'\n\nconst scale = new Vector3()\n\nexport const SphereGeometryBufferDefaultParameters = Object.assign({\n sphereDetail: 1\n}, BufferDefaultParameters)\nexport type SphereGeometryBufferParameters = BufferParameters & { sphereDetail: number }\n\n/**\n * Sphere geometry buffer.\n *\n * @example\n * var sphereGeometryBuffer = new SphereGeometryBuffer({\n * position: new Float32Array([ 0, 0, 0 ]),\n * color: new Float32Array([ 1, 0, 0 ]),\n * radius: new Float32Array([ 1 ])\n * });\n */\nclass SphereGeometryBuffer extends GeometryBuffer {\n get defaultParameters() { return SphereGeometryBufferDefaultParameters }\n parameters: SphereGeometryBufferParameters\n\n private _radius: Float32Array\n\n /**\n * @param {Object} data - attribute object\n * @param {Float32Array} data.position - positions\n * @param {Float32Array} data.color - colors\n * @param {Float32Array} data.radius - radii\n * @param {Picker} [data.picking] - picking ids\n * @param {BufferParameters} params - parameter object\n */\n constructor (data: SphereBufferData, params: Partial = {}) {\n super(data, params, new IcosahedronGeometry(1, defaults(params.sphereDetail, 1)))\n\n this.setAttributes(data, true)\n }\n\n applyPositionTransform (matrix: Matrix4, i: number) {\n const r = this._radius[ i ]\n scale.set(r, r, r)\n matrix.scale(scale)\n }\n\n setAttributes (data: Partial = {}, initNormals?: boolean) {\n if (data.radius) this._radius = data.radius\n\n super.setAttributes(data, initNormals)\n }\n}\n\nexport default SphereGeometryBuffer\n","/**\n * @file Mapped Buffer\n * @author Alexander Rose \n * @private\n */\n\nimport { getUintArray } from '../utils'\nimport { calculateCenterArray, serialArray } from '../math/array-utils'\nimport Buffer, { BufferParameters, BufferData } from './buffer'\n\nexport type MappingType = 'v2'|'v3'\n\n/**\n * Mapped buffer. Sends mapping attribute to the GPU and repeats data in\n * others attributes. Used to render imposters.\n * @interface\n */\nabstract class MappedBuffer extends Buffer {\n index: Uint32Array|Uint16Array\n\n constructor (mappingType: MappingType, data: BufferData, params: Partial = {}) {\n super(data, params)\n\n this.index = getUintArray(this.indexSize, this.attributeSize)\n this.makeIndex()\n this.initIndex(this.index)\n\n this.addAttributes({\n 'mapping': { type: mappingType, value: null }\n })\n\n this.setAttributes({ primitiveId: serialArray(this.size) })\n }\n\n abstract get mapping (): Float32Array\n abstract get mappingIndices (): Uint32Array|Uint16Array\n abstract get mappingIndicesSize (): number\n abstract get mappingSize (): number\n abstract get mappingItemSize (): number\n\n get attributeSize () {\n return this.size * this.mappingSize\n }\n\n get indexSize () {\n return this.size * this.mappingIndicesSize\n }\n\n addAttributes (attributes: any) {\n const nullValueAttributes: any = {}\n for (const name in attributes) {\n const a = attributes[ name ]\n nullValueAttributes[ name ] = {\n type: a.type,\n value: null\n }\n }\n\n super.addAttributes(nullValueAttributes)\n }\n\n getAttributeIndex (dataIndex: number) {\n return dataIndex * 3 * this.mappingSize\n }\n\n setAttributes (data: any) { // TODO\n if (data && !data.position && data.position1 && data.position2) {\n data.position = calculateCenterArray(data.position1, data.position2)\n }\n\n const size = this.size\n const mappingSize = this.mappingSize\n const attributes = this.geometry.attributes as any // TODO\n\n let a, d, itemSize, array, n, i, j\n\n for (const name in data) {\n if (name === 'index' || name === 'picking') continue\n\n d = data[ name ]\n a = attributes[ name ]\n itemSize = a.itemSize\n array = a.array\n\n for (let k = 0; k < size; ++k) {\n n = k * itemSize\n i = n * mappingSize\n\n for (let l = 0; l < mappingSize; ++l) {\n j = i + (itemSize * l)\n\n for (let m = 0; m < itemSize; ++m) {\n array[ j + m ] = d[ n + m ]\n }\n }\n }\n\n a.needsUpdate = true\n }\n }\n\n makeMapping () {\n const size = this.size\n const mapping = this.mapping\n const mappingSize = this.mappingSize\n const mappingItemSize = this.mappingItemSize\n\n const attributes = this.geometry.attributes as any // TODO\n const aMapping = attributes.mapping.array\n\n for (let v = 0; v < size; v++) {\n aMapping.set(mapping, v * mappingItemSize * mappingSize)\n }\n }\n\n makeIndex () {\n const size = this.size\n const mappingSize = this.mappingSize\n const mappingIndices = this.mappingIndices\n const mappingIndicesSize = this.mappingIndicesSize\n\n const index = this.index\n\n for (let v = 0; v < size; v++) {\n const ix = v * mappingIndicesSize\n const it = v * mappingSize\n\n index.set(mappingIndices, ix)\n\n for (let s = 0; s < mappingIndicesSize; ++s) {\n index[ ix + s ] += it\n }\n }\n }\n}\n\nexport default MappedBuffer\n","/**\n * @file Mapped Quad Buffer\n * @author Alexander Rose \n * @private\n */\n\nimport { BufferParameters, BufferData } from './buffer'\nimport MappedBuffer from './mapped-buffer'\n\nconst mapping = new Float32Array([\n -1.0, 1.0,\n -1.0, -1.0,\n 1.0, 1.0,\n 1.0, -1.0\n])\n\nconst mappingIndices = new Uint16Array([\n 0, 1, 2,\n 1, 3, 2\n])\n\n/**\n * Mapped Quad buffer. Draws screen-aligned quads. Used to render impostors.\n * @interface\n */\nclass MappedQuadBuffer extends MappedBuffer {\n constructor(data: BufferData, params: Partial = {}) {\n super('v2', data, params)\n }\n get mapping () { return mapping }\n get mappingIndices () { return mappingIndices }\n get mappingIndicesSize () { return 6 }\n get mappingSize () { return 4 }\n get mappingItemSize () { return 2 }\n}\n\nexport default MappedQuadBuffer\n","/**\n * @file Sphere Impostor Buffer\n * @author Alexander Rose \n * @private\n */\n\nimport { Matrix4 } from 'three'\n\nimport '../shader/SphereImpostor.vert'\nimport '../shader/SphereImpostor.frag'\n\nimport MappedQuadBuffer from './mappedquad-buffer'\nimport { SphereBufferData } from './sphere-buffer'\nimport { BufferParameters } from './buffer'\n\n/**\n * Sphere impostor buffer.\n *\n * @example\n * var sphereImpostorBuffer = new SphereImpostorBuffer({\n * position: new Float32Array([ 0, 0, 0 ]),\n * color: new Float32Array([ 1, 0, 0 ]),\n * radius: new Float32Array([ 1 ])\n * });\n */\nclass SphereImpostorBuffer extends MappedQuadBuffer {\n isImpostor = true\n vertexShader = 'SphereImpostor.vert'\n fragmentShader = 'SphereImpostor.frag'\n\n /**\n * @param {Object} data - attribute object\n * @param {Float32Array} data.position - positions\n * @param {Float32Array} data.color - colors\n * @param {Float32Array} data.radius - radii\n * @param {Picker} [data.picking] - picking ids\n * @param {BufferParameters} params - parameter object\n */\n constructor (data: SphereBufferData, params: Partial = {}) {\n super(data, params)\n\n this.addUniforms({\n 'projectionMatrixInverse': { value: new Matrix4() },\n 'ortho': { value: 0.0 }\n })\n\n this.addAttributes({\n 'radius': { type: 'f', value: null }\n })\n\n this.setAttributes(data)\n this.makeMapping()\n }\n}\n\nexport default SphereImpostorBuffer\n","/**\n * @file Sphere Buffer\n * @author Alexander Rose \n * @private\n */\n\n// @ts-ignore: unused import Vector3, Matrix4 required for declaration only \nimport { Vector3, Matrix4 } from 'three'\nimport { BufferRegistry, ExtensionFragDepth } from '../globals'\nimport SphereGeometryBuffer, { SphereGeometryBufferDefaultParameters, SphereGeometryBufferParameters } from './spheregeometry-buffer'\nimport SphereImpostorBuffer from './sphereimpostor-buffer'\nimport { BufferData } from './buffer'\n\nexport interface SphereBufferData extends BufferData {\n radius: Float32Array\n}\n\nexport const SphereBufferDefaultParameters = Object.assign({\n disableImpostor: false\n}, SphereGeometryBufferDefaultParameters)\nexport type SphereBufferParameters = SphereGeometryBufferParameters & { disableImpostor: boolean }\n\nclass SphereBufferImpl {\n /**\n * @param {Object} data - buffer data\n * @param {Float32Array} data.position - positions\n * @param {Float32Array} data.color - colors\n * @param {Float32Array} data.radius - radii\n * @param {Picker} [data.picking] - picking ids\n * @param {BufferParameters} params - parameters object\n * @return {SphereGeometryBuffer|SphereImpostorBuffer} the buffer object\n */\n constructor (data: SphereBufferData, params: SphereBufferParameters) {\n if (!ExtensionFragDepth || (params && params.disableImpostor)) {\n return new SphereGeometryBuffer(data, params)\n } else {\n return new SphereImpostorBuffer(data, params)\n }\n }\n}\n\n/**\n * Sphere buffer. Depending on the value {@link ExtensionFragDepth} and\n * `params.disableImpostor` the constructor returns either a\n * {@link SphereGeometryBuffer} or a {@link SphereImpostorBuffer}\n * @implements {Buffer}\n *\n * @example\n * var sphereBuffer = new SphereBuffer( {\n * position: new Float32Array( [ 0, 0, 0 ] ),\n * color: new Float32Array( [ 1, 0, 0 ] ),\n * radius: new Float32Array( [ 1 ] )\n * } );\n */\n//@ts-expect-error Incompatible constructor signatures\nconst SphereBuffer: {\n new(data: SphereBufferData, params: SphereBufferParameters): SphereGeometryBuffer | SphereImpostorBuffer;\n} = SphereBufferImpl;\n\ntype SphereBuffer = SphereGeometryBuffer | SphereImpostorBuffer;\n\nBufferRegistry.add('sphere', SphereBuffer)\n\nexport default SphereBuffer\n","/**\n * @file Point Buffer\n * @author Alexander Rose \n * @private\n */\n\n// @ts-ignore: unused import Vector3, Matrix4 required for declaration only\nimport { DataTexture, Vector3, Matrix4 } from 'three'\n\nimport '../shader/Point.vert'\nimport '../shader/Point.frag'\n\nimport { BufferRegistry } from '../globals'\nimport { defaults } from '../utils'\nimport { smoothstep } from '../math/math-utils'\nimport Buffer, { BufferDefaultParameters, BufferParameterTypes, BufferData, BufferTypes, BufferParameters } from './buffer'\n\nfunction distance (x0: number, y0: number, x1: number, y1: number) {\n const dx = x1 - x0\n const dy = y1 - y0\n return Math.sqrt(dx * dx + dy * dy)\n}\n\ninterface PointTextureParams {\n width?: number\n height?: number\n delta?: number\n}\n\nfunction makePointTexture (params: PointTextureParams) {\n const p = params || {}\n\n const width = defaults(p.width, 256)\n const height = defaults(p.height, 256)\n const center = [ width / 2, height / 2 ]\n const radius = Math.min(width / 2, height / 2)\n const delta = defaults(p.delta, 1 / (radius + 1)) * radius\n\n let x = 0\n let y = 0\n const data = new Uint8Array(width * height * 4)\n\n for (let i = 0, il = data.length; i < il; i += 4) {\n const dist = distance(x, y, center[ 0 ], center[ 1 ])\n const value = 1 - smoothstep(radius - delta, radius, dist)\n\n data[ i ] = value * 255\n data[ i + 1 ] = value * 255\n data[ i + 2 ] = value * 255\n data[ i + 3 ] = value * 255\n\n if (++x === width) {\n x = 0\n y++\n }\n }\n\n const tex = new DataTexture(data, width, height)\n tex.needsUpdate = true\n\n return tex\n}\n\nexport const PointBufferDefaultParameters = Object.assign({\n pointSize: 1,\n sizeAttenuation: true,\n sortParticles: false,\n alphaTest: 0.5,\n useTexture: false,\n forceTransparent: false,\n edgeBleach: 0.0\n}, BufferDefaultParameters)\nexport type PointBufferParameters = BufferParameters & {\n pointSize: number,\n sizeAttenuation: boolean,\n sortParticles: boolean,\n alphaTest: number,\n useTexture: boolean,\n forceTransparent: boolean,\n edgeBleach: number\n}\n\nconst PointBufferParameterTypes = Object.assign({\n pointSize: { uniform: 'size' },\n sizeAttenuation: { updateShader: true },\n sortParticles: {},\n alphaTest: { uniform: true },\n useTexture: { updateShader: true },\n forceTransparent: {},\n edgeBleach: { uniform: true }\n}, BufferParameterTypes)\n\n/**\n * Point buffer. Draws points. Optionally textured.\n *\n * @example\n * var pointBuffer = new PointBuffer( {\n * position: new Float32Array( [ 0, 0, 0 ] ),\n * color: new Float32Array( [ 1, 0, 0 ] )\n * } );\n */\nclass PointBuffer extends Buffer {\n parameterTypes = PointBufferParameterTypes\n get defaultParameters() { return PointBufferDefaultParameters }\n parameters: PointBufferParameters\n\n vertexShader = 'Point.vert'\n fragmentShader ='Point.frag'\n\n isPoint = true\n tex: DataTexture\n\n /**\n * @param {Object} data - attribute object\n * @param {Float32Array} data.position - positions\n * @param {Float32Array} data.color - colors\n * @param {BufferParameters} params - parameter object\n */\n constructor (data: BufferData, params: Partial = {}) {\n super(data, params)\n\n this.addUniforms({\n 'size': { value: this.parameters.pointSize },\n 'canvasHeight': { value: 1.0 },\n 'pixelRatio': { value: 1.0 },\n 'map': { value: null },\n 'alphaTest': {value: this.parameters.alphaTest}\n })\n }\n\n makeMaterial () {\n super.makeMaterial()\n\n this.makeTexture()\n\n const m = this.material\n const wm = this.wireframeMaterial\n const pm = this.pickingMaterial\n\n m.uniforms.map.value = this.tex\n m.needsUpdate = true\n\n wm.uniforms.map.value = this.tex\n wm.needsUpdate = true\n\n pm.uniforms.map.value = this.tex\n pm.needsUpdate = true\n }\n\n makeTexture () {\n if (this.tex) this.tex.dispose()\n this.tex = makePointTexture({ delta: this.parameters.edgeBleach })\n }\n\n getDefines (type?: BufferTypes) {\n const defines = super.getDefines(type)\n\n if (this.parameters.sizeAttenuation) {\n defines.USE_SIZEATTENUATION = 1\n }\n\n if (this.parameters.useTexture) {\n defines.USE_MAP = 1\n }\n\n if (this.parameters.alphaTest > 0 && this.parameters.alphaTest <= 1) {\n defines.USE_ALPHATEST = 1\n }\n\n return defines\n }\n\n setUniforms (data: any) {\n if (data && data.edgeBleach !== undefined) {\n this.makeTexture()\n data.map = this.tex\n }\n\n super.setUniforms(data)\n }\n\n dispose () {\n super.dispose()\n\n if (this.tex) this.tex.dispose()\n }\n}\n\nBufferRegistry.add('point', PointBuffer)\n\nexport default PointBuffer\n","/**\n * @file Dot Representation\n * @author Alexander Rose \n * @private\n */\n\nimport { ExtensionFragDepth } from '../globals'\nimport { defaults } from '../utils'\nimport Representation, { RepresentationParameters } from './representation'\nimport Volume from '../surface/volume'\nimport FilteredVolume from '../surface/filtered-volume'\nimport SphereBuffer, { SphereBufferData, SphereBufferParameters } from '../buffer/sphere-buffer'\nimport PointBuffer from '../buffer/point-buffer'\nimport Surface from '../surface/surface';\nimport Viewer from '../viewer/viewer';\nimport SphereGeometryBuffer from '../buffer/spheregeometry-buffer';\n\nexport interface DotDataFields {\n color?: boolean,\n radius?: boolean,\n scale?: boolean\n}\n\n/**\n * Dot representation parameter object. Extends {@link RepresentationParameters}\n *\n * @typedef {Object} DotRepresentationParameters - dot representation parameters\n *\n * @property {String} thresholdType - Meaning of the threshold values. Either *value* for the literal value or *sigma* as a factor of the sigma of the data. For volume data only.\n * @property {Number} thresholdMin - Minimum value to be displayed. For volume data only.\n * @property {Number} thresholdMax - Maximum value to be displayed. For volume data only.\n * @property {Number} thresholdOut - Show only values falling outside of the treshold minumum and maximum. For volume data only.\n */\nexport interface DotRepresentationParameters extends RepresentationParameters {\n thresholdType: 'value'|'value'|'sigma'|'sigma'\n thresholdMin: number\n thresholdMax: number\n thresholdOut: boolean\n dotType: ''|'sphere'|'point'\n radiusType: ''|'value'|'abs-value'|'value-min'|'deviation'|'size'|'radius' //TODO had to add 'radius' because of test in line 333\n radius: number\n scale: number\n sphereDetail: number\n disableImpostor: boolean\n pointSize: number\n sizeAttenuation: boolean\n sortParticles: boolean\n useTexture: boolean\n alphaTest: number\n forceTransparent: boolean\n edgeBleach: number\n}\n/**\n * Dot representation\n */\nclass DotRepresentation extends Representation {\n protected thresholdType: 'value'|'value'|'sigma'|'sigma'\n protected thresholdMin: number\n protected thresholdMax: number\n protected thresholdOut: boolean\n protected dotType: ''|'sphere'|'point'\n protected radiusType: ''|'value'|'abs-value'|'value-min'|'deviation'|'size'|'radius' //TODO had to add 'radius' because of test in line 333\n protected radius: number\n protected scale: number\n protected sphereDetail: number\n protected disableImpostor: boolean\n protected pointSize: number\n protected sizeAttenuation: boolean\n protected sortParticles: boolean\n protected useTexture: boolean\n protected alphaTest: number\n protected forceTransparent: boolean\n protected edgeBleach: number\n\n protected surface: Surface|undefined\n protected volume: FilteredVolume|undefined\n protected dotBuffer: SphereBuffer|PointBuffer\n /**\n * Create Dot representation object\n * @param {Surface|Volume} surface - the surface or volume to be represented\n * @param {Viewer} viewer - a viewer object\n * @param {DotRepresentationParameters} params - dot representation parameters\n */\n constructor (surface: Surface, viewer: Viewer, params: Partial) {\n super(surface, viewer, params)\n\n this.type = 'dot'\n\n this.parameters = Object.assign({\n\n thresholdType: {\n type: 'select',\n rebuild: true,\n options: {\n 'value': 'value', 'sigma': 'sigma'\n }\n },\n thresholdMin: {\n type: 'number', precision: 3, max: Infinity, min: -Infinity, rebuild: true\n },\n thresholdMax: {\n type: 'number', precision: 3, max: Infinity, min: -Infinity, rebuild: true\n },\n thresholdOut: {\n type: 'boolean', rebuild: true\n },\n dotType: {\n type: 'select',\n rebuild: true,\n options: {\n '': '',\n 'sphere': 'sphere',\n 'point': 'point'\n }\n },\n radiusType: {\n type: 'select',\n options: {\n '': '',\n 'value': 'value',\n 'abs-value': 'abs-value',\n 'value-min': 'value-min',\n 'deviation': 'deviation',\n 'size': 'size'\n }\n },\n radius: {\n type: 'number', precision: 3, max: 10.0, min: 0.001, property: 'size'\n },\n scale: {\n type: 'number', precision: 3, max: 10.0, min: 0.001\n },\n sphereDetail: true,\n disableImpostor: true,\n\n pointSize: {\n type: 'number', precision: 1, max: 100, min: 0, buffer: true\n },\n sizeAttenuation: {\n type: 'boolean', buffer: true\n },\n sortParticles: {\n type: 'boolean', rebuild: true\n },\n useTexture: {\n type: 'boolean', buffer: true\n },\n alphaTest: {\n type: 'range', step: 0.001, max: 1, min: 0, buffer: true\n },\n forceTransparent: {\n type: 'boolean', buffer: true\n },\n edgeBleach: {\n type: 'range', step: 0.001, max: 1, min: 0, buffer: true\n }\n\n }, this.parameters, {\n\n colorScheme: {\n type: 'select',\n update: 'color',\n options: {\n '': '',\n 'value': 'value',\n 'uniform': 'uniform',\n 'random': 'random'\n }\n }\n\n })\n\n if (surface instanceof Volume) {\n this.surface = undefined\n this.volume = new FilteredVolume(surface)\n } else {\n this.surface = surface\n this.volume = undefined\n }\n\n this.init(params)\n }\n\n init (params: Partial) {\n var p = params || {}\n p.colorScheme = defaults(p.colorScheme, 'uniform')\n p.colorValue = defaults(p.colorValue, 0xDDDDDD)\n\n this.thresholdType = defaults(p.thresholdType, 'sigma')\n this.thresholdMin = defaults(p.thresholdMin, 2.0)\n this.thresholdMax = defaults(p.thresholdMax, Infinity)\n this.thresholdOut = defaults(p.thresholdOut, false)\n this.dotType = defaults(p.dotType, 'point')\n this.radius = defaults(p.radius, 0.1)\n this.scale = defaults(p.scale, 1.0)\n\n this.pointSize = defaults(p.pointSize, 1)\n this.sizeAttenuation = defaults(p.sizeAttenuation, true)\n this.sortParticles = defaults(p.sortParticles, false)\n this.useTexture = defaults(p.useTexture, false)\n this.alphaTest = defaults(p.alphaTest, 0.5)\n this.forceTransparent = defaults(p.forceTransparent, false)\n this.edgeBleach = defaults(p.edgeBleach, 0.0)\n\n super.init(p)\n\n this.build()\n }\n\n attach (callback: () => void) {\n this.bufferList.forEach(buffer => {\n this.viewer.add(buffer)\n })\n this.setVisibility(this.visible)\n\n callback()\n }\n\n create () {\n var dotData: SphereBufferData|{} = {}\n\n if (this.volume) {\n var volume = this.volume\n var thresholdMin, thresholdMax\n\n if (this.thresholdType === 'sigma') {\n thresholdMin = volume.getValueForSigma(this.thresholdMin)\n thresholdMax = volume.getValueForSigma(this.thresholdMax)\n } else {\n thresholdMin = this.thresholdMin\n thresholdMax = this.thresholdMax\n }\n volume.setFilter(thresholdMin, thresholdMax, this.thresholdOut)\n\n Object.assign(dotData, {\n position: volume.getDataPosition(),\n color: volume.getDataColor(this.getColorParams())\n })\n if (this.dotType === 'sphere') {\n Object.assign(dotData, {\n radius: volume.getDataSize(this.radius, this.scale),\n picking: volume.getDataPicking()\n })\n }\n } else {\n var surface = this.surface\n Object.assign(dotData, {\n position: (surface as Surface).getPosition(),\n color: (surface as Surface).getColor(this.getColorParams())\n })\n if (this.dotType === 'sphere') {\n Object.assign(dotData, {\n radius: (surface as Surface).getSize(this.radius, this.scale),\n picking: (surface as Surface).getPicking()\n })\n }\n }\n\n if (this.dotType === 'sphere') {\n this.dotBuffer = new SphereBuffer(\n dotData as SphereBufferData,\n this.getBufferParams({\n sphereDetail: this.sphereDetail,\n disableImpostor: this.disableImpostor,\n dullInterior: false\n }) as SphereBufferParameters\n ) as SphereGeometryBuffer\n } else {\n this.dotBuffer = new PointBuffer(\n dotData,\n this.getBufferParams({\n pointSize: this.pointSize,\n sizeAttenuation: this.sizeAttenuation,\n sortParticles: this.sortParticles,\n useTexture: this.useTexture,\n alphaTest: this.alphaTest,\n forceTransparent: this.forceTransparent,\n edgeBleach: this.edgeBleach\n })\n )\n }\n\n this.bufferList.push(this.dotBuffer as SphereGeometryBuffer)\n }\n\n update (what: DotDataFields = {}) {\n if (this.bufferList.length === 0) return\n\n const dotData: SphereBufferData|{} = {}\n\n if (what.color) {\n if (this.volume) {\n Object.assign(dotData, {\n color: this.volume.getDataColor(\n this.getColorParams()\n )\n })\n } else {\n Object.assign(dotData, {\n color: (this.surface as Surface).getColor(\n this.getColorParams()\n )\n })\n }\n }\n\n if (this.dotType === 'sphere' && (what.radius || what.scale)) {\n if (this.volume) {\n Object.assign(dotData, {\n radius: this.volume.getDataSize(\n this.radius, this.scale\n )\n })\n } else {\n Object.assign(dotData, {\n radius: (this.surface as Surface).getSize(\n this.radius, this.scale\n )\n })\n }\n }\n\n (this.dotBuffer as SphereGeometryBuffer).setAttributes(dotData)\n }\n\n setParameters (params: Partial, what: DotDataFields = {}, rebuild: boolean) {\n \n if (params && params.thresholdType !== undefined &&\n this.volume instanceof Volume\n ) {\n if (this.thresholdType === 'value' &&\n params.thresholdType === 'sigma'\n ) {\n this.thresholdMin = this.volume.getSigmaForValue(\n this.thresholdMin\n )\n this.thresholdMax = this.volume.getSigmaForValue(\n this.thresholdMax\n )\n } else if (this.thresholdType === 'sigma' &&\n params.thresholdType === 'value'\n ) {\n this.thresholdMin = this.volume.getValueForSigma(\n this.thresholdMin\n )\n this.thresholdMax = this.volume.getValueForSigma(\n this.thresholdMax\n )\n }\n\n this.thresholdType = params.thresholdType\n }\n\n if (params && params.radiusType !== undefined) {\n if (params.radiusType === 'radius') {\n this.radius = 0.1\n } else {\n this.radius = parseFloat(params.radiusType)\n }\n what.radius = true\n if (this.dotType === 'sphere' &&\n (!ExtensionFragDepth || this.disableImpostor)\n ) {\n rebuild = true\n }\n }\n\n if (params && params.radius !== undefined) {\n what.radius = true\n if (this.dotType === 'sphere' &&\n (!ExtensionFragDepth || this.disableImpostor)\n ) {\n rebuild = true\n }\n }\n\n if (params && params.scale !== undefined) {\n what.scale = true\n if (this.dotType === 'sphere' &&\n (!ExtensionFragDepth || this.disableImpostor)\n ) {\n rebuild = true\n }\n }\n\n super.setParameters(params, what, rebuild)\n\n return this\n }\n}\n\nexport default DotRepresentation\n","/**\n * @file Image Buffer\n * @author Alexander Rose \n * @private\n */\n\nimport {\n // @ts-ignore: unused import Vector3, Matrix4 required for declaration only\n Vector2, Vector3, Matrix4, BufferAttribute, DataTexture,\n NormalBlending, NearestFilter, LinearFilter\n} from 'three'\n\nimport '../shader/Image.vert'\nimport '../shader/Image.frag'\n\nimport { Picker } from '../utils/picker'\nimport Buffer, { BufferDefaultParameters, BufferParameterTypes, BufferTypes } from './buffer'\n\n\nconst quadIndices = new Uint16Array([\n 0, 1, 2,\n 1, 3, 2\n])\n\nconst quadUvs = new Float32Array([\n 0, 1,\n 0, 0,\n 1, 1,\n 1, 0\n])\n\ntype ImageFilterTypes = 'nearest'|'linear'|'cubic-bspline'|'cubic-catmulrom'|'cubic-mitchell'\n\nexport interface ImageBufferData {\n position: Float32Array\n imageData: Uint8Array\n width: number\n height: number\n\n picking?: Picker\n}\n\nexport const ImageBufferDefaultParameters = Object.assign({\n filter: 'nearest' as ImageFilterTypes,\n forceTransparent: true\n}, BufferDefaultParameters)\nexport type ImageBufferParameters = typeof ImageBufferDefaultParameters\n\nexport const ImageBufferParameterTypes = Object.assign({\n filter: { updateShader: true, uniform: true }\n}, BufferParameterTypes)\n\n/**\n * Image buffer. Draw a single image. Optionally interpolate.\n */\nclass ImageBuffer extends Buffer {\n parameterTypes = ImageBufferParameterTypes\n get defaultParameters() { return ImageBufferDefaultParameters }\n parameters: ImageBufferParameters\n\n alwaysTransparent = true\n hasWireframe = false\n vertexShader = 'Image.vert'\n fragmentShader = 'Image.frag'\n\n tex: DataTexture\n pickingTex: DataTexture\n\n /**\n * @param {Object} data - buffer data\n * @param {Float32Array} data.position - image position\n * @param {Float32Array} data.imageData - image data, rgba channels\n * @param {Float32Array} data.width - image width\n * @param {Float32Array} data.height - image height\n * @param {Picker} [data.picking] - picking ids\n * @param {BufferParameters} [params] - parameters object\n */\n constructor (data: ImageBufferData, params: ImageBufferParameters) {\n super({\n position: data.position,\n index: quadIndices,\n picking: data.picking\n }, params)\n\n const {imageData, width, height} = data\n\n const tex = new DataTexture(imageData, width, height)\n tex.flipY = true\n this.tex = tex\n\n const n = imageData.length\n const pickingData = new Uint8Array(n)\n for (let i = 0; i < n; i += 4) {\n const j = i / 4\n pickingData[ i ] = j >> 16 & 255\n pickingData[ i + 1 ] = j >> 8 & 255\n pickingData[ i + 2 ] = j & 255\n }\n\n const pickingTex = new DataTexture(pickingData, width, height)\n pickingTex.flipY = true\n pickingTex.minFilter = NearestFilter\n pickingTex.magFilter = NearestFilter\n this.pickingTex = pickingTex\n\n this.addUniforms({\n 'map': { value: tex },\n 'pickingMap': { value: pickingTex },\n 'mapSize': { value: new Vector2(width, height) }\n })\n\n this.geometry.setAttribute('uv', new BufferAttribute(quadUvs, 2))\n }\n\n getDefines (type: BufferTypes) {\n const defines = super.getDefines(type)\n const filter = this.parameters.filter\n\n if (filter.startsWith('cubic')) {\n defines.CUBIC_INTERPOLATION = 1\n if (filter.endsWith('bspline')) {\n defines.BSPLINE_FILTER = 1\n } else if (filter.endsWith('catmulrom')) {\n defines.CATMULROM_FILTER = 1\n } else if (filter.endsWith('mitchell')) {\n defines.MITCHELL_FILTER = 1\n }\n }\n\n return defines\n }\n\n updateTexture () {\n const tex = this.tex\n const filter = this.parameters.filter\n\n if (filter.startsWith('cubic')) {\n tex.minFilter = NearestFilter\n tex.magFilter = NearestFilter\n } else if (filter === 'linear') {\n tex.minFilter = LinearFilter\n tex.magFilter = LinearFilter\n } else { // filter === \"nearest\"\n tex.minFilter = NearestFilter\n tex.magFilter = NearestFilter\n }\n\n tex.needsUpdate = true\n this.pickingTex.needsUpdate = true\n }\n\n makeMaterial () {\n super.makeMaterial()\n this.updateTexture()\n\n const m = this.material\n m.uniforms.map.value = this.tex\n m.blending = NormalBlending\n m.needsUpdate = true\n\n const wm = this.wireframeMaterial\n wm.uniforms.map.value = this.tex\n wm.blending = NormalBlending\n wm.needsUpdate = true\n\n const pm = this.pickingMaterial\n pm.uniforms.map.value = this.tex\n pm.uniforms.pickingMap.value = this.pickingTex\n pm.blending = NormalBlending\n pm.needsUpdate = true\n }\n\n setUniforms (data: any) { // TODO\n if (data && data.filter !== undefined) {\n this.updateTexture()\n data.map = this.tex\n }\n\n super.setUniforms(data)\n }\n}\n\nexport default ImageBuffer\n","/**\n * @file Volume Slice\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3 } from 'three'\n\nimport { ColormakerRegistry } from '../globals'\nimport { defaults } from '../utils'\nimport { SlicePicker } from '../utils/picker'\nimport { Volume } from '../ngl';\nimport { SliceRepresentationParameters } from '../representation/slice-representation';\n\nclass VolumeSlice {\n dimension: 'x'|'y'|'z'\n positionType: 'percent'|'coordinate'\n position: number\n thresholdType: 'sigma'|'value'\n thresholdMin: number\n thresholdMax: number\n normalize: boolean\n volume: Volume\n\n constructor (volume: Volume, params: Partial) {\n const p = params || {}\n\n this.dimension = defaults(p.dimension, 'x')\n this.positionType = defaults(p.positionType, 'percent')\n this.position = defaults(p.position, 30)\n this.thresholdType = defaults(p.thresholdType, 'sigma')\n this.thresholdMin = defaults(p.thresholdMin, -Infinity)\n this.thresholdMax = defaults(p.thresholdMax, Infinity)\n this.normalize = defaults(p.normalize, false)\n\n this.volume = volume\n }\n\n getPositionFromCoordinate (coord: number) {\n const dim = this.dimension\n const v = this.volume\n const m = v.matrix\n\n const mp = new Vector3().setFromMatrixPosition(m)[ dim ]\n const ms = new Vector3().setFromMatrixScale(m)[ dim ]\n\n let vn\n if (dim === 'x') {\n vn = v.nx\n } else if (dim === 'y') {\n vn = v.ny\n } else {\n vn = v.nz\n }\n\n return Math.round((((coord - mp) / (vn / 100)) + 1) / ms)\n }\n\n getData (params: any) {\n params = params || {}\n\n const v = this.volume\n const d = v.data\n const m = v.matrix\n\n let p: number\n if (this.positionType === 'coordinate') {\n p = this.getPositionFromCoordinate(this.position)\n } else {\n p = this.position\n }\n\n function pos (dimLen: number) {\n return Math.round((dimLen / 100) * (p - 1))\n }\n\n function index (x: number, y: number, z: number, i: number) {\n return (z * v.ny * v.nx + y * v.nx + x) * 3 + i\n }\n\n const position = new Float32Array(4 * 3)\n const vec = new Vector3()\n\n let width, height\n let x\n let y\n let z\n let x0 = 0\n let y0 = 0\n let z0 = 0\n let nx = v.nx\n let ny = v.ny\n let nz = v.nz\n\n function setVec (x: number, y: number, z: number, offset: number) {\n vec.set(x, y, z).applyMatrix4(m).toArray(position as any, offset)\n }\n\n if (this.dimension === 'x') {\n x = pos(v.nx)\n y = v.ny - 1\n z = v.nz - 1\n\n width = v.nz\n height = v.ny\n\n x0 = x\n nx = x0 + 1\n\n setVec(x, 0, 0, 0)\n setVec(x, y, 0, 3)\n setVec(x, 0, z, 6)\n setVec(x, y, z, 9)\n } else if (this.dimension === 'y') {\n x = v.nx - 1\n y = pos(v.ny)\n z = v.nz - 1\n\n width = v.nz\n height = v.nx\n\n y0 = y\n ny = y0 + 1\n\n setVec(0, y, 0, 0)\n setVec(x, y, 0, 3)\n setVec(0, y, z, 6)\n setVec(x, y, z, 9)\n } else if (this.dimension === 'z') {\n x = v.nx - 1\n y = v.ny - 1\n z = pos(v.nz)\n\n width = v.nx\n height = v.ny\n\n z0 = z\n nz = z0 + 1\n\n setVec(0, 0, z, 0)\n setVec(0, y, z, 3)\n setVec(x, 0, z, 6)\n setVec(x, y, z, 9)\n }\n\n let i = 0\n let j = 0\n const imageData = new Uint8Array(width * height * 4)\n const pickingArray = new Float32Array(width * height)\n\n let tMin, tMax\n if (this.thresholdType === 'sigma') {\n tMin = v.getValueForSigma(this.thresholdMin)\n tMax = v.getValueForSigma(this.thresholdMax)\n } else {\n tMin = this.thresholdMin\n tMax = this.thresholdMax\n }\n\n const cp = Object.assign({}, params.colorParams, { volume: v })\n if (this.normalize) {\n cp.domain = [ 0, 1 ]\n }\n const colormaker = ColormakerRegistry.getScheme(cp)\n const tmp = new Float32Array(3)\n const scale = colormaker.getScale()\n\n let min = 0, max, diff = 0\n if (this.normalize) {\n min = +Infinity\n max = -Infinity\n for (let iy = y0; iy < ny; ++iy) {\n for (let ix = x0; ix < nx; ++ix) {\n for (let iz = z0; iz < nz; ++iz) {\n const idx = index(ix, iy, iz, 0) / 3\n const val = d[ idx ]\n if (val < min) min = val\n if (val > max) max = val\n }\n }\n }\n diff = max - min\n }\n\n for (let iy = y0; iy < ny; ++iy) {\n for (let ix = x0; ix < nx; ++ix) {\n for (let iz = z0; iz < nz; ++iz) {\n const idx = index(ix, iy, iz, 0) / 3\n let val = d[ idx ]\n if (this.normalize) {\n val = (val - min) / diff\n }\n\n colormaker.colorToArray(scale(val), tmp)\n imageData[ i ] = Math.round(tmp[ 0 ] * 255)\n imageData[ i + 1 ] = Math.round(tmp[ 1 ] * 255)\n imageData[ i + 2 ] = Math.round(tmp[ 2 ] * 255)\n imageData[ i + 3 ] = (val > tMin && val < tMax) ? 255 : 0\n\n pickingArray[ j ] = idx\n\n ++j\n i += 4\n }\n }\n }\n\n const picking = new SlicePicker(pickingArray, v)\n\n return { position, imageData, width, height, picking }\n }\n}\n\nexport default VolumeSlice\n","/**\n * @file Slice Representation\n * @author Alexander Rose \n * @private\n */\n\nimport { defaults } from '../utils'\nimport Representation, { RepresentationParameters } from './representation'\nimport ImageBuffer, { ImageBufferParameters, ImageBufferData } from '../buffer/image-buffer'\nimport VolumeSlice from '../surface/volume-slice'\nimport Viewer from '../viewer/viewer';\nimport { Volume } from '../ngl';\n\n/**\n * Slice representation parameter object. Extends {@link RepresentationParameters}\n *\n * @typedef {Object} SliceRepresentationParameters - slice representation parameters\n *\n * @property {String} filter - filter applied to map the volume data on the slice, one of \"nearest\", \"linear\", \"cubic-bspline\", \"cubic-catmulrom\", \"cubic-mitchell\".\n * @property {String} positionType - Meaning of the position value. Either \"percent\" od \"coordinate\".\n * @property {Number} position - position of the slice.\n * @property {String} dimension - one of \"x\", \"y\" or \"z\"\n * @property {String} thresholdType - Meaning of the threshold values. Either *value* for the literal value or *sigma* as a factor of the sigma of the data. For volume data only.\n * @property {Number} thresholdMin - Minimum value to be displayed. For volume data only.\n * @property {Number} thresholdMax - Maximum value to be displayed. For volume data only.\n * @property {Boolean} normalize - Flag indicating wheather to normalize the data in a slice when coloring.\n */\nexport interface SliceRepresentationParameters extends RepresentationParameters {\n filter: 'nearest'|'linear'|'cubic-bspline'|'cubic-catmulrom'|'cubic-mitchell'\n positionType: 'percent'|'coordinate'\n position: number\n dimension: 'x'|'y'|'z'\n thresholdType: 'value'|'sigma'\n thresholdMin: number\n thresholdMax: number\n normalize: boolean\n}\n/**\n * Slice representation\n */\nclass SliceRepresentation extends Representation {\n protected filter: 'nearest'|'linear'|'cubic-bspline'|'cubic-catmulrom'|'cubic-mitchell'\n protected positionType: 'percent'|'coordinate'\n protected position: number\n protected dimension: 'x'|'y'|'z'\n protected thresholdType: 'value'|'sigma'\n protected thresholdMin: number\n protected thresholdMax: number\n protected normalize: boolean\n protected volume: Volume\n /**\n * Create Slice representation object\n * @param {Volume} surface - the volume to be represented\n * @param {Viewer} viewer - a viewer object\n * @param {SliceRepresentationParameters} params - slice representation parameters\n */\n constructor (volume: Volume, viewer: Viewer, params: Partial) {\n super(volume, viewer, params)\n\n this.type = 'slice'\n\n this.parameters = Object.assign({\n\n filter: {\n type: 'select',\n buffer: true,\n options: {\n 'nearest': 'nearest',\n 'linear': 'linear',\n 'cubic-bspline': 'cubic-bspline',\n 'cubic-catmulrom': 'cubic-catmulrom',\n 'cubic-mitchell': 'cubic-mitchell'\n }\n },\n positionType: {\n type: 'select',\n rebuild: true,\n options: {\n 'percent': 'percent', 'coordinate': 'coordinate'\n }\n },\n position: {\n type: 'range',\n step: 0.1,\n max: 100,\n min: 1,\n rebuild: true\n },\n dimension: {\n type: 'select',\n rebuild: true,\n options: {\n 'x': 'x', 'y': 'y', 'z': 'z'\n }\n },\n thresholdType: {\n type: 'select',\n rebuild: true,\n options: {\n 'value': 'value', 'sigma': 'sigma'\n }\n },\n thresholdMin: {\n type: 'number', precision: 3, max: Infinity, min: -Infinity, rebuild: true\n },\n thresholdMax: {\n type: 'number', precision: 3, max: Infinity, min: -Infinity, rebuild: true\n },\n normalize: {\n type: 'boolean', rebuild: true\n }\n\n }, this.parameters, {\n\n flatShaded: null,\n side: null,\n wireframe: null,\n linewidth: null,\n colorScheme: null,\n\n roughness: null,\n metalness: null,\n diffuse: null\n\n })\n\n this.volume = volume\n\n this.init(params)\n }\n\n init (params: Partial) {\n const v = this.volume\n const p = params || {}\n p.colorDomain = defaults(p.colorDomain, [ v.min, v.max ])\n p.colorScheme = defaults(p.colorScheme, 'value')\n p.colorScale = defaults(p.colorScale, 'Spectral')\n\n this.colorScheme = 'value'\n this.dimension = defaults(p.dimension, 'x')\n this.filter = defaults(p.filter, 'cubic-bspline')\n this.positionType = defaults(p.positionType, 'percent')\n this.position = defaults(p.position, 30)\n this.thresholdType = defaults(p.thresholdType, 'sigma')\n this.thresholdMin = defaults(p.thresholdMin, -Infinity)\n this.thresholdMax = defaults(p.thresholdMax, Infinity)\n this.normalize = defaults(p.normalize, false)\n\n super.init(p)\n\n this.build()\n }\n\n attach (callback: () => void) {\n this.bufferList.forEach(buffer => {\n this.viewer.add(buffer)\n })\n this.setVisibility(this.visible)\n\n callback()\n }\n\n create () {\n const volumeSlice = new VolumeSlice(this.volume, {\n positionType: this.positionType,\n position: this.position,\n dimension: this.dimension,\n thresholdType: this.thresholdType,\n thresholdMin: this.thresholdMin,\n thresholdMax: this.thresholdMax,\n normalize: this.normalize\n })\n\n const sliceBuffer = new ImageBuffer(\n volumeSlice.getData({ colorParams: this.getColorParams() }) as ImageBufferData,\n this.getBufferParams({\n filter: this.filter\n }) as ImageBufferParameters\n )\n\n this.bufferList.push(sliceBuffer)\n }\n}\n\nexport default SliceRepresentation\n","/**\n * @file Representation Utils\n * @author Alexander Rose \n * @private\n */\n\nimport { Debug, Log, RepresentationRegistry } from '../globals'\n\nimport Viewer from '../viewer/viewer'\nimport Structure from '../structure/structure'\nimport Surface from '../surface/surface'\nimport Volume from '../surface/volume'\nimport Shape from '../geometry/shape'\n\nimport BufferRepresentation from './buffer-representation'\nimport SurfaceRepresentation from './surface-representation'\nimport DotRepresentation from './dot-representation'\nimport SliceRepresentation from './slice-representation'\n\nfunction logReprUnknown (type: string) {\n Log.error(`makeRepresentation: representation type ${type} unknown`)\n}\n\nexport function makeRepresentation (type: string, object: any, viewer: Viewer, params: any) { // TODO\n if (Debug) Log.time('makeRepresentation ' + type)\n\n var ReprClass\n\n if (object instanceof Structure) {\n ReprClass = RepresentationRegistry.get(type)\n\n if (!ReprClass) {\n logReprUnknown(type)\n return\n }\n } else if (object instanceof Surface) {\n if (type === 'surface') {\n ReprClass = SurfaceRepresentation\n } else if (type === 'dot') {\n ReprClass = DotRepresentation\n } else {\n logReprUnknown(type)\n return\n }\n } else if (object instanceof Volume) {\n if (type === 'surface') {\n ReprClass = SurfaceRepresentation\n } else if (type === 'dot') {\n ReprClass = DotRepresentation\n } else if (type === 'slice') {\n ReprClass = SliceRepresentation\n } else {\n logReprUnknown(type)\n return\n }\n } else if (object instanceof Shape) {\n ReprClass = BufferRepresentation\n object = object.getBufferList()\n } else if (type === 'buffer') {\n ReprClass = BufferRepresentation\n } else {\n Log.error('makeRepresentation: object ' + object + ' unknown')\n return\n }\n\n const repr = new ReprClass(object, viewer, params)\n\n if (Debug) Log.timeEnd('makeRepresentation ' + type)\n\n return repr\n}\n","/**\n * @file Element\n * @author Alexander Rose \n * @private\n */\n\nimport { Signal } from 'signals'\n\nimport { createParams } from '../utils'\nimport { generateUUID } from '../math/math-utils'\nimport Stage from '../stage/stage'\n\nexport const ElementDefaultParameters = {\n name: 'some element',\n status: ''\n}\nexport type ElementParameters = typeof ElementDefaultParameters\n\nexport interface ElementSignals {\n statusChanged: Signal // on status change\n nameChanged: Signal // on name change\n disposed: Signal // on dispose\n}\n\n/**\n * Element base class\n */\nabstract class Element {\n /**\n * Events emitted by the element\n */\n signals: ElementSignals = {\n statusChanged: new Signal(),\n nameChanged: new Signal(),\n disposed: new Signal()\n }\n readonly parameters: ElementParameters\n readonly uuid: string\n\n get defaultParameters() { return ElementDefaultParameters }\n\n /**\n * @param {Stage} stage - stage object the component belongs to\n * @param {ElementParameters} params - component parameters\n */\n constructor (readonly stage: Stage, params: Partial = {}) {\n this.parameters = createParams(params, this.defaultParameters)\n this.uuid = generateUUID()\n }\n\n abstract get type (): string\n\n get name () { return this.parameters.name }\n\n setStatus (value: string) {\n this.parameters.status = value\n this.signals.statusChanged.dispatch(value)\n\n return this\n }\n\n setName (value: string) {\n this.parameters.name = value\n this.signals.nameChanged.dispatch(value)\n\n return this\n }\n\n dispose () {\n this.signals.disposed.dispatch()\n }\n}\n\nexport default Element\n","/**\n * @file Representation Element\n * @author Alexander Rose \n * @private\n */\n\nimport { Signal } from 'signals'\nimport { Color } from 'three'\n\nimport Stage from '../stage/stage'\nimport Representation, { RepresentationParameters } from '../representation/representation'\nimport Component from './component'\nimport Element, { ElementDefaultParameters, ElementSignals } from './element'\n\nexport const RepresentationElementDefaultParameters = Object.assign({\n visible: true\n}, ElementDefaultParameters)\nexport type RepresentationElementParameters = typeof RepresentationElementDefaultParameters\n\nexport interface RepresentationElementSignals extends ElementSignals {\n visibilityChanged: Signal // on visibility change\n parametersChanged: Signal // on parameters change\n}\n\n/**\n * Element wrapping a {@link Representation} object\n */\nclass RepresentationElement extends Element {\n signals: RepresentationElementSignals\n parameters: RepresentationElementParameters\n get defaultParameters() { return RepresentationElementDefaultParameters }\n\n repr: Representation\n\n /**\n * Create representation component\n * @param {Stage} stage - stage object the component belongs to\n * @param {Representation} repr - representation object to wrap\n * @param {RepresentationParameters} [params] - component parameters\n * @param {Component} [parent] - parent component\n */\n constructor (stage: Stage, repr: Representation, params: Partial = {}, readonly parent: Component) {\n super(stage, Object.assign({ name: repr.type }, params))\n\n this.signals = Object.assign({\n visibilityChanged: new Signal(),\n parametersChanged: new Signal()\n }, this.signals)\n\n this.setRepresentation(repr)\n }\n\n get visible () { return this.parameters.visible }\n\n /**\n * Component type\n * @type {String}\n */\n get type () { return 'representation' }\n\n getType () {\n return this.repr.type\n }\n\n setRepresentation (repr: Representation) {\n this._disposeRepresentation()\n this.repr = repr\n // this.name = repr.type;\n this.stage.tasks.listen(this.repr.tasks)\n this.updateVisibility()\n }\n\n _disposeRepresentation () {\n if (this.repr) {\n this.stage.tasks.unlisten(this.repr.tasks)\n this.repr.dispose()\n }\n }\n\n dispose () {\n if (this.parent && this.parent.hasRepresentation(this)) {\n this.parent.removeRepresentation(this)\n } else {\n this._disposeRepresentation()\n this.signals.disposed.dispatch()\n }\n }\n\n /**\n * Set the visibility of the component, takes parent visibility into account\n * @param {Boolean} value - visibility flag\n * @return {RepresentationElement} this object\n */\n setVisibility (value: boolean) {\n this.parameters.visible = value\n this.updateVisibility()\n this.signals.visibilityChanged.dispatch(this.parameters.visible)\n\n return this\n }\n\n getVisibility () {\n if (this.parent) {\n return this.parent.parameters.visible && this.parameters.visible\n } else {\n return this.parameters.visible\n }\n }\n\n /**\n * Toggle visibility of the component, takes parent visibility into account\n * @return {RepresentationElement} this object\n */\n toggleVisibility () {\n return this.setVisibility(!this.parameters.visible)\n }\n\n updateVisibility () {\n this.repr.setVisibility(this.getVisibility())\n }\n\n /**\n * Set selection\n * @param {Object} what - flags indicating what attributes to update\n * @param {Boolean} what.position - update position attribute\n * @param {Boolean} what.color - update color attribute\n * @param {Boolean} what.radius - update radius attribute\n * @return {RepresentationElement} this object\n */\n update (what: any) { // TODO\n (this.repr as any).update(what) // TODO\n\n return this\n }\n\n build (params?: any) { // TODO\n this.repr.build(params)\n\n return this\n }\n\n /**\n * Set selection\n * @param {String} string - selection string\n * @return {RepresentationElement} this object\n */\n setSelection (string: string) {\n const repr: any = this.repr // TODO\n\n if (repr.setSelection) {\n repr.setSelection(string)\n }\n\n return this\n }\n\n /**\n * Set representation parameters\n * @param {RepresentationParameters} params - parameter object\n * @return {RepresentationElement} this object\n */\n setParameters (params: any) { // TODO\n this.repr.setParameters(params)\n this.signals.parametersChanged.dispatch(\n this.repr.getParameters()\n )\n\n return this\n }\n\n /**\n * Get representation parameters\n * @return {RepresentationParameters} parameter object\n */\n getParameters (): Partial {\n return this.repr.getParameters()\n }\n\n /**\n * Set color\n * @param {String|Color|Hex} value - color value\n * @return {RepresentationElement} this object\n */\n setColor (value: string|number|Color) {\n this.repr.setColor(value)\n\n return this\n }\n}\n\nexport default RepresentationElement\n","\n/**\n * @file Component\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3, Quaternion, Matrix4, Euler, Box3 } from 'three'\nimport { Signal } from 'signals'\n\nimport { defaults, createParams } from '../utils'\nimport { generateUUID } from '../math/math-utils'\nimport Annotation, { AnnotationParams } from '../component/annotation'\nimport ComponentControls from '../controls/component-controls'\nimport { makeRepresentation } from '../representation/representation-utils'\nimport RepresentationElement from './representation-element'\nimport Stage from '../stage/stage'\nimport Viewer from '../viewer/viewer'\n\nconst _m = new Matrix4()\nconst _v = new Vector3()\n\nexport const ComponentDefaultParameters = {\n name: '',\n status: '',\n visible: true\n}\nexport type ComponentParameters = typeof ComponentDefaultParameters\n\n\nexport interface ComponentSignals {\n representationAdded: Signal // when a representation is added\n representationRemoved: Signal // when a representation is removed\n visibilityChanged: Signal // on visibility change\n matrixChanged: Signal // on matrix change\n statusChanged: Signal // on status change\n nameChanged: Signal // on name change\n disposed: Signal // on dispose\n}\n\n/**\n * Base class for components\n */\nabstract class Component {\n /**\n * Events emitted by the component\n */\n readonly signals: ComponentSignals = {\n representationAdded: new Signal(),\n representationRemoved: new Signal(),\n visibilityChanged: new Signal(),\n matrixChanged: new Signal(),\n statusChanged: new Signal(),\n nameChanged: new Signal(),\n disposed: new Signal()\n }\n\n readonly parameters: ComponentParameters\n get defaultParameters () { return ComponentDefaultParameters }\n\n readonly uuid: string\n readonly viewer: Viewer\n\n reprList: RepresentationElement[] = []\n annotationList: Annotation[] = []\n\n matrix = new Matrix4()\n position = new Vector3()\n quaternion = new Quaternion()\n scale = new Vector3(1, 1, 1)\n transform = new Matrix4()\n\n controls: ComponentControls\n\n /**\n * @param {Stage} stage - stage object the component belongs to\n * @param {ComponentParameters} params - parameter object\n */\n constructor (readonly stage: Stage, readonly object: any, params: Partial = {}) {\n this.parameters = createParams(params, this.defaultParameters)\n this.uuid = generateUUID()\n this.viewer = stage.viewer\n\n this.controls = new ComponentControls(this)\n }\n\n abstract get type (): string\n\n get name () { return this.parameters.name }\n get status () { return this.parameters.status }\n get visible () { return this.parameters.visible }\n\n /**\n * Set position transform\n *\n * @example\n * // translate by 25 angstrom along x axis\n * component.setPosition([ 25, 0, 0 ]);\n *\n * @param {Vector3|Array} p - the coordinates\n * @return {Component} this object\n */\n setPosition (p: [number, number, number]|Vector3) {\n if (Array.isArray(p)) {\n this.position.fromArray(p)\n } else {\n this.position.copy(p)\n }\n this.updateMatrix()\n\n return this\n }\n\n /**\n * Set local rotation transform\n * (for global rotation use setTransform)\n *\n * @example\n * // rotate by 2 degree radians on x axis\n * component.setRotation( [ 2, 0, 0 ] );\n *\n * @param {Quaternion|Euler|Array} r - the rotation\n * @return {Component} this object\n */\n setRotation (r: [number, number, number]|Euler|Quaternion) {\n if (Array.isArray(r)) {\n if (r.length === 3) {\n const e = new Euler().fromArray(r)\n this.quaternion.setFromEuler(e)\n } else {\n this.quaternion.fromArray(r)\n }\n } else if (r instanceof Euler) {\n this.quaternion.setFromEuler(r)\n } else {\n this.quaternion.copy(r)\n }\n this.updateMatrix()\n\n return this\n }\n\n /**\n * Set scale transform\n *\n * @example\n * // scale by factor of two\n * component.setScale( 2 );\n *\n * @param {Number} s - the scale\n * @return {Component} this object\n */\n setScale (s: number) {\n this.scale.set(s, s, s)\n this.updateMatrix()\n\n return this\n }\n\n /**\n * Set general transform. Is applied before and in addition\n * to the position, rotation and scale transformations\n *\n * @example\n * component.setTransform( matrix );\n *\n * @param {Matrix4} m - the matrix\n * @return {Component} this object\n */\n setTransform (m: Matrix4) {\n this.transform.copy(m)\n this.updateMatrix()\n\n return this\n }\n\n updateMatrix () {\n const c = this.getCenterUntransformed(_v)\n this.matrix.makeTranslation(-c.x, -c.y, -c.z)\n\n _m.makeRotationFromQuaternion(this.quaternion)\n this.matrix.premultiply(_m)\n\n _m.makeScale(this.scale.x, this.scale.y, this.scale.z)\n this.matrix.premultiply(_m)\n\n const p = this.position\n _m.makeTranslation(p.x + c.x, p.y + c.y, p.z + c.z)\n this.matrix.premultiply(_m)\n\n this.matrix.premultiply(this.transform)\n\n this.updateRepresentationMatrices()\n\n this.stage.viewer.updateBoundingBox()\n\n this.signals.matrixChanged.dispatch(this.matrix)\n }\n\n /**\n * Propogates our matrix to each representation\n */\n updateRepresentationMatrices () {\n this.reprList.forEach(repr => {\n repr.setParameters({ matrix: this.matrix })\n })\n }\n\n /**\n * Add an anotation object\n * @param {Vector3} position - the 3d position\n * @param {String|Element} content - the HTML content\n * @param {Object} [params] - parameters\n * @param {Integer} params.offsetX - 2d offset in x direction\n * @param {Integer} params.offsetY - 2d offset in y direction\n * @return {Annotation} the added annotation object\n */\n addAnnotation (position: Vector3, content: string|HTMLElement, params: AnnotationParams) {\n const annotation = new Annotation(this, position, content, params)\n this.annotationList.push(annotation)\n\n return annotation\n }\n\n /**\n * Iterator over each annotation and executing the callback\n * @param {Function} callback - function to execute\n * @return {undefined}\n */\n eachAnnotation (callback: (a: Annotation) => void) {\n this.annotationList.slice().forEach(callback)\n }\n\n /**\n * Remove the give annotation from the component\n * @param {Annotation} annotation - the annotation to remove\n * @return {undefined}\n */\n removeAnnotation (annotation: Annotation) {\n const idx = this.annotationList.indexOf(annotation)\n if (idx !== -1) {\n this.annotationList.splice(idx, 1)\n annotation.dispose()\n }\n }\n\n /**\n * Remove all annotations from the component\n * @return {undefined}\n */\n removeAllAnnotations () {\n this.eachAnnotation(annotation => annotation.dispose())\n this.annotationList.length = 0\n }\n\n /**\n * Add a new representation to the component\n * @param {String} type - the name of the representation\n * @param {Object} object - the object on which the representation should be based\n * @param {RepresentationParameters} [params] - representation parameters\n * @return {RepresentationElement} the created representation wrapped into\n * a representation element object\n */\n protected _addRepresentation (type: string, object: any, params: any, hidden = false) { // TODO\n const p = params || {}\n const sp = this.stage.getParameters() as any // TODO\n p.matrix = this.matrix.clone()\n p.quality = p.quality || sp.quality\n p.disableImpostor = defaults(p.disableImpostor, !sp.impostor)\n p.useWorker = defaults(p.useWorker, sp.workerDefault)\n p.visible = defaults(p.visible, true)\n\n const p2 = Object.assign({}, p, { visible: this.parameters.visible && p.visible })\n const repr = makeRepresentation(type, object, this.viewer, p2)\n const reprElem = new RepresentationElement(this.stage, repr, p, this)\n\n if (!hidden) {\n this.reprList.push(reprElem)\n this.signals.representationAdded.dispatch(reprElem)\n }\n return reprElem\n }\n\n abstract addRepresentation (type: any, params: any): any\n\n addBufferRepresentation (buffer: any, params: any) { // TODO\n return this._addRepresentation.call(this, 'buffer', buffer, params)\n }\n\n hasRepresentation (repr: RepresentationElement) {\n return this.reprList.indexOf(repr) !== -1\n }\n\n /**\n * Iterator over each representation and executing the callback\n * @param {Function} callback - function to execute\n * @return {undefined}\n */\n eachRepresentation (callback: (repr: RepresentationElement) => void) {\n this.reprList.slice().forEach(callback)\n }\n\n /**\n * Removes a representation component\n * @param {RepresentationElement} repr - the representation element\n * @return {undefined}\n */\n removeRepresentation (repr: RepresentationElement) {\n const idx = this.reprList.indexOf(repr)\n if (idx !== -1) {\n this.reprList.splice(idx, 1)\n repr.dispose()\n this.signals.representationRemoved.dispatch(repr)\n }\n }\n\n updateRepresentations (what: any) { // TODO\n this.reprList.forEach(repr => repr.update(what))\n this.stage.viewer.requestRender()\n }\n\n /**\n * Removes all representation components\n * @return {undefined}\n */\n removeAllRepresentations () {\n this.eachRepresentation(repr => repr.dispose())\n }\n\n dispose () {\n this.removeAllAnnotations()\n this.removeAllRepresentations()\n\n this.reprList.length = 0\n\n this.signals.disposed.dispatch()\n }\n\n /**\n * Set the visibility of the component, including added representations\n * @param {Boolean} value - visibility flag\n * @return {Component} this object\n */\n setVisibility (value: boolean) {\n this.parameters.visible = value\n\n this.eachRepresentation((repr: RepresentationElement) => repr.updateVisibility())\n this.eachAnnotation((annotation: Annotation) => annotation.updateVisibility())\n\n this.signals.visibilityChanged.dispatch(value)\n\n return this\n }\n\n setStatus (value: string) {\n this.parameters.status = value\n this.signals.statusChanged.dispatch(value)\n\n return this\n }\n\n setName (value: string) {\n this.parameters.name = value\n this.signals.nameChanged.dispatch(value)\n\n return this\n }\n\n /**\n * @return {Box3} the component's bounding box\n */\n getBox (...args: any[]) {\n return this.getBoxUntransformed(...args)\n .clone().applyMatrix4(this.matrix)\n }\n\n /**\n * @return {Vector3} the component's center position\n */\n getCenter (...args: any[]) {\n return this.getCenterUntransformed(...args)\n .clone().applyMatrix4(this.matrix)\n }\n\n getZoom (...args: any[]) {\n return this.stage.getZoomForBox(this.getBox(...args))\n }\n\n /**\n * @abstract\n * @return {Box3} the untransformed component's bounding box\n */\n getBoxUntransformed (...args: any[]): Box3 {\n return new Box3()\n }\n\n getCenterUntransformed (...args: any[]) {\n return this.getBoxUntransformed().getCenter(new Vector3())\n }\n\n /**\n * Automatically center and zoom the component\n * @param {Integer} [duration] - duration of the animation, defaults to 0\n * @return {undefined}\n */\n autoView (duration?: number) {\n this.stage.animationControls.zoomMove(\n this.getCenter(),\n this.getZoom(),\n defaults(duration, 0)\n )\n }\n}\n\nexport default Component\n","/**\n * @file Collection\n * @author Alexander Rose \n * @private\n */\n\nimport Component from './component'\nimport Element from './element'\n\nclass Collection {\n constructor (readonly list: T[] = []) {\n // remove elements from list when they get disposed\n const n = list.length\n\n for (let i = 0; i < n; ++i) {\n const elm = list[ i ]\n elm.signals.disposed.add(this._remove, this)\n }\n }\n\n _remove (elm: T) {\n const idx = this.list.indexOf(elm)\n\n if (idx !== -1) {\n this.list.splice(idx, 1)\n }\n }\n\n get first () {\n return this.list.length > 0 ? this.list[0] : undefined\n }\n\n forEach (fn: (x: T) => any) {\n this.list.forEach(fn)\n\n return this\n }\n\n dispose () {\n return this.forEach((elm) => elm.dispose())\n }\n}\n\nexport default Collection\n","/**\n * @file Component Collection\n * @author Alexander Rose \n * @private\n */\n\nimport RepresentationElement from './representation-element'\nimport Collection from './collection'\nimport { GenericColor } from '../types'\n\nclass RepresentationCollection extends Collection {\n setParameters (params: any) {\n return this.forEach((repr) => repr.setParameters(params))\n }\n\n setVisibility (value: boolean) {\n return this.forEach((repr) => repr.setVisibility(value))\n }\n\n setSelection (string: string) {\n return this.forEach((repr) => repr.setSelection(string))\n }\n\n setColor (color: GenericColor) {\n return this.forEach((repr) => repr.setColor(color))\n }\n\n update (what: any) {\n return this.forEach((repr) => repr.update(what))\n }\n\n build (params?: any) {\n return this.forEach((repr) => repr.build(params))\n }\n\n dispose (params?: any) {\n return this.forEach((repr) => repr.dispose())\n }\n}\n\nexport default RepresentationCollection\n","/**\n * @file Trajectory Component\n * @author Alexander Rose \n * @private\n */\n\nimport { Signal } from 'signals'\n\nimport Element, { ElementSignals, ElementDefaultParameters } from './element'\nimport Stage from '../stage/stage'\nimport Trajectory, { TrajectoryParameters } from '../trajectory/trajectory'\nimport TrajectoryPlayer, {\n TrajectoryPlayerDirection, TrajectoryPlayerMode, TrajectoryPlayerInterpolateType\n} from '../trajectory/trajectory-player'\n\n/**\n * Trajectory component parameter object.\n * @typedef {Object} TrajectoryComponentParameters - component parameters\n *\n * @property {String} name - component name\n * @property {Integer} initialFrame - initial frame the trajectory is set to\n * @property {Integer} defaultStep - default step size to be used by trajectory players\n * @property {Integer} defaultTimeout - default timeout to be used by trajectory players\n * @property {String} defaultInterpolateType - one of \"\" (empty string), \"linear\" or \"spline\"\n * @property {Integer} defaultInterpolateStep - window size used for interpolation\n * @property {String} defaultMode - either \"loop\" or \"once\"\n * @property {String} defaultDirection - either \"forward\" or \"backward\"\n */\n\nexport const TrajectoryElementDefaultParameters = Object.assign({\n defaultStep: 1,\n defaultTimeout: 50,\n defaultInterpolateType: '' as TrajectoryPlayerInterpolateType,\n defaultInterpolateStep: 5,\n defaultMode: 'loop' as TrajectoryPlayerMode,\n defaultDirection: 'forward' as TrajectoryPlayerDirection,\n initialFrame: 0\n}, ElementDefaultParameters)\nexport type TrajectoryElementParameters = typeof TrajectoryElementDefaultParameters\n\nexport interface TrajectoryElementSignals extends ElementSignals {\n frameChanged: Signal // on frame change\n playerChanged: Signal // on player change\n countChanged: Signal // when frame count is available\n parametersChanged: Signal // on parameters change\n}\n\n/**\n * Component wrapping a {@link Trajectory} object\n */\nclass TrajectoryElement extends Element {\n signals: TrajectoryElementSignals\n parameters: TrajectoryElementParameters\n get defaultParameters () { return TrajectoryElementDefaultParameters }\n\n /**\n * @param {Stage} stage - stage object the component belongs to\n * @param {Trajectory} trajectory - the trajectory object\n * @param {TrajectoryComponentParameters} params - component parameters\n * @param {StructureComponent} parent - the parent structure\n */\n constructor (stage: Stage, readonly trajectory: Trajectory, params: Partial = {}) {\n super(stage, Object.assign({ name: trajectory.name }, params))\n\n this.signals = Object.assign(this.signals, {\n frameChanged: new Signal(),\n playerChanged: new Signal(),\n countChanged: new Signal(),\n parametersChanged: new Signal()\n })\n\n // signals\n\n trajectory.signals.frameChanged.add((i: number) => {\n this.signals.frameChanged.dispatch(i)\n })\n\n trajectory.signals.playerChanged.add((player: TrajectoryPlayer) => {\n this.signals.playerChanged.dispatch(player)\n })\n\n trajectory.signals.countChanged.add((n: number) => {\n this.signals.countChanged.dispatch(n)\n })\n\n //\n\n if (params.initialFrame !== undefined) {\n this.setFrame(params.initialFrame)\n }\n }\n\n /**\n * Component type\n * @type {String}\n */\n get type () { return 'trajectory' }\n\n /**\n * Set the frame of the trajectory\n * @param {Integer} i - frame number\n * @return {undefined}\n */\n setFrame (i: number) {\n this.trajectory.setFrame(i)\n }\n\n /**\n * Set trajectory parameters\n * @param {TrajectoryParameters} params - trajectory parameters\n * @return {undefined}\n */\n setParameters (params: Partial = {}) {\n this.trajectory.setParameters(params)\n this.signals.parametersChanged.dispatch(params)\n }\n\n dispose () {\n this.trajectory.dispose()\n super.dispose()\n }\n}\n\nexport default TrajectoryElement\n","/**\n * @file Frames\n * @author Alexander Rose \n * @private\n */\n\nexport default class Frames {\n coordinates = []\n boxes = []\n times = []\n\n timeOffset = 0\n deltaTime = 1\n\n constructor (readonly name: string, readonly path: string) {}\n\n get type () { return 'Frames' }\n}\n","/**\n * @file Superposition\n * @author Alexander Rose \n * @private\n */\n\nimport { Matrix4 } from 'three'\nimport { Debug, Log } from '../globals'\nimport {\n Matrix, svd, meanRows, subRows, transpose,\n multiplyABt, invert3x3, multiply3x3, mat3x3determinant, multiply\n} from '../math/matrix-utils'\nimport Structure from '../structure/structure'\n\nclass Superposition {\n coords1t: Matrix\n coords2t: Matrix\n\n transformationMatrix: Matrix4\n\n mean1: number[]\n mean2: number[]\n\n A = new Matrix(3, 3)\n W = new Matrix(1, 3)\n U = new Matrix(3, 3)\n V = new Matrix(3, 3)\n VH = new Matrix(3, 3)\n R = new Matrix(3, 3)\n\n private tmp = new Matrix(3, 3)\n private c = new Matrix(3, 3)\n\n constructor (atoms1: Structure|Float32Array, atoms2: Structure|Float32Array) {\n // allocate & init data structures\n\n let n1\n if (atoms1 instanceof Structure) {\n n1 = atoms1.atomCount\n } else if (atoms1 instanceof Float32Array) {\n n1 = atoms1.length / 3\n } else {\n return\n }\n\n let n2\n if (atoms2 instanceof Structure) {\n n2 = atoms2.atomCount\n } else if (atoms2 instanceof Float32Array) {\n n2 = atoms2.length / 3\n } else {\n return\n }\n\n const n = Math.min(n1, n2)\n\n const coords1 = new Matrix(3, n)\n const coords2 = new Matrix(3, n)\n\n this.coords1t = new Matrix(n, 3)\n this.coords2t = new Matrix(n, 3)\n\n this.transformationMatrix = new Matrix4()\n\n this.c.data.set([ 1, 0, 0, 0, 1, 0, 0, 0, -1 ])\n\n // prep coords\n\n this.prepCoords(atoms1, coords1, n, false)\n this.prepCoords(atoms2, coords2, n, false)\n\n // superpose\n\n this._superpose(coords1, coords2)\n }\n\n _superpose (coords1: Matrix, coords2: Matrix) {\n this.mean1 = meanRows(coords1)\n this.mean2 = meanRows(coords2)\n\n subRows(coords1, this.mean1)\n subRows(coords2, this.mean2)\n\n transpose(this.coords1t, coords1)\n transpose(this.coords2t, coords2)\n\n multiplyABt(this.A, this.coords2t, this.coords1t)\n\n svd(this.A, this.W, this.U, this.V)\n\n invert3x3(this.V, this.VH)\n multiply3x3(this.R, this.U, this.VH)\n\n if (mat3x3determinant(this.R) < 0.0) {\n if (Debug) Log.log('R not a right handed system')\n\n multiply3x3(this.tmp, this.c, this.VH)\n multiply3x3(this.R, this.U, this.tmp)\n }\n\n //get the transformation matrix\n\n const transformMat_ = new Matrix(4,4)\n const tmp_1 = new Matrix(4,4)\n const tmp_2 = new Matrix(4,4)\n\n const sub = new Matrix(4,4)\n const mult = new Matrix(4,4)\n const add = new Matrix(4,4)\n\n const R = this.R.data\n const M1 = this.mean1\n const M2 = this.mean2\n\n sub.data.set([ 1, 0, 0, -M1[0],\n 0, 1, 0, -M1[1],\n 0, 0, 1, -M1[2],\n 0, 0, 0, 1 ])\n\n mult.data.set([ R[0], R[1], R[2], 0,\n R[3], R[4], R[5], 0,\n R[6], R[7], R[8], 0,\n 0, 0, 0, 1 ])\n\n add.data.set([ 1, 0, 0, M2[0],\n 0, 1, 0, M2[1],\n 0, 0, 1, M2[2],\n 0, 0, 0, 1 ])\n\n transpose(tmp_1,sub)\n multiplyABt(transformMat_,mult,tmp_1)\n transpose(tmp_2,transformMat_)\n multiplyABt(tmp_1,add,tmp_2)\n\n transpose(transformMat_,tmp_1)\n this.transformationMatrix.elements = transformMat_.data as unknown as number[]\n\n }\n\n prepCoords (atoms: Structure|Float32Array, coords: Matrix, n: number, is4X4: boolean) {\n let i = 0\n const cd = coords.data\n\n let c = 3\n let d = n * 3\n\n if (is4X4) {\n d = n * 4\n c = 4\n }\n if (atoms instanceof Structure) {\n atoms.eachAtom(function (a) {\n if (i < d) {\n cd[ i + 0 ] = a.x\n cd[ i + 1 ] = a.y\n cd[ i + 2 ] = a.z\n if (is4X4) cd[ i + 3 ] = 1\n\n i += c\n }\n })\n } else if (atoms instanceof Float32Array) {\n for (; i < d; i += c){\n if (i < d) {\n cd[ i ] = atoms[ i ]\n cd[ i + 1 ] = atoms[ i + 1 ]\n cd[ i + 2 ] = atoms[ i + 2 ]\n if (is4X4) cd[ i + 3 ] = 1\n }\n }\n } else {\n Log.warn('prepCoords: input type unknown')\n }\n }\n\n transform (atoms: Structure|Float32Array) {\n // allocate data structures\n\n let n\n if (atoms instanceof Structure) {\n n = atoms.atomCount\n } else if (atoms instanceof Float32Array) {\n n = atoms.length / 3\n } else {\n return\n }\n\n const coords = new Matrix(4, n)\n const tCoords = new Matrix(n,4)\n\n // prep coords\n\n this.prepCoords(atoms, coords, n, true)\n\n // check for transformation matrix correctness\n\n const transform = this.transformationMatrix\n const det = transform.determinant()\n if (!det){\n return det\n }\n\n // do transform\n\n const mult = new Matrix(4,4)\n mult.data = transform.elements as unknown as Float32Array\n multiply(tCoords,coords,mult)\n\n let i = 0\n const cd = tCoords.data\n if (atoms instanceof Structure) {\n atoms.eachAtom(function (a) {\n a.x = cd[ i ]\n a.y = cd[ i + 1 ]\n a.z = cd[ i + 2 ]\n i += 4\n })\n\n //update transformation matrices for each assembly\n\n const invertTrasform = new Matrix4()\n invertTrasform.copy(transform).invert()\n\n const biomolDict = atoms.biomolDict\n\n for (let key in biomolDict) {\n\n if (biomolDict.hasOwnProperty(key)) {\n let assembly = biomolDict[key]\n\n assembly.partList.forEach(function(part){\n\n part.matrixList.forEach(function(mat){\n\n mat.premultiply(transform)\n mat.multiply(invertTrasform)\n\n })\n })\n }\n }\n } else if (atoms instanceof Float32Array) {\n\n const n4 = n * 4\n for (; i < n4; i += 4){\n\n atoms[ i ] = cd[ i ]\n atoms[ i + 1 ] = cd[ i + 1 ]\n atoms[ i + 2 ] = cd[ i + 2 ]\n\n }\n } else {\n Log.warn('transform: input type unknown')\n }\n\n return this.transformationMatrix\n }\n}\nexport default Superposition\n","/**\n * @file Trajectory Player\n * @author Alexander Rose \n * @private\n */\n\nimport { Signal } from 'signals'\n\nimport { defaults, createParams, updateParams } from '../utils'\nimport Trajectory from './trajectory'\n\nexport type TrajectoryPlayerInterpolateType = ''|'linear'|'spline'\nexport type TrajectoryPlayerMode = 'loop'|'once'\nexport type TrajectoryPlayerDirection = 'forward'|'backward'|'bounce'\n\nexport const TrajectoryPlayerDefaultParameters = {\n step: 1, // how many frames to advance when playing\n timeout: 50, // how many milliseconds to wait between playing frames\n start: 0, // first frame to play\n end: 0, // last frame to play\n interpolateType: '' as TrajectoryPlayerInterpolateType,\n interpolateStep: 5, // window size used for interpolation\n mode: 'loop' as TrajectoryPlayerMode,\n direction: 'forward' as TrajectoryPlayerDirection\n}\nexport type TrajectoryPlayerParameters = typeof TrajectoryPlayerDefaultParameters\n\nexport interface TrajectoryPlayerSignals {\n startedRunning: Signal\n haltedRunning: Signal\n}\n\n/**\n * Trajectory player for animating coordinate frames\n * @example\n * var player = new TrajectoryPlayer(trajectory, {step: 1, timeout: 50});\n * player.play();\n */\nclass TrajectoryPlayer {\n signals: TrajectoryPlayerSignals = {\n startedRunning: new Signal(),\n haltedRunning: new Signal()\n }\n\n parameters: TrajectoryPlayerParameters\n traj: Trajectory\n\n private _run = false\n private _previousTime = 0\n private _currentTime = 0\n private _currentStep = 1\n private _currentFrame: number|[number, number, number, number]\n private _direction: TrajectoryPlayerDirection\n\n /**\n * make trajectory player\n * @param {Trajectory} traj - the trajectory\n * @param {TrajectoryPlayerParameters} [params] - parameter object\n */\n constructor (traj: Trajectory, params: Partial = {}) {\n traj.signals.playerChanged.add((player: TrajectoryPlayer) => {\n if (player !== this) {\n this.pause()\n }\n }, this)\n\n const n = defaults(traj.frameCount, 1)\n\n this.traj = traj\n this.parameters = createParams(params, TrajectoryPlayerDefaultParameters)\n this.parameters.end = Math.min(defaults(params.end, n - 1), n - 1)\n this.parameters.step = defaults(params.step, Math.ceil((n + 1) / 100))\n\n this._currentFrame = this.parameters.start\n this._direction = this.parameters.direction === 'bounce' ? 'forward' : this.parameters.direction\n\n traj.signals.countChanged.add((n: number) => {\n this.parameters.end = Math.min(defaults(this.parameters.end, n - 1), n - 1)\n }, this)\n\n this._animate = this._animate.bind(this)\n }\n\n get isRunning () { return this._run }\n\n /**\n * set player parameters\n * @param {TrajectoryPlayerParameters} [params] - parameter object\n */\n setParameters (params: Partial = {}) {\n updateParams(this.parameters, params)\n\n if (params.direction !== undefined && this.parameters.direction !== 'bounce') {\n this._direction = this.parameters.direction\n }\n }\n\n _animate () {\n if (!this._run) return\n\n this._currentTime = window.performance.now()\n const dt = this._currentTime - this._previousTime\n const step = this.parameters.interpolateType ? this.parameters.interpolateStep : 1\n const timeout = this.parameters.timeout / step\n const traj = this.traj\n\n if (traj && traj.frameCount && !traj.inProgress && dt >= timeout) {\n if (this.parameters.interpolateType) {\n if (this._currentStep > this.parameters.interpolateStep) {\n this._currentStep = 1\n }\n if (this._currentStep === 1) {\n this._currentFrame = this._nextInterpolated()\n }\n if (traj.hasFrame(this._currentFrame)) {\n this._currentStep += 1\n const t = this._currentStep / (this.parameters.interpolateStep + 1)\n const [i, ip, ipp, ippp] = this._currentFrame as [number, number, number, number]\n traj.setFrameInterpolated(\n i, ip, ipp, ippp, t, this.parameters.interpolateType\n )\n this._previousTime = this._currentTime\n } else {\n traj.loadFrame(this._currentFrame)\n }\n } else {\n const i = this._next()\n if (traj.hasFrame(i)) {\n traj.setFrame(i)\n this._previousTime = this._currentTime\n } else {\n traj.loadFrame(i)\n }\n }\n }\n\n window.requestAnimationFrame(this._animate)\n }\n\n _next () {\n const p = this.parameters\n let i\n\n if (this._direction === 'forward') {\n i = this.traj.currentFrame + p.step\n } else {\n i = this.traj.currentFrame - p.step\n }\n\n if (i > p.end || i < p.start) {\n if (p.direction === 'bounce') {\n if (this._direction === 'forward') {\n this._direction = 'backward'\n } else {\n this._direction = 'forward'\n }\n }\n\n if (p.mode === 'once') {\n this.pause()\n\n if (p.direction === 'forward') {\n i = p.end\n } else if (p.direction === 'backward') {\n i = p.start\n } else {\n if (this._direction === 'forward') {\n i = p.start\n } else {\n i = p.end\n }\n }\n } else {\n if (this._direction === 'forward') {\n i = p.start\n if (p.interpolateType) {\n i = Math.min(p.end, i + p.step)\n }\n } else {\n i = p.end\n if (p.interpolateType) {\n i = Math.max(p.start, i - p.step)\n }\n }\n }\n }\n\n return i\n }\n\n _nextInterpolated () {\n const p = this.parameters\n const i = this._next()\n let ip, ipp, ippp\n\n if (this._direction === 'forward') {\n ip = Math.max(p.start, i - p.step)\n ipp = Math.max(p.start, i - 2 * p.step)\n ippp = Math.max(p.start, i - 3 * p.step)\n } else {\n ip = Math.min(p.end, i + p.step)\n ipp = Math.min(p.end, i + 2 * p.step)\n ippp = Math.min(p.end, i + 3 * p.step)\n }\n\n return [i, ip, ipp, ippp] as [number, number, number, number]\n }\n\n /**\n * toggle between playing and pausing the animation\n * @return {undefined}\n */\n toggle () {\n if (this._run) {\n this.pause()\n } else {\n this.play()\n }\n }\n\n /**\n * start the animation\n * @return {undefined}\n */\n play () {\n if (!this._run) {\n if (this.traj.player !== this) {\n this.traj.setPlayer(this)\n }\n this._currentStep = 1\n\n const p = this.parameters\n const frame = this.traj.currentFrame\n\n // snap to the grid implied by this.step division and multiplication\n // thus minimizing cache misses\n let i = Math.ceil(frame / p.step) * p.step\n // wrap when restarting from the limit (i.e. end or start)\n if (p.direction === 'forward' && frame >= p.end) {\n i = p.start\n } else if (p.direction === 'backward' && frame <= p.start) {\n i = p.end\n }\n\n this.traj.setFrame(i)\n\n this._run = true\n this._animate()\n this.signals.startedRunning.dispatch()\n }\n }\n\n /**\n * pause the animation\n * @return {undefined}\n */\n pause () {\n this._run = false\n this.signals.haltedRunning.dispatch()\n }\n\n /**\n * stop the animation (pause and go to start-frame)\n * @return {undefined}\n */\n stop () {\n this.pause()\n this.traj.setFrame(this.parameters.start)\n }\n}\n\nexport default TrajectoryPlayer\n","/**\n * @file Trajectory\n * @author Alexander Rose \n * @private\n */\n\nimport { Signal } from 'signals'\n\nimport { Log } from '../globals'\nimport { defaults } from '../utils'\nimport { NumberArray } from '../types'\nimport { circularMean, arrayMean } from '../math/array-utils'\nimport { lerp, spline } from '../math/math-utils'\nimport Selection from '../selection/selection'\nimport Superposition from '../align/superposition'\nimport Structure from '../structure/structure'\nimport AtomProxy from '../proxy/atom-proxy'\nimport TrajectoryPlayer, { TrajectoryPlayerInterpolateType } from './trajectory-player'\n\n\nfunction centerPbc (coords: NumberArray, mean: number[], box: ArrayLike) {\n if (box[ 0 ] === 0 || box[ 8 ] === 0 || box[ 4 ] === 0) {\n return\n }\n\n const n = coords.length\n\n const bx = box[ 0 ]\n const by = box[ 1 ]\n const bz = box[ 2 ]\n const mx = mean[ 0 ]\n const my = mean[ 1 ]\n const mz = mean[ 2 ]\n\n const fx = -mx + bx + bx / 2\n const fy = -my + by + by / 2\n const fz = -mz + bz + bz / 2\n\n for (let i = 0; i < n; i += 3) {\n coords[ i + 0 ] = (coords[ i + 0 ] + fx) % bx\n coords[ i + 1 ] = (coords[ i + 1 ] + fy) % by\n coords[ i + 2 ] = (coords[ i + 2 ] + fz) % bz\n }\n}\n\nfunction removePbc (x: NumberArray, box: ArrayLike) {\n if (box[ 0 ] === 0 || box[ 8 ] === 0 || box[ 4 ] === 0) {\n return\n }\n\n // ported from GROMACS src/gmxlib/rmpbc.c:rm_gropbc()\n // in-place\n\n const n = x.length\n\n for (let i = 3; i < n; i += 3) {\n for (let j = 0; j < 3; ++j) {\n const dist = x[ i + j ] - x[ i - 3 + j ]\n\n if (Math.abs(dist) > 0.9 * box[ j * 3 + j ]) {\n if (dist > 0) {\n for (let d = 0; d < 3; ++d) {\n x[ i + d ] -= box[ j * 3 + d ]\n }\n } else {\n for (let d = 0; d < 3; ++d) {\n x[ i + d ] += box[ j * 3 + d ]\n }\n }\n }\n }\n }\n\n return x\n}\n\nfunction removePeriodicity (x: NumberArray, box: ArrayLike, mean: number[]) {\n if (box[ 0 ] === 0 || box[ 8 ] === 0 || box[ 4 ] === 0) {\n return\n }\n\n const n = x.length\n for (let i = 3; i < n; i += 3) {\n for (let j = 0; j < 3; ++j) {\n const f = (x[ i + j ] - mean[ j ]) / box[ j * 3 + j ]\n if (Math.abs(f) > 0.5) {\n x[ i + j ] -= box[ j * 3 + j ] * Math.round(f)\n }\n }\n }\n\n return x\n}\n\nfunction circularMean3 (indices: NumberArray, coords: NumberArray, box: ArrayLike) {\n return [\n circularMean(coords, box[ 0 ], 3, 0, indices),\n circularMean(coords, box[ 1 ], 3, 1, indices),\n circularMean(coords, box[ 2 ], 3, 2, indices)\n ]\n}\n\nfunction arrayMean3 (coords: NumberArray) {\n return [\n arrayMean(coords, 3, 0),\n arrayMean(coords, 3, 1),\n arrayMean(coords, 3, 2)\n ]\n}\n\nfunction interpolateSpline (c: NumberArray, cp: NumberArray, cpp: NumberArray, cppp: NumberArray, t: number) {\n const m = c.length\n const coords = new Float32Array(m)\n\n for (let j0 = 0; j0 < m; j0 += 3) {\n const j1 = j0 + 1\n const j2 = j0 + 2\n coords[ j0 ] = spline(cppp[ j0 ], cpp[ j0 ], cp[ j0 ], c[ j0 ], t, 1)\n coords[ j1 ] = spline(cppp[ j1 ], cpp[ j1 ], cp[ j1 ], c[ j1 ], t, 1)\n coords[ j2 ] = spline(cppp[ j2 ], cpp[ j2 ], cp[ j2 ], c[ j2 ], t, 1)\n }\n\n return coords\n}\n\nfunction interpolateLerp (c: NumberArray, cp: NumberArray, t: number) {\n const m = c.length\n const coords = new Float32Array(m)\n\n for (let j0 = 0; j0 < m; j0 += 3) {\n const j1 = j0 + 1\n const j2 = j0 + 2\n coords[ j0 ] = lerp(cp[ j0 ], c[ j0 ], t)\n coords[ j1 ] = lerp(cp[ j1 ], c[ j1 ], t)\n coords[ j2 ] = lerp(cp[ j2 ], c[ j2 ], t)\n }\n\n return coords\n}\n\n/**\n * Trajectory parameter object.\n * @typedef {Object} TrajectoryParameters - parameters\n *\n * @property {Number} deltaTime - timestep between frames in picoseconds\n * @property {Number} timeOffset - starting time of frames in picoseconds\n * @property {String} sele - to restrict atoms used for superposition\n * @property {Boolean} centerPbc - center on initial frame\n * @property {Boolean} removePeriodicity - move atoms into the origin box\n * @property {Boolean} remo - try fixing periodic boundary discontinuities\n * @property {Boolean} superpose - superpose on initial frame\n */\n\n/**\n * @example\n * trajectory.signals.frameChanged.add( function(i){ ... } );\n *\n * @typedef {Object} TrajectorySignals\n * @property {Signal} countChanged - when the frame count is changed\n * @property {Signal} frameChanged - when the set frame is changed\n * @property {Signal} playerChanged - when the player is changed\n */\n\nexport interface TrajectoryParameters {\n deltaTime: number // timestep between frames in picoseconds\n timeOffset: number // starting time of frames in picoseconds\n sele: string // to restrict atoms used for superposition\n centerPbc: boolean // center on initial frame\n removePbc: boolean // move atoms into the origin box\n removePeriodicity: boolean // try fixing periodic boundary discontinuities\n superpose: boolean // superpose on initial frame\n}\n\nexport interface TrajectorySignals {\n countChanged: Signal\n frameChanged: Signal\n playerChanged: Signal\n}\n\n/**\n * Base class for trajectories, tying structures and coordinates together\n * @interface\n */\nclass Trajectory {\n signals: TrajectorySignals = {\n countChanged: new Signal(),\n frameChanged: new Signal(),\n playerChanged: new Signal()\n }\n\n deltaTime: number\n timeOffset: number\n sele: string\n centerPbc: boolean\n removePbc: boolean\n removePeriodicity: boolean\n superpose: boolean\n\n name: string\n frame: number\n trajPath: string\n\n initialCoords: Float32Array\n structureCoords: Float32Array\n selectionIndices: NumberArray\n backboneIndices: NumberArray\n\n coords1: Float32Array\n coords2: Float32Array\n\n frameCache: { [k: number]: Float32Array } = {}\n loadQueue: { [k: number]: boolean } = {}\n boxCache: { [k: number]: ArrayLike } = {}\n pathCache = {}\n frameCacheSize = 0\n\n atomCount: number\n inProgress: boolean\n\n selection: Selection // selection to restrict atoms used for superposition\n structure: Structure\n player: TrajectoryPlayer\n\n private _frameCount = 0\n private _currentFrame = -1\n private _disposed = false\n\n /**\n * @param {String} trajPath - trajectory source\n * @param {Structure} structure - the structure object\n * @param {TrajectoryParameters} params - trajectory parameters\n */\n constructor (trajPath: string, structure: Structure, params: Partial = {}) {\n this.deltaTime = defaults(params.deltaTime, 0)\n this.timeOffset = defaults(params.timeOffset, 0)\n this.centerPbc = defaults(params.centerPbc, false)\n this.removePbc = defaults(params.removePbc, false)\n this.removePeriodicity = defaults(params.removePeriodicity, false)\n this.superpose = defaults(params.superpose, false)\n\n this.name = trajPath.replace(/^.*[\\\\/]/, '')\n this.trajPath = trajPath\n\n this.selection = new Selection(\n defaults(params.sele, 'backbone and not hydrogen')\n )\n\n this.selection.signals.stringChanged.add(() => {\n this.selectionIndices = this.structure.getAtomIndices(this.selection)!\n this._resetCache()\n this._saveInitialCoords()\n this.setFrame(this._currentFrame)\n })\n }\n\n /**\n * Number of frames in the trajectory\n */\n get frameCount () {\n return this._frameCount\n }\n\n /**\n * Currently set frame of the trajectory\n */\n get currentFrame () {\n return this._currentFrame\n }\n\n _init (structure: Structure) {\n this.setStructure(structure)\n this._loadFrameCount()\n this.setPlayer(new TrajectoryPlayer(this))\n }\n\n _loadFrameCount () {}\n\n setStructure (structure: Structure) {\n this.structure = structure\n this.atomCount = structure.atomCount\n\n this.backboneIndices = this._getIndices(\n new Selection('backbone and not hydrogen')\n )\n this._makeAtomIndices()\n this._saveStructureCoords()\n\n this.selectionIndices = this._getIndices(this.selection)\n this._resetCache()\n this._saveInitialCoords()\n this.setFrame(this._currentFrame)\n }\n\n _saveInitialCoords () {\n if (this.structure.hasCoords()) {\n this.initialCoords = new Float32Array(this.structureCoords)\n this._makeSuperposeCoords()\n } else if (this.frameCache[0]) {\n this.initialCoords = new Float32Array(this.frameCache[0])\n this._makeSuperposeCoords()\n } else {\n this.loadFrame(0, () => this._saveInitialCoords())\n }\n }\n\n _saveStructureCoords () {\n const p = { what: { position: true } }\n this.structureCoords = this.structure.getAtomData(p).position!\n }\n\n setSelection (string: string) {\n this.selection.setString(string)\n return this\n }\n\n _getIndices (selection: Selection) {\n let i = 0\n const test = selection.test\n const indices: number[] = []\n\n if (test) {\n this.structure.eachAtom((ap: AtomProxy) => {\n if (test(ap)) indices.push(i)\n i += 1\n })\n }\n\n return indices\n }\n\n _makeSuperposeCoords () {\n const n = this.selectionIndices.length * 3\n\n this.coords1 = new Float32Array(n)\n this.coords2 = new Float32Array(n)\n\n const y = this.initialCoords\n const coords2 = this.coords2\n\n for (let i = 0; i < n; i += 3) {\n const j = this.selectionIndices[ i / 3 ] * 3\n\n coords2[ i + 0 ] = y[ j + 0 ]\n coords2[ i + 1 ] = y[ j + 1 ]\n coords2[ i + 2 ] = y[ j + 2 ]\n }\n }\n\n _makeAtomIndices () {\n Log.error('Trajectory._makeAtomIndices not implemented')\n }\n\n _resetCache () {\n this.frameCache = {}\n this.loadQueue = {}\n this.boxCache = {}\n this.pathCache = {}\n this.frameCacheSize = 0\n this.initialCoords = new Float32Array(0)\n }\n\n setParameters (params: Partial = {}) {\n let resetCache = false\n\n if (params.centerPbc !== undefined && params.centerPbc !== this.centerPbc) {\n this.centerPbc = params.centerPbc\n resetCache = true\n }\n\n if (params.removePeriodicity !== undefined && params.removePeriodicity !== this.removePeriodicity) {\n this.removePeriodicity = params.removePeriodicity\n resetCache = true\n }\n\n if (params.removePbc !== undefined && params.removePbc !== this.removePbc) {\n this.removePbc = params.removePbc\n resetCache = true\n }\n\n if (params.superpose !== undefined && params.superpose !== this.superpose) {\n this.superpose = params.superpose\n resetCache = true\n }\n\n this.deltaTime = defaults(params.deltaTime, this.deltaTime)\n this.timeOffset = defaults(params.timeOffset, this.timeOffset)\n\n if (resetCache) {\n this._resetCache()\n this.setFrame(this._currentFrame)\n }\n }\n\n /**\n * Check if a frame is available\n * @param {Integer|Integer[]} i - the frame index\n * @return {Boolean} frame availability\n */\n hasFrame (i: number|number[]) {\n if (Array.isArray(i)) {\n return i.every(j => !!this.frameCache[j])\n } else {\n return !!this.frameCache[i]\n }\n }\n\n /**\n * Set trajectory to a frame index\n * @param {Integer} i - the frame index\n * @param {Function} [callback] - fired when the frame has been set\n */\n setFrame (i: number, callback?: Function) {\n if (i === undefined) return this\n\n this.inProgress = true\n\n // i = parseInt(i) // TODO\n\n if (i === -1 || this.frameCache[ i ]) {\n this._updateStructure(i)\n if (callback) callback()\n } else {\n this.loadFrame(i, () => {\n this._updateStructure(i)\n if (callback) callback()\n })\n }\n\n return this\n }\n\n _interpolate (i: number, ip: number, ipp: number, ippp: number, t: number, type: TrajectoryPlayerInterpolateType) {\n const fc = this.frameCache\n\n let coords\n if (type === 'spline') {\n coords = interpolateSpline(fc[ i ], fc[ ip ], fc[ ipp ], fc[ ippp ], t)\n } else {\n coords = interpolateLerp(fc[ i ], fc[ ip ], t)\n }\n\n this.structure.updatePosition(coords)\n this._currentFrame = i\n this.signals.frameChanged.dispatch(i)\n }\n\n /**\n * Interpolated and set trajectory to frame indices\n * @param {Integer} i - the frame index\n * @param {Integer} ip - one before frame index\n * @param {Integer} ipp - two before frame index\n * @param {Integer} ippp - three before frame index\n * @param {Number} t - interpolation step [0,1]\n * @param {String} type - interpolation type, '', 'spline' or 'linear'\n * @param {Function} callback - fired when the frame has been set\n */\n setFrameInterpolated (i: number, ip: number, ipp: number, ippp: number, t: number, type: TrajectoryPlayerInterpolateType, callback?: Function) {\n if (i === undefined) return this\n\n const fc = this.frameCache\n const iList: number[] = []\n\n if (!fc[ ippp ]) iList.push(ippp)\n if (!fc[ ipp ]) iList.push(ipp)\n if (!fc[ ip ]) iList.push(ip)\n if (!fc[ i ]) iList.push(i)\n\n if (iList.length) {\n this.loadFrame(iList, () => {\n this._interpolate(i, ip, ipp, ippp, t, type)\n if (callback) callback()\n })\n } else {\n this._interpolate(i, ip, ipp, ippp, t, type)\n if (callback) callback()\n }\n\n return this\n }\n\n /**\n * Load frame index\n * @param {Integer|Integer[]} i - the frame index\n * @param {Function} callback - fired when the frame has been loaded\n */\n loadFrame (i: number|number[], callback?: Function) {\n if (Array.isArray(i)) {\n i.forEach(j => {\n if (!this.loadQueue[j] && !this.frameCache[j]) {\n this.loadQueue[j] = true\n this._loadFrame(j, () => {\n delete this.loadQueue[j]\n })\n }\n })\n } else {\n if (!this.loadQueue[i] && !this.frameCache[i]) {\n this.loadQueue[i] = true\n this._loadFrame(i, () => {\n delete this.loadQueue[i]\n if (callback) callback()\n })\n }\n }\n }\n\n /**\n * Load frame index\n * @abstract\n * @param {Integer} i - the frame index\n * @param {Function} callback - fired when the frame has been loaded\n */\n _loadFrame (i: number, callback?: Function) {\n Log.error('Trajectory._loadFrame not implemented', i, callback)\n }\n\n _updateStructure (i: number) {\n if (this._disposed) {\n console.error('updateStructure: traj disposed')\n return\n }\n\n if (i === -1) {\n if (this.structureCoords) {\n this.structure.updatePosition(this.structureCoords)\n }\n } else {\n this.structure.updatePosition(this.frameCache[ i ])\n }\n\n this.structure.trajectory = {\n name: this.trajPath,\n frame: i\n }\n\n this._currentFrame = i\n this.inProgress = false\n this.signals.frameChanged.dispatch(i)\n }\n\n _doSuperpose (x: Float32Array) {\n const n = this.selectionIndices.length * 3\n\n const coords1 = this.coords1\n const coords2 = this.coords2\n\n for (let i = 0; i < n; i += 3) {\n const j = this.selectionIndices[ i / 3 ] * 3\n\n coords1[ i + 0 ] = x[ j + 0 ]\n coords1[ i + 1 ] = x[ j + 1 ]\n coords1[ i + 2 ] = x[ j + 2 ]\n }\n\n // TODO re-use superposition object\n const sp = new Superposition(coords1, coords2)\n sp.transform(x)\n }\n\n _process (i: number, box: ArrayLike, coords: Float32Array, frameCount: number) {\n this._setFrameCount(frameCount)\n\n if (box) {\n if (this.backboneIndices.length > 0 && this.centerPbc) {\n const box2 = [ box[ 0 ], box[ 4 ], box[ 8 ] ]\n const circMean = circularMean3(this.backboneIndices, coords, box2)\n centerPbc(coords, circMean, box2)\n }\n\n if (this.removePeriodicity) {\n const mean = arrayMean3(coords)\n removePeriodicity(coords, box, mean)\n }\n\n if (this.removePbc) {\n removePbc(coords, box)\n }\n }\n\n if (this.selectionIndices.length > 0 && this.coords1 && this.superpose) {\n this._doSuperpose(coords)\n }\n\n this.frameCache[ i ] = coords\n this.boxCache[ i ] = box\n this.frameCacheSize += 1\n }\n\n _setFrameCount (n: number) {\n if (n !== this._frameCount) {\n this._frameCount = n\n this.signals.countChanged.dispatch(n)\n }\n }\n\n /**\n * Dispose of the trajectory object\n * @return {undefined}\n */\n dispose () {\n this._resetCache() // aid GC\n this._disposed = true\n if (this.player) this.player.stop()\n }\n\n /**\n * Set player for this trajectory\n * @param {TrajectoryPlayer} player - the player\n */\n setPlayer (player: TrajectoryPlayer) {\n this.player = player\n this.signals.playerChanged.dispatch(player)\n }\n\n /**\n * Get time for frame\n * @param {Integer} i - frame index\n * @return {Number} time in picoseconds\n */\n getFrameTime (i: number) {\n return this.timeOffset + i * this.deltaTime\n }\n}\n\nexport default Trajectory\n","/**\n * @file Frames Trajectory\n * @author Alexander Rose \n * @private\n */\n\nimport { defaults } from '../utils'\nimport Structure from '../structure/structure'\nimport Frames from './frames'\nimport Trajectory, { TrajectoryParameters } from './trajectory'\n\n/**\n * Frames trajectory class. Gets data from a frames object.\n */\nclass FramesTrajectory extends Trajectory {\n path: string\n\n frames: ArrayLike[]\n boxes: ArrayLike[]\n\n atomIndices?: ArrayLike\n\n constructor (frames: Frames, structure: Structure, params: TrajectoryParameters) {\n const p = params || {}\n p.timeOffset = defaults(p.timeOffset, frames.timeOffset)\n p.deltaTime = defaults(p.deltaTime, frames.deltaTime)\n\n super('', structure, p)\n\n this.name = frames.name\n this.path = frames.path\n\n this.frames = frames.coordinates\n this.boxes = frames.boxes\n\n this._init(structure)\n }\n\n get type () { return 'frames' }\n\n _makeAtomIndices () {\n if (this.structure.type === 'StructureView') {\n this.atomIndices = this.structure.getAtomIndices()\n } else {\n this.atomIndices = undefined\n }\n }\n\n _loadFrame (i: number, callback?: Function) {\n let coords\n const frame = this.frames[ i ]\n\n if (this.atomIndices) {\n const indices = this.atomIndices\n const m = indices.length\n\n coords = new Float32Array(m * 3)\n\n for (let j = 0; j < m; ++j) {\n const j3 = j * 3\n const idx3 = indices[ j ] * 3\n\n coords[ j3 + 0 ] = frame[ idx3 + 0 ]\n coords[ j3 + 1 ] = frame[ idx3 + 1 ]\n coords[ j3 + 2 ] = frame[ idx3 + 2 ]\n }\n } else {\n coords = new Float32Array(frame)\n }\n\n const box = this.boxes[ i ]\n const frameCount = this.frames.length\n\n this._process(i, box, coords, frameCount)\n\n if (typeof callback === 'function') {\n callback()\n }\n }\n\n _loadFrameCount () {\n if (this.frames) {\n this._setFrameCount(this.frames.length)\n }\n }\n}\n\nexport default FramesTrajectory\n","/**\n * @file Structure Trajectory\n * @author Alexander Rose \n * @private\n */\n\nimport Structure from '../structure/structure'\nimport Trajectory, { TrajectoryParameters } from './trajectory'\n\n/**\n * Structure trajectory class. Gets data from a structure object.\n */\nclass StructureTrajectory extends Trajectory {\n atomIndices?: ArrayLike\n\n constructor (trajPath: string, structure: Structure, params: TrajectoryParameters) {\n super('', structure, params)\n this._init(structure)\n }\n\n get type () { return 'structure' }\n\n _makeAtomIndices () {\n if (this.structure.atomSet && this.structure.atomSet.getSize() < this.structure.atomStore.count) {\n this.atomIndices = this.structure.getAtomIndices()\n } else {\n this.atomIndices = undefined\n }\n }\n\n _loadFrame (i: number, callback?: Function) {\n let coords\n const structure = this.structure\n const frame = structure.frames[ i ]\n\n if (this.atomIndices) {\n const indices = this.atomIndices\n const m = indices.length\n\n coords = new Float32Array(m * 3)\n\n for (let j = 0; j < m; ++j) {\n const j3 = j * 3\n const idx3 = indices[ j ] * 3\n\n coords[ j3 + 0 ] = frame[ idx3 + 0 ]\n coords[ j3 + 1 ] = frame[ idx3 + 1 ]\n coords[ j3 + 2 ] = frame[ idx3 + 2 ]\n }\n } else {\n coords = new Float32Array(frame)\n }\n\n const box = structure.boxes[ i ]\n const frameCount = structure.frames.length\n\n this._process(i, box, coords, frameCount)\n\n if (typeof callback === 'function') {\n callback()\n }\n }\n\n _loadFrameCount () {\n this._setFrameCount(this.structure.frames.length)\n }\n}\n\nexport default StructureTrajectory\n","/**\n * @file Remote Trajectory\n * @author Alexander Rose \n * @private\n */\n\nimport { Log, TrajectoryDatasource } from '../globals'\nimport Structure from '../structure/structure'\nimport Trajectory, { TrajectoryParameters } from './trajectory'\n\n/**\n * Remote trajectory class. Gets data from an MDsrv instance.\n */\nclass RemoteTrajectory extends Trajectory {\n atomIndices: number[][]\n\n constructor (trajPath: string, structure: Structure, params: TrajectoryParameters) {\n super(trajPath, structure, params)\n this._init(structure)\n }\n\n get type () { return 'remote' }\n\n _makeAtomIndices () {\n const atomIndices = []\n\n if (this.structure.type === 'StructureView') {\n const indices = this.structure.getAtomIndices()! // TODO\n const n = indices.length\n\n let p = indices[ 0 ]\n let q = indices[ 0 ]\n\n for (let i = 1; i < n; ++i) {\n const r = indices[ i ]\n\n if (q + 1 < r) {\n atomIndices.push([ p, q + 1 ])\n p = r\n }\n\n q = r\n }\n\n atomIndices.push([ p, q + 1 ])\n } else {\n atomIndices.push([ 0, this.atomCount ])\n }\n\n this.atomIndices = atomIndices\n }\n\n _loadFrame (i: number, callback?: Function) {\n // TODO implement max frameCache size, re-use arrays\n\n const request = new XMLHttpRequest()\n\n const url = TrajectoryDatasource.getFrameUrl(this.trajPath, i)\n const params = TrajectoryDatasource.getFrameParams(this.trajPath, this.atomIndices)\n\n request.open('POST', url, true)\n request.responseType = 'arraybuffer'\n request.setRequestHeader(\n 'Content-type', 'application/x-www-form-urlencoded'\n )\n\n request.addEventListener('load', () => {\n const arrayBuffer = request.response\n if (!arrayBuffer) {\n Log.error(`empty arrayBuffer for '${url}'`)\n return\n }\n\n const frameCount = new Int32Array(arrayBuffer, 0, 1)[ 0 ]\n // const time = new Float32Array( arrayBuffer, 1 * 4, 1 )[ 0 ];\n const box = new Float32Array(arrayBuffer, 2 * 4, 9)\n const coords = new Float32Array(arrayBuffer, 11 * 4)\n\n this._process(i, box, coords, frameCount)\n if (typeof callback === 'function') {\n callback()\n }\n }, false)\n\n request.send(params)\n }\n\n _loadFrameCount () {\n const request = new XMLHttpRequest()\n\n const url = TrajectoryDatasource.getCountUrl(this.trajPath)\n\n request.open('GET', url, true)\n request.addEventListener('load', () => {\n this._setFrameCount(parseInt(request.response))\n }, false)\n request.send()\n }\n}\n\nexport default RemoteTrajectory\n","/**\n * @file Callback Trajectory\n * @author Tarn W. Burton \n * @private\n */\n\nimport Structure from '../structure/structure'\nimport Trajectory, { TrajectoryParameters } from './trajectory'\n\ntype RequestCallback = (responseCallback: Function, i?: number, atomIndices?: number[][]) => void\n\n/**\n * Callback trajectory class. Gets data from an JavaScript function.\n */\nclass CallbackTrajectory extends Trajectory {\n atomIndices: number[][]\n requestCallback: RequestCallback\n\n constructor (requestCallback: RequestCallback, structure: Structure, params: TrajectoryParameters) {\n super('', structure, params)\n this.requestCallback = requestCallback;\n this._init(structure)\n }\n\n get type () { return 'callback' }\n\n _makeAtomIndices () {\n const atomIndices = []\n\n if (this.structure.type === 'StructureView') {\n const indices = this.structure.getAtomIndices()! // TODO\n const n = indices.length\n\n let p = indices[ 0 ]\n let q = indices[ 0 ]\n\n for (let i = 1; i < n; ++i) {\n const r = indices[ i ]\n\n if (q + 1 < r) {\n atomIndices.push([ p, q + 1 ])\n p = r\n }\n\n q = r\n }\n\n atomIndices.push([ p, q + 1 ])\n } else {\n atomIndices.push([ 0, this.atomCount ])\n }\n\n this.atomIndices = atomIndices\n }\n\n _loadFrame (i: number, callback?: Function) {\n this.requestCallback(\n (i: number, box: ArrayLike, coords: Float32Array, frameCount: number) => {\n this._process(i, box, coords, frameCount)\n if (typeof callback === 'function') {\n callback()\n }\n }, i, this.atomIndices)\n }\n\n _loadFrameCount () {\n this.requestCallback((count: number) => this._setFrameCount(count))\n }\n}\n\nexport default CallbackTrajectory\n\n","/**\n * @file Structure View\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3, Box3 } from 'three'\n\nimport { Debug, Log } from '../globals'\nimport Structure from './structure'\nimport Selection from '../selection/selection'\nimport BitArray from '../utils/bitarray'\n\nimport BondProxy from '../proxy/bond-proxy'\nimport AtomProxy from '../proxy/atom-proxy'\nimport ResidueProxy from '../proxy/residue-proxy'\nimport ChainProxy from '../proxy/chain-proxy'\nimport ModelProxy from '../proxy/model-proxy'\nimport SpatialHash from '../geometry/spatial-hash';\nimport BondHash from '../store/bond-hash';\nimport ResidueMap from '../store/residue-map';\nimport AtomMap from '../store/atom-map';\nimport ModelStore from '../store/model-store';\nimport ChainStore from '../store/chain-store';\nimport ResidueStore from '../store/residue-store';\nimport AtomStore from '../store/atom-store';\nimport BondStore from '../store/bond-store';\nimport Validation from './validation';\nimport Unitcell from '../symmetry/unitcell';\nimport Entity from './entity';\nimport Assembly from '../symmetry/assembly';\nimport { Data } from './data';\n\n/**\n * Get view on structure restricted to the selection\n * @param {Selection} selection - the selection\n * @return {StructureView} the view on the structure\n */\nStructure.prototype.getView = function (this: Structure, selection: Selection) {\n // added here to avoid cyclic import dependency\n return new StructureView(this, selection)\n}\n\n/**\n * View on the structure, restricted to the selection\n */\nclass StructureView extends Structure {\n structure: Structure\n selection: Selection\n\n /**\n * @param {Structure} structure - the structure\n * @param {Selection} selection - the selection\n */\n constructor (structure: Structure, selection: Selection) {\n super()\n\n this.structure = structure\n this.selection = selection\n\n this.center = new Vector3()\n this.boundingBox = new Box3()\n\n this._bp = this.getBondProxy()\n this._ap = this.getAtomProxy()\n this._rp = this.getResidueProxy()\n this._cp = this.getChainProxy()\n\n if (this.selection) {\n this.selection.signals.stringChanged.add(this.refresh, this)\n }\n\n this.structure.signals.refreshed.add(this.refresh, this)\n\n this.refresh()\n }\n\n init () {}\n\n get type () { return 'StructureView' }\n\n get name () { return this.structure.name }\n get path () { return this.structure.path }\n get title () { return this.structure.title }\n get id () { return this.structure.id }\n get data (): Data { return this.structure.data }\n get atomSetDict () { return this.structure.atomSetDict }\n get biomolDict (): {[k: string]: Assembly} { return this.structure.biomolDict }\n get entityList (): Entity[] { return this.structure.entityList }\n get unitcell (): Unitcell|undefined { return this.structure.unitcell }\n get frames () { return this.structure.frames }\n get boxes () { return this.structure.boxes }\n get validation (): Validation|undefined { return this.structure.validation }\n get bondStore () { return this.structure.bondStore }\n get backboneBondStore () { return this.structure.backboneBondStore }\n get rungBondStore (): BondStore { return this.structure.rungBondStore }\n get atomStore (): AtomStore { return this.structure.atomStore }\n get residueStore (): ResidueStore { return this.structure.residueStore }\n get chainStore (): ChainStore { return this.structure.chainStore }\n get modelStore (): ModelStore { return this.structure.modelStore }\n get atomMap (): AtomMap { return this.structure.atomMap }\n get residueMap (): ResidueMap { return this.structure.residueMap }\n get bondHash (): BondHash|undefined { return this.structure.bondHash }\n get spatialHash (): SpatialHash|undefined { return this.structure.spatialHash }\n\n get _hasCoords () { return this.structure._hasCoords }\n set _hasCoords (value) { this.structure._hasCoords = value }\n\n /**\n * Updates atomSet, bondSet, atomSetCache, atomCount, bondCount, boundingBox, center.\n * @emits {Structure.signals.refreshed} when refreshed\n * @return {undefined}\n */\n refresh () {\n if (Debug) Log.time('StructureView.refresh')\n\n this.atomSetCache = {}\n const structure = this.structure\n\n if (this.selection.isAllSelection() &&\n structure !== this && structure.atomSet && structure.bondSet\n ) {\n this.atomSet = structure.atomSet.clone()\n this.bondSet = structure.bondSet.clone()\n\n for (let name in this.atomSetDict) {\n const atomSet = this.atomSetDict[ name ]\n this.atomSetCache[ '__' + name ] = atomSet.clone()\n }\n\n this.atomCount = structure.atomCount\n this.bondCount = structure.bondCount\n\n this.boundingBox.copy(structure.boundingBox)\n this.center.copy(structure.center)\n } else if (this.selection.isNoneSelection() &&\n structure !== this && structure.atomSet && structure.bondSet\n ) {\n this.atomSet = new BitArray(structure.atomCount)\n this.bondSet = new BitArray(structure.bondCount)\n\n for (let name in this.atomSetDict) {\n this.atomSetCache[ '__' + name ] = new BitArray(structure.atomCount)\n }\n\n this.atomCount = 0\n this.bondCount = 0\n\n this.boundingBox.makeEmpty()\n this.center.set(0, 0, 0)\n } else {\n this.atomSet = this.getAtomSet(this.selection, true)\n if (structure.atomSet) {\n this.atomSet = this.atomSet.intersection(structure.atomSet)\n }\n\n this.bondSet = this.getBondSet()\n\n for (let name in this.atomSetDict) {\n const atomSet = this.atomSetDict[ name ]\n this.atomSetCache[ '__' + name ] = atomSet.makeIntersection(this.atomSet)\n }\n\n this.atomCount = this.atomSet.getSize()\n this.bondCount = this.bondSet.getSize()\n\n this.boundingBox = this.getBoundingBox()\n this.center = this.boundingBox.getCenter(new Vector3())\n }\n\n if (Debug) Log.timeEnd('StructureView.refresh')\n\n this.signals.refreshed.dispatch()\n }\n\n //\n\n setSelection (selection: Selection) {\n this.selection = selection\n\n this.refresh()\n }\n\n getSelection (selection?: Selection) {\n const seleList: string[] = []\n\n if (selection && selection.string) {\n seleList.push(selection.string)\n }\n\n const parentSelection = this.structure.getSelection()\n if (parentSelection && parentSelection.string) {\n seleList.push(parentSelection.string)\n }\n\n if (this.selection && this.selection.string) {\n seleList.push(this.selection.string)\n }\n\n let sele = ''\n if (seleList.length > 0) {\n sele = `( ${seleList.join(' ) AND ( ')} )`\n }\n\n return new Selection(sele)\n }\n\n getStructure () {\n return this.structure.getStructure()\n }\n\n //\n\n eachBond (callback: (entity: BondProxy) => any, selection?: Selection) {\n this.structure.eachBond(callback, this.getSelection(selection))\n }\n\n eachAtom (callback: (entity: AtomProxy) => any, selection?: Selection) {\n const ap = this.getAtomProxy()\n const atomSet = this.getAtomSet(selection)\n const n = this.atomStore.count\n\n if (atomSet.getSize() < n) {\n atomSet.forEach(function (index) {\n ap.index = index\n callback(ap)\n })\n } else {\n for (let i = 0; i < n; ++i) {\n ap.index = i\n callback(ap)\n }\n }\n }\n\n eachResidue (callback: (entity: ResidueProxy) => any, selection?: Selection) {\n this.structure.eachResidue(callback, this.getSelection(selection))\n }\n\n /**\n * Not implemented\n * @alias StructureView#eachResidueN\n * @return {undefined}\n */\n eachResidueN (n: number, callback: (entity: ResidueProxy) => any) {\n console.error('StructureView.eachResidueN() not implemented')\n }\n\n eachChain (callback: (entity: ChainProxy) => any, selection?: Selection) {\n this.structure.eachChain(callback, this.getSelection(selection))\n }\n\n eachModel (callback: (entity: ModelProxy) => any, selection?: Selection) {\n this.structure.eachModel(callback, this.getSelection(selection))\n }\n\n //\n\n getAtomSet (selection?: boolean|Selection|BitArray, ignoreView = false) {\n let atomSet = this.structure.getAtomSet(selection)\n if (!ignoreView && this.atomSet) {\n atomSet = atomSet.makeIntersection(this.atomSet)\n }\n\n return atomSet\n }\n\n //\n\n getAtomIndices (selection?: Selection) {\n return this.structure.getAtomIndices(this.getSelection(selection))\n }\n\n refreshPosition () {\n return this.structure.refreshPosition()\n }\n\n //\n\n dispose () {\n if (this.selection) {\n this.selection.signals.stringChanged.remove(this.refresh, this)\n }\n\n this.structure.signals.refreshed.remove(this.refresh, this)\n\n this.structure = new Structure() // delete old data\n\n delete this.atomSet\n delete this.bondSet\n\n }\n}\n\nexport default StructureView\n","/**\n * @file Alignment\n * @author Alexander Rose \n * @private\n */\n\nimport { Debug, Log } from '../globals'\n\n// const nucleotides = 'ACTG';\nconst aminoacidsX = 'ACDEFGHIKLMNPQRSTVWY'\nconst aminoacids = 'ARNDCQEGHILKMFPSTWYVBZ?'\n\nconst blosum62x = [\n [4, 0, -2, -1, -2, 0, -2, -1, -1, -1, -1, -2, -1, -1, -1, 1, 0, 0, -3, -2], // A\n [0, 9, -3, -4, -2, -3, -3, -1, -3, -1, -1, -3, -3, -3, -3, -1, -1, -1, -2, -2], // C\n [-2, -3, 6, 2, -3, -1, -1, -3, -1, -4, -3, 1, -1, 0, -2, 0, -1, -3, -4, -3], // D\n [-1, -4, 2, 5, -3, -2, 0, -3, 1, -3, -2, 0, -1, 2, 0, 0, -1, -2, -3, -2], // E\n [-2, -2, -3, -3, 6, -3, -1, 0, -3, 0, 0, -3, -4, -3, -3, -2, -2, -1, 1, 3], // F\n [0, -3, -1, -2, -3, 6, -2, -4, -2, -4, -3, 0, -2, -2, -2, 0, -2, -3, -2, -3], // G\n [-2, -3, -1, 0, -1, -2, 8, -3, -1, -3, -2, 1, -2, 0, 0, -1, -2, -3, -2, 2], // H\n [-1, -1, -3, -3, 0, -4, -3, 4, -3, 2, 1, -3, -3, -3, -3, -2, -1, 3, -3, -1], // I\n [-1, -3, -1, 1, -3, -2, -1, -3, 5, -2, -1, 0, -1, 1, 2, 0, -1, -2, -3, -2], // K\n [-1, -1, -4, -3, 0, -4, -3, 2, -2, 4, 2, -3, -3, -2, -2, -2, -1, 1, -2, -1], // L\n [-1, -1, -3, -2, 0, -3, -2, 1, -1, 2, 5, -2, -2, 0, -1, -1, -1, 1, -1, -1], // M\n [-2, -3, 1, 0, -3, 0, 1, -3, 0, -3, -2, 6, -2, 0, 0, 1, 0, -3, -4, -2], // N\n [-1, -3, -1, -1, -4, -2, -2, -3, -1, -3, -2, -2, 7, -1, -2, -1, -1, -2, -4, -3], // P\n [-1, -3, 0, 2, -3, -2, 0, -3, 1, -2, 0, 0, -1, 5, 1, 0, -1, -2, -2, -1], // Q\n [-1, -3, -2, 0, -3, -2, 0, -3, 2, -2, -1, 0, -2, 1, 5, -1, -1, -3, -3, -2], // R\n [1, -1, 0, 0, -2, 0, -1, -2, 0, -2, -1, 1, -1, 0, -1, 4, 1, -2, -3, -2], // S\n [0, -1, -1, -1, -2, -2, -2, -1, -1, -1, -1, 0, -1, -1, -1, 1, 5, 0, -2, -2], // T\n [0, -1, -3, -2, -1, -3, -3, 3, -2, 1, 1, -3, -2, -2, -3, -2, 0, 4, -3, -1], // V\n [-3, -2, -4, -3, 1, -2, -2, -3, -3, -2, -1, -4, -4, -2, -3, -3, -2, -3, 11, 2], // W\n [-2, -2, -3, -2, 3, -3, 2, -1, -2, -1, -1, -2, -3, -1, -2, -2, -2, -1, 2, 7] // Y\n]\n\nconst blosum62 = [\n // A R N D C Q E G H I L K M F P S T W Y V B Z X\n [4, -1, -2, -2, 0, -1, -1, 0, -2, -1, -1, -1, -1, -2, -1, 1, 0, -3, -2, 0, -2, -1, 0], // A\n [-1, 5, 0, -2, -3, 1, 0, -2, 0, -3, -2, 2, -1, -3, -2, -1, -1, -3, -2, -3, -1, 0, -1], // R\n [-2, 0, 6, 1, -3, 0, 0, 0, 1, -3, -3, 0, -2, -3, -2, 1, 0, -4, -2, -3, 3, 0, -1], // N\n [-2, -2, 1, 6, -3, 0, 2, -1, -1, -3, -4, -1, -3, -3, -1, 0, -1, -4, -3, -3, 4, 1, -1], // D\n [0, -3, -3, -3, 9, -3, -4, -3, -3, -1, -1, -3, -1, -2, -3, -1, -1, -2, -2, -1, -3, -3, -2], // C\n [-1, 1, 0, 0, -3, 5, 2, -2, 0, -3, -2, 1, 0, -3, -1, 0, -1, -2, -1, -2, 0, 3, -1], // Q\n [-1, 0, 0, 2, -4, 2, 5, -2, 0, -3, -3, 1, -2, -3, -1, 0, -1, -3, -2, -2, 1, 4, -1], // E\n [0, -2, 0, -1, -3, -2, -2, 6, -2, -4, -4, -2, -3, -3, -2, 0, -2, -2, -3, -3, -1, -2, -1], // G\n [-2, 0, 1, -1, -3, 0, 0, -2, 8, -3, -3, -1, -2, -1, -2, -1, -2, -2, 2, -3, 0, 0, -1], // H\n [-1, -3, -3, -3, -1, -3, -3, -4, -3, 4, 2, -3, 1, 0, -3, -2, -1, -3, -1, 3, -3, -3, -1], // I\n [-1, -2, -3, -4, -1, -2, -3, -4, -3, 2, 4, -2, 2, 0, -3, -2, -1, -2, -1, 1, -4, -3, -1], // L\n [-1, 2, 0, -1, -3, 1, 1, -2, -1, -3, -2, 5, -1, -3, -1, 0, -1, -3, -2, -2, 0, 1, -1], // K\n [-1, -1, -2, -3, -1, 0, -2, -3, -2, 1, 2, -1, 5, 0, -2, -1, -1, -1, -1, 1, -3, -1, -1], // M\n [-2, -3, -3, -3, -2, -3, -3, -3, -1, 0, 0, -3, 0, 6, -4, -2, -2, 1, 3, -1, -3, -3, -1], // F\n [-1, -2, -2, -1, -3, -1, -1, -2, -2, -3, -3, -1, -2, -4, 7, -1, -1, -4, -3, -2, -2, -1, -2], // P\n [1, -1, 1, 0, -1, 0, 0, 0, -1, -2, -2, 0, -1, -2, -1, 4, 1, -3, -2, -2, 0, 0, 0], // S\n [0, -1, 0, -1, -1, -1, -1, -2, -2, -1, -1, -1, -1, -2, -1, 1, 5, -2, -2, 0, -1, -1, 0], // T\n [-3, -3, -4, -4, -2, -2, -3, -2, -2, -3, -2, -3, -1, 1, -4, -3, -2, 11, 2, -3, -4, -3, -2], // W\n [-2, -2, -2, -3, -2, -1, -2, -3, 2, -1, -1, -2, -1, 3, -3, -2, -2, 2, 7, -1, -3, -2, -1], // Y\n [0, -3, -3, -3, -1, -2, -2, -3, -3, 3, 1, -2, 1, -1, -2, -2, 0, -3, -1, 4, -3, -2, -1], // V\n [-2, -1, 3, 4, -3, 0, 1, -1, 0, -3, -4, 0, -3, -3, -2, 0, -1, -4, -3, -3, 4, 1, -1], // B\n [-1, 0, 0, 1, -3, 3, 4, -2, 0, -3, -3, 1, -1, -3, -1, 0, -1, -3, -2, -2, 1, 4, -1], // Z\n [0, -1, -1, -1, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, 0, 0, -2, -1, -1, -1, -1, -1] // X\n]\n\nfunction prepareMatrix (cellNames: string, mat: number[][]) {\n let j: number\n let i = 0\n const matDict: { [k: string]: { [k: string]: number } } = {}\n mat.forEach(function (row) {\n j = 0\n const rowDict: { [k: string]: number } = {}\n row.forEach(function (elm) {\n rowDict[ cellNames[ j++ ] ] = elm\n })\n matDict[ cellNames[ i++ ] ] = rowDict\n })\n return matDict\n}\n\nconst SubstitutionMatrices = (function () {\n return {\n blosum62: prepareMatrix(aminoacids, blosum62),\n blosum62x: prepareMatrix(aminoacidsX, blosum62x)\n }\n}())\nexport type SubstitutionMatrix = ''|'blosum62'|'blosum62x'\n\nclass Alignment {\n substMatrix: { [k: string]: { [k: string]: number } }\n\n n: number\n m: number\n score?: number\n ali: string\n\n S: number[][]\n V: number[][]\n H: number[][]\n\n ali1: string\n ali2: string\n\n constructor (readonly seq1: string, readonly seq2: string, readonly gapPenalty = -10, readonly gapExtensionPenalty = -1, substMatrix: SubstitutionMatrix = 'blosum62') {\n // TODO try encoding seqs as integers and use array subst matrix, maybe faster\n\n if (substMatrix) {\n this.substMatrix = SubstitutionMatrices[ substMatrix ]\n }\n }\n\n initMatrices () {\n this.n = this.seq1.length\n this.m = this.seq2.length\n\n // Log.log(this.n, this.m);\n\n this.score = undefined\n this.ali = ''\n\n this.S = []\n this.V = []\n this.H = []\n\n for (let i = 0; i <= this.n; ++i) {\n this.S[ i ] = []\n this.V[ i ] = []\n this.H[ i ] = []\n\n for (let j = 0; j <= this.m; ++j) {\n this.S[ i ][ j ] = 0\n this.V[ i ][ j ] = 0\n this.H[ i ][ j ] = 0\n }\n }\n\n for (let i = 0; i <= this.n; ++i) {\n this.S[ i ][ 0 ] = this.gap(0)\n this.H[ i ][ 0 ] = -Infinity\n }\n\n for (let j = 0; j <= this.m; ++j) {\n this.S[ 0 ][ j ] = this.gap(0)\n this.V[ 0 ][ j ] = -Infinity\n }\n\n this.S[ 0 ][ 0 ] = 0\n\n // Log.log(this.S, this.V, this.H);\n }\n\n gap (len: number) {\n return this.gapPenalty + len * this.gapExtensionPenalty\n }\n\n makeScoreFn () {\n const seq1 = this.seq1\n const seq2 = this.seq2\n\n const substMatrix = this.substMatrix\n\n if (substMatrix) {\n return function score (i: number, j: number) {\n const c1 = seq1[ i ]\n const c2 = seq2[ j ]\n\n try {\n return substMatrix[ c1 ][ c2 ]\n } catch (e) {\n return -4\n }\n }\n } else {\n Log.warn('Alignment: no subst matrix')\n\n return function scoreNoSubstMat (i: number, j: number) {\n const c1 = seq1[ i ]\n const c2 = seq2[ j ]\n\n return c1 === c2 ? 5 : -3\n }\n }\n }\n\n calc () {\n if (Debug) Log.time('Alignment.calc')\n\n this.initMatrices()\n\n const gap0 = this.gap(0)\n const scoreFn = this.makeScoreFn()\n const gapExtensionPenalty = this.gapExtensionPenalty\n\n const V = this.V\n const H = this.H\n const S = this.S\n\n const n = this.n\n const m = this.m\n\n let Vi1, Si1, Vi, Hi, Si\n\n for (let i = 1; i <= n; ++i) {\n Si1 = S[ i - 1 ]\n Vi1 = V[ i - 1 ]\n\n Vi = V[ i ]\n Hi = H[ i ]\n Si = S[ i ]\n\n for (let j = 1; j <= m; ++j) {\n Vi[j] = Math.max(\n Si1[ j ] + gap0,\n Vi1[ j ] + gapExtensionPenalty\n )\n\n Hi[j] = Math.max(\n Si[ j - 1 ] + gap0,\n Hi[ j - 1 ] + gapExtensionPenalty\n )\n\n Si[j] = Math.max(\n Si1[ j - 1 ] + scoreFn(i - 1, j - 1), // match\n Vi[ j ], // del\n Hi[ j ] // ins\n )\n }\n }\n\n if (Debug) Log.timeEnd('Alignment.calc')\n\n if (Debug) Log.log(this.S, this.V, this.H)\n }\n\n trace () {\n if (Debug) Log.time('Alignment.trace')\n\n this.ali1 = ''\n this.ali2 = ''\n\n const scoreFn = this.makeScoreFn()\n\n let i = this.n\n let j = this.m\n let mat\n\n if (this.S[i][j] >= this.V[i][j]) {\n mat = 'S'\n this.score = this.S[i][j]\n } else if (this.V[i][j] >= this.H[i][j]) {\n mat = 'V'\n this.score = this.V[i][j]\n } else {\n mat = 'H'\n this.score = this.H[i][j]\n }\n\n if (Debug) Log.log('Alignment: SCORE', this.score)\n if (Debug) Log.log('Alignment: S, V, H', this.S[i][j], this.V[i][j], this.H[i][j])\n\n while (i > 0 && j > 0) {\n if (mat === 'S') {\n if (this.S[i][j] === this.S[i - 1][j - 1] + scoreFn(i - 1, j - 1)) {\n this.ali1 = this.seq1[i - 1] + this.ali1\n this.ali2 = this.seq2[j - 1] + this.ali2\n --i\n --j\n mat = 'S'\n } else if (this.S[i][j] === this.V[i][j]) {\n mat = 'V'\n } else if (this.S[i][j] === this.H[i][j]) {\n mat = 'H'\n } else {\n // Log.debug('Alignment: S');\n --i\n --j\n }\n } else if (mat === 'V') {\n if (this.V[i][j] === this.V[i - 1][j] + this.gapExtensionPenalty) {\n this.ali1 = this.seq1[i - 1] + this.ali1\n this.ali2 = '-' + this.ali2\n --i\n mat = 'V'\n } else if (this.V[i][j] === this.S[i - 1][j] + this.gap(0)) {\n this.ali1 = this.seq1[i - 1] + this.ali1\n this.ali2 = '-' + this.ali2\n --i\n mat = 'S'\n } else {\n // Log.debug('Alignment: V');\n --i\n }\n } else if (mat === 'H') {\n if (this.H[i][j] === this.H[i][j - 1] + this.gapExtensionPenalty) {\n this.ali1 = '-' + this.ali1\n this.ali2 = this.seq2[j - 1] + this.ali2\n --j\n mat = 'H'\n } else if (this.H[i][j] === this.S[i][j - 1] + this.gap(0)) {\n this.ali1 = '-' + this.ali1\n this.ali2 = this.seq2[j - 1] + this.ali2\n --j\n mat = 'S'\n } else {\n // Log.debug('Alignment: H');\n --j\n }\n } else {\n Log.error('Alignment: no matrix')\n }\n }\n\n while (i > 0) {\n this.ali1 = this.seq1[ i - 1 ] + this.ali1\n this.ali2 = '-' + this.ali2\n --i\n }\n\n while (j > 0) {\n this.ali1 = '-' + this.ali1\n this.ali2 = this.seq2[ j - 1 ] + this.ali2\n --j\n }\n\n if (Debug) Log.timeEnd('Alignment.trace')\n\n if (Debug) Log.log([this.ali1, this.ali2])\n }\n}\n\nexport default Alignment\n","/**\n * @file Align Utils\n * @author Alexander Rose \n * @private\n */\n\nimport Structure from '../structure/structure'\nimport Selection from '../selection/selection'\nimport Alignment from './alignment'\nimport Superposition from './superposition'\n\n/**\n * Perform structural superposition of two structures,\n * optionally guided by a sequence alignment\n * @param {Structure|StructureView} s1 - structure 1 which is superposed onto structure 2\n * @param {Structure|StructureView} s2 - structure 2 onto which structure 1 is superposed\n * @param {Boolean} [align] - guide the superposition by a sequence alignment\n * @param {String} [sele1] - selection string for structure 1\n * @param {String} [sele2] - selection string for structure 2\n * @return {undefined}\n */\nfunction superpose (s1: Structure, s2: Structure, align = false, sele1 = '', sele2 = '') {\n let i: number\n let j: number\n let n: number\n let atoms1\n let atoms2\n\n if (align) {\n let _s1 = s1\n let _s2 = s2\n\n if (sele1 && sele2) {\n _s1 = s1.getView(new Selection(sele1))\n _s2 = s2.getView(new Selection(sele2))\n }\n\n const seq1 = _s1.getSequence()\n const seq2 = _s2.getSequence()\n\n // Log.log( seq1.join(\"\") );\n // Log.log( seq2.join(\"\") );\n\n const ali = new Alignment(seq1.join(''), seq2.join(''))\n\n ali.calc()\n ali.trace()\n\n // Log.log( \"superpose alignment score\", ali.score );\n\n // Log.log( ali.ali1 );\n // Log.log( ali.ali2 );\n\n let _i, _j\n i = 0\n j = 0\n n = ali.ali1.length\n const aliIdx1: boolean[] = []\n const aliIdx2: boolean[] = []\n\n for (let l = 0; l < n; ++l) {\n const x = ali.ali1[ l ]\n const y = ali.ali2[ l ]\n\n _i = 0\n _j = 0\n\n if (x === '-') {\n aliIdx2[ j ] = false\n } else {\n aliIdx2[ j ] = true\n _i = 1\n }\n\n if (y === '-') {\n aliIdx1[ i ] = false\n } else {\n aliIdx1[ i ] = true\n _j = 1\n }\n\n i += _i\n j += _j\n }\n\n // Log.log( i, j );\n\n // Log.log( aliIdx1 );\n // Log.log( aliIdx2 );\n\n const _atoms1: number[] = []\n const _atoms2: number[] = []\n const ap1 = _s1.getAtomProxy()\n const ap2 = _s2.getAtomProxy()\n\n i = 0\n _s1.eachResidue(function (r) {\n if (r.traceAtomIndex === undefined ||\n r.traceAtomIndex !== r.getAtomIndexByName('CA')) return\n\n if (aliIdx1[ i ]) {\n ap1.index = r.getAtomIndexByName('CA')! // TODO\n _atoms1.push(ap1.x, ap1.y, ap1.z)\n }\n i += 1\n })\n\n i = 0\n _s2.eachResidue(function (r) {\n if (r.traceAtomIndex === undefined ||\n r.traceAtomIndex !== r.getAtomIndexByName('CA')) return\n\n if (aliIdx2[ i ]) {\n ap2.index = r.getAtomIndexByName('CA')! // TODO\n _atoms2.push(ap2.x, ap2.y, ap2.z)\n }\n i += 1\n })\n\n atoms1 = new Float32Array(_atoms1)\n atoms2 = new Float32Array(_atoms2)\n } else {\n const sviewCa1 = s1.getView(new Selection(`${sele1} and .CA`))\n const sviewCa2 = s2.getView(new Selection(`${sele2} and .CA`))\n\n atoms1 = sviewCa1\n atoms2 = sviewCa2\n }\n\n const superpose = new Superposition(atoms1, atoms2)\n const result = superpose.transform(s1)\n s1.refreshPosition()\n return result\n}\n\nexport {\n superpose\n}\n","/**\n * @file Sturucture Component\n * @author Alexander Rose \n * @private\n */\n\nimport { Signal } from 'signals'\n\nimport { ComponentRegistry, MeasurementDefaultParams } from '../globals'\nimport {\n defaults, /*deepEqual, */createRingBuffer, RingBuffer, createSimpleDict, SimpleDict\n} from '../utils'\nimport { smoothstep } from '../math/math-utils'\nimport Component, { ComponentSignals, ComponentDefaultParameters } from './component'\nimport RepresentationCollection from './representation-collection'\nimport TrajectoryElement from './trajectory-element'\nimport RepresentationElement from './representation-element'\nimport { makeTrajectory } from '../trajectory/trajectory-utils'\nimport { TrajectoryParameters } from '../trajectory/trajectory'\nimport Selection from '../selection/selection'\nimport Structure from '../structure/structure'\nimport StructureView from '../structure/structure-view'\nimport { superpose } from '../align/align-utils'\nimport Stage from '../stage/stage'\nimport StructureRepresentation, { StructureRepresentationParameters } from '../representation/structure-representation'\nimport AtomProxy from '../proxy/atom-proxy'\nimport { Vector3, Box3 } from 'three';\nimport { AngleRepresentationParameters } from '../representation/angle-representation';\nimport { AxesRepresentationParameters } from '../representation/axes-representation';\nimport { BallAndStickRepresentationParameters } from '../representation/ballandstick-representation';\nimport { CartoonRepresentationParameters } from '../representation/cartoon-representation';\nimport { ContactRepresentationParameters } from '../representation/contact-representation';\nimport { DihedralRepresentationParameters } from '../representation/dihedral-representation';\nimport { DihedralHistogramRepresentationParameters } from '../representation/dihedral-histogram-representation';\nimport { DistanceRepresentationParameters } from '../representation/distance-representation';\nimport { HyperballRepresentationParameters } from '../representation/hyperball-representation';\nimport { LabelRepresentationParameters } from '../representation/label-representation';\nimport { LineRepresentationParameters } from '../representation/line-representation';\nimport { PointRepresentationParameters } from '../representation/point-representation';\nimport { SurfaceRepresentationParameters } from '../representation/surface-representation';\nimport { RibbonRepresentationParameters } from '../representation/ribbon-representation';\nimport { RocketRepresentationParameters } from '../representation/rocket-representation';\nimport { TraceRepresentationParameters } from '../representation/trace-representation';\nimport { UnitcellRepresentationParameters } from '../representation/unitcell-representation';\nimport { SliceRepresentationParameters } from '../representation/slice-representation'\nimport { MolecularSurfaceRepresentationParameters } from '../representation/molecularsurface-representation'\nimport { DotRepresentationParameters } from '../representation/dot-representation'\n\nexport type StructureRepresentationType = keyof StructureRepresentationParametersMap\n\ninterface StructureRepresentationParametersMap {\n 'angle': AngleRepresentationParameters,\n 'axes' : AxesRepresentationParameters,\n 'backbone': BallAndStickRepresentationParameters,\n 'ball+stick': BallAndStickRepresentationParameters,\n 'base': BallAndStickRepresentationParameters,\n 'cartoon': CartoonRepresentationParameters,\n 'contact': ContactRepresentationParameters,\n 'dihedral': DihedralRepresentationParameters,\n 'dihedral-histogram': DihedralHistogramRepresentationParameters,\n 'distance': DistanceRepresentationParameters,\n 'dot': DotRepresentationParameters,\n 'helixorient': StructureRepresentationParameters,\n 'hyperball': HyperballRepresentationParameters,\n 'label': LabelRepresentationParameters,\n 'licorice': BallAndStickRepresentationParameters,\n 'line': LineRepresentationParameters,\n 'molecularsurface': MolecularSurfaceRepresentationParameters,\n 'point': PointRepresentationParameters,\n 'ribbon': RibbonRepresentationParameters,\n 'rocket': RocketRepresentationParameters,\n 'rope': CartoonRepresentationParameters,\n 'slice': SliceRepresentationParameters,\n 'spacefill': BallAndStickRepresentationParameters,\n 'surface': SurfaceRepresentationParameters,\n 'trace': TraceRepresentationParameters,\n 'tube': CartoonRepresentationParameters,\n 'unitcell': UnitcellRepresentationParameters,\n 'validation': StructureRepresentationParameters\n}\n\nexport const StructureComponentDefaultParameters = Object.assign({\n sele: '',\n defaultAssembly: ''\n}, ComponentDefaultParameters)\nexport type StructureComponentParameters = typeof StructureComponentDefaultParameters\n\nexport interface StructureComponentSignals extends ComponentSignals {\n trajectoryAdded: Signal // when a trajectory is added\n trajectoryRemoved: Signal // when a trajectory is removed\n defaultAssemblyChanged: Signal // on default assembly change\n}\n\n/**\n * Component wrapping a {@link Structure} object\n *\n * @example\n * // get a structure component by loading a structure file into the stage\n * stage.loadFile( \"rcsb://4opj\" ).then( function( structureComponent ){\n * structureComponent.addRepresentation( \"cartoon\" );\n * structureComponent.autoView();\n * } );\n */\nclass StructureComponent extends Component {\n readonly signals: StructureComponentSignals\n readonly parameters: StructureComponentParameters\n get defaultParameters () { return StructureComponentDefaultParameters }\n\n selection: Selection\n structureView: StructureView\n readonly trajList: TrajectoryElement[] = []\n\n pickBuffer: RingBuffer\n pickDict: SimpleDict\n lastPick?: number\n\n spacefillRepresentation: RepresentationElement\n distanceRepresentation: RepresentationElement\n angleRepresentation: RepresentationElement\n dihedralRepresentation: RepresentationElement\n\n measureRepresentations: RepresentationCollection\n\n constructor (stage: Stage, readonly structure: Structure, params: Partial = {}) {\n super(stage, structure, Object.assign({ name: structure.name }, params))\n\n this.signals = Object.assign(this.signals, {\n trajectoryAdded: new Signal(),\n trajectoryRemoved: new Signal(),\n defaultAssemblyChanged: new Signal()\n })\n\n this.initSelection(this.parameters.sele)\n\n //\n\n this.pickBuffer = createRingBuffer(4)\n this.pickDict = createSimpleDict()\n\n this.spacefillRepresentation = this.addRepresentation('spacefill', {\n sele: 'none',\n opacity: MeasurementDefaultParams.opacity,\n color: MeasurementDefaultParams.color,\n disablePicking: true,\n radiusType: 'data'\n }, true)\n\n this.distanceRepresentation = this.addRepresentation(\n 'distance', MeasurementDefaultParams, true\n )\n this.angleRepresentation = this.addRepresentation(\n 'angle', MeasurementDefaultParams, true\n )\n this.dihedralRepresentation = this.addRepresentation(\n 'dihedral', MeasurementDefaultParams, true\n )\n\n this.measureRepresentations = new RepresentationCollection([\n this.spacefillRepresentation,\n this.distanceRepresentation,\n this.angleRepresentation,\n this.dihedralRepresentation\n ])\n\n //\n\n this.setDefaultAssembly(this.parameters.defaultAssembly)\n\n this.structure.signals.refreshed.add(() => {\n this.updateRepresentations({ position: true })\n })\n }\n\n /**\n * Component type\n * @type {String}\n */\n get type () { return 'structure' }\n\n /**\n * Initialize selection\n * @private\n * @param {String} sele - selection string\n * @return {undefined}\n */\n initSelection (sele: string) {\n /**\n * Selection for {@link StructureComponent#structureView}\n * @private\n * @type {Selection}\n */\n this.selection = new Selection(sele)\n\n /**\n * View on {@link StructureComponent#structure}.\n * Change its selection via {@link StructureComponent#setSelection}.\n * @type {StructureView}\n */\n this.structureView = new StructureView(\n this.structure, this.selection\n )\n\n this.selection.signals.stringChanged.add(() => {\n this.structureView.setSelection(this.selection)\n\n this.rebuildRepresentations()\n this.rebuildTrajectories()\n })\n }\n\n /**\n * Set selection of {@link StructureComponent#structureView}\n * @param {String} string - selection string\n * @return {StructureComponent} this object\n */\n setSelection (string: string) {\n this.parameters.sele = string\n this.selection.setString(string)\n return this\n }\n\n /**\n * Set the default assembly\n * @param {String} value - assembly name\n * @return {undefined}\n */\n setDefaultAssembly (value:string) {\n // filter out non-exsisting assemblies\n if (this.structure.biomolDict[value] === undefined) value = ''\n // only set default assembly when changed\n if (this.parameters.defaultAssembly !== value) {\n const reprParams = { defaultAssembly: value }\n this.reprList.forEach(repr => repr.setParameters(reprParams))\n this.measureRepresentations.setParameters(reprParams)\n this.parameters.defaultAssembly = value\n this.signals.defaultAssemblyChanged.dispatch(value)\n }\n return this\n }\n\n /**\n * Rebuild all representations\n * @return {undefined}\n */\n rebuildRepresentations () {\n this.reprList.forEach((repr: RepresentationElement) => {\n repr.build()\n })\n this.measureRepresentations.build()\n }\n\n /**\n * Rebuild all trajectories\n * @return {undefined}\n */\n rebuildTrajectories () {\n this.trajList.forEach(trajComp => {\n trajComp.trajectory.setStructure(this.structureView)\n })\n }\n\n updateRepresentations (what: any) {\n super.updateRepresentations(what)\n this.measureRepresentations.update(what)\n }\n\n /**\n * Overrides {@link Component.updateRepresentationMatrices} \n * to also update matrix for measureRepresentations \n */\n updateRepresentationMatrices () {\n super.updateRepresentationMatrices()\n this.measureRepresentations.setParameters({ matrix: this.matrix })\n }\n\n addRepresentation (\n type: K,\n params: Partial|{defaultAssembly: string} = {},\n hidden = false\n ) {\n params.defaultAssembly = this.parameters.defaultAssembly\n\n const reprComp = this._addRepresentation(type, this.structureView, params, hidden)\n if (!hidden) {\n reprComp.signals.parametersChanged.add(() => this.measureUpdate())\n }\n return reprComp\n }\n\n /**\n * Add a new trajectory component to the structure\n */\n addTrajectory (trajPath = '', params: { [k: string]: any } = {}) {\n const traj = makeTrajectory(trajPath, this.structureView, params as TrajectoryParameters)\n\n const trajComp = new TrajectoryElement(this.stage, traj, params)\n this.trajList.push(trajComp)\n this.signals.trajectoryAdded.dispatch(trajComp)\n\n return trajComp\n }\n\n removeTrajectory (traj: TrajectoryElement) {\n const idx = this.trajList.indexOf(traj)\n if (idx !== -1) {\n this.trajList.splice(idx, 1)\n }\n\n traj.dispose()\n\n this.signals.trajectoryRemoved.dispatch(traj)\n }\n\n dispose () {\n // copy via .slice because side effects may change trajList\n this.trajList.slice().forEach(traj => traj.dispose())\n\n this.trajList.length = 0\n this.structure.dispose()\n this.measureRepresentations.dispose()\n\n super.dispose()\n }\n\n /**\n * Automatically center and zoom the component\n * @param {String|Integer} [sele] - selection string or duration if integer\n * @param {Integer} [duration] - duration of the animation, defaults to 0\n * @return {undefined}\n */\n autoView (sele?: string|number, duration?: number) {\n if (typeof sele === 'number') {\n duration = sele\n sele = ''\n }\n\n this.stage.animationControls.zoomMove(\n this.getCenter(sele),\n this.getZoom(sele),\n defaults(duration, 0)\n )\n }\n\n getBoxUntransformed (sele: string): Box3 {\n let bb\n\n if (sele) {\n bb = this.structureView.getBoundingBox(new Selection(sele))\n } else {\n bb = this.structureView.boundingBox\n }\n\n return bb\n }\n\n getCenterUntransformed (sele: string): Vector3 {\n if (sele && typeof sele === 'string') {\n return this.structure.atomCenter(new Selection(sele))\n } else {\n return this.structure.center\n }\n }\n\n superpose (component: StructureComponent, align: boolean, sele1: string, sele2: string) {\n superpose(\n this.structureView, component.structureView, align, sele1, sele2\n )\n\n this.updateRepresentations({ 'position': true })\n\n return this\n }\n\n getMaxRepresentationRadius (atomIndex: number) {\n let maxRadius = 0\n const atom = this.structure.getAtomProxy(atomIndex)\n this.eachRepresentation(reprElem => {\n if (reprElem.getVisibility()) {\n const repr: StructureRepresentation = reprElem.repr as any // TODO\n maxRadius = Math.max(repr.getAtomRadius(atom), maxRadius)\n }\n })\n return maxRadius\n }\n\n measurePick (atom: AtomProxy) {\n const pickCount = this.pickBuffer.count\n\n if (this.lastPick === atom.index && pickCount >= 1) {\n if (pickCount > 1) {\n const atomList = this.pickBuffer.data\n const atomListSorted = this.pickBuffer.data.sort()\n if (this.pickDict.has(atomListSorted)) {\n this.pickDict.del(atomListSorted)\n } else {\n this.pickDict.add(atomListSorted, atomList)\n }\n if (pickCount === 2) {\n this.distanceRepresentation.setParameters({\n atomPair: this.pickDict.values.filter(l => l.length === 2)\n })\n } else if (pickCount === 3) {\n this.angleRepresentation.setParameters({\n atomTriple: this.pickDict.values.filter(l => l.length === 3)\n })\n } else if (pickCount === 4) {\n this.dihedralRepresentation.setParameters({\n atomQuad: this.pickDict.values.filter(l => l.length === 4)\n })\n }\n }\n this.pickBuffer.clear()\n this.lastPick = undefined\n } else {\n if (!this.pickBuffer.has(atom.index)) {\n this.pickBuffer.push(atom.index)\n }\n this.lastPick = atom.index\n }\n\n this.measureUpdate()\n }\n\n measureClear () {\n this.pickBuffer.clear()\n this.lastPick = undefined\n this.spacefillRepresentation.setSelection('none')\n }\n\n measureBuild () {\n const md = this.measureData()\n this.distanceRepresentation.setParameters({ atomPair: md.distance })\n this.angleRepresentation.setParameters({ atomTriple: md.angle })\n this.dihedralRepresentation.setParameters({ atomQuad: md.dihedral })\n }\n\n measureUpdate () {\n const pickData = this.pickBuffer.data\n const radiusData: { [k: number]: number } = {}\n pickData.forEach(ai => {\n const r = Math.max(0.1, this.getMaxRepresentationRadius(ai))\n radiusData[ ai ] = r * (2.3 - smoothstep(0.1, 2, r))\n })\n this.spacefillRepresentation.setSelection(\n pickData.length ? ( '@' + pickData.join(',') ) : 'none'\n )\n if (pickData.length)\n this.spacefillRepresentation.setParameters({ radiusData })\n }\n\n measureData () {\n const pv = this.pickDict.values\n return {\n distance: pv.filter(l => l.length === 2),\n angle: pv.filter(l => l.length === 3),\n dihedral: pv.filter(l => l.length === 4)\n }\n }\n\n /**\n * Remove all measurements, optionally limit to distance, angle or dihedral\n */\n removeAllMeasurements (type?: MeasurementFlags) {\n const pd = this.pickDict\n const pv = pd.values\n const remove = function (len: number) {\n pv.filter(l => l.length === len).forEach(l => pd.del(l.slice().sort()))\n }\n if (!type || type & MeasurementFlags.Distance) remove(2)\n if (!type || type & MeasurementFlags.Angle) remove(3)\n if (!type || type & MeasurementFlags.Dihedral) remove(4)\n this.measureBuild()\n }\n\n /**\n * Remove a measurement given as a pair, triple, quad of atom indices\n */\n removeMeasurement (atomList: number[]) {\n this.pickDict.del(atomList.slice().sort())\n this.measureBuild()\n }\n\n /**\n * Add a measurement given as a pair, triple, quad of atom indices\n */\n addMeasurement (atomList: number[]) {\n if (atomList.length < 2 || atomList.length > 4) return\n const atomListSorted = atomList.slice().sort()\n if (!this.pickDict.has(atomListSorted)) {\n this.pickDict.add(atomListSorted, atomList)\n }\n this.measureBuild()\n }\n}\n\nexport const enum MeasurementFlags {\n Distance = 0x1,\n Angle = 0x2,\n Dihedral = 0x4\n}\n\nComponentRegistry.add('structure', StructureComponent)\nComponentRegistry.add('structureview', StructureComponent)\n\nexport default StructureComponent\n","/**\n * @file Trajectory Utils\n * @author Alexander Rose \n * @private\n */\n\nimport Structure from '../structure/structure'\nimport Frames from './frames'\nimport { TrajectoryParameters } from './trajectory'\nimport FramesTrajectory from './frames-trajectory'\nimport StructureTrajectory from './structure-trajectory'\nimport RemoteTrajectory from './remote-trajectory'\nimport CallbackTrajectory from './callback-trajectory'\n\nexport function makeTrajectory (trajSrc: string|Frames, structure: Structure, params: TrajectoryParameters) {\n let traj\n\n if (trajSrc && trajSrc instanceof Frames) {\n traj = new FramesTrajectory(trajSrc, structure, params)\n } else if (!trajSrc && structure.frames) {\n traj = new StructureTrajectory(trajSrc, structure, params)\n } else if (trajSrc && typeof trajSrc === 'function') {\n traj = new CallbackTrajectory(trajSrc, structure, params)\n } else {\n traj = new RemoteTrajectory(trajSrc, structure, params)\n }\n\n return traj\n}\n\n","/**\n * @file Surface Component\n * @author Alexander Rose \n * @private\n */\n\nimport { ComponentRegistry } from '../globals'\nimport Component, { ComponentParameters } from './component'\nimport Stage from '../stage/stage'\nimport Surface from '../surface/surface'\nimport { Vector3, Box3 } from 'three';\nimport RepresentationElement from './representation-element';\n\nexport type SurfaceRepresentationType = 'surface'|'dot'\n\n/**\n * Component wrapping a {@link Surface} object\n *\n * @example\n * // get a surface component by loading a surface file into the stage\n * stage.loadFile( \"url/for/surface\" ).then( function( surfaceComponent ){\n * surfaceComponent.addRepresentation( \"surface\" );\n * surfaceComponent.autoView();\n * } );\n */\nclass SurfaceComponent extends Component {\n /**\n * @param {Stage} stage - stage object the component belongs to\n * @param {Surface} surface - surface object to wrap\n * @param {ComponentParameters} params - component parameters\n */\n constructor (stage: Stage, readonly surface: Surface, params: Partial = {}) {\n super(stage, surface, Object.assign({ name: surface.name }, params))\n }\n\n /**\n * Component type\n * @type {String}\n */\n get type () { return 'surface' }\n\n /**\n * Add a new surface representation to the component\n * @param {String} type - the name of the representation, one of:\n * surface, dot.\n * @param {SurfaceRepresentationParameters} params - representation parameters\n * @return {RepresentationComponent} the created representation wrapped into\n * a representation component object\n */\n addRepresentation (type: SurfaceRepresentationType, params: { [k: string]: any } = {}): RepresentationElement {\n return this._addRepresentation(type, this.surface, params)\n }\n\n getBoxUntransformed (): Box3 {\n return this.surface.boundingBox\n }\n\n getCenterUntransformed (): Vector3 {\n return this.surface.center\n }\n\n dispose () {\n this.surface.dispose()\n super.dispose()\n }\n}\n\nComponentRegistry.add('surface', SurfaceComponent)\n\nexport default SurfaceComponent\n","/**\n * @file Volume Component\n * @author Alexander Rose \n * @private\n */\n\nimport { ComponentRegistry } from '../globals'\nimport Component, { ComponentParameters } from './component'\nimport Stage from '../stage/stage'\nimport Volume from '../surface/volume'\nimport { Box3, Vector3 } from 'three';\nimport RepresentationElement from './representation-element';\n\nexport type VolumeRepresentationType = 'surface'|'slice'|'dot'\n\n/**\n * Component wrapping a {@link Volume} object\n *\n * @example\n * // get a volume component by loading a volume file into the stage\n * stage.loadFile( \"url/for/volume\" ).then(function(volumeComponent){\n * volumeComponent.addRepresentation('surface');\n * volumeComponent.autoView();\n * });\n */\nclass VolumeComponent extends Component {\n /**\n * @param {Stage} stage - stage object the component belongs to\n * @param {Volume} volume - volume object to wrap\n * @param {ComponentParameters} params - component parameters\n */\n constructor (stage: Stage, readonly volume: Volume, params: Partial = {}) {\n super(stage, volume, Object.assign({ name: volume.name }, params))\n }\n\n /**\n * Component type\n * @type {String}\n */\n get type () { return 'volume' }\n\n /**\n * Add a new volume representation to the component\n */\n addRepresentation (type: VolumeRepresentationType, params: { [k: string]: any } = {}): RepresentationElement {\n return this._addRepresentation(type, this.volume, params)\n }\n\n getBoxUntransformed (): Box3 {\n return this.volume.boundingBox\n }\n\n getCenterUntransformed (): Vector3 {\n return this.volume.center\n }\n\n dispose () {\n this.volume.dispose()\n\n super.dispose()\n }\n}\n\nComponentRegistry.add('volume', VolumeComponent)\n\nexport default VolumeComponent\n","/**\n * @file Component Collection\n * @author Alexander Rose \n * @private\n */\n\nimport Component from './component'\nimport Collection from './collection'\n\nclass ComponentCollection extends Collection {\n addRepresentation (name: string, params: any) {\n \treturn this.forEach((comp) => comp.addRepresentation(name, params))\n }\n\n autoView (duration: number) {\n return this.forEach((comp) => comp.autoView(duration))\n }\n}\n\nexport default ComponentCollection\n","/**\n * @file Stage\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3, Box3 } from 'three'\nimport { Signal } from 'signals'\n\nimport {\n Debug, Log, Mobile, ComponentRegistry, ParserRegistry\n} from '../globals'\nimport { defaults, createParams, updateParams } from '../utils'\nimport { degToRad, clamp, pclamp } from '../math/math-utils'\nimport Counter from '../utils/counter'\nimport Viewer from '../viewer/viewer'\nimport { ImageParameters } from '../viewer/viewer-utils'\nimport MouseObserver from './mouse-observer'\n\nimport TrackballControls from '../controls/trackball-controls'\nimport PickingControls from '../controls/picking-controls'\nimport ViewerControls from '../controls/viewer-controls'\nimport AnimationControls from '../controls/animation-controls'\nimport MouseControls, { MouseControlPreset } from '../controls/mouse-controls'\nimport KeyControls from '../controls/key-controls'\n\nimport PickingBehavior from './picking-behavior'\nimport MouseBehavior from './mouse-behavior'\nimport AnimationBehavior from './animation-behavior'\nimport KeyBehavior from './key-behavior'\n\nimport Component, { ComponentParameters } from '../component/component'\nimport RepresentationElement from '../component/representation-element'\nimport StructureComponent from '../component/structure-component'\nimport SurfaceComponent from '../component/surface-component'\nimport VolumeComponent from '../component/volume-component'\nimport ComponentCollection from '../component/component-collection'\nimport RepresentationCollection from '../component/representation-collection'\nimport { autoLoad, getFileInfo, LoaderParameters } from '../loader/loader-utils'\nimport { ParserParams } from '../loader/parser-loader'\nimport AtomProxy from '../proxy/atom-proxy'\nimport Animation from '../animation/animation'\nimport Selection from '../selection/selection'\n\nimport Structure from '../structure/structure'\nimport Surface from '../surface/surface'\nimport Volume from '../surface/volume'\nimport Shape from '../geometry/shape'\nimport Script from '../script'\nimport { GenericColor } from '../types'\n\nfunction matchName (name: string|RegExp, object: { name: string }) {\n if (name instanceof RegExp) {\n return object.name.match(name) !== null\n } else {\n return object.name === name\n }\n}\n\nconst tmpZoomVector = new Vector3()\n\ndeclare global {\n interface Document {\n mozFullScreen: boolean\n mozFullScreenEnabled: boolean\n mozFullScreenElement: Element\n mozCancelFullScreen(): void\n\n msFullscreenEnabled: boolean\n msFullscreenElement: Element\n msExitFullscreen(): void\n }\n\n interface Element {\n mozRequestFullScreen(): void\n msRequestFullscreen(): void\n }\n}\n\n/**\n * Stage parameter object.\n * @typedef {Object} StageParameters - stage parameters\n * @property {Color} backgroundColor - background color\n * @property {Integer} sampleLevel - sampling level for antialiasing, between -1 and 5;\n * -1: no sampling, 0: only sampling when not moving\n * @property {Boolean} workerDefault - default value for useWorker parameter of representations\n * @property {Float} rotateSpeed - camera-controls rotation speed, between 0 and 10\n * @property {Float} zoomSpeed - camera-controls zoom speed, between 0 and 10\n * @property {Float} panSpeed - camera-controls pan speed, between 0 and 10\n * @property {Float} clipNear - position of camera near/front clipping plane\n * in percent of scene bounding box\n * @property {Float} clipFar - position of camera far/back clipping plane\n * in percent of scene bounding box\n * @property {Float} clipDist - camera clipping distance in Angstrom\n * @property {String} clipMode - how to interpret clipNear/Far and fogNear/Far values: \"scene\" for scene-relative, \"camera\" for camera-relative\n * @property {String} clipScale - \"relative\" or \"absolute\": interpret clipNear/Far and fogNear/Far as percentage of bounding box or absolute Angstroms (ignored when clipMode==camera)\n * @property {Float} fogNear - position of the start of the fog effect\n * in percent of scene bounding box\n * @property {Float} fogFar - position where the fog is in full effect\n * in percent of scene bounding box\n * @property {String} cameraType - type of camera, either 'persepective' or 'orthographic'\n * @property {Float} cameraFov - perspective camera field of view in degree, between 15 and 120\n * @property {Float} cameraEyeSep - stereo camera eye seperation\n * @property {Color} lightColor - point light color\n * @property {Float} lightIntensity - point light intensity\n * @property {Color} ambientColor - ambient light color\n * @property {Float} ambientIntensity - ambient light intensity\n * @property {Integer} hoverTimeout - timeout for hovering\n */\n\nexport interface StageSignals {\n parametersChanged: Signal\n fullscreenChanged: Signal\n componentAdded: Signal\n componentRemoved: Signal\n clicked: Signal\n hovered: Signal\n}\n\nexport type RenderQualityType = 'auto'|'low'|'medium'|'high'\n\nexport const StageDefaultParameters = {\n impostor: true,\n quality: 'medium' as RenderQualityType,\n workerDefault: true,\n sampleLevel: 0,\n backgroundColor: 'black' as GenericColor,\n rotateSpeed: 2.0,\n zoomSpeed: 1.2,\n panSpeed: 1.0,\n clipNear: 0,\n clipFar: 100,\n clipDist: 10,\n clipMode: 'scene',\n clipScale: 'relative',\n fogNear: 50,\n fogFar: 100,\n cameraFov: 40,\n cameraEyeSep: 0.3,\n cameraType: 'perspective' as 'perspective'|'orthographic'|'stereo',\n lightColor: 0xdddddd as GenericColor,\n lightIntensity: 1.2,\n ambientColor: 0xdddddd as GenericColor,\n ambientIntensity: 0.3,\n hoverTimeout: 0,\n tooltip: true,\n mousePreset: 'default' as MouseControlPreset\n}\nexport type StageParameters = typeof StageDefaultParameters\n\nexport interface StageLoadFileParams extends LoaderParameters {\n defaultRepresentation: boolean,\n assembly: string\n}\n\n/**\n * Stage class, central for creating molecular scenes with NGL.\n *\n * @example\n * var stage = new Stage( \"elementId\", { backgroundColor: \"white\" } );\n */\nclass Stage {\n signals: StageSignals = {\n parametersChanged: new Signal(),\n fullscreenChanged: new Signal(),\n componentAdded: new Signal(),\n componentRemoved: new Signal(),\n clicked: new Signal(),\n hovered: new Signal()\n }\n parameters: StageParameters\n\n /**\n * Counter that keeps track of various potentially long-running tasks,\n * including file loading and surface calculation.\n */\n tasks = new Counter()\n compList: Component[] = []\n defaultFileParams = {}\n logList: string[] = []\n\n transformComponent?: Component\n transformAtom?: AtomProxy\n\n viewer: Viewer\n tooltip: HTMLElement\n lastFullscreenElement: HTMLElement\n\n mouseObserver: MouseObserver\n viewerControls: ViewerControls\n trackballControls: TrackballControls\n pickingControls: PickingControls\n animationControls: AnimationControls\n mouseControls: MouseControls\n keyControls: KeyControls\n\n pickingBehavior: PickingBehavior\n mouseBehavior: MouseBehavior\n animationBehavior: AnimationBehavior\n keyBehavior: KeyBehavior\n\n spinAnimation: Animation\n rockAnimation: Animation\n\n constructor (idOrElement: string|HTMLElement, params: Partial = {}) {\n this.viewer = new Viewer(idOrElement)\n if (!this.viewer.renderer) return\n\n this.tooltip = document.createElement('div')\n Object.assign(this.tooltip.style, {\n display: 'none',\n position: 'fixed',\n zIndex: '1000000',\n pointerEvents: 'none',\n backgroundColor: 'rgba( 0, 0, 0, 0.6 )',\n color: 'lightgrey',\n padding: '8px',\n fontFamily: 'sans-serif'\n })\n this.viewer.container.appendChild(this.tooltip)\n\n this.mouseObserver = new MouseObserver(this.viewer.renderer.domElement)\n this.viewerControls = new ViewerControls(this)\n this.trackballControls = new TrackballControls(this)\n this.pickingControls = new PickingControls(this)\n this.animationControls = new AnimationControls(this)\n this.mouseControls = new MouseControls(this)\n this.keyControls = new KeyControls(this)\n\n this.pickingBehavior = new PickingBehavior(this)\n this.mouseBehavior = new MouseBehavior(this)\n this.animationBehavior = new AnimationBehavior(this)\n this.keyBehavior = new KeyBehavior(this)\n\n this.spinAnimation = this.animationControls.spin([ 0, 1, 0 ], 0.005)\n this.spinAnimation.pause(true)\n this.rockAnimation = this.animationControls.rock([ 0, 1, 0 ], 0.005)\n this.rockAnimation.pause(true)\n\n // must come after the viewer has been instantiated\n this.parameters = createParams(params, StageDefaultParameters)\n this.setParameters(this.parameters)\n\n this.viewer.animate()\n }\n\n /**\n * Set stage parameters\n */\n setParameters (params: Partial = {}) {\n updateParams(this.parameters, params)\n\n const p = params\n const tp = this.parameters\n\n const viewer = this.viewer\n const controls = this.trackballControls\n\n // apply parameters\n if (p.quality !== undefined) this.setQuality(tp.quality)\n if (p.impostor !== undefined) this.setImpostor(tp.impostor)\n if (p.rotateSpeed !== undefined) controls.rotateSpeed = tp.rotateSpeed\n if (p.zoomSpeed !== undefined) controls.zoomSpeed = tp.zoomSpeed\n if (p.panSpeed !== undefined) controls.panSpeed = tp.panSpeed\n if (p.mousePreset !== undefined) this.mouseControls.preset(tp.mousePreset)\n this.mouseObserver.setParameters({ hoverTimeout: tp.hoverTimeout })\n viewer.setClip(tp.clipNear, tp.clipFar, tp.clipDist, tp.clipMode, tp.clipScale)\n viewer.setFog(undefined, tp.fogNear, tp.fogFar)\n viewer.setCamera(tp.cameraType, tp.cameraFov, tp.cameraEyeSep)\n viewer.setSampling(tp.sampleLevel)\n viewer.setBackground(tp.backgroundColor)\n viewer.setLight(tp.lightColor, tp.lightIntensity, tp.ambientColor, tp.ambientIntensity)\n\n this.signals.parametersChanged.dispatch(this.getParameters())\n\n return this\n }\n\n log (msg: string) {\n console.log('STAGE LOG', msg)\n this.logList.push(msg)\n }\n\n /**\n * Get stage parameters\n */\n getParameters () {\n return Object.assign({}, this.parameters)\n }\n\n /**\n * Create default representations for the given component\n * @param {StructureComponent|SurfaceComponent} object - component to create the representations for\n * @return {undefined}\n */\n defaultFileRepresentation (component: Component) {\n if (component instanceof StructureComponent) {\n component.setSelection('/0')\n\n let atomCount, residueCount, instanceCount\n const structure = component.structure\n\n if (structure.biomolDict.BU1) {\n const assembly = structure.biomolDict.BU1\n atomCount = assembly.getAtomCount(structure)\n residueCount = assembly.getResidueCount(structure)\n instanceCount = assembly.getInstanceCount()\n component.setDefaultAssembly('BU1')\n } else {\n atomCount = structure.getModelProxy(0).atomCount\n residueCount = structure.getModelProxy(0).residueCount\n instanceCount = 1\n }\n\n let sizeScore = atomCount\n\n if (Mobile) {\n sizeScore *= 4\n }\n\n const backboneOnly = structure.atomStore.count / structure.residueStore.count < 2\n if (backboneOnly) {\n sizeScore *= 10\n }\n\n let colorScheme = 'chainname'\n let colorScale = 'RdYlBu'\n let colorReverse = false\n if (structure.getChainnameCount(new Selection('polymer and /0')) === 1) {\n colorScheme = 'residueindex'\n colorScale = 'Spectral'\n colorReverse = true\n }\n\n if (Debug) console.log(sizeScore, atomCount, instanceCount, backboneOnly)\n\n if (residueCount / instanceCount < 4) {\n component.addRepresentation('ball+stick', {\n colorScheme: 'element',\n radiusScale: 2.0,\n aspectRatio: 1.5,\n bondScale: 0.3,\n bondSpacing: 0.75,\n quality: 'auto'\n })\n } else if ((instanceCount > 5 && sizeScore > 15000) || sizeScore > 700000) {\n let scaleFactor = (\n Math.min(\n 2.0,\n Math.max(\n 0.1,\n 6000 / (sizeScore / instanceCount)\n )\n )\n )\n if (backboneOnly) scaleFactor = Math.min(scaleFactor, 0.5)\n\n component.addRepresentation('surface', {\n colorScheme, colorScale, colorReverse,\n sele: 'polymer',\n surfaceType: 'av',\n probeRadius: 1.4,\n scaleFactor: scaleFactor,\n useWorker: false\n })\n } else if (sizeScore > 250000) {\n component.addRepresentation('backbone', {\n colorScheme, colorScale, colorReverse,\n lineOnly: true\n })\n } else if (sizeScore > 100000) {\n component.addRepresentation('backbone', {\n colorScheme, colorScale, colorReverse,\n quality: 'low',\n disableImpostor: true,\n radiusScale: 2.0\n })\n } else if (sizeScore > 80000) {\n component.addRepresentation('backbone', {\n colorScheme, colorScale, colorReverse,\n radiusScale: 2.0\n })\n } else {\n component.addRepresentation('cartoon', {\n colorScheme, colorScale, colorReverse,\n radiusScale: 0.7,\n aspectRatio: 5,\n quality: 'auto'\n })\n if (sizeScore < 50000) {\n component.addRepresentation('base', {\n colorScheme, colorScale, colorReverse,\n quality: 'auto'\n })\n }\n component.addRepresentation('ball+stick', {\n sele: 'ligand',\n colorScheme: 'element',\n radiusScale: 2.0,\n aspectRatio: 1.5,\n bondScale: 0.3,\n bondSpacing: 0.75,\n quality: 'auto'\n })\n }\n\n // add frames as trajectory\n if (component.structure.frames.length) {\n component.addTrajectory()\n }\n } else if (component instanceof SurfaceComponent) {\n component.addRepresentation('surface')\n } else if (component instanceof VolumeComponent) {\n component.addRepresentation('surface')\n }\n\n this.tasks.onZeroOnce(this.autoView, this)\n }\n\n /**\n * Load a file onto the stage\n *\n * @example\n * // load from URL\n * stage.loadFile( \"http://files.rcsb.org/download/5IOS.cif\" );\n *\n * @example\n * // load binary data in CCP4 format via a Blob\n * var binaryBlob = new Blob( [ ccp4Data ], { type: 'application/octet-binary'} );\n * stage.loadFile( binaryBlob, { ext: \"ccp4\" } );\n *\n * @example\n * // load string data in PDB format via a Blob\n * var stringBlob = new Blob( [ pdbData ], { type: 'text/plain'} );\n * stage.loadFile( stringBlob, { ext: \"pdb\" } );\n *\n * @example\n * // load a File object\n * stage.loadFile( file );\n *\n * @example\n * // load from URL and add a 'ball+stick' representation with double/triple bonds\n * stage.loadFile( \"http://files.rcsb.org/download/1crn.cif\" ).then( function( comp ){\n * comp.addRepresentation( \"ball+stick\", { multipleBond: true } );\n * } );\n *\n * @param {String|File|Blob} path - either a URL or an object containing the file data\n * @param {LoaderParameters} params - loading parameters\n * @param {Boolean} params.asTrajectory - load multi-model structures as a trajectory\n * @return {Promise} A Promise object that resolves to a {@link StructureComponent},\n * a {@link SurfaceComponent} or a {@link ScriptComponent} object,\n * depending on the type of the loaded file.\n */\n loadFile (path: string|File|Blob, params: Partial = {}) {\n const p = Object.assign({}, this.defaultFileParams, params)\n const name = getFileInfo(path).name\n\n this.tasks.increment()\n this.log(`loading file '${name}'`)\n\n const onLoadFn = (object: Structure|Surface|Volume) => {\n this.log(`loaded '${name}'`)\n\n const component = this.addComponentFromObject(object, p)\n if (p.defaultRepresentation) {\n this.defaultFileRepresentation(component as Component)\n }\n this.tasks.decrement()\n\n return component\n }\n\n const onErrorFn = (e: Error|string) => {\n this.tasks.decrement()\n const errorMsg = `error loading file: '${e}'`\n this.log(errorMsg)\n throw errorMsg // throw so it can be catched\n }\n\n const ext = defaults(p.ext, getFileInfo(path).ext)\n let promise: Promise\n\n if (ParserRegistry.isTrajectory(ext)) {\n promise = Promise.reject(\n new Error(`loadFile: ext '${ext}' is a trajectory and must be loaded into a structure component`)\n )\n } else {\n promise = autoLoad(path, p)\n }\n\n return promise.then(onLoadFn, onErrorFn)\n }\n\n loadScript (path: string|File|Blob) {\n const name = getFileInfo(path).name\n\n this.log(`loading script '${name}'`)\n\n return autoLoad(path).then(\n (script: Script) => {\n this.tasks.increment()\n this.log(`running script '${name}'`)\n script.run(this).then(() => {\n this.tasks.decrement()\n this.log(`finished script '${name}'`)\n })\n this.log(`called script '${name}'`)\n },\n (error: Error|string) => {\n this.tasks.decrement()\n const errorMsg = `errored script '${name}' \"${error}\"`\n this.log(errorMsg)\n throw errorMsg // throw so it can be catched\n }\n )\n }\n\n /**\n * Add the given component to the stage\n * @param {Component} component - the component to add\n * @return {undefined}\n */\n addComponent (component: Component) {\n if (!component) {\n Log.warn('Stage.addComponent: no component given')\n return\n }\n\n this.compList.push(component)\n this.signals.componentAdded.dispatch(component)\n }\n\n /**\n * Create a component from the given object and add to the stage\n */\n addComponentFromObject (object: Structure|Surface|Volume|Shape, params: Partial = {}): void|Component {\n const CompClass = ComponentRegistry.get(object.type)\n\n if (CompClass) {\n const component = new CompClass(this, object, params)\n this.addComponent(component)\n return component\n }\n\n Log.warn('no component for object type', object.type)\n }\n\n /**\n * Remove the given component\n * @param {Component} component - the component to remove\n * @return {undefined}\n */\n removeComponent (component: Component) {\n const idx = this.compList.indexOf(component)\n if (idx !== -1) {\n this.compList.splice(idx, 1)\n component.dispose()\n this.signals.componentRemoved.dispatch(component)\n }\n }\n\n /**\n * Remove all components from the stage\n */\n removeAllComponents () {\n this.compList.slice().forEach(o => this.removeComponent(o))\n }\n\n /**\n * Handle any size-changes of the container element\n * @return {undefined}\n */\n handleResize () {\n this.viewer.handleResize()\n }\n\n /**\n * Set width and height\n * @param {String} width - CSS width value\n * @param {String} height - CSS height value\n * @return {undefined}\n */\n setSize (width: string, height: string) {\n const container = this.viewer.container\n\n if (container !== document.body) {\n if (width !== undefined) container.style.width = width\n if (height !== undefined) container.style.height = height\n this.handleResize()\n }\n }\n\n /**\n * Toggle fullscreen\n * @param {Element} [element] - document element to put into fullscreen,\n * defaults to the viewer container\n * @return {undefined}\n */\n toggleFullscreen (element: HTMLElement) {\n if (!document.fullscreenEnabled && !document.mozFullScreenEnabled &&\n !(document as any).webkitFullscreenEnabled && !document.msFullscreenEnabled\n ) {\n Log.log('fullscreen mode (currently) not possible')\n return\n }\n\n const self = this\n element = element || this.viewer.container\n this.lastFullscreenElement = element\n\n //\n\n function getFullscreenElement () {\n return document.fullscreenElement || document.mozFullScreenElement ||\n (document as any).webkitFullscreenElement || document.msFullscreenElement\n }\n\n function resizeElement () {\n if (!getFullscreenElement() && self.lastFullscreenElement) {\n const element = self.lastFullscreenElement\n element.style.width = element.dataset.normalWidth || ''\n element.style.height = element.dataset.normalHeight || ''\n\n document.removeEventListener('fullscreenchange', resizeElement)\n document.removeEventListener('mozfullscreenchange', resizeElement)\n document.removeEventListener('webkitfullscreenchange', resizeElement)\n document.removeEventListener('MSFullscreenChange', resizeElement)\n\n self.handleResize()\n self.signals.fullscreenChanged.dispatch(false)\n }\n }\n\n //\n\n if (!getFullscreenElement()) {\n element.dataset.normalWidth = element.style.width || ''\n element.dataset.normalHeight = element.style.height || ''\n element.style.width = window.screen.width + 'px'\n element.style.height = window.screen.height + 'px'\n\n if (element.requestFullscreen) {\n element.requestFullscreen()\n } else if (element.msRequestFullscreen) {\n element.msRequestFullscreen()\n } else if (element.mozRequestFullScreen) {\n element.mozRequestFullScreen()\n } else if ((element as any).webkitRequestFullscreen) {\n (element as any).webkitRequestFullscreen()\n }\n\n document.addEventListener('fullscreenchange', resizeElement)\n document.addEventListener('mozfullscreenchange', resizeElement)\n document.addEventListener('webkitfullscreenchange', resizeElement)\n document.addEventListener('MSFullscreenChange', resizeElement)\n\n this.handleResize()\n this.signals.fullscreenChanged.dispatch(true)\n\n // workaround for Safari\n setTimeout(function () { self.handleResize() }, 100)\n } else {\n if (document.exitFullscreen) {\n document.exitFullscreen()\n } else if (document.msExitFullscreen) {\n document.msExitFullscreen()\n } else if (document.mozCancelFullScreen) {\n document.mozCancelFullScreen()\n } else if ((document as any).webkitExitFullscreen) {\n (document as any).webkitExitFullscreen()\n }\n }\n }\n\n /**\n * Set spin\n * @param {Boolean} flag - if true start rocking and stop spinning\n * @return {undefined}\n */\n setSpin (flag: boolean) {\n if (flag) {\n this.spinAnimation.resume(true)\n this.rockAnimation.pause(true)\n } else {\n this.spinAnimation.pause(true)\n }\n }\n\n /**\n * Set rock\n * @param {Boolean} flag - if true start rocking and stop spinning\n * @return {undefined}\n */\n setRock (flag: boolean) {\n if (flag) {\n this.rockAnimation.resume(true)\n this.spinAnimation.pause(true)\n } else {\n this.rockAnimation.pause(true)\n }\n }\n\n /**\n * Toggle spin\n * @return {undefined}\n */\n toggleSpin () {\n this.setSpin(this.spinAnimation.paused)\n }\n\n /**\n * Toggle rock\n * @return {undefined}\n */\n toggleRock () {\n this.setRock(this.rockAnimation.paused)\n }\n\n /**\n * Get the current focus from the current clipNear value expressed\n * as 0 (full view) to 100 (completely clipped)\n * Negative values may be returned in some cases.\n *\n * In 'camera' clipMode focus isn't applicable, this method returns 0.0\n *\n * @return {number} focus\n */\n getFocus () : number {\n const p = this.parameters\n if (p.clipMode !== 'scene') return 0.0\n\n let clipNear = p.clipNear\n if (p.clipScale === 'absolute') {\n clipNear = this.viewer.absoluteToRelative(clipNear)\n }\n return clipNear * 2\n }\n\n\n /**\n * Set the focus, a value of 0 sets clipping planes to show full scene,\n * while a value of 100 will compltely clip the scene.\n *\n * @param {number} value focus\n */\n setFocus (value: number) {\n if (this.parameters.clipMode !== 'scene') return\n\n let clipNear\n let clipFar\n let fogNear\n let fogFar\n\n if (this.parameters.clipScale === 'relative') {\n clipNear = clamp(value / 2.0, 0.0, 49.9)\n clipFar = 100 - clipNear\n fogNear = 50\n fogFar = pclamp(2 * clipFar - 50)\n\n } else {\n clipNear = this.viewer.relativeToAbsolute(value / 2.0)\n clipFar = clipNear\n fogNear = 0\n fogFar = 2 * clipFar\n }\n\n this.setParameters({ clipNear, clipFar, fogNear, fogFar })\n }\n\n getZoomForBox (boundingBox: Box3) {\n const bbSize = boundingBox.getSize(tmpZoomVector)\n const maxSize = Math.max(bbSize.x, bbSize.y, bbSize.z)\n const minSize = Math.min(bbSize.x, bbSize.y, bbSize.z)\n let distance = maxSize + Math.sqrt(minSize)\n\n const fov = degToRad(this.viewer.perspectiveCamera.fov)\n const width = this.viewer.width\n const height = this.viewer.height\n const aspect = width / height\n const aspectFactor = (height < width ? 1 : aspect)\n\n distance = Math.abs(\n ((distance * 0.5) / aspectFactor) / Math.sin(fov / 2)\n )\n distance += this.parameters.clipDist\n return -distance\n }\n\n getBox () {\n return this.viewer.boundingBox\n }\n\n getZoom () {\n return this.getZoomForBox(this.getBox())\n }\n\n getCenter (optionalTarget?: Vector3) {\n return this.getBox().getCenter(optionalTarget || new Vector3())\n }\n\n /**\n * Add a zoom and a move animation with automatic targets\n * @param {Integer} duration - animation time in milliseconds\n * @return {undefined}\n */\n autoView (duration?: number) {\n this.animationControls.zoomMove(\n this.getCenter(),\n this.getZoom(),\n defaults(duration, 0)\n )\n }\n\n /**\n * Make image from what is shown in a viewer canvas\n */\n makeImage (params: Partial = {}) {\n return new Promise((resolve, reject) => {\n this.tasks.onZeroOnce(() => {\n this.tasks.increment()\n this.viewer.makeImage(params).then(blob => {\n this.tasks.decrement()\n resolve(blob)\n }).catch(e => {\n this.tasks.decrement()\n reject(e)\n })\n })\n })\n }\n\n setImpostor (value: boolean) {\n this.parameters.impostor = value\n\n const types = [\n 'spacefill', 'ball+stick', 'licorice', 'hyperball',\n 'backbone', 'rocket', 'helixorient', 'contact', 'distance',\n 'dot'\n ]\n\n this.eachRepresentation(function (reprElem) {\n if (!types.includes(reprElem.getType())) return\n\n const p = reprElem.getParameters() as any // TODO\n p.disableImpostor = !value\n reprElem.build(p)\n })\n }\n\n setQuality (value: RenderQualityType) {\n this.parameters.quality = value\n\n const types = [\n 'tube', 'cartoon', 'ribbon', 'trace', 'rope'\n ]\n\n const impostorTypes = [\n 'spacefill', 'ball+stick', 'licorice', 'hyperball',\n 'backbone', 'rocket', 'helixorient', 'contact', 'distance',\n 'dot'\n ]\n\n this.eachRepresentation(function (repr) {\n const p = repr.getParameters() as any // TODO\n\n if (!types.includes(repr.getType())) {\n if (!impostorTypes.includes(repr.getType())) return\n\n if (!p.disableImpostor) {\n (repr.repr as any).quality = value // TODO\n return\n }\n }\n\n p.quality = value\n repr.build(p)\n })\n }\n\n /**\n * Iterator over each component and executing the callback\n */\n eachComponent (callback: (comp: Component) => void, type?: string) {\n this.compList.slice().forEach(comp => {\n if (type === undefined || type === comp.type) callback(comp)\n })\n }\n\n /**\n * Iterator over each representation and executing the callback\n */\n eachRepresentation (callback: (reprElem: RepresentationElement, comp: Component) => void, type?: string) {\n this.eachComponent(comp => {\n comp.reprList.slice().forEach(reprElem => {\n if (type === undefined || type === reprElem.getType()) callback(reprElem, comp)\n })\n })\n }\n\n /**\n * Get collection of components by name\n */\n getComponentsByName (name: string|RegExp) {\n const compList: Component[] = []\n\n this.eachComponent(comp => {\n if (name === undefined || matchName(name, comp)) compList.push(comp)\n })\n\n return new ComponentCollection(compList)\n }\n\n /**\n * Get collection of components by object\n */\n getComponentsByObject (object: Structure|Surface|Volume|Shape) {\n const compList: Component[] = []\n\n this.eachComponent(comp => {\n if (comp.object === object) compList.push(comp)\n })\n\n return new ComponentCollection(compList)\n }\n\n /**\n * Get collection of representations by name\n */\n getRepresentationsByName (name: string|RegExp) {\n const reprList: RepresentationElement[] = []\n\n this.eachRepresentation((repr, comp) => {\n if (name === undefined || matchName(name, repr)) reprList.push(repr)\n })\n\n return new RepresentationCollection(reprList)\n }\n\n measureClear () {\n this.eachComponent((sc: StructureComponent) => sc.measureClear(), 'structure')\n }\n\n measureUpdate () {\n this.eachComponent((sc: StructureComponent) => sc.measureUpdate(), 'structure')\n }\n\n /**\n * Cleanup when disposing of a stage object\n */\n dispose () {\n this.tasks.dispose()\n this.viewer.dispose()\n this.mouseObserver.dispose()\n }\n}\n\nexport default Stage\n","/**\n * @file Shape Component\n * @author Alexander Rose \n * @private\n */\n\nimport { ComponentRegistry } from '../globals'\nimport Component, { ComponentParameters } from './component'\nimport Stage from '../stage/stage'\nimport Shape from '../geometry/shape'\nimport { Vector3, Box3 } from 'three';\nimport RepresentationElement from './representation-element';\n\nexport type ShapeRepresentationType = 'buffer'\n\n/**\n * Component wrapping a {@link Shape} object\n *\n * @example\n * // get a shape component by adding a shape object to the stage\n * var shape = new NGL.Shape( \"shape\" );\n * shape.addSphere( [ 0, 0, 0 ], [ 1, 0, 0 ], 1.5 );\n * var shapeComponent = stage.addComponentFromObject( shape );\n * shapeComponent.addRepresentation( \"buffer\" );\n */\nclass ShapeComponent extends Component {\n constructor (stage: Stage, readonly shape: Shape, params: Partial = {}) {\n super(stage, shape, Object.assign({ name: shape.name }, params))\n }\n\n /**\n * Component type\n * @type {String}\n */\n get type () { return 'shape' }\n\n /**\n * Add a new shape representation to the component\n * @param {String} type - the name of the representation, one of:\n * buffer.\n * @param {BufferRepresentationParameters} params - representation parameters\n * @return {RepresentationComponent} the created representation wrapped into\n * a representation component object\n */\n addRepresentation (type: ShapeRepresentationType, params: { [k: string]: any } = {}): RepresentationElement {\n return this._addRepresentation(type, this.shape, params)\n }\n\n getBoxUntransformed (): Box3 {\n return this.shape.boundingBox\n }\n\n getCenterUntransformed (): Vector3 {\n return this.shape.center\n }\n\n dispose () {\n this.shape.dispose()\n super.dispose()\n }\n}\n\nComponentRegistry.add('shape', ShapeComponent)\n\nexport default ShapeComponent\n","/******************************************************************************\r\nCopyright (c) Microsoft Corporation.\r\n\r\nPermission to use, copy, modify, and/or distribute this software for any\r\npurpose with or without fee is hereby granted.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\r\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\r\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\r\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\r\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\r\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\r\nPERFORMANCE OF THIS SOFTWARE.\r\n***************************************************************************** */\r\n/* global Reflect, Promise, SuppressedError, Symbol */\r\n\r\nvar extendStatics = function(d, b) {\r\n extendStatics = Object.setPrototypeOf ||\r\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\r\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\r\n return extendStatics(d, b);\r\n};\r\n\r\nexport function __extends(d, b) {\r\n if (typeof b !== \"function\" && b !== null)\r\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\r\n extendStatics(d, b);\r\n function __() { this.constructor = d; }\r\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\r\n}\r\n\r\nexport var __assign = function() {\r\n __assign = Object.assign || function __assign(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\r\n }\r\n return t;\r\n }\r\n return __assign.apply(this, arguments);\r\n}\r\n\r\nexport function __rest(s, e) {\r\n var t = {};\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\r\n t[p] = s[p];\r\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\r\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\r\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\r\n t[p[i]] = s[p[i]];\r\n }\r\n return t;\r\n}\r\n\r\nexport function __decorate(decorators, target, key, desc) {\r\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\r\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\r\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\r\n return c > 3 && r && Object.defineProperty(target, key, r), r;\r\n}\r\n\r\nexport function __param(paramIndex, decorator) {\r\n return function (target, key) { decorator(target, key, paramIndex); }\r\n}\r\n\r\nexport function __esDecorate(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {\r\n function accept(f) { if (f !== void 0 && typeof f !== \"function\") throw new TypeError(\"Function expected\"); return f; }\r\n var kind = contextIn.kind, key = kind === \"getter\" ? \"get\" : kind === \"setter\" ? \"set\" : \"value\";\r\n var target = !descriptorIn && ctor ? contextIn[\"static\"] ? ctor : ctor.prototype : null;\r\n var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});\r\n var _, done = false;\r\n for (var i = decorators.length - 1; i >= 0; i--) {\r\n var context = {};\r\n for (var p in contextIn) context[p] = p === \"access\" ? {} : contextIn[p];\r\n for (var p in contextIn.access) context.access[p] = contextIn.access[p];\r\n context.addInitializer = function (f) { if (done) throw new TypeError(\"Cannot add initializers after decoration has completed\"); extraInitializers.push(accept(f || null)); };\r\n var result = (0, decorators[i])(kind === \"accessor\" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);\r\n if (kind === \"accessor\") {\r\n if (result === void 0) continue;\r\n if (result === null || typeof result !== \"object\") throw new TypeError(\"Object expected\");\r\n if (_ = accept(result.get)) descriptor.get = _;\r\n if (_ = accept(result.set)) descriptor.set = _;\r\n if (_ = accept(result.init)) initializers.unshift(_);\r\n }\r\n else if (_ = accept(result)) {\r\n if (kind === \"field\") initializers.unshift(_);\r\n else descriptor[key] = _;\r\n }\r\n }\r\n if (target) Object.defineProperty(target, contextIn.name, descriptor);\r\n done = true;\r\n};\r\n\r\nexport function __runInitializers(thisArg, initializers, value) {\r\n var useValue = arguments.length > 2;\r\n for (var i = 0; i < initializers.length; i++) {\r\n value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);\r\n }\r\n return useValue ? value : void 0;\r\n};\r\n\r\nexport function __propKey(x) {\r\n return typeof x === \"symbol\" ? x : \"\".concat(x);\r\n};\r\n\r\nexport function __setFunctionName(f, name, prefix) {\r\n if (typeof name === \"symbol\") name = name.description ? \"[\".concat(name.description, \"]\") : \"\";\r\n return Object.defineProperty(f, \"name\", { configurable: true, value: prefix ? \"\".concat(prefix, \" \", name) : name });\r\n};\r\n\r\nexport function __metadata(metadataKey, metadataValue) {\r\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\r\n}\r\n\r\nexport function __awaiter(thisArg, _arguments, P, generator) {\r\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\r\n return new (P || (P = Promise))(function (resolve, reject) {\r\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\r\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\r\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\r\n step((generator = generator.apply(thisArg, _arguments || [])).next());\r\n });\r\n}\r\n\r\nexport function __generator(thisArg, body) {\r\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\r\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\r\n function verb(n) { return function (v) { return step([n, v]); }; }\r\n function step(op) {\r\n if (f) throw new TypeError(\"Generator is already executing.\");\r\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\r\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\r\n if (y = 0, t) op = [op[0] & 2, t.value];\r\n switch (op[0]) {\r\n case 0: case 1: t = op; break;\r\n case 4: _.label++; return { value: op[1], done: false };\r\n case 5: _.label++; y = op[1]; op = [0]; continue;\r\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\r\n default:\r\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\r\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\r\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\r\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\r\n if (t[2]) _.ops.pop();\r\n _.trys.pop(); continue;\r\n }\r\n op = body.call(thisArg, _);\r\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\r\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\r\n }\r\n}\r\n\r\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n var desc = Object.getOwnPropertyDescriptor(m, k);\r\n if (!desc || (\"get\" in desc ? !m.__esModule : desc.writable || desc.configurable)) {\r\n desc = { enumerable: true, get: function() { return m[k]; } };\r\n }\r\n Object.defineProperty(o, k2, desc);\r\n}) : (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n o[k2] = m[k];\r\n});\r\n\r\nexport function __exportStar(m, o) {\r\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\r\n}\r\n\r\nexport function __values(o) {\r\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\r\n if (m) return m.call(o);\r\n if (o && typeof o.length === \"number\") return {\r\n next: function () {\r\n if (o && i >= o.length) o = void 0;\r\n return { value: o && o[i++], done: !o };\r\n }\r\n };\r\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\r\n}\r\n\r\nexport function __read(o, n) {\r\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\r\n if (!m) return o;\r\n var i = m.call(o), r, ar = [], e;\r\n try {\r\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\r\n }\r\n catch (error) { e = { error: error }; }\r\n finally {\r\n try {\r\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\r\n }\r\n finally { if (e) throw e.error; }\r\n }\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spread() {\r\n for (var ar = [], i = 0; i < arguments.length; i++)\r\n ar = ar.concat(__read(arguments[i]));\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spreadArrays() {\r\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\r\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\r\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\r\n r[k] = a[j];\r\n return r;\r\n}\r\n\r\nexport function __spreadArray(to, from, pack) {\r\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\r\n if (ar || !(i in from)) {\r\n if (!ar) ar = Array.prototype.slice.call(from, 0, i);\r\n ar[i] = from[i];\r\n }\r\n }\r\n return to.concat(ar || Array.prototype.slice.call(from));\r\n}\r\n\r\nexport function __await(v) {\r\n return this instanceof __await ? (this.v = v, this) : new __await(v);\r\n}\r\n\r\nexport function __asyncGenerator(thisArg, _arguments, generator) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\r\n return i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i;\r\n function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }\r\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\r\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\r\n function fulfill(value) { resume(\"next\", value); }\r\n function reject(value) { resume(\"throw\", value); }\r\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\r\n}\r\n\r\nexport function __asyncDelegator(o) {\r\n var i, p;\r\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\r\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v; } : f; }\r\n}\r\n\r\nexport function __asyncValues(o) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var m = o[Symbol.asyncIterator], i;\r\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\r\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\r\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\r\n}\r\n\r\nexport function __makeTemplateObject(cooked, raw) {\r\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\r\n return cooked;\r\n};\r\n\r\nvar __setModuleDefault = Object.create ? (function(o, v) {\r\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\r\n}) : function(o, v) {\r\n o[\"default\"] = v;\r\n};\r\n\r\nexport function __importStar(mod) {\r\n if (mod && mod.__esModule) return mod;\r\n var result = {};\r\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\r\n __setModuleDefault(result, mod);\r\n return result;\r\n}\r\n\r\nexport function __importDefault(mod) {\r\n return (mod && mod.__esModule) ? mod : { default: mod };\r\n}\r\n\r\nexport function __classPrivateFieldGet(receiver, state, kind, f) {\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");\r\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\r\n}\r\n\r\nexport function __classPrivateFieldSet(receiver, state, value, kind, f) {\r\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");\r\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\r\n}\r\n\r\nexport function __classPrivateFieldIn(state, receiver) {\r\n if (receiver === null || (typeof receiver !== \"object\" && typeof receiver !== \"function\")) throw new TypeError(\"Cannot use 'in' operator on non-object\");\r\n return typeof state === \"function\" ? receiver === state : state.has(receiver);\r\n}\r\n\r\nexport function __addDisposableResource(env, value, async) {\r\n if (value !== null && value !== void 0) {\r\n if (typeof value !== \"object\" && typeof value !== \"function\") throw new TypeError(\"Object expected.\");\r\n var dispose;\r\n if (async) {\r\n if (!Symbol.asyncDispose) throw new TypeError(\"Symbol.asyncDispose is not defined.\");\r\n dispose = value[Symbol.asyncDispose];\r\n }\r\n if (dispose === void 0) {\r\n if (!Symbol.dispose) throw new TypeError(\"Symbol.dispose is not defined.\");\r\n dispose = value[Symbol.dispose];\r\n }\r\n if (typeof dispose !== \"function\") throw new TypeError(\"Object not disposable.\");\r\n env.stack.push({ value: value, dispose: dispose, async: async });\r\n }\r\n else if (async) {\r\n env.stack.push({ async: true });\r\n }\r\n return value;\r\n}\r\n\r\nvar _SuppressedError = typeof SuppressedError === \"function\" ? SuppressedError : function (error, suppressed, message) {\r\n var e = new Error(message);\r\n return e.name = \"SuppressedError\", e.error = error, e.suppressed = suppressed, e;\r\n};\r\n\r\nexport function __disposeResources(env) {\r\n function fail(e) {\r\n env.error = env.hasError ? new _SuppressedError(e, env.error, \"An error was suppressed during disposal.\") : e;\r\n env.hasError = true;\r\n }\r\n function next() {\r\n while (env.stack.length) {\r\n var rec = env.stack.pop();\r\n try {\r\n var result = rec.dispose && rec.dispose.call(rec.value);\r\n if (rec.async) return Promise.resolve(result).then(next, function(e) { fail(e); return next(); });\r\n }\r\n catch (e) {\r\n fail(e);\r\n }\r\n }\r\n if (env.hasError) throw env.error;\r\n }\r\n return next();\r\n}\r\n\r\nexport default {\r\n __extends: __extends,\r\n __assign: __assign,\r\n __rest: __rest,\r\n __decorate: __decorate,\r\n __param: __param,\r\n __metadata: __metadata,\r\n __awaiter: __awaiter,\r\n __generator: __generator,\r\n __createBinding: __createBinding,\r\n __exportStar: __exportStar,\r\n __values: __values,\r\n __read: __read,\r\n __spread: __spread,\r\n __spreadArrays: __spreadArrays,\r\n __spreadArray: __spreadArray,\r\n __await: __await,\r\n __asyncGenerator: __asyncGenerator,\r\n __asyncDelegator: __asyncDelegator,\r\n __asyncValues: __asyncValues,\r\n __makeTemplateObject: __makeTemplateObject,\r\n __importStar: __importStar,\r\n __importDefault: __importDefault,\r\n __classPrivateFieldGet: __classPrivateFieldGet,\r\n __classPrivateFieldSet: __classPrivateFieldSet,\r\n __classPrivateFieldIn: __classPrivateFieldIn,\r\n __addDisposableResource: __addDisposableResource,\r\n __disposeResources: __disposeResources,\r\n};\r\n","/**\n * @file Atomindex Colormaker\n * @author Alexander Rose \n * @private\n */\n\nimport { ColormakerRegistry } from '../globals'\nimport { defaults } from '../utils'\nimport Colormaker, { StuctureColormakerParams, ColormakerScale, manageColor } from './colormaker'\nimport AtomProxy from '../proxy/atom-proxy'\nimport ModelProxy from '../proxy/model-proxy'\n\n/**\n * Color by atom index. The {@link AtomProxy.index} property is used for coloring.\n * Each {@link ModelProxy} of a {@link Structure} is colored seperately. The\n * `params.domain` parameter is ignored.\n *\n * __Name:__ _atomindex_\n *\n * @example\n * stage.loadFile( \"rcsb://1crn\" ).then( function( o ){\n * o.addRepresentation( \"ball+stick\", { colorScheme: \"atomindex\" } );\n * o.autoView();\n * } );\n */\nclass AtomindexColormaker extends Colormaker {\n scalePerModel: { [k: number]: ColormakerScale }\n\n constructor (params: StuctureColormakerParams) {\n super(params)\n\n if (!params.scale) {\n this.parameters.scale = 'rainbow'\n this.parameters.reverse = defaults(params.reverse, true)\n }\n\n this.scalePerModel = {}\n\n params.structure.eachModel((mp: ModelProxy) => {\n this.parameters.domain = [ mp.atomOffset, mp.atomEnd ]\n this.scalePerModel[ mp.index ] = this.getScale() // TODO\n })\n }\n\n /**\n * get color for an atom\n * @param {AtomProxy} atom - atom to get color for\n * @return {Integer} hex atom color\n */\n @manageColor\n atomColor (atom: AtomProxy) {\n return this.scalePerModel[ atom.modelIndex ](atom.index)\n }\n}\n\nColormakerRegistry.add('atomindex', AtomindexColormaker)\n\nexport default AtomindexColormaker\n","/**\n * @file Bfactor Colormaker\n * @author Alexander Rose \n * @private\n */\n\nimport { ColormakerRegistry } from '../globals'\nimport Colormaker, { StuctureColormakerParams, ColormakerScale, manageColor } from './colormaker'\nimport AtomProxy from '../proxy/atom-proxy'\nimport Selection from '../selection/selection'\n\n/**\n * Color by b-factor. The {@link AtomProxy.bfactor} property is used for coloring.\n * By default the min and max b-factor values are used for the scale`s domain.\n *\n * __Name:__ _bfactor_\n *\n * @example\n * stage.loadFile( \"rcsb://1crn\" ).then( function( o ){\n * o.addRepresentation( \"ball+stick\", { colorScheme: \"bfactor\" } );\n * o.autoView();\n * } );\n */\nclass BfactorColormaker extends Colormaker {\n bfactorScale: ColormakerScale\n\n constructor (params: { sele?: string } & StuctureColormakerParams) {\n super(params)\n\n if (!params.scale) {\n this.parameters.scale = 'OrRd'\n }\n\n if (!params.domain) {\n let selection\n let min = Infinity\n let max = -Infinity\n\n if (params.sele) {\n selection = new Selection(params.sele)\n }\n\n params.structure.eachAtom(function (a) {\n const bfactor = a.bfactor\n min = Math.min(min, bfactor)\n max = Math.max(max, bfactor)\n }, selection)\n\n this.parameters.domain = [ min, max ]\n }\n\n this.bfactorScale = this.getScale()\n }\n\n @manageColor\n atomColor (a: AtomProxy) {\n return this.bfactorScale(a.bfactor)\n }\n}\n\nColormakerRegistry.add('bfactor', BfactorColormaker)\n\nexport default BfactorColormaker\n","/**\n * @file Chainid Colormaker\n * @author Alexander Rose \n * @private\n */\n\nimport { ColormakerRegistry } from '../globals'\nimport Colormaker, { StuctureColormakerParams, ColormakerScale, manageColor } from './colormaker'\nimport AtomProxy from '../proxy/atom-proxy'\nimport ChainProxy from '../proxy/chain-proxy'\nimport ModelProxy from '../proxy/model-proxy'\n\nexport type ChainidDict = { [k: string]: number }\n\n/**\n * Color by chain id\n */\nclass ChainidColormaker extends Colormaker {\n chainidDictPerModel: { [k: number]: ChainidDict } = {}\n scalePerModel: { [k: number]: ColormakerScale } = {}\n\n constructor (params: StuctureColormakerParams) {\n super(params)\n\n if (!params.scale) {\n this.parameters.scale = 'Spectral'\n }\n\n params.structure.eachModel((mp: ModelProxy) => {\n let i = 0\n const chainidDict: ChainidDict = {}\n mp.eachChain(function (cp: ChainProxy) {\n if (chainidDict[ cp.chainid ] === undefined) {\n chainidDict[ cp.chainid ] = i\n i += 1\n }\n })\n this.parameters.domain = [ 0, i - 1 ]\n this.chainidDictPerModel[ mp.index ] = chainidDict\n this.scalePerModel[ mp.index ] = this.getScale()\n })\n }\n\n @manageColor\n atomColor (a: AtomProxy) {\n const chainidDict = this.chainidDictPerModel[ a.modelIndex ]\n return this.scalePerModel[ a.modelIndex ](chainidDict[ a.chainid ])\n }\n}\n\nColormakerRegistry.add('chainid', ChainidColormaker)\n\nexport default ChainidColormaker\n","/**\n * @file Chainindex Colormaker\n * @author Alexander Rose \n * @private\n */\n\nimport { ColormakerRegistry } from '../globals'\nimport Colormaker, { StuctureColormakerParams, ColormakerScale, manageColor } from './colormaker'\nimport AtomProxy from '../proxy/atom-proxy'\nimport ModelProxy from '../proxy/model-proxy'\n\n/**\n * Color by chain index\n */\nclass ChainindexColormaker extends Colormaker {\n scalePerModel: { [k: number]: ColormakerScale } = {}\n\n constructor (params: StuctureColormakerParams) {\n super(params)\n\n if (!params.scale) {\n this.parameters.scale = 'Spectral'\n }\n\n params.structure.eachModel((mp: ModelProxy) => {\n this.parameters.domain = [ mp.chainOffset, mp.chainEnd ]\n this.scalePerModel[ mp.index ] = this.getScale()\n })\n }\n\n @manageColor\n atomColor (a: AtomProxy) {\n return this.scalePerModel[ a.modelIndex ](a.chainIndex)\n }\n}\n\nColormakerRegistry.add('chainindex', ChainindexColormaker)\n\nexport default ChainindexColormaker\n","/**\n * @file Chainname Colormaker\n * @author Alexander Rose \n * @private\n */\n\nimport { ColormakerRegistry } from '../globals'\nimport Colormaker, { StuctureColormakerParams, ColormakerScale, manageColor } from './colormaker'\nimport AtomProxy from '../proxy/atom-proxy'\nimport ChainProxy from '../proxy/chain-proxy'\nimport ModelProxy from '../proxy/model-proxy'\n\nexport type ChainnameDict = { [k: string]: number }\n\n/**\n * Color by chain name\n */\nclass ChainnameColormaker extends Colormaker {\n chainnameDictPerModel: { [k: number]: ChainnameDict } = {}\n scalePerModel: { [k: number]: ColormakerScale } = {}\n\n constructor (params: StuctureColormakerParams) {\n super(params)\n\n if (!params.scale) {\n this.parameters.scale = 'Spectral'\n }\n\n params.structure.eachModel((mp: ModelProxy) => {\n let i = 0\n const chainnameDict: ChainnameDict = {}\n mp.eachChain(function (cp: ChainProxy) {\n if (chainnameDict[ cp.chainname ] === undefined) {\n chainnameDict[ cp.chainname ] = i\n i += 1\n }\n })\n this.parameters.domain = [ 0, i - 1 ]\n this.chainnameDictPerModel[ mp.index ] = chainnameDict\n this.scalePerModel[ mp.index ] = this.getScale()\n })\n }\n\n @manageColor\n atomColor (a: AtomProxy) {\n const chainnameDict = this.chainnameDictPerModel[ a.modelIndex ]\n return this.scalePerModel[ a.modelIndex ](chainnameDict[ a.chainname ])\n }\n}\n\nColormakerRegistry.add('chainname', ChainnameColormaker)\n\nexport default ChainnameColormaker\n","/**\n * @file Densityfit Colormaker\n * @author Alexander Rose \n * @private\n */\n\nimport { ColormakerRegistry } from '../globals'\nimport Colormaker, { StuctureColormakerParams, ColormakerScale, manageColor } from './colormaker'\nimport AtomProxy from '../proxy/atom-proxy'\n\n/**\n * Color by validation density fit\n */\nclass DensityfitColormaker extends Colormaker {\n rsrzScale: ColormakerScale\n rsccScale: ColormakerScale\n\n rsrzDict: { [k: string]: number|undefined } = {}\n rsccDict: { [k: string]: number|undefined } = {}\n\n constructor (params: StuctureColormakerParams) {\n super(params)\n\n if (!params.scale) {\n this.parameters.scale = 'RdYlBu'\n }\n\n this.rsrzScale = this.getScale({ domain: [ 2, 0 ] })\n this.rsccScale = this.getScale({ domain: [ 0.678, 1.0 ] })\n\n const val = params.structure.validation\n if (val) {\n this.rsrzDict = val.rsrzDict\n this.rsccDict = val.rsccDict\n }\n\n }\n\n @manageColor\n atomColor (atom: AtomProxy) {\n let sele = atom.resno + ''\n if (atom.inscode) sele += '^' + atom.inscode\n if (atom.chainname) sele += ':' + atom.chainname\n sele += '/' + atom.modelIndex\n\n const rsrz = this.rsrzDict[ sele ]\n if (rsrz !== undefined) {\n return this.rsrzScale(rsrz)\n }\n\n const rscc = this.rsccDict[ sele ]\n if (rscc !== undefined) {\n return this.rsccScale(rscc)\n }\n\n return 0x909090\n }\n}\n\nColormakerRegistry.add('densityfit', DensityfitColormaker)\n\nexport default DensityfitColormaker\n","/**\n * @file Atomindex Colormaker\n * @author Fred Ludlow \n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3 } from 'three'\n\nimport { ColormakerRegistry } from '../globals'\nimport Colormaker, { StuctureColormakerParams, ColormakerScale, manageColor } from './colormaker'\nimport AtomProxy from '../proxy/atom-proxy'\nimport SpatialHash from '../geometry/spatial-hash'\n\n// from CHARMM\nconst partialCharges: { [k: string]: { [k: string]: number } } = {\n 'ARG': {\n 'CD': 0.1,\n 'CZ': 0.5,\n 'NE': -0.1\n },\n 'ASN': {\n 'CG': 0.55,\n 'OD1': -0.55\n },\n 'ASP': {\n 'CB': -0.16,\n 'CG': 0.36,\n 'OD1': -0.6,\n 'OD2': -0.6\n },\n 'CYS': {\n 'CB': 0.19,\n 'SG': -0.19\n },\n 'GLN': {\n 'CD': 0.55,\n 'OE1': -0.55\n },\n 'GLU': {\n 'CD': 0.36,\n 'CG': -0.16,\n 'OE1': -0.6,\n 'OE2': -0.6\n },\n 'HIS': {\n 'CB': 0.1,\n 'CD2': 0.2,\n 'CE1': 0.45,\n 'CG': 0.15,\n 'ND1': 0.05,\n 'NE2': 0.05\n },\n 'LYS': {\n 'CE': 0.25,\n 'NZ': 0.75\n },\n 'MET': {\n 'CE': 0.06,\n 'CG': 0.06,\n 'SD': -0.12\n },\n 'PTR': {\n 'C': 0.55,\n 'CA': 0.1,\n 'CZ': 0.25,\n 'N': -0.35,\n 'O': -0.55,\n 'O1P': -0.85,\n 'O2P': -0.85,\n 'O3P': -0.85,\n 'OG1': -1.1,\n 'P': 1.4\n },\n 'SEP': {\n 'C': 0.55,\n 'CA': 0.1,\n 'CB': 0.25,\n 'N': -0.35,\n 'O': -0.55,\n 'O1P': -0.85,\n 'O2P': -0.85,\n 'O3P': -0.85,\n 'OG1': -1.1,\n 'P': 1.4\n },\n 'SER': {\n 'CB': 0.25,\n 'OG': -0.25\n },\n 'THR': {\n 'CB': 0.25,\n 'OG1': -0.25\n },\n 'TPO': {\n 'C': 0.55,\n 'CA': 0.1,\n 'CB': 0.25,\n 'N': -0.35,\n 'O': -0.55,\n 'OG1': -1.1,\n 'O1P': -0.85,\n 'O2P': -0.85,\n 'O3P': -0.85,\n 'P': 1.4\n },\n 'TRP': {\n 'CD1': 0.06,\n 'CD2': 0.1,\n 'CE2': -0.04,\n 'CE3': -0.03,\n 'CG': -0.03,\n 'NE1': -0.06\n },\n 'TYR': {\n 'CZ': 0.25,\n 'OH': -0.25\n },\n 'backbone': {\n 'C': 0.55,\n 'O': -0.55,\n 'N': -0.35,\n 'CA': 0.1\n }\n}\n\nconst maxRadius = 12.0\nconst nHBondDistance = 1.04\nconst nHCharge = 0.25\n\n/**\n * Populates position vector with location of implicit or explicit H\n * Returns position or undefined if not able to locate H\n *\n * @param {AtomProxy} ap - the nitrogen atom\n * @param {Vector3} [position] - optional target\n * @return {Vectors|undefined} the hydrogen atom position\n */\nfunction backboneNHPosition (ap: AtomProxy, position = new Vector3()) {\n let h = false\n let ca = false\n let c = false\n position.set(2 * ap.x, 2 * ap.y, 2 * ap.z)\n\n ap.eachBondedAtom(function (a2: AtomProxy) {\n // Any time we detect H, reset position and skip\n // future tests\n if (h) return\n if (a2.atomname === 'H') {\n position.set(a2.x, a2.y, a2.z)\n h = true\n return\n }\n if (!ca && a2.atomname === 'CA') {\n position.sub(a2 as any) // TODO\n ca = true\n } else if (!c && a2.atomname === 'C') {\n c = true\n position.sub(a2 as any) // TODO\n }\n })\n\n if (h) { return position }\n\n if (ca && c) {\n position.normalize()\n position.multiplyScalar(nHBondDistance)\n position.add(ap as any)\n return position\n }\n}\n\n/**\n * Takes an array of Vector3 objects and\n * converts to an object that looks like an AtomStore\n *\n * @param {Vector3[]} positions - array of positions\n * @return {Object} AtomStore-like object\n */\nfunction buildStoreLike (positions: Vector3[]) {\n const n = positions.length\n const x = new Float32Array(n)\n const y = new Float32Array(n)\n const z = new Float32Array(n)\n\n for (let i = 0; i < positions.length; i++) {\n const v = positions[ i ]\n x[ i ] = v.x\n y[ i ] = v.y\n z[ i ] = v.z\n }\n\n return { x: x, y: y, z: z, count: n }\n}\n\nfunction chargeForAtom (a: AtomProxy): number {\n if (a.partialCharge !== null) return a.partialCharge\n if (!a.isProtein()) { return 0.0 }\n return (\n (partialCharges[ a.resname ] &&\n partialCharges[ a.resname ][ a.atomname ]) ||\n partialCharges[ 'backbone' ][ a.atomname ] || 0.0\n )\n}\n\n/**\n * Color a surface by electrostatic charge. This is a highly approximate\n * calculation! The partial charges are CHARMM with hydrogens added to heavy\n * atoms and hydrogen positions generated for amides.\n *\n * __Name:__ _electrostatic_\n *\n * @example\n * stage.loadFile( \"rcsb://3dqb\" ).then( function( o ){\n * o.addRepresentation( \"surface\", { colorScheme: \"electrostatic\" } );\n * o.autoView();\n * } );\n */\nclass ElectrostaticColormaker extends Colormaker {\n scale: ColormakerScale\n hHash: SpatialHash\n hash: SpatialHash\n charges: Float32Array\n hStore: { x: Float32Array, y: Float32Array, z: Float32Array, count: number }\n atomProxy: AtomProxy\n\n delta = new Vector3()\n hCharges: number[] = []\n\n constructor (params: StuctureColormakerParams) {\n super(params)\n\n if (!params.scale) {\n this.parameters.scale = 'rwb'\n }\n if (!params.domain) {\n this.parameters.domain = [ -50, 50 ]\n }\n\n this.scale = this.getScale()\n\n this.charges = new Float32Array(params.structure.atomCount)\n const hPositions: Vector3[] = []\n\n params.structure.eachAtom((ap: AtomProxy) => {\n this.charges[ ap.index ] = chargeForAtom(ap) * ap.occupancy\n if (ap.atomname === 'N') {\n\n // In the specific case where N forms two bonds to\n // CA and C, try and place a dummy hydrogen\n\n if (ap.bondCount >= 3) return; // Skip if 3 bonds already (e.g. PRO)\n\n if (ap.bondToElementCount(1)) return; // Skip if any H specificed\n\n const hPos = backboneNHPosition(ap)\n if (hPos !== undefined) {\n hPositions.push(hPos)\n this.hCharges.push(nHCharge * ap.occupancy)\n }\n }\n })\n\n const bbox = params.structure.getBoundingBox()\n bbox.expandByScalar(nHBondDistance) // Worst case\n\n // SpatialHash requires x,y,z and count\n this.hStore = buildStoreLike(hPositions)\n this.hHash = new SpatialHash(this.hStore as any, bbox) // TODO\n this.hash = new SpatialHash(params.structure.atomStore, bbox)\n }\n\n @manageColor\n positionColor (v: Vector3) {\n\n const charges = this.charges\n const hCharges = this.hCharges\n\n let p = 0.0\n this.hash.eachWithin(v.x, v.y, v.z, maxRadius, (atomIndex, dSq) => {\n const charge = charges[atomIndex]\n if (charge === 0.0) return\n p += charge / dSq\n })\n\n this.hHash.eachWithin(v.x, v.y, v.z, maxRadius, (atomIndex, dSq) => {\n const charge = hCharges[atomIndex]\n if (charge === 0.0) return\n p += charge / dSq\n })\n\n return this.scale(p * 332) // 332 to convert to kcal/mol\n }\n}\n\nColormakerRegistry.add('electrostatic', ElectrostaticColormaker)\n\nexport default ElectrostaticColormaker\n","/**\n * @file Element Colormaker\n * @author Alexander Rose \n * @private\n */\n\nimport { ColormakerRegistry } from '../globals'\nimport { defaults } from '../utils'\nimport Colormaker, { ColormakerParameters, manageColor } from './colormaker'\nimport AtomProxy from '../proxy/atom-proxy'\n\n// from Jmol http://jmol.sourceforge.net/jscolors/ (or 0xFFFFFF)\nconst ElementColors: { [k: string]: number } = {\n 'H': 0xFFFFFF,\n 'HE': 0xD9FFFF,\n 'LI': 0xCC80FF,\n 'BE': 0xC2FF00,\n 'B': 0xFFB5B5,\n 'C': 0x909090,\n 'N': 0x3050F8,\n 'O': 0xFF0D0D,\n 'F': 0x90E050,\n 'NE': 0xB3E3F5,\n 'NA': 0xAB5CF2,\n 'MG': 0x8AFF00,\n 'AL': 0xBFA6A6,\n 'SI': 0xF0C8A0,\n 'P': 0xFF8000,\n 'S': 0xFFFF30,\n 'CL': 0x1FF01F,\n 'AR': 0x80D1E3,\n 'K': 0x8F40D4,\n 'CA': 0x3DFF00,\n 'SC': 0xE6E6E6,\n 'TI': 0xBFC2C7,\n 'V': 0xA6A6AB,\n 'CR': 0x8A99C7,\n 'MN': 0x9C7AC7,\n 'FE': 0xE06633,\n 'CO': 0xF090A0,\n 'NI': 0x50D050,\n 'CU': 0xC88033,\n 'ZN': 0x7D80B0,\n 'GA': 0xC28F8F,\n 'GE': 0x668F8F,\n 'AS': 0xBD80E3,\n 'SE': 0xFFA100,\n 'BR': 0xA62929,\n 'KR': 0x5CB8D1,\n 'RB': 0x702EB0,\n 'SR': 0x00FF00,\n 'Y': 0x94FFFF,\n 'ZR': 0x94E0E0,\n 'NB': 0x73C2C9,\n 'MO': 0x54B5B5,\n 'TC': 0x3B9E9E,\n 'RU': 0x248F8F,\n 'RH': 0x0A7D8C,\n 'PD': 0x006985,\n 'AG': 0xC0C0C0,\n 'CD': 0xFFD98F,\n 'IN': 0xA67573,\n 'SN': 0x668080,\n 'SB': 0x9E63B5,\n 'TE': 0xD47A00,\n 'I': 0x940094,\n 'XE': 0x940094,\n 'CS': 0x57178F,\n 'BA': 0x00C900,\n 'LA': 0x70D4FF,\n 'CE': 0xFFFFC7,\n 'PR': 0xD9FFC7,\n 'ND': 0xC7FFC7,\n 'PM': 0xA3FFC7,\n 'SM': 0x8FFFC7,\n 'EU': 0x61FFC7,\n 'GD': 0x45FFC7,\n 'TB': 0x30FFC7,\n 'DY': 0x1FFFC7,\n 'HO': 0x00FF9C,\n 'ER': 0x00E675,\n 'TM': 0x00D452,\n 'YB': 0x00BF38,\n 'LU': 0x00AB24,\n 'HF': 0x4DC2FF,\n 'TA': 0x4DA6FF,\n 'W': 0x2194D6,\n 'RE': 0x267DAB,\n 'OS': 0x266696,\n 'IR': 0x175487,\n 'PT': 0xD0D0E0,\n 'AU': 0xFFD123,\n 'HG': 0xB8B8D0,\n 'TL': 0xA6544D,\n 'PB': 0x575961,\n 'BI': 0x9E4FB5,\n 'PO': 0xAB5C00,\n 'AT': 0x754F45,\n 'RN': 0x428296,\n 'FR': 0x420066,\n 'RA': 0x007D00,\n 'AC': 0x70ABFA,\n 'TH': 0x00BAFF,\n 'PA': 0x00A1FF,\n 'U': 0x008FFF,\n 'NP': 0x0080FF,\n 'PU': 0x006BFF,\n 'AM': 0x545CF2,\n 'CM': 0x785CE3,\n 'BK': 0x8A4FE3,\n 'CF': 0xA136D4,\n 'ES': 0xB31FD4,\n 'FM': 0xB31FBA,\n 'MD': 0xB30DA6,\n 'NO': 0xBD0D87,\n 'LR': 0xC70066,\n 'RF': 0xCC0059,\n 'DB': 0xD1004F,\n 'SG': 0xD90045,\n 'BH': 0xE00038,\n 'HS': 0xE6002E,\n 'MT': 0xEB0026,\n 'DS': 0xFFFFFF,\n 'RG': 0xFFFFFF,\n 'CN': 0xFFFFFF,\n 'UUT': 0xFFFFFF,\n 'FL': 0xFFFFFF,\n 'UUP': 0xFFFFFF,\n 'LV': 0xFFFFFF,\n 'UUH': 0xFFFFFF,\n\n 'D': 0xFFFFC0,\n 'T': 0xFFFFA0\n}\nconst DefaultElementColor = 0xFFFFFF\n\n/**\n * Color by element\n */\nclass ElementColormaker extends Colormaker {\n constructor (params: ColormakerParameters) {\n params.value = defaults(params.value, ElementColors.C)\n\n super(params)\n }\n\n\n @manageColor\n atomColor (a: AtomProxy) {\n const element = a.element\n\n if (element === 'C') {\n return this.parameters.value\n } else {\n return ElementColors[ element ] || DefaultElementColor\n }\n }\n}\n\nColormakerRegistry.add('element', ElementColormaker)\n\nexport default ElementColormaker\n","/**\n * @file Entityindex Colormaker\n * @author Alexander Rose \n * @private\n */\n\nimport { ColormakerRegistry } from '../globals'\nimport Colormaker, { StuctureColormakerParams, ColormakerScale, manageColor } from './colormaker'\nimport AtomProxy from '../proxy/atom-proxy'\n\n/**\n * Color by entity index\n */\nclass EntityindexColormaker extends Colormaker {\n entityindexScale: ColormakerScale\n\n constructor (params: StuctureColormakerParams) {\n super(params)\n\n if (!params.scale) {\n this.parameters.scale = 'Spectral'\n }\n if (!params.domain) {\n this.parameters.domain = [ 0, params.structure.entityList.length - 1 ]\n }\n\n this.entityindexScale = this.getScale()\n }\n\n @manageColor\n atomColor (a: AtomProxy) {\n return this.entityindexScale(a.entityIndex)\n }\n}\n\nColormakerRegistry.add('entityindex', EntityindexColormaker)\n\nexport default EntityindexColormaker\n","/**\n * @file Entitytype Colormaker\n * @author Alexander Rose \n * @private\n */\n\nimport { ColormakerRegistry } from '../globals'\nimport Colormaker, { manageColor } from './colormaker'\nimport AtomProxy from '../proxy/atom-proxy'\n\nimport {\n PolymerEntity, NonPolymerEntity, MacrolideEntity, WaterEntity\n} from '../structure/structure-constants'\n\n/**\n * Color by entity type\n */\nclass EntitytypeColormaker extends Colormaker {\n @manageColor\n atomColor (a: AtomProxy) {\n const e = a.entity\n const et = e ? e.entityType : undefined\n switch (et) {\n case PolymerEntity:\n return 0x7fc97f\n case NonPolymerEntity:\n return 0xfdc086\n case MacrolideEntity:\n return 0xbeaed4\n case WaterEntity:\n return 0x386cb0\n default:\n return 0xffff99\n }\n }\n}\n\nColormakerRegistry.add('entitytype', EntitytypeColormaker)\n\nexport default EntitytypeColormaker\n","/**\n * @file Geoquality Colormaker\n * @author Alexander Rose \n * @private\n */\n\nimport { ColormakerRegistry } from '../globals'\nimport Colormaker, { StuctureColormakerParams, manageColor } from './colormaker'\nimport AtomProxy from '../proxy/atom-proxy'\nimport { countSetBits } from '../math/math-utils'\n\n/**\n * Color by validation gometry quality\n */\nclass GeoqualityColormaker extends Colormaker {\n geoAtomDict: { [k: string]: { [k: string]: number } } = {}\n geoDict: { [k: string]: number|undefined } = {}\n\n constructor (params: StuctureColormakerParams) {\n super(params)\n\n const val = params.structure.validation\n if (val) {\n this.geoAtomDict = val.geoAtomDict\n this.geoDict = val.geoDict\n }\n }\n\n @manageColor\n atomColor (atom: AtomProxy) {\n let sele = atom.resno + ''\n if (atom.inscode) sele += '^' + atom.inscode\n if (atom.chainname) sele += ':' + atom.chainname\n sele += '/' + atom.modelIndex\n\n let geoProblemCount\n const geoAtom = this.geoAtomDict[ sele ]\n if (geoAtom !== undefined) {\n const atomProblems: number = geoAtom[ atom.atomname ] || 0\n geoProblemCount = countSetBits(atomProblems)\n } else {\n geoProblemCount = this.geoDict[ sele ] || 0\n }\n\n if (geoProblemCount === 0) {\n return 0x2166ac\n } else if (geoProblemCount === 1) {\n return 0xfee08b\n } else if (geoProblemCount === 2) {\n return 0xf46d43\n } else if (geoProblemCount >= 3) {\n return 0xa50026\n }\n return 0x909090\n }\n}\n\nColormakerRegistry.add('geoquality', GeoqualityColormaker)\n\nexport default GeoqualityColormaker\n","/**\n * @file Hydrophobicity Colormaker\n * @author Alexander Rose \n * @private\n */\n\nimport { ColormakerRegistry } from '../globals'\nimport Colormaker, { ColormakerParameters, ColormakerScale, manageColor } from './colormaker'\nimport AtomProxy from '../proxy/atom-proxy'\n\nimport {\n ResidueHydrophobicity, DefaultResidueHydrophobicity\n} from '../structure/structure-constants'\n\n/**\n * Color by hydrophobicity\n */\nclass HydrophobicityColormaker extends Colormaker {\n hfScale: ColormakerScale\n resHF: { [k: string]: number } = {}\n defaultResidueHydrophobicity: number\n\n constructor (params: ColormakerParameters) {\n super(params)\n\n if (!params.scale) {\n this.parameters.scale = 'RdYlGn'\n }\n\n const idx = 0 // 0: DGwif, 1: DGwoct, 2: Oct-IF\n\n for (const name in ResidueHydrophobicity) {\n this.resHF[ name ] = ResidueHydrophobicity[ name ][ idx ]\n }\n this.defaultResidueHydrophobicity = DefaultResidueHydrophobicity[idx]\n\n if (!params.domain) {\n let min = Infinity\n let max = -Infinity\n\n for (const name in this.resHF) {\n const val = this.resHF[ name ]\n min = Math.min(min, val)\n max = Math.max(max, val)\n }\n\n this.parameters.domain = [ min, 0, max ]\n }\n\n this.hfScale = this.getScale()\n }\n\n @manageColor\n atomColor (a: AtomProxy) {\n return this.hfScale(this.resHF[ a.resname ] || this.defaultResidueHydrophobicity)\n }\n}\n\nColormakerRegistry.add('hydrophobicity', HydrophobicityColormaker)\n\nexport default HydrophobicityColormaker\n","/**\n * @file Modelindex Colormaker\n * @author Alexander Rose \n * @private\n */\n\nimport { ColormakerRegistry } from '../globals'\nimport Colormaker, { StuctureColormakerParams, ColormakerScale, manageColor } from './colormaker'\nimport AtomProxy from '../proxy/atom-proxy'\n\n/**\n * Color by model index\n */\nclass ModelindexColormaker extends Colormaker {\n modelindexScale: ColormakerScale\n\n constructor (params: StuctureColormakerParams) {\n super(params)\n\n if (!params.scale) {\n this.parameters.scale = 'rainbow'\n }\n if (!params.domain) {\n this.parameters.domain = [ 0, params.structure.modelStore.count ]\n }\n\n this.modelindexScale = this.getScale()\n }\n\n @manageColor\n atomColor (a: AtomProxy) {\n return this.modelindexScale(a.modelIndex)\n }\n}\n\nColormakerRegistry.add('modelindex', ModelindexColormaker)\n\nexport default ModelindexColormaker\n","/**\n * @file Moleculetype Colormaker\n * @author Alexander Rose \n * @private\n */\n\nimport { ColormakerRegistry } from '../globals'\nimport Colormaker, { manageColor } from './colormaker'\nimport AtomProxy from '../proxy/atom-proxy'\n\nimport {\n WaterType, IonType, ProteinType, RnaType, DnaType, SaccharideType\n} from '../structure/structure-constants'\n\n/**\n * Color by molecule type\n */\nclass MoleculetypeColormaker extends Colormaker {\n @manageColor\n atomColor (a: AtomProxy) {\n switch (a.residueType.moleculeType) {\n case WaterType:\n return 0x386cb0\n case IonType:\n return 0xf0027f\n case ProteinType:\n return 0xbeaed4\n case RnaType:\n return 0xfdc086\n case DnaType:\n return 0xbf5b17\n case SaccharideType:\n return 0x7fc97f\n default:\n return 0xffff99\n }\n }\n}\n\nColormakerRegistry.add('moleculetype', MoleculetypeColormaker)\n\nexport default MoleculetypeColormaker\n","/**\n * @file Occupancy Colormaker\n * @author Alexander Rose \n * @private\n */\n\nimport { ColormakerRegistry } from '../globals'\nimport Colormaker, { ColormakerParameters, ColormakerScale, manageColor } from './colormaker'\nimport AtomProxy from '../proxy/atom-proxy'\n\n/**\n * Color by occupancy\n */\nclass OccupancyColormaker extends Colormaker {\n occupancyScale: ColormakerScale\n\n constructor (params: ColormakerParameters) {\n super(params)\n\n if (!params.scale) {\n this.parameters.scale = 'PuBu'\n }\n\n if (!params.domain) {\n this.parameters.domain = [ 0.0, 1.0 ]\n }\n\n this.occupancyScale = this.getScale()\n }\n\n @manageColor\n atomColor (a: AtomProxy) {\n return this.occupancyScale(a.occupancy)\n }\n}\n\nColormakerRegistry.add('occupancy', OccupancyColormaker)\n\nexport default OccupancyColormaker\n","/**\n * @file Partialcharge Colormaker\n * @author Alexander Rose \n * @private\n */\n\nimport { ColormakerRegistry } from '../globals'\nimport Colormaker, { ColormakerParameters, ColormakerScale, manageColor } from './colormaker'\nimport AtomProxy from '../proxy/atom-proxy'\n\n/**\n * Color by partial charge. The {@link AtomProxy.partialCharge} property is used for coloring.\n * The default domain is [-1, 1].\n *\n * __Name:__ _partialCharge_\n *\n * @example\n * stage.loadFile(\"rcsb://1crn\").then(function (o) {\n * o.addRepresentation(\"ball+stick\", {colorScheme: \"partialCharge\"});\n * o.autoView();\n * });\n */\nclass PartialchargeColormaker extends Colormaker {\n partialchargeScale: ColormakerScale\n\n constructor (params: ColormakerParameters) {\n super(params)\n\n if (!params.scale) {\n this.parameters.scale = 'rwb'\n }\n\n if (!params.domain) {\n this.parameters.domain = [-1, 1]\n }\n\n this.partialchargeScale = this.getScale()\n }\n\n @manageColor\n atomColor (a: AtomProxy) {\n return this.partialchargeScale(a.partialCharge || 0)\n }\n}\n\nColormakerRegistry.add('partialcharge', PartialchargeColormaker)\n\nexport default PartialchargeColormaker\n","/**\n * @file Random Colormaker\n * @author Alexander Rose \n * @private\n */\n\nimport { ColormakerRegistry } from '../globals'\nimport Colormaker, { manageColor } from './colormaker'\n\nfunction randomColor () {\n return Math.random() * 0xFFFFFF\n}\n\n/**\n * Class by random color\n */\nclass RandomColormaker extends Colormaker {\n /**\n * get color for an atom\n * @return {Integer} random hex color\n */\n @manageColor\n atomColor () {\n return randomColor()\n }\n\n /**\n * get color for volume cell\n * @return {Integer} random hex color\n */\n @manageColor\n volumeColor () {\n return randomColor()\n }\n\n /**\n * get color for coordinates in space\n * @return {Integer} random hex color\n */\n @manageColor\n positionColor () {\n return randomColor()\n }\n}\n\nColormakerRegistry.add('random', RandomColormaker)\n\nexport default RandomColormaker\n","/**\n * @file Randomcoilindex Colormaker\n * @author Alexander Rose \n * @private\n */\n\nimport { ColormakerRegistry } from '../globals'\nimport Colormaker, { StuctureColormakerParams, ColormakerScale, manageColor } from './colormaker'\nimport AtomProxy from '../proxy/atom-proxy'\n\n/**\n * Color by random coil index\n */\nclass RandomcoilindexColormaker extends Colormaker {\n rciScale: ColormakerScale\n rciDict: { [k: string]: number|undefined } = {}\n\n constructor (params: StuctureColormakerParams) {\n super(params)\n\n if (!params.scale) {\n this.parameters.scale = 'RdYlBu'\n }\n\n this.rciScale = this.getScale({ domain: [ 0.6, 0 ] })\n\n const val = params.structure.validation\n if (val) this.rciDict = val.rciDict\n\n }\n\n @manageColor\n atomColor (atom: AtomProxy) {\n let sele = `[${atom.resname}]${atom.resno}`\n if (atom.chainname) sele += ':' + atom.chainname\n\n const rci = this.rciDict[ sele ]\n return rci !== undefined ? this.rciScale(rci) : 0x909090\n }\n}\n\nColormakerRegistry.add('randomcoilindex', RandomcoilindexColormaker)\n\nexport default RandomcoilindexColormaker\n","/**\n * @file Residueindex Colormaker\n * @author Alexander Rose \n * @private\n */\n\nimport { ColormakerRegistry } from '../globals'\nimport { defaults } from '../utils'\nimport Colormaker, { StuctureColormakerParams, ColormakerScale, manageColor } from './colormaker'\nimport AtomProxy from '../proxy/atom-proxy'\nimport ChainProxy from '../proxy/chain-proxy'\n\n/**\n * Color by residue index\n */\nclass ResidueindexColormaker extends Colormaker {\n scalePerChain: { [k: number]: ColormakerScale } = {}\n\n constructor (params: StuctureColormakerParams) {\n super(params)\n\n if (!params.scale) {\n this.parameters.scale = 'rainbow'\n this.parameters.reverse = defaults(params.reverse, true)\n }\n\n params.structure.eachChain((cp: ChainProxy) => {\n this.parameters.domain = [ cp.residueOffset, cp.residueEnd ]\n this.scalePerChain[ cp.index ] = this.getScale()\n })\n }\n\n @manageColor\n atomColor (a: AtomProxy) {\n return this.scalePerChain[ a.chainIndex ](a.residueIndex)\n }\n}\n\nColormakerRegistry.add('residueindex', ResidueindexColormaker)\n\nexport default ResidueindexColormaker\n","/**\n * @file Resname Colormaker\n * @author Alexander Rose \n * @private\n */\n\nimport { ColormakerRegistry } from '../globals'\nimport Colormaker, { manageColor } from './colormaker'\nimport AtomProxy from '../proxy/atom-proxy'\n\n// protein colors from Jmol http://jmol.sourceforge.net/jscolors/\nconst ResidueColors: { [k: string]: number } = {\n 'ALA': 0x8CFF8C,\n 'ARG': 0x00007C,\n 'ASN': 0xFF7C70,\n 'ASP': 0xA00042,\n 'CYS': 0xFFFF70,\n 'GLN': 0xFF4C4C,\n 'GLU': 0x660000,\n 'GLY': 0xFFFFFF,\n 'HIS': 0x7070FF,\n 'ILE': 0x004C00,\n 'LEU': 0x455E45,\n 'LYS': 0x4747B8,\n 'MET': 0xB8A042,\n 'PHE': 0x534C52,\n 'PRO': 0x525252,\n 'SER': 0xFF7042,\n 'THR': 0xB84C00,\n 'TRP': 0x4F4600,\n 'TYR': 0x8C704C,\n 'VAL': 0xFF8CFF,\n\n 'ASX': 0xFF00FF,\n 'GLX': 0xFF00FF,\n 'ASH': 0xFF00FF,\n 'GLH': 0xFF00FF,\n\n 'A': 0xDC143C, // Crimson Red\n 'G': 0x32CD32, // Lime Green\n 'I': 0x9ACD32, // Yellow Green\n 'X': 0x7CFC00, // Lawn Green\n 'C': 0xFFD700, // Gold Yellow\n 'T': 0x4169E1, // Royal Blue\n 'U': 0x40E0D0, // Turquoise Cyan\n 'D': 0x008B8B, // Dark Cyan\n\n 'DA': 0xDC143C,\n 'DG': 0x32CD32,\n 'DI': 0x9ACD32,\n 'DX': 0x7CFC00,\n 'DC': 0xFFD700,\n 'DT': 0x4169E1,\n 'DU': 0x40E0D0,\n 'DD': 0x008B8B\n}\nconst DefaultResidueColor = 0xFF00FF\n\n/**\n * Color by residue name\n */\nclass ResnameColormaker extends Colormaker {\n @manageColor\n atomColor (a: AtomProxy) {\n return ResidueColors[ a.resname ] || DefaultResidueColor\n }\n}\n\nColormakerRegistry.add('resname', ResnameColormaker)\n\nexport default ResnameColormaker\n","/**\n * @file Sstruc Colormaker\n * @author Alexander Rose \n * @private\n */\n\nimport { ColormakerRegistry } from '../globals'\nimport Colormaker, { StuctureColormakerParams, manageColor } from './colormaker'\nimport AtomProxy from '../proxy/atom-proxy'\nimport ResidueProxy from '../proxy/residue-proxy'\n\n// from Jmol http://jmol.sourceforge.net/jscolors/ (shapely)\nconst StructureColors = {\n 'alphaHelix': 0xFF0080,\n 'threeTenHelix': 0xA00080,\n 'piHelix': 0x600080,\n 'betaStrand': 0xFFC800,\n 'betaTurn': 0x6080FF,\n 'coil': 0xFFFFFF,\n\n 'dna': 0xAE00FE,\n 'rna': 0xFD0162,\n\n 'carbohydrate': 0xA6A6FA\n}\nconst DefaultStructureColor = 0x808080\n\n/**\n * Color by secondary structure\n */\nclass SstrucColormaker extends Colormaker {\n residueProxy: ResidueProxy\n\n constructor (params: StuctureColormakerParams) {\n super(params)\n\n this.residueProxy = params.structure.getResidueProxy()\n }\n\n @manageColor\n atomColor (ap: AtomProxy) {\n const sstruc = ap.sstruc\n const rp = this.residueProxy\n\n if (sstruc === 'h') {\n return StructureColors.alphaHelix\n } else if (sstruc === 'g') {\n return StructureColors.threeTenHelix\n } else if (sstruc === 'i') {\n return StructureColors.piHelix\n } else if (sstruc === 'e' || sstruc === 'b') {\n return StructureColors.betaStrand\n } else if (sstruc === 't') {\n return StructureColors.betaTurn\n } else {\n rp.index = ap.residueIndex\n if (rp.isDna()) {\n return StructureColors.dna\n } else if (rp.isRna()) {\n return StructureColors.rna\n } else if (rp.isSaccharide()) {\n return StructureColors.carbohydrate\n } else if (rp.isProtein() || sstruc === 's' || sstruc === 'l') {\n return StructureColors.coil\n } else {\n return DefaultStructureColor\n }\n }\n }\n}\n\nColormakerRegistry.add('sstruc', SstrucColormaker)\n\nexport default SstrucColormaker\n","/**\n * @file Colordata Colormaker\n * @author Fred Ludlow \n * @private\n */\n\nimport { ColormakerRegistry } from '../globals'\nimport Colormaker, { ColorData, ColormakerScale, manageColor, StuctureColormakerParams } from './colormaker'\nimport AtomProxy from '../proxy/atom-proxy'\nimport BondProxy from '../proxy/bond-proxy'\n\n\nclass StructuredataColormaker extends Colormaker {\n atomData?: ColorData['atomData']\n bondData?: ColorData['bondData']\n scale: ColormakerScale\n\n constructor(params: StuctureColormakerParams) {\n super(params)\n if (!params.scale) {\n this.parameters.scale = 'rwb'\n }\n this.atomData = this.parameters.data?.atomData\n this.bondData = this.parameters.data?.bondData\n this.scale = this.getScale(this.parameters)\n }\n\n @manageColor\n atomColor(a: AtomProxy) {\n const val = this.atomData?.[a.index]\n return (val !== undefined) ? this.scale(val) : this.parameters.value\n }\n\n @manageColor\n bondColor(bond: BondProxy, fromTo: boolean) {\n const val = this.bondData?.[bond.index]\n \n // Explicit bond data?\n if (val !== undefined) return this.scale(val)\n \n \n if (this.atomProxy) {\n this.atomProxy.index = fromTo ? bond.atomIndex1 : bond.atomIndex2\n return this.atomColor(this.atomProxy)\n } \n \n // Fallback\n return this.parameters.value\n }\n}\n\nColormakerRegistry.add('structuredata', StructuredataColormaker)\n\nexport default StructuredataColormaker","/**\n * @file Uniform Colormaker\n * @author Alexander Rose \n * @private\n */\n\nimport { ColormakerRegistry } from '../globals'\nimport Colormaker, { manageColor } from './colormaker'\n\n/**\n * Color by uniform color\n */\nclass UniformColormaker extends Colormaker {\n @manageColor\n atomColor () {\n return this.parameters.value\n }\n\n @manageColor\n bondColor () {\n return this.parameters.value\n }\n\n @manageColor\n valueColor () {\n return this.parameters.value\n }\n\n @manageColor\n volumeColor () {\n return this.parameters.value\n }\n}\n\nColormakerRegistry.add('uniform', UniformColormaker)\n\nexport default UniformColormaker\n","/**\n * @file Value Colormaker\n * @author Alexander Rose \n * @private\n */\n\nimport { ColormakerRegistry } from '../globals'\nimport Colormaker, { VolumeColormakerParams, ColormakerScale, manageColor } from './colormaker'\n\n/**\n * Color by volume value\n */\nclass ValueColormaker extends Colormaker {\n valueScale: ColormakerScale\n\n constructor (params: VolumeColormakerParams) {\n super(params)\n this.valueScale = this.getScale()\n }\n\n /**\n * return the color for a volume cell\n * @param {Integer} index - volume cell index\n * @return {Integer} hex cell color\n */\n @manageColor\n volumeColor (index: number) {\n return this.valueScale((this.parameters.volume! as any).data[ index ]) // TODO\n }\n}\n\nColormakerRegistry.add('value', ValueColormaker)\n\nexport default ValueColormaker\n","/**\n * @file Volume Colormaker\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3 } from 'three'\nimport { lerp } from '../math/math-utils'\n\nimport { ColormakerRegistry } from '../globals'\nimport Colormaker, { VolumeColormakerParams, ColormakerScale, manageColor } from './colormaker'\n\n/**\n * Color by volume position\n */\nclass VolumeColormaker extends Colormaker {\n valueScale: ColormakerScale\n vec = new Vector3()\n\n constructor (params: VolumeColormakerParams) {\n super(params)\n this.valueScale = this.getScale()\n }\n\n /**\n * return the color for coordinates in space\n * @param {Vector3} coords - xyz coordinates\n * @return {Integer} hex coords color\n */\n @manageColor\n positionColor (coords: Vector3) {\n const volume = this.parameters.volume as any // TODO\n\n if (!volume || !volume.inverseMatrix) {\n return this.parameters.value\n }\n\n const vec = this.vec\n const data = volume.data\n const nx = volume.nx\n const ny = volume.ny\n const nxy = nx * ny\n\n vec.copy(coords)\n vec.applyMatrix4(volume.inverseMatrix)\n\n // position of grid cell\n const x0 = Math.floor(vec.x)\n const y0 = Math.floor(vec.y)\n const z0 = Math.floor(vec.z)\n\n // Indices\n const i = ((((z0 * ny) + y0) * nx) + x0)\n const i1 = i + 1\n const iy = i + nx\n const iz = i + nxy\n const i1y = iy + 1\n const i1z = iz + 1\n const iyz = iy + nxy\n const i1yz = iyz + 1\n\n // Values\n const v = data[ i ]\n const v1 = data[ i1 ]\n const vy = data[ iy ]\n const vz = data[ iz ]\n const v1y = data[ i1y ]\n const v1z = data[ i1z ]\n const vyz = data[ iyz ]\n const v1yz = data[ i1yz ]\n\n // Position of point in fraction of grid\n const xd = vec.x - x0\n const yd = vec.y - y0\n const zd = vec.z - z0\n\n // 1st Dimension\n const c00 = lerp(v, v1, xd)\n const c01 = lerp(vz, v1z, xd)\n const c10 = lerp(vy, v1y, xd)\n const c11 = lerp(vyz, v1yz, xd)\n\n // 2nd Dimension\n const c0 = lerp(c00, c10, yd)\n const c1 = lerp(c01, c11, yd)\n\n // 3rd Dimension\n const c = lerp(c0, c1, zd)\n\n return this.valueScale(c)\n }\n}\n\nColormakerRegistry.add('volume', VolumeColormaker)\n\nexport default VolumeColormaker\n","/**\n * @file Structure Representation\n * @author Alexander Rose \n * @private\n */\n\nimport { ExtensionFragDepth, Mobile } from '../globals'\nimport { defaults } from '../utils'\nimport { RepresentationParameters, default as Representation } from './representation'\nimport Selection from '../selection/selection'\nimport RadiusFactory, { RadiusFactoryTypes, RadiusType } from '../utils/radius-factory'\nimport Structure from '../structure/structure'\nimport Viewer from '../viewer/viewer'\n// @ts-ignore: unused import Volume required for declaration only\nimport { Assembly, Volume } from '../ngl';\nimport StructureView from '../structure/structure-view';\nimport AtomProxy from '../proxy/atom-proxy';\nimport Polymer from '../proxy/polymer';\nimport Buffer from '../buffer/buffer';\nimport { AtomDataFields, BondDataFields, AtomDataParams, BondDataParams } from '../structure/structure-data';\n// @ts-ignore: unused import Surface required for declaration only\nimport Surface from '../surface/surface'\n\n/**\n * Structure representation parameter object.\n * @typedef {Object} StructureRepresentationParameters - structure representation parameters\n * @mixes RepresentationParameters\n *\n * @property {String} radiusType - A list of possible sources of the radius used for rendering the representation. The radius can be based on the *vdW radius*, the *covalent radius* or the *B-factor* value of the corresponding atom. Additionally the radius can be based on the *secondary structure*. Alternatively, when set to *size*, the value from the *radius* parameter is used for all atoms.\n * @property {Float} radius - A number providing a fixed radius used for rendering the representation.\n * @property {Float} scale - A number that scales the value defined by the *radius* or the *radiusType* parameter.\n * @property {String} assembly - name of an assembly object. Included are the asymmetric unit (*AU*) corresponding to the coordinates given in the structure file, biological assemblies from *PDB*, *mmCIF* or *MMTF* files (*BU1*, *BU2*, ...), a filled (crystallographic) unitcell of a given space group (*UNITCELL*), a supercell consisting of a center unitcell and its 26 direct neighbors (*SUPERCELL*). Set to *default* to use the default asemmbly of the structure object.\n */\nexport interface StructureRepresentationParameters extends RepresentationParameters {\n radiusType: string\n radius: number\n scale: number\n assembly: string\n}\nexport interface StructureRepresentationData {\n bufferList: Buffer[]\n polymerList?: Polymer[]\n sview?: StructureView | Structure\n [k: string]: any\n}\n/**\n * Structure representation\n * @interface\n */\nabstract class StructureRepresentation extends Representation {\n\n protected selection: Selection\n protected dataList: StructureRepresentationData[]\n structure: Structure\n structureView: StructureView\n\n protected radiusType: RadiusType\n protected radiusData: {[k: number]: number}\n protected radiusSize: number\n protected radiusScale: number\n protected assembly: string\n protected defaultAssembly: string\n protected needsBuild: boolean\n\n /**\n * Create Structure representation object\n * @param {Structure} structure - the structure to be represented\n * @param {Viewer} viewer - a viewer object\n * @param {StructureRepresentationParameters} params - structure representation parameters\n */\n constructor (structure: Structure, viewer: Viewer, params: Partial) {\n const p = params || {}\n\n super(structure, viewer, p)\n\n this.type = 'structure'\n\n this.parameters = Object.assign({\n radiusType: {\n type: 'select', options: RadiusFactory.types\n },\n radiusData: {\n type: 'hidden'\n },\n radiusSize: {\n type: 'number', precision: 3, max: 10.0, min: 0.001\n },\n radiusScale: {\n type: 'number', precision: 3, max: 10.0, min: 0.001\n },\n assembly: null,\n defaultAssembly: {\n type: 'hidden'\n }\n }, this.parameters)\n\n /**\n * @type {Selection}\n * @private\n */\n this.selection = new Selection(p.sele)\n\n /**\n * @type {Array}\n * @private\n */\n this.dataList = []\n\n /**\n * @type {Structure}\n */\n this.structure = structure\n\n /**\n * @type {StructureView}\n */\n this.structureView = this.structure.getView(this.selection)\n\n if (structure.biomolDict) {\n const biomolOptions:{[key: string]: string} = {\n 'default': 'default',\n '': (structure.unitcell ? 'AU' : 'FULL')\n }\n Object.keys(structure.biomolDict).forEach(function (k) {\n biomolOptions[ k ] = k\n })\n this.parameters.assembly = {\n type: 'select',\n options: biomolOptions,\n rebuild: true\n }\n } else {\n this.parameters.assembly = null\n }\n }\n\n get defaultScale () {\n return {\n 'vdw': 1.0,\n 'covalent': 1.0,\n 'bfactor': 0.01,\n 'sstruc': 1.0\n }\n }\n\n init (params: Partial) {\n const p = params || {}\n p.colorScheme = defaults(p.colorScheme, 'element')\n\n this.setRadius(p.radius, p)\n\n this.radiusType = defaults(p.radiusType, 'vdw')\n this.radiusData = defaults(p.radiusData, {})\n this.radiusSize = defaults(p.radiusSize, 1.0)\n this.radiusScale = defaults(p.radiusScale, 1.0)\n this.assembly = defaults(p.assembly, 'default')\n this.defaultAssembly = defaults(p.defaultAssembly, '')\n\n if (p.quality === 'auto') {\n p.quality = this.getQuality()\n }\n\n super.init(p)\n\n this.selection.signals.stringChanged.add((/* sele */) => {\n this.build()\n })\n\n this.build()\n }\n\n setRadius (value: string | number | undefined, p: Partial) {\n const types = Object.keys(RadiusFactoryTypes)\n\n if (typeof value === 'string' && types.includes(value.toLowerCase())) {\n p.radiusType = value\n } else if (value !== undefined) {\n p.radiusType = 'size'\n p.radiusSize = value\n }\n\n return this\n }\n\n getAssembly (): Assembly {\n const name = this.assembly === 'default' ? this.defaultAssembly : this.assembly\n return this.structure.biomolDict[ name ]\n }\n\n getQuality () {\n let atomCount\n const s = this.structureView\n const assembly = this.getAssembly()\n if (assembly) {\n atomCount = assembly.getAtomCount(s)\n } else {\n atomCount = s.atomCount\n }\n if (Mobile) {\n atomCount *= 4\n }\n const backboneOnly = s.atomStore.count / s.residueStore.count < 2\n if (backboneOnly) {\n atomCount *= 10\n }\n\n if (atomCount < 15000) {\n return 'high'\n } else if (atomCount < 80000) {\n return 'medium'\n } else {\n return 'low'\n }\n }\n\n create () {\n if (this.structureView.atomCount === 0) return\n\n if (!this.structureView.hasCoords()) {\n this.needsBuild = true\n return\n } else {\n this.needsBuild = false\n }\n\n const assembly = this.getAssembly()\n\n if (assembly) {\n assembly.partList.forEach((part, i) => {\n const sview = part.getView(this.structureView)\n if (sview.atomCount === 0) return\n const data = this.createData(sview, i)\n if (data) {\n data.sview = sview\n data.instanceList = part.getInstanceList()\n this.dataList.push(data)\n }\n })\n } else {\n const data = this.createData(this.structureView, 0)\n if (data) {\n data.sview = this.structureView\n this.dataList.push(data)\n }\n }\n }\n\n abstract createData (sview: StructureView, k?: number): StructureRepresentationData|undefined\n\n update (what: AtomDataFields|BondDataFields) {\n if (this.lazy && !this.visible) {\n Object.assign(this.lazyProps.what, what)\n return\n }\n\n if (this.needsBuild) {\n this.build()\n return\n }\n\n this.dataList.forEach((data) => {\n if (data.bufferList.length > 0) {\n this.updateData(what, data)\n }\n }, this)\n }\n\n updateData (what?: AtomDataFields|BondDataFields, data?: any) {\n this.build()\n }\n\n getColorParams () {\n return {\n ...super.getColorParams(),\n structure: this.structure\n }\n }\n\n getRadiusParams (param?: any) {\n return {\n type: this.radiusType,\n scale: this.radiusScale,\n size: this.radiusSize,\n data: this.radiusData\n }\n }\n\n getAtomParams (what?: AtomDataFields, params?: AtomDataParams) {\n return Object.assign({\n what: what,\n colorParams: this.getColorParams(),\n radiusParams: this.getRadiusParams()\n }, params)\n }\n\n getBondParams (what?: BondDataFields, params?: BondDataParams) {\n return Object.assign({\n what: what,\n colorParams: this.getColorParams(),\n radiusParams: this.getRadiusParams()\n }, params)\n }\n\n getAtomRadius (atom: AtomProxy) {\n if (this.structureView.atomSet!.isSet(atom.index)) {\n const radiusFactory = new RadiusFactory(this.getRadiusParams())\n return radiusFactory.atomRadius(atom)\n }\n return 0\n }\n\n /**\n * Set representation parameters\n * @alias StructureRepresentation#setSelection\n * @param {String} string - selection string, see {@tutorial selection-language}\n * @param {Boolean} [silent] - don't trigger a change event in the selection\n * @return {StructureRepresentation} this object\n */\n setSelection (string: string, silent?: boolean) {\n this.selection.setString(string, silent)\n\n return this\n }\n\n /**\n * Set representation parameters\n * @alias StructureRepresentation#setParameters\n * @param {StructureRepresentationParameters} params - structure parameter object\n * @param {Object} [what] - buffer data attributes to be updated,\n * note that this needs to be implemented in the\n * derived classes. Generally it allows more\n * fine-grained control over updating than\n * forcing a rebuild.\n * @param {Boolean} what.position - update position data\n * @param {Boolean} what.color - update color data\n * @param {Boolean} [rebuild] - whether or not to rebuild the representation\n * @return {StructureRepresentation} this object\n */\n setParameters (params: Partial, what: AtomDataFields = {}, rebuild = false) {\n const p = params || {}\n\n this.setRadius(p.radius, p)\n\n if (p.radiusType !== undefined || p.radiusData !== undefined || p.radiusSize !== undefined || p.radiusScale !== undefined) {\n what.radius = true\n if (!ExtensionFragDepth || this.disableImpostor) {\n rebuild = true\n }\n }\n\n if (p.defaultAssembly !== undefined &&\n p.defaultAssembly !== this.defaultAssembly &&\n ((this.assembly === 'default' && p.assembly === undefined) ||\n p.assembly === 'default')\n ) {\n rebuild = true\n }\n\n super.setParameters(p, what, rebuild)\n\n return this\n }\n\n getParameters () {\n const params = Object.assign(\n super.getParameters(),\n {\n sele: this.selection ? this.selection.string : undefined,\n defaultAssembly: this.defaultAssembly\n }\n )\n\n return params\n }\n\n attach (callback: ()=> void) {\n const viewer = this.viewer\n const bufferList = this.bufferList\n\n this.dataList.forEach(function (data) {\n data.bufferList.forEach(function (buffer) {\n bufferList.push(buffer)\n viewer.add(buffer, data.instanceList)\n })\n })\n\n this.setVisibility(this.visible)\n callback()\n }\n\n clear () {\n this.dataList.length = 0\n\n super.clear()\n }\n\n dispose () {\n this.structureView.dispose()\n\n super.dispose()\n }\n}\n\nexport default StructureRepresentation\n","/**\n * @file Measurement Representation\n * @author Fred Ludlow \n * @private\n */\n\n// @ts-ignore: unused import Vector3, Matrix4 required for declaration only\nimport { Color, Vector3, Matrix4 } from 'three'\n\nimport Selection from '../selection/selection'\nimport { Browser } from '../globals'\nimport { defaults } from '../utils'\nimport StructureRepresentation, { StructureRepresentationParameters } from './structure-representation'\nimport { uniformArray, uniformArray3 } from '../math/array-utils'\nimport { Structure } from '../ngl';\nimport Viewer from '../viewer/viewer';\nimport StructureView from '../structure/structure-view';\nimport { LabelRepresentationParameters } from './label-representation';\nimport TextBuffer, { TextBufferData } from '../buffer/text-buffer';\nimport { GenericColor } from '../types'\n\nexport interface LabelDataField {\n position?: boolean\n labelColor?: boolean\n labelSize?: boolean\n radius?: boolean\n labelText?: boolean\n}\n\n/**\n * Measurement representation parameter object.\n * @typedef {Object} MeasurementRepresentationParameters - measurement representation parameters\n * @mixes RepresentationParameters\n * @mixes StructureRepresentationParameters\n *\n * @property {Float} labelSize - size of the distance label\n * @property {Color} labelColor - color of the distance label\n * @property {Boolean} labelVisible - visibility of the distance label\n * @property {Float} labelZOffset - offset in z-direction (i.e. in camera direction)\n */\nexport interface MeasurementRepresentationParameters extends StructureRepresentationParameters {\n labelVisible: boolean\n labelSize: number\n labelColor: GenericColor\n labelType: 'atomname'|'atomindex'|'occupancy'|'bfactor'|'serial'|'element'|'atom'|'resname'|'resno'|'res'|'text'|'qualified'\n labelText: string\n labelFormat: string\n labelGrouping: 'atom'|'residue'\n labelFontFamily: 'sans-serif'|'monospace'|'serif'\n labelFontStyle: 'normal'|'italic'\n labelFontWeight: 'normal'|'bold'\n labelsdf: boolean\n labelXOffset: number\n labelYOffset: number\n labelZOffset: number\n labelAttachment: 'bottom-left'|'bottom-center'|'bottom-right'|'middle-left'|'middle-center'|'middle-right'|'top-left'|'top-center'|'top-right'\n labelBorder: boolean\n labelBorderColor: GenericColor\n labelBorderWidth: number\n labelBackground: boolean\n labelBackgroundColor: GenericColor\n labelBackgroundMargin: number\n labelBackgroundOpacity: number\n labelFixedSize: boolean\n lineOpacity: number\n linewidth: number\n}\n\n/**\n * Measurement representation\n * @interface\n */\nabstract class MeasurementRepresentation extends StructureRepresentation {\n protected n: number\n protected labelVisible: boolean\n protected labelSize: number\n protected labelColor: GenericColor\n protected labelType: 'atomname'|'atomindex'|'occupancy'|'bfactor'|'serial'|'element'|'atom'|'resname'|'resno'|'res'|'text'|'qualified'\n protected labelText: string\n protected labelFormat: string\n protected labelGrouping: 'atom'|'residue'\n protected labelFontFamily: 'sans-serif'|'monospace'|'serif'\n protected labelFontStyle: 'normal'|'italic'\n protected labelFontWeight: 'normal'|'bold'\n protected labelsdf: boolean\n protected labelXOffset: number\n protected labelYOffset: number\n protected labelZOffset: number\n protected labelAttachment: 'bottom-left'|'bottom-center'|'bottom-right'|'middle-left'|'middle-center'|'middle-right'|'top-left'|'top-center'|'top-right'\n protected labelBorder: boolean\n protected labelBorderColor: GenericColor\n protected labelBorderWidth: number\n protected labelBackground: boolean\n protected labelBackgroundColor: GenericColor\n protected labelBackgroundMargin: number\n protected labelBackgroundOpacity: number\n protected labelFixedSize: boolean\n protected lineOpacity: number\n protected linewidth: number\n protected lineVisible: boolean\n\n protected textBuffer: TextBuffer\n /**\n * Handles common label settings and position logic for\n * distance, angle and dihedral representations\n */\n constructor (structure: Structure, viewer: Viewer, params: Partial) {\n super(structure, viewer, params)\n\n this.n = 0 // Subclass create sets value\n this.parameters = Object.assign({\n labelVisible: {\n type: 'boolean'\n },\n labelSize: {\n type: 'number', precision: 3, max: 10.0, min: 0.001\n },\n labelColor: {\n type: 'color'\n },\n labelFontFamily: {\n type: 'select',\n options: {\n 'sans-serif': 'sans-serif',\n 'monospace': 'monospace',\n 'serif': 'serif'\n },\n buffer: 'fontFamily'\n },\n labelFontStyle: {\n type: 'select',\n options: {\n 'normal': 'normal',\n 'italic': 'italic'\n },\n buffer: 'fontStyle'\n },\n labelFontWeight: {\n type: 'select',\n options: {\n 'normal': 'normal',\n 'bold': 'bold'\n },\n buffer: 'fontWeight'\n },\n labelsdf: {\n type: 'boolean', buffer: 'sdf'\n },\n labelXOffset: {\n type: 'number', precision: 1, max: 20, min: -20, buffer: 'xOffset'\n },\n labelYOffset: {\n type: 'number', precision: 1, max: 20, min: -20, buffer: 'yOffset'\n },\n labelZOffset: {\n type: 'number', precision: 1, max: 20, min: -20, buffer: 'zOffset'\n },\n labelAttachment: {\n type: 'select',\n options: {\n 'bottom-left': 'bottom-left',\n 'bottom-center': 'bottom-center',\n 'bottom-right': 'bottom-right',\n 'middle-left': 'middle-left',\n 'middle-center': 'middle-center',\n 'middle-right': 'middle-right',\n 'top-left': 'top-left',\n 'top-center': 'top-center',\n 'top-right': 'top-right'\n },\n rebuild: true\n },\n labelBorder: {\n type: 'boolean', buffer: 'showBorder'\n },\n labelBorderColor: {\n type: 'color', buffer: 'borderColor'\n },\n labelBorderWidth: {\n type: 'number', precision: 2, max: 0.3, min: 0, buffer: 'borderWidth'\n },\n labelBackground: {\n type: 'boolean', rebuild: true\n },\n labelBackgroundColor: {\n type: 'color', buffer: 'backgroundColor'\n },\n labelBackgroundMargin: {\n type: 'number', precision: 2, max: 2, min: 0, rebuild: true\n },\n labelBackgroundOpacity: {\n type: 'range', step: 0.01, max: 1, min: 0, buffer: 'backgroundOpacity'\n },\n labelFixedSize: {\n type: 'boolean', buffer: 'fixedSize'\n },\n lineOpacity: {\n type: 'range', min: 0.0, max: 1.0, step: 0.01\n },\n linewidth: {\n type: 'integer', max: 50, min: 1, buffer: true\n }\n }, this.parameters, {\n flatShaded: null\n })\n }\n\n init (params: Partial) {\n const p = params || {}\n this.labelVisible = defaults(p.labelVisible, true)\n this.labelSize = defaults(p.labelSize, 2.0)\n this.labelColor = defaults(p.labelColor, 0xFFFFFF)\n this.labelFontFamily = defaults(p.labelFontFamily, 'sans-serif')\n this.labelFontStyle = defaults(p.labelFontstyle, 'normal')\n this.labelFontWeight = defaults(p.labelFontWeight, 'bold')\n this.labelsdf = defaults(p.labelsdf, Browser === 'Chrome')\n this.labelXOffset = defaults(p.labelXOffset, 0.0)\n this.labelYOffset = defaults(p.labelYOffset, 0.0)\n this.labelZOffset = defaults(p.labelZOffset, 0.5)\n this.labelAttachment = defaults(p.labelAttachment, 'bottom-left')\n this.labelBorder = defaults(p.labelBorder, false)\n this.labelBorderColor = defaults(p.labelBorderColor, 'lightgrey')\n this.labelBorderWidth = defaults(p.labelBorderWidth, 0.15)\n this.labelBackground = defaults(p.labelBackground, false)\n this.labelBackgroundColor = defaults(p.labelBackgroundColor, 'lightgrey')\n this.labelBackgroundMargin = defaults(p.labelBackgroundMargin, 0.5)\n this.labelBackgroundOpacity = defaults(p.labelBackgroundOpacity, 1.0)\n this.labelFixedSize = defaults(p.labelFixedSize, false)\n this.lineOpacity = defaults(p.lineOpacity, 1.0)\n this.linewidth = defaults(p.linewidth, 2)\n\n super.init(p)\n }\n\n // All measurements need to rebuild on position change\n update (what: LabelDataField) {\n if (what.position) {\n this.build()\n } else {\n super.update(what)\n }\n }\n\n updateData (what: LabelDataField & {[k: string]: any}, data: any) {\n const textData: TextBufferData | {} = {}\n if (!what || what.labelSize) {\n Object.assign(textData, {size: uniformArray(this.n, this.labelSize)})\n }\n\n if (!what || what.labelColor) {\n const c = new Color(this.labelColor)\n Object.assign(textData, {color: uniformArray3(this.n, c.r, c.g, c.b)})\n }\n\n this.textBuffer.setAttributes(textData as TextBufferData)\n }\n\n setParameters (params: Partial, what: LabelDataField = {}, rebuild = false) {\n if (params && params.labelSize) {\n what.labelSize = true\n }\n\n if (params && (params.labelColor || params.labelColor === 0x000000)) {\n what.labelColor = true\n rebuild = true\n }\n\n super.setParameters(params, what, rebuild)\n\n if (params && params.opacity !== undefined) {\n this.textBuffer.setParameters({ opacity: 1.0 }) // only opaque labels\n }\n\n if (params && params.labelVisible !== undefined) {\n this.setVisibility(this.visible)\n }\n\n return this\n }\n\n setVisibility (value: boolean, noRenderRequest?: boolean) {\n super.setVisibility(value, true)\n if (this.textBuffer) {\n this.textBuffer.setVisibility(\n this.labelVisible && this.visible\n )\n }\n\n if (!noRenderRequest) this.viewer.requestRender()\n\n return this\n }\n\n getLabelBufferParams (params: Partial = {}) {\n return super.getBufferParams(Object.assign({\n fontFamily: this.labelFontFamily,\n fontStyle: this.labelFontStyle,\n fontWeight: this.labelFontWeight,\n sdf: this.labelsdf,\n xOffset: this.labelXOffset,\n yOffset: this.labelYOffset,\n zOffset: this.labelZOffset,\n attachment: this.labelAttachment,\n showBorder: this.labelBorder,\n borderColor: this.labelBorderColor,\n borderWidth: this.labelBorderWidth,\n showBackground: this.labelBackground,\n backgroundColor: this.labelBackgroundColor,\n backgroundMargin: this.labelBackgroundMargin,\n backgroundOpacity: this.labelBackgroundOpacity,\n fixedSize: this.labelFixedSize,\n disablePicking: true,\n visible: this.labelVisible\n }, params, {\n opacity: 1.0 // only opaque labels\n }))\n }\n\n getAtomRadius () {\n return 0\n }\n}\n\n/**\n * MeasurementRepresentations take atom[Pair|Triple|Quad] parameters.\n *\n * Parses nested array of either integer atom indices or selection\n * expressions into a flat array of coordinates.\n *\n * @param {Structure} sview The structure to which the atoms refer\n * @param {Array} atoms Nested array of atom pairs|triples|quads as\n * Integer indices or selection expressions\n * @return {Float32Array} Flattened array of position coordinates\n */\nfunction parseNestedAtoms (sview: StructureView, atoms: (number|string)[][]) {\n const ap = sview.getAtomProxy()\n const sele = new Selection()\n\n const nSets = atoms.length\n if (nSets === 0) return new Float32Array(0)\n\n // Peek-ahead at first item to determine order and parse mode\n const order = atoms[ 0 ].length\n const selected = sview.getAtomSet()\n\n const a = new Float32Array(nSets * order * 3)\n\n let p = 0\n atoms.forEach(function (group) {\n let _break = false\n for (let j = 0; j < order; j++) {\n const value = group[ j ]\n if (typeof (value) === 'number' && Number.isInteger(value)) {\n if (selected.get(value)) {\n ap.index = value\n } else {\n _break = true\n break\n }\n } else {\n sele.setString(value as string)\n const atomIndices = sview.getAtomIndices(sele)\n if (atomIndices!.length) {\n ap.index = atomIndices![ 0 ]\n } else {\n _break = true\n break\n }\n }\n\n let offset = p + j * 3\n a[ offset++ ] = ap.x\n a[ offset++ ] = ap.y\n a[ offset++ ] = ap.z\n }\n if (!_break) p += 3 * order\n })\n\n return a.subarray(0, p)\n}\n\n/* out = v1 * cos(angle) + v2 * sin(angle) */\nfunction calcArcPoint (out: Float32Array, center: Float32Array, v1: Float32Array, v2: Float32Array, angle: number) {\n const x = Math.cos(angle)\n const y = Math.sin(angle)\n out[ 0 ] = center[ 0 ] + v1[ 0 ] * x + v2[ 0 ] * y\n out[ 1 ] = center[ 1 ] + v1[ 1 ] * x + v2[ 1 ] * y\n out[ 2 ] = center[ 2 ] + v1[ 2 ] * x + v2[ 2 ] * y\n}\n\nexport {\n MeasurementRepresentation as default,\n calcArcPoint,\n parseNestedAtoms\n}\n","/**\n * @file Edt\n * @author Alexander Rose \n * @private\n */\n\nimport { NumberArray } from '../types'\n\n// 2D Euclidean distance transform by Felzenszwalb & Huttenlocher https://cs.brown.edu/~pff/papers/dt-final.pdf\nexport function edt(data: NumberArray, width: number, height: number, f: NumberArray, d: NumberArray, v: NumberArray, z: NumberArray) {\n for (let x = 0; x < width; x++) {\n for (let y = 0; y < height; y++) {\n f[y] = data[y * width + x]\n }\n edt1d(f, d, v, z, height)\n for (let y = 0; y < height; y++) {\n data[y * width + x] = d[y]\n }\n }\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n f[x] = data[y * width + x]\n }\n edt1d(f, d, v, z, width)\n for (let x = 0; x < width; x++) {\n data[y * width + x] = Math.sqrt(d[x])\n }\n }\n}\n\n// 1D squared distance transform\nfunction edt1d(f: NumberArray, d: NumberArray, v: NumberArray, z: NumberArray, n: number) {\n v[0] = 0\n z[0] = Number.MIN_SAFE_INTEGER\n z[1] = Number.MAX_SAFE_INTEGER\n\n for (let q = 1, k = 0; q < n; q++) {\n let s = ((f[q] + q * q) - (f[v[k]] + v[k] * v[k])) / (2 * q - 2 * v[k])\n while (s <= z[k]) {\n k--\n s = ((f[q] + q * q) - (f[v[k]] + v[k] * v[k])) / (2 * q - 2 * v[k])\n }\n k++\n v[k] = q\n z[k] = s\n z[k + 1] = Number.MAX_SAFE_INTEGER\n }\n\n for (let q = 0, k = 0; q < n; q++) {\n while (z[k + 1] < q) k++\n d[q] = (q - v[k]) * (q - v[k]) + f[v[k]]\n }\n}\n","/**\n * @file Text Buffer\n * @author Alexander Rose \n * @private\n */\n\n// @ts-ignore: unused import Vector3, Matrix4 required for declaration only\nimport { Color, CanvasTexture, Vector3, Matrix4 } from 'three'\n\nimport '../shader/SDFFont.vert'\nimport '../shader/SDFFont.frag'\n\nimport { BufferRegistry } from '../globals'\nimport { createParams } from '../utils'\nimport MappedQuadBuffer from './mappedquad-buffer'\nimport { IgnorePicker } from '../utils/picker'\nimport { edt } from '../utils/edt'\nimport { BufferDefaultParameters, BufferParameterTypes, BufferData, BufferTypes, BufferParameters } from './buffer'\nimport { GenericColor } from '../types'\n\nconst TextAtlasCache: { [k: string]: TextAtlas } = {}\n\nfunction getTextAtlas (params: Partial) {\n const hash = JSON.stringify(params)\n if (TextAtlasCache[ hash ] === undefined) {\n TextAtlasCache[ hash ] = new TextAtlas(params)\n }\n return TextAtlasCache[ hash ]\n}\n\ntype TextFonts = 'sans-serif'|'monospace'|'serif'\ntype TextStyles = 'normal'|'italic'\ntype TextVariants = 'normal'\ntype TextWeights = 'normal'|'bold'\n\nexport const TextAtlasDefaultParams = {\n font: 'sans-serif' as TextFonts,\n size: 36,\n style: 'normal' as TextStyles,\n variant: 'normal' as TextVariants,\n weight: 'normal' as TextWeights,\n outline: 3,\n width: 1024,\n height: 1024\n}\nexport type TextAtlasParams = typeof TextAtlasDefaultParams\n\nexport type TextAtlasMap = { x: number, y: number, w: number, h: number }\n\nexport class TextAtlas {\n parameters: TextAtlasParams\n\n gamma = 1\n mapped: { [k: string]: TextAtlasMap } = {}\n scratchW = 0\n scratchH = 0\n currentX = 0\n currentY = 0\n\n cutoff = 0.25\n padding: number\n radius: number\n\n gridOuter: Float64Array\n gridInner: Float64Array\n f: Float64Array\n d: Float64Array\n z: Float64Array\n v: Int16Array\n\n paddedSize: number\n middle: number\n\n texture: CanvasTexture\n canvas: HTMLCanvasElement\n context: CanvasRenderingContext2D\n\n lineHeight: number\n maxWidth: number\n colors: string[]\n scratch: Uint8Array\n canvas2: HTMLCanvasElement\n context2: CanvasRenderingContext2D\n data: Uint8Array\n\n placeholder: TextAtlasMap\n\n constructor (params: Partial = {}) {\n this.parameters = createParams(params, TextAtlasDefaultParams)\n const p = this.parameters\n\n this.radius = p.size / 8\n this.padding = p.size / 3\n\n // Prepare line-height with room for outline and descenders/ascenders\n const lineHeight = this.lineHeight = p.size + 2 * p.outline + Math.round(p.size / 4)\n const maxWidth = this.maxWidth = p.width / 4\n\n // Prepare scratch canvas\n const canvas = this.canvas = document.createElement('canvas')\n canvas.width = maxWidth\n canvas.height = lineHeight\n\n const ctx = this.context = this.canvas.getContext('2d', { willReadFrequently: true})!\n ctx.font = `${p.style} ${p.variant} ${p.weight} ${p.size}px ${p.font}`\n ctx.fillStyle = 'black'\n ctx.textAlign = 'left'\n ctx.textBaseline = 'bottom'\n ctx.lineJoin = 'round'\n\n // temporary arrays for the distance transform\n this.gridOuter = new Float64Array(lineHeight * maxWidth)\n this.gridInner = new Float64Array(lineHeight * maxWidth)\n this.f = new Float64Array(Math.max(lineHeight, maxWidth))\n this.d = new Float64Array(Math.max(lineHeight, maxWidth))\n this.z = new Float64Array(Math.max(lineHeight, maxWidth) + 1)\n this.v = new Int16Array(Math.max(lineHeight, maxWidth))\n\n //\n this.data = new Uint8Array(p.width * p.height * 4)\n this.canvas2 = document.createElement('canvas')\n this.canvas2.width = p.width\n this.canvas2.height = p.height\n this.context2 = this.canvas2.getContext('2d')!\n\n // Replacement Character\n this.placeholder = this.map(String.fromCharCode(0xFFFD))\n\n // Basic Latin (subset)\n for (let i = 0x0020; i <= 0x007E; ++i) {\n this.map(String.fromCharCode(i))\n }\n\n // TODO: to slow to always prepare them\n // // Latin-1 Supplement (subset)\n // for (let i = 0x00A1; i <= 0x00FF; ++i) {\n // this.map(String.fromCharCode(i))\n // }\n\n // Degree sign\n this.map(String.fromCharCode(0x00B0))\n\n // // Greek and Coptic (subset)\n // for (let i = 0x0391; i <= 0x03C9; ++i) {\n // this.map(String.fromCharCode(i))\n // }\n\n // // Cyrillic (subset)\n // for (let i = 0x0400; i <= 0x044F; ++i) {\n // this.map(String.fromCharCode(i))\n // }\n\n // Angstrom Sign\n this.map(String.fromCharCode(0x212B))\n\n this.texture = new CanvasTexture(this.canvas2)\n this.texture.flipY = false\n this.texture.needsUpdate = true\n }\n\n map (text: string) {\n const p = this.parameters\n\n if (this.mapped[ text ] === undefined) {\n this.draw(text)\n\n if (this.currentX + this.scratchW > p.width) {\n this.currentX = 0\n this.currentY += this.scratchH\n }\n if (this.currentY + this.scratchH > p.height) {\n console.warn('canvas to small')\n }\n\n this.mapped[ text ] = {\n x: this.currentX,\n y: this.currentY,\n w: this.scratchW,\n h: this.scratchH\n }\n\n this.context2.drawImage(\n this.canvas,\n 0, 0,\n this.scratchW, this.scratchH,\n this.currentX, this.currentY,\n this.scratchW, this.scratchH\n )\n\n this.currentX += this.scratchW\n }\n\n return this.mapped[ text ]\n }\n\n get (text: string) {\n return this.mapped[ text ] || this.placeholder\n }\n\n draw (text: string) {\n const p = this.parameters\n\n const h = this.lineHeight\n const o = p.outline\n const ctx = this.context\n // const dst = this.scratch\n const max = this.maxWidth\n // const colors = this.colors\n\n // Bottom aligned, take outline into account\n const x = o\n const y = h - p.outline\n\n // Measure text\n const m = ctx.measureText(text)\n const w = Math.min(max, Math.ceil(m.width + 2 * x + 1))\n\n const n = w * h\n\n // Clear scratch area\n ctx.clearRect(0, 0, w, h)\n\n // Draw text\n ctx.fillText(text, x, y)\n\n const imageData = ctx.getImageData(0, 0, w, h)\n const data = imageData.data\n\n for (let i = 0; i < n; i++) {\n const a = imageData.data[i * 4 + 3] / 255; // alpha value\n this.gridOuter[i] = a === 1 ? 0 : a === 0 ? Number.MAX_SAFE_INTEGER : Math.pow(Math.max(0, 0.5 - a), 2);\n this.gridInner[i] = a === 1 ? Number.MAX_SAFE_INTEGER : a === 0 ? 0 : Math.pow(Math.max(0, a - 0.5), 2);\n }\n\n edt(this.gridOuter, w, h, this.f, this.d, this.v, this.z);\n edt(this.gridInner, w, h, this.f, this.d, this.v, this.z);\n\n for (let i = 0; i < n; i++) {\n const d = this.gridOuter[i] - this.gridInner[i];\n data[i * 4 + 3] = Math.max(0, Math.min(255, Math.round(255 - 255 * (d / this.radius + this.cutoff))));\n }\n\n ctx.putImageData(imageData, 0, 0)\n this.scratchW = w\n this.scratchH = h\n }\n}\n\n/**\n * Text buffer parameter object.\n * @typedef {Object} TextBufferParameters - text buffer parameters\n *\n * @property {Float} opacity - translucency: 1 is fully opaque, 0 is fully transparent\n * @property {Integer} clipNear - position of camera near/front clipping plane\n * in percent of scene bounding box\n * @property {String} labelType - type of the label, one of:\n * \"atomname\", \"atomindex\", \"occupancy\", \"bfactor\",\n * \"serial\", \"element\", \"atom\", \"resname\", \"resno\",\n * \"res\", \"text\", \"qualified\". When set to \"text\", the\n * `labelText` list is used.\n * @property {String[]} labelText - list of label strings, must set `labelType` to \"text\"\n * to take effect\n * @property {String} fontFamily - font family, one of: \"sans-serif\", \"monospace\", \"serif\"\n * @property {String} fontStyle - font style, \"normal\" or \"italic\"\n * @property {String} fontWeight - font weight, \"normal\" or \"bold\"\n * @property {Float} xOffset - offset in x-direction\n * @property {Float} yOffset - offset in y-direction\n * @property {Float} zOffset - offset in z-direction (i.e. in camera direction)\n * @property {String} attachment - attachment of the label, one of:\n * \"bottom-left\", \"bottom-center\", \"bottom-right\",\n * \"middle-left\", \"middle-center\", \"middle-right\",\n * \"top-left\", \"top-center\", \"top-right\"\n * @property {Boolean} showBorder - show border/outline\n * @property {Color} borderColor - color of the border/outline\n * @property {Float} borderWidth - width of the border/outline\n * @property {Boolean} showBackground - show background rectangle\n * @property {Color} backgroundColor - color of the background\n * @property {Float} backgroundMargin - width of the background\n * @property {Float} backgroundOpacity - opacity of the background\n * @property {Boolean} fixedSize - show text with a fixed pixel size\n */\n\nexport interface TextBufferData extends BufferData {\n size: Float32Array\n text: string[]\n}\n\ntype TextAttachments = 'bottom-left'|'bottom-center'|'bottom-right'|'middle-left'|'middle-center'|'middle-right'|'top-left'|'top-center'|'top-right'\n\nexport const TextBufferDefaultParameters = Object.assign({\n fontFamily: 'sans-serif' as TextFonts,\n fontStyle: 'normal' as TextStyles,\n fontWeight: 'bold' as TextWeights,\n fontSize: 36,\n xOffset: 0.0,\n yOffset: 0.0,\n zOffset: 0.5,\n attachment: 'bottom-left' as TextAttachments,\n showBorder: false,\n borderColor: 'lightgrey' as number|string,\n borderWidth: 0.15,\n showBackground: false,\n backgroundColor: 'lightgrey' as number|string,\n backgroundMargin: 0.5,\n backgroundOpacity: 1.0,\n forceTransparent: true,\n fixedSize: false\n}, BufferDefaultParameters)\nexport type TextBufferParameters = BufferParameters & {\n fontFamily: TextFonts,\n fontStyle: TextStyles,\n fontWeight: TextWeights,\n fontSize: number,\n xOffset: number,\n yOffset: number,\n zOffset: number,\n attachment: TextAttachments,\n showBorder: boolean,\n borderColor: GenericColor,\n borderWidth: number,\n showBackground: boolean,\n backgroundColor: GenericColor,\n backgroundMargin: number,\n backgroundOpacity: number,\n forceTransparent: boolean,\n fixedSize: boolean\n}\n\nconst TextBufferParameterTypes = Object.assign({\n fontFamily: { uniform: true },\n fontStyle: { uniform: true },\n fontWeight: { uniform: true },\n fontSize: { uniform: true },\n xOffset: { uniform: true },\n yOffset: { uniform: true },\n zOffset: { uniform: true },\n showBorder: { uniform: true },\n borderColor: { uniform: true },\n borderWidth: { uniform: true },\n backgroundColor: { uniform: true },\n backgroundOpacity: { uniform: true },\n fixedSize: { updateShader: true }\n}, BufferParameterTypes)\n\nfunction getCharCount (data: TextBufferData, params: Partial) {\n const n = data.position!.length / 3\n let charCount = 0\n for (let i = 0; i < n; ++i) {\n charCount += data.text[ i ].length\n }\n if (params.showBackground) charCount += n\n\n return charCount\n}\n\n/**\n * Text buffer. Renders screen-aligned text strings.\n *\n * @example\n * var textBuffer = new TextBuffer({\n * position: new Float32Array([ 0, 0, 0 ]),\n * color: new Float32Array([ 1, 0, 0 ]),\n * size: new Float32Array([ 2 ]),\n * text: [ \"Hello\" ]\n * });\n */\nclass TextBuffer extends MappedQuadBuffer {\n parameterTypes = TextBufferParameterTypes\n get defaultParameters() { return TextBufferDefaultParameters }\n parameters: TextBufferParameters\n\n alwaysTransparent = true\n hasWireframe = false\n isText = true\n vertexShader = 'SDFFont.vert'\n fragmentShader = 'SDFFont.frag'\n\n text: string[]\n positionCount: number\n texture: CanvasTexture\n textAtlas: TextAtlas\n\n /**\n * @param {Object} data - attribute object\n * @param {Float32Array} data.position - positions\n * @param {Float32Array} data.color - colors\n * @param {Float32Array} data.size - sizes\n * @param {String[]} data.text - text strings\n * @param {TextBufferParameters} params - parameters object\n */\n constructor (data: TextBufferData, params: Partial = {}) {\n super({\n position: new Float32Array(getCharCount(data, params) * 3),\n color: new Float32Array(getCharCount(data, params) * 3),\n picking: new IgnorePicker()\n }, params)\n\n this.text = data.text\n this.positionCount = data.position!.length / 3\n\n this.addUniforms({\n 'fontTexture': { value: null },\n 'xOffset': { value: this.parameters.xOffset },\n 'yOffset': { value: this.parameters.yOffset },\n 'zOffset': { value: this.parameters.zOffset },\n 'ortho': { value: false },\n 'showBorder': { value: this.parameters.showBorder },\n 'borderColor': { value: new Color(this.parameters.borderColor as number) },\n 'borderWidth': { value: this.parameters.borderWidth },\n 'backgroundColor': { value: new Color(this.parameters.backgroundColor as number) },\n 'backgroundOpacity': { value: this.parameters.backgroundOpacity },\n 'canvasHeight': { value: 1.0 },\n 'pixelRatio': { value: 1.0 }\n })\n\n this.addAttributes({\n 'inputTexCoord': { type: 'v2', value: null },\n 'inputSize': { type: 'f', value: null }\n })\n\n this.setAttributes(data)\n\n this.makeTexture()\n this.makeMapping()\n }\n\n makeMaterial () {\n super.makeMaterial()\n\n const tex = this.texture\n\n const m = this.material\n m.transparent = true\n m.extensions.derivatives = true\n m.lights = false\n m.uniforms.fontTexture.value = tex\n m.needsUpdate = true\n\n const wm = this.wireframeMaterial\n wm.transparent = true\n wm.extensions.derivatives = true\n wm.lights = false\n wm.uniforms.fontTexture.value = tex\n wm.needsUpdate = true\n\n const pm = this.pickingMaterial\n pm.extensions.derivatives = true\n pm.lights = false\n pm.uniforms.fontTexture.value = tex\n pm.needsUpdate = true\n }\n\n setAttributes (data: Partial = {}) {\n let position, size, color\n let aPosition, inputSize, aColor\n\n const text = this.text\n const attributes = this.geometry.attributes as any // TODO\n\n if (data.position) {\n position = data.position\n aPosition = attributes.position.array\n attributes.position.needsUpdate = true\n }\n\n if (data.size) {\n size = data.size\n inputSize = attributes.inputSize.array\n attributes.inputSize.needsUpdate = true\n }\n\n if (data.color) {\n color = data.color\n aColor = attributes.color.array\n attributes.color.needsUpdate = true\n }\n\n const n = this.positionCount\n\n let j, o\n let iCharAll = 0\n let txt, iChar, nChar\n\n for (let v = 0; v < n; ++v) {\n o = 3 * v\n txt = text[ v ]\n nChar = txt.length\n if (this.parameters.showBackground) nChar += 1\n\n for (iChar = 0; iChar < nChar; ++iChar, ++iCharAll) {\n for (let m = 0; m < 4; m++) {\n j = iCharAll * 4 * 3 + (3 * m)\n\n if (position) {\n aPosition[ j ] = position[ o ]\n aPosition[ j + 1 ] = position[ o + 1 ]\n aPosition[ j + 2 ] = position[ o + 2 ]\n }\n\n if (size) {\n inputSize[ (iCharAll * 4) + m ] = size[ v ]\n }\n\n if (color) {\n aColor[ j ] = color[ o ]\n aColor[ j + 1 ] = color[ o + 1 ]\n aColor[ j + 2 ] = color[ o + 2 ]\n }\n }\n }\n }\n }\n\n makeTexture () {\n this.textAtlas = getTextAtlas({\n font: this.parameters.fontFamily,\n style: this.parameters.fontStyle,\n weight: this.parameters.fontWeight,\n size: this.parameters.fontSize\n })\n\n this.texture = this.textAtlas.texture\n }\n\n makeMapping () {\n const ta = this.textAtlas\n const text = this.text\n const attachment = this.parameters.attachment\n const margin = (ta.lineHeight * this.parameters.backgroundMargin * 0.1) - 10\n\n const attribs = this.geometry.attributes as any // TODO\n const inputTexCoord = attribs.inputTexCoord.array\n const inputMapping = attribs.mapping.array\n\n const n = this.positionCount\n let iCharAll = 0\n let c, i, txt, xadvance, iChar, nChar, xShift, yShift\n\n for (let v = 0; v < n; ++v) {\n txt = text[ v ]\n xadvance = 0\n nChar = txt.length\n\n // calculate width\n for (iChar = 0; iChar < nChar; ++iChar) {\n c = ta.get(txt[ iChar ])\n xadvance += c.w - 2 * ta.parameters.outline\n }\n\n // attachment\n if (attachment.startsWith('top')) {\n yShift = ta.lineHeight / 1.25\n } else if (attachment.startsWith('middle')) {\n yShift = ta.lineHeight / 2.5\n } else {\n yShift = 0 // \"bottom\"\n }\n if (attachment.endsWith('right')) {\n xShift = xadvance\n } else if (attachment.endsWith('center')) {\n xShift = xadvance / 2\n } else {\n xShift = 0 // \"left\"\n }\n xShift += ta.parameters.outline\n yShift += ta.parameters.outline\n\n // background\n if (this.parameters.showBackground) {\n i = iCharAll * 2 * 4\n inputMapping[ i + 0 ] = -ta.lineHeight / 6 - xShift - margin // top left\n inputMapping[ i + 1 ] = ta.lineHeight - yShift + margin\n inputMapping[ i + 2 ] = -ta.lineHeight / 6 - xShift - margin // bottom left\n inputMapping[ i + 3 ] = 0 - yShift - margin\n inputMapping[ i + 4 ] = xadvance + ta.lineHeight / 6 - xShift + 2 * ta.parameters.outline + margin // top right\n inputMapping[ i + 5 ] = ta.lineHeight - yShift + margin\n inputMapping[ i + 6 ] = xadvance + ta.lineHeight / 6 - xShift + 2 * ta.parameters.outline + margin // bottom right\n inputMapping[ i + 7 ] = 0 - yShift - margin\n inputTexCoord[ i + 0 ] = 10\n inputTexCoord[ i + 2 ] = 10\n inputTexCoord[ i + 4 ] = 10\n inputTexCoord[ i + 6 ] = 10\n iCharAll += 1\n }\n\n xadvance = 0\n\n for (iChar = 0; iChar < nChar; ++iChar, ++iCharAll) {\n c = ta.get(txt[ iChar ])\n i = iCharAll * 2 * 4\n\n inputMapping[ i + 0 ] = xadvance - xShift // top left\n inputMapping[ i + 1 ] = c.h - yShift\n inputMapping[ i + 2 ] = xadvance - xShift // bottom left\n inputMapping[ i + 3 ] = 0 - yShift\n inputMapping[ i + 4 ] = xadvance + c.w - xShift // top right\n inputMapping[ i + 5 ] = c.h - yShift\n inputMapping[ i + 6 ] = xadvance + c.w - xShift // bottom right\n inputMapping[ i + 7 ] = 0 - yShift\n\n const texWidth = ta.parameters.width\n const texHeight = ta.parameters.height\n\n const texCoords = [\n c.x / texWidth, c.y / texHeight, // top left\n c.x / texWidth, (c.y + c.h) / texHeight, // bottom left\n (c.x + c.w) / texWidth, c.y / texHeight, // top right\n (c.x + c.w) / texWidth, (c.y + c.h) / texHeight // bottom right\n ]\n inputTexCoord.set(texCoords, i)\n\n xadvance += c.w - 2 * ta.parameters.outline\n }\n }\n\n attribs.inputTexCoord.needsUpdate = true\n attribs.mapping.needsUpdate = true\n }\n\n getDefines (type: BufferTypes) {\n const defines = super.getDefines(type)\n\n if (this.parameters.fixedSize) {\n defines.FIXED_SIZE = 1\n }\n\n return defines\n }\n\n setUniforms (data: any) { // TODO\n if (data && (\n data.fontFamily !== undefined ||\n data.fontStyle !== undefined ||\n data.fontWeight !== undefined ||\n data.fontSize !== undefined\n )) {\n this.makeTexture()\n this.makeMapping()\n this.texture.needsUpdate = true\n data.fontTexture = this.texture\n }\n\n super.setUniforms(data)\n }\n}\n\nBufferRegistry.add('text', TextBuffer)\n\nexport default TextBuffer\n","/**\n * @file Wide Line Buffer\n * @author Alexander Rose \n * @private\n */\n\n// @ts-ignore: unused import Vector3 required for declaration only\nimport { Vector2, Vector3, Matrix4 } from 'three'\n\nimport '../shader/WideLine.vert'\nimport '../shader/WideLine.frag'\n\nimport { BufferRegistry } from '../globals'\nimport MappedQuadBuffer from './mappedquad-buffer'\nimport { BufferDefaultParameters, BufferParameterTypes, BufferData, BufferParameters } from './buffer'\n\nexport interface WideLineBufferData extends BufferData {\n position1: Float32Array\n position2: Float32Array\n color2: Float32Array\n}\n\nexport const WideLineBufferDefaultParameters = Object.assign({\n linewidth: 2\n}, BufferDefaultParameters)\nexport type WideLineBufferParameters = BufferParameters & { linewidth: number }\n\nconst WideLineBufferParameterTypes = Object.assign({\n linewidth: { uniform: true }\n}, BufferParameterTypes)\n\n/**\n * Wide Line buffer. Draws lines with a fixed width in pixels.\n *\n * @example\n * var lineBuffer = new WideLineBuffer({\n * position1: new Float32Array([ 0, 0, 0 ]),\n * position2: new Float32Array([ 1, 1, 1 ]),\n * color: new Float32Array([ 1, 0, 0 ]),\n * color2: new Float32Array([ 0, 1, 0 ])\n * });\n */\nclass WideLineBuffer extends MappedQuadBuffer {\n parameterTypes = WideLineBufferParameterTypes\n get defaultParameters() { return WideLineBufferDefaultParameters }\n parameters: WideLineBufferParameters\n\n vertexShader = 'WideLine.vert'\n fragmentShader ='WideLine.frag'\n\n constructor (data: Partial, params: Partial = {}) {\n super(data, params)\n\n if (!data.color2 && data.color) data.color2 = data.color\n\n this.addUniforms({\n 'linewidth': { value: this.parameters.linewidth },\n 'resolution': { value: new Vector2() },\n 'projectionMatrixInverse': { value: new Matrix4() }\n })\n\n this.addAttributes({\n 'position1': { type: 'v3', value: null },\n 'position2': { type: 'v3', value: null },\n 'color2': { type: 'c', value: null }\n })\n\n this.setAttributes(data)\n this.makeMapping()\n }\n\n setParameters (params: Partial) {\n super.setParameters(params)\n }\n}\n\nBufferRegistry.add('wideline', WideLineBuffer)\n\nexport default WideLineBuffer\n","/**\n * @file Angle Representation\n * @author Fred Ludlow \n * @private\n */\nimport { Color } from 'three'\n\nimport { RepresentationRegistry } from '../globals'\nimport MeasurementRepresentation, { parseNestedAtoms, calcArcPoint, MeasurementRepresentationParameters, LabelDataField } from './measurement-representation'\nimport { defaults } from '../utils'\n\nimport MeshBuffer from '../buffer/mesh-buffer'\nimport TextBuffer, { TextBufferData, TextBufferParameters } from '../buffer/text-buffer'\nimport WideLineBuffer, { WideLineBufferData } from '../buffer/wideline-buffer'\n\nimport { v3add, v3cross, v3dot, v3fromArray, v3length, v3new,\n v3normalize, v3sub, v3toArray } from '../math/vector-utils'\nimport { copyArray, uniformArray, uniformArray3 } from '../math/array-utils'\nimport { RAD2DEG } from '../math/math-constants'\nimport { getFixedLengthWrappedDashData } from '../geometry/dash'\nimport { Structure } from '../ngl';\nimport Viewer from '../viewer/viewer';\nimport StructureView from '../structure/structure-view';\nimport { BufferData } from '../buffer/buffer';\nimport { StructureRepresentationData, StructureRepresentationParameters } from './structure-representation';\n\n/**\n * @typedef {Object} AngleRepresentationParameters - angle representation parameters\n * @mixes RepresentationParameters\n * @mixes StructureRepresentationParameters\n * @mixes MeasurementRepresentationParameters\n *\n * @property {String} atomTriple - list of triplets of selection strings\n * or atom indices\n * @property {Boolean} vectorVisible - Indicate the 3 points for each angle by drawing lines 1-2-3\n * @property {Boolean} arcVisible - Show the arc outline for each angle\n * @property {Number} lineOpacity - opacity for the line part of the representation\n * @property {Number} linewidth - width for line part of representation\n * @property {Boolean} sectorVisible - Show the filled arc for each angle\n */\n\nexport interface AngleRepresentationParameters extends MeasurementRepresentationParameters {\n atomTriple: (number|string)[][]\n vectorVisible: boolean\n arcVisible: boolean\n lineOpacity: number\n lineWidth: number\n sectorVisible: boolean\n}\n\n/**\n * Angle representation object\n *\n * Reperesentation consists of four parts, visibility can be set for each\n * label - the text label with the angle size\n * vectors - lines joining the three points\n * sector - triangles representing the angle\n * arc - line bordering the sector\n *\n * @param {Structure} structure - the structure to measure angles in\n * @param {Viewer} viewer - a viewer object\n * @param {AngleRepresentationParameters} params - angle representation parameters\n */\nclass AngleRepresentation extends MeasurementRepresentation {\n protected atomTriple: (number|string)[][]\n protected vectorVisible: boolean\n protected arcVisible: boolean\n protected lineOpacity: number\n protected lineWidth: number\n protected sectorVisible: boolean\n protected vectorBuffer: WideLineBuffer\n arcLength: number\n sectorLength: number\n arcBuffer: WideLineBuffer\n sectorBuffer: MeshBuffer\n\n constructor (structure: Structure, viewer: Viewer, params: Partial) {\n super(structure, viewer, params)\n\n this.type = 'angle'\n\n this.parameters = Object.assign({\n atomTriple: {\n type: 'hidden', rebuild: true\n },\n vectorVisible: {\n type: 'boolean', default: true\n },\n arcVisible: {\n type: 'boolean', default: true\n },\n sectorVisible: {\n type: 'boolean', default: true\n }\n }, this.parameters)\n\n this.init(params)\n }\n\n init (params: Partial) {\n const p = params || {}\n p.side = defaults(p.side, 'double')\n p.opacity = defaults(p.opacity, 0.5)\n\n this.atomTriple = defaults(p.atomTriple, [])\n this.arcVisible = defaults(p.arcVisible, true)\n this.sectorVisible = defaults(p.sectorVisible, true)\n this.vectorVisible = defaults(p.vectorVisible, true)\n\n super.init(p)\n }\n\n createData (sview: StructureView) {\n if (!sview.atomCount || !this.atomTriple.length) return\n\n const atomPosition = atomTriplePositions(sview, this.atomTriple)\n const angleData = getAngleData(atomPosition)\n const n = this.n = angleData.labelPosition.length / 3\n\n const labelColor = new Color(this.labelColor)\n\n // Create buffers\n this.textBuffer = new TextBuffer({\n position: angleData.labelPosition,\n size: uniformArray(n, this.labelSize),\n color: uniformArray3(n, labelColor.r, labelColor.g, labelColor.b),\n text: angleData.labelText\n } as TextBufferData, this.getLabelBufferParams() as TextBufferParameters)\n\n const c = new Color(this.colorValue)\n\n this.vectorBuffer = new WideLineBuffer(\n getFixedLengthWrappedDashData({\n position1: angleData.vectorPosition1,\n position2: angleData.vectorPosition2,\n color: uniformArray3(2 * n, c.r, c.g, c.b),\n color2: uniformArray3(2 * n, c.r, c.g, c.b)\n } as WideLineBufferData),\n this.getBufferParams({\n linewidth: this.linewidth,\n visible: this.vectorVisible,\n opacity: this.lineOpacity\n })\n )\n\n this.arcLength = angleData.arcPosition1.length / 3\n\n this.arcBuffer = new WideLineBuffer(\n getFixedLengthWrappedDashData({\n position1: angleData.arcPosition1,\n position2: angleData.arcPosition2,\n color: uniformArray3(this.arcLength, c.r, c.g, c.b),\n color2: uniformArray3(this.arcLength, c.r, c.g, c.b)\n } as WideLineBufferData), this.getBufferParams({\n linewidth: this.linewidth,\n visible: this.arcVisible,\n opacity: this.lineOpacity\n }))\n\n this.sectorLength = angleData.sectorPosition.length / 3\n\n this.sectorBuffer = new MeshBuffer({\n position: angleData.sectorPosition,\n color: uniformArray3(this.sectorLength, c.r, c.g, c.b)\n } as BufferData, this.getBufferParams({\n visible: this.sectorVisible\n }))\n\n return {\n bufferList: [\n this.textBuffer,\n this.vectorBuffer,\n this.arcBuffer,\n this.sectorBuffer\n ]\n }\n }\n\n updateData (what: LabelDataField & {color?: boolean}, data: StructureRepresentationData) {\n super.updateData(what, data)\n const vectorData = {}\n const arcData = {}\n const sectorData = {}\n\n if (what.color) {\n const c = new Color(this.colorValue)\n Object.assign(vectorData, {\n color: uniformArray3(this.n * 2, c.r, c.g, c.b),\n color2: uniformArray3(this.n * 2, c.r, c.g, c.b)\n })\n Object.assign(arcData, {\n color: uniformArray3(this.arcLength, c.r, c.g, c.b),\n color2: uniformArray3(this.arcLength, c.r, c.g, c.b)\n })\n Object.assign(sectorData, {\n color: uniformArray3(this.sectorLength, c.r, c.g, c.b)\n })\n }\n\n // if (what.sectorOpacity) {\n // this.sectorBuffer.opacity = what.sectorOpacity\n // }\n\n this.vectorBuffer.setAttributes(vectorData)\n this.arcBuffer.setAttributes(arcData)\n this.sectorBuffer.setAttributes(sectorData)\n }\n\n setParameters (params: Partial) {\n var rebuild = false\n var what = {}\n\n super.setParameters(params, what, rebuild)\n\n if (params && (\n params.vectorVisible !== undefined ||\n params.arcVisible !== undefined ||\n params.sectorVisible !== undefined)) {\n this.setVisibility(this.visible)\n }\n\n if (params && params.lineOpacity) {\n this.vectorBuffer.setParameters({ opacity: params.lineOpacity })\n this.arcBuffer.setParameters({ opacity: params.lineOpacity })\n }\n\n if (params && params.opacity !== undefined) {\n this.vectorBuffer.setParameters({ opacity: this.lineOpacity })\n this.arcBuffer.setParameters({ opacity: this.lineOpacity })\n }\n\n if (params && params.linewidth) {\n this.vectorBuffer.setParameters({ linewidth: params.linewidth })\n this.arcBuffer.setParameters({ linewidth: params.linewidth })\n }\n\n return this\n }\n\n setVisibility (value: boolean, noRenderRequest?: boolean) {\n super.setVisibility(value, true)\n\n if (this.vectorBuffer) {\n this.vectorBuffer.setVisibility(this.vectorVisible && this.visible)\n }\n\n if (this.arcBuffer) {\n this.arcBuffer.setVisibility(this.arcVisible && this.visible)\n }\n\n if (this.sectorBuffer) {\n this.sectorBuffer.setVisibility(this.sectorVisible && this.visible)\n }\n\n if (!noRenderRequest) this.viewer.requestRender()\n\n return this\n }\n}\n\n/**\n * Ensure mid point does not coincide with first or second\n * @param {Float32Array} position 9*nAngle array of coordinates\n * @return {Float32Array} Filtered position array, may be shorter\n */\nfunction validatePositions (position: Float32Array) {\n const include = []\n const n = position.length / 9\n for (let i = 0; i < n; i++) {\n // Check that first point not same as second and that second not same as third\n let okay = true\n for (let j = i; j < i + 3; j += 3) {\n if (position[j] === position[j + 3] &&\n position[j + 1] === position[j + 4] &&\n position[j + 2] === position[j + 5]) {\n okay = false\n }\n }\n if (okay) include.push(i)\n }\n const outPosition = new Float32Array(include.length * 9)\n let outIdx = 0\n include.forEach(function (i) {\n copyArray(position, outPosition, i * 9, outIdx * 9, 9)\n outIdx++\n })\n return outPosition\n}\n\nfunction atomTriplePositions (sview: StructureView, atomTriple: (number|string)[][]) {\n return validatePositions(parseNestedAtoms(sview, atomTriple))\n}\n\n/**\n * Converts triple positions into data required to build various buffers.\n */\nfunction getAngleData (position: Float32Array, params: Partial = {}) {\n const angleStep = defaults(params.angleStep, Math.PI / 90)\n const n = position.length / 9\n const angles = new Float32Array(n)\n const labelPosition = new Float32Array(n * 3)\n const labelText = new Array(n)\n\n const vectorPosition1 = new Float32Array(n * 6) // Two lines per angle\n const vectorPosition2 = new Float32Array(n * 6)\n\n const arcPositionTmp1 = new Array(n) // Start points for arc lines\n const arcPositionTmp2 = new Array(n) // End points for arc lines\n const sectorPositionTmp = new Array(n) // Triangle points\n\n let totalSegments = 0\n\n // Re-used vectors etc\n const p1 = v3new() // Positions of points for each angel\n const p2 = v3new()\n const p3 = v3new()\n const v21 = v3new() // Vectors\n const v23 = v3new()\n const cross = v3new() // Cross product v21xv23\n const cross2 = v3new() // In-plane cross product v21 x (v21 x v23)\n const labelTmp = v3new()\n const arcPoint = v3new()\n\n for (var i = 0; i < n; i++) {\n let p = 9 * i\n v3fromArray(p1, position, p)\n v3fromArray(p2, position, p + 3)\n v3fromArray(p3, position, p + 6)\n\n let v = 6 * i\n v3toArray(p1, vectorPosition1, v)\n v3toArray(p2, vectorPosition2, v)\n v3toArray(p2, vectorPosition1, v + 3)\n v3toArray(p3, vectorPosition2, v + 3)\n\n v3sub(v21, p1, p2)\n v3sub(v23, p3, p2)\n\n v3normalize(v21, v21) // validatePositions ensures valid\n v3normalize(v23, v23)\n\n v3cross(cross, v21, v23)\n const crossLength = v3length(cross)\n const dot = v3dot(v21, v23)\n\n const angle = angles[i] = Math.atan2(crossLength, dot)\n labelText[i] = (RAD2DEG * angle).toFixed(1) + String.fromCharCode(0x00B0)\n\n if (v3length(cross) === 0.0) {\n // Angle exactly 0/180, pick an arbitrary direction\n cross[ 0 ] = 1.0\n cross[ 1 ] = 0.0\n cross[ 2 ] = 0.0\n }\n v3cross(cross2, cross, v21)\n v3normalize(cross2, cross2)\n\n calcArcPoint(labelTmp, p2, v21, cross2, angle / 2.0)\n // TODO: Scale label position?\n v3toArray(labelTmp, labelPosition, 3 * i)\n\n // Build the arc and sector\n\n const nSegments = Math.ceil(angle / angleStep)\n const sectorVertices = new Float32Array(nSegments * 9)\n sectorPositionTmp[ i ] = sectorVertices\n const arcVertices1 = new Float32Array(nSegments * 3)\n const arcVertices2 = new Float32Array(nSegments * 3)\n arcPositionTmp1[ i ] = arcVertices1\n arcPositionTmp2[ i ] = arcVertices2\n\n v3add(arcPoint, p2, v21) // Our initial arc point\n\n const appendArcSection = function (a: number, j: number) {\n const si = j * 9\n const ai = j * 3\n v3toArray(p2, sectorVertices, si)\n v3toArray(arcPoint, sectorVertices, si + 3)\n v3toArray(arcPoint, arcVertices1, ai)\n\n calcArcPoint(arcPoint, p2, v21, cross2, a)\n\n v3toArray(arcPoint, sectorVertices, si + 6)\n v3toArray(arcPoint, arcVertices2, ai)\n }\n\n let j = 0\n for (let a = angleStep; a < angle; a += angleStep) {\n appendArcSection(a, j)\n j++\n }\n appendArcSection(angle, j)\n totalSegments += nSegments\n }\n\n // Flatten nested arrays of arc/segment points\n const arcSize = totalSegments * 3\n const sectorSize = totalSegments * 9\n const arcPosition1 = new Float32Array(arcSize)\n const arcPosition2 = new Float32Array(arcSize)\n const sectorPosition = new Float32Array(sectorSize)\n\n let sectorOffset = 0\n let arcOffset = 0\n for (let i = 0; i < n; i++) {\n const ap1 = arcPositionTmp1[ i ]\n const ap2 = arcPositionTmp2[ i ]\n copyArray(ap1, arcPosition1, 0, arcOffset, ap1.length)\n copyArray(ap2, arcPosition2, 0, arcOffset, ap2.length)\n arcOffset += ap1.length // === ap2.length\n\n const sp = sectorPositionTmp[ i ]\n copyArray(sp, sectorPosition, 0, sectorOffset, sp.length)\n sectorOffset += sp.length\n }\n\n return {\n labelPosition,\n labelText,\n vectorPosition1,\n vectorPosition2,\n arcPosition1,\n arcPosition2,\n sectorPosition\n }\n}\n\nRepresentationRegistry.add('angle', AngleRepresentation)\n\nexport default AngleRepresentation\n","/**\n * @file Cylinder Geometry Buffer\n * @author Alexander Rose \n * @private\n */\n\nimport { Matrix4, Vector3, CylinderGeometry } from 'three'\n\nimport { defaults } from '../utils'\nimport { calculateCenterArray, serialBlockArray } from '../math/array-utils'\nimport GeometryBuffer from './geometry-buffer'\nimport { CylinderBufferData } from './cylinder-buffer'\nimport { BufferDefaultParameters, BufferParameters } from './buffer'\n\nconst scale = new Vector3()\nconst eye = new Vector3()\nconst target = new Vector3()\nconst up = new Vector3(0, 1, 0)\n\nexport const CylinderGeometryBufferDefaultParameters = Object.assign({\n radialSegments: 1,\n openEnded: true\n}, BufferDefaultParameters)\nexport type CylinderGeometryBufferParameters = BufferParameters & {radialSegments: number, openEnded: boolean}\n\nfunction getData (data: CylinderBufferData, params: Partial = {}) {\n const geo = getGeo(params)\n\n const n = data.position1.length\n\n const geoLength = (geo.attributes as any).position.array.length / 3\n const count = n / 3\n const primitiveId = new Float32Array(count * 2 * geoLength)\n serialBlockArray(count, geoLength, 0, primitiveId)\n serialBlockArray(count, geoLength, count * geoLength, primitiveId)\n\n const position = new Float32Array(n * 2)\n const color = new Float32Array(n * 2)\n\n return {\n position, color, primitiveId, picking: data.picking\n }\n}\n\nfunction getGeo (params: Partial = {}) {\n const radialSegments = defaults(params.radialSegments, 10)\n const openEnded = defaults(params.openEnded, true)\n const matrix = new Matrix4().makeRotationX(Math.PI / 2)\n\n const geo = new CylinderGeometry(\n 1, // radiusTop,\n 1, // radiusBottom,\n 1, // height,\n radialSegments, // radialSegments,\n 1, // heightSegments,\n openEnded // openEnded\n )\n geo.applyMatrix4(matrix)\n\n return geo\n}\n\n/**\n * Cylinder geometry buffer.\n *\n * @example\n * var cylinderGeometryBuffer = new CylinderGeometryBuffer({\n * position1: new Float32Array([ 0, 0, 0 ]),\n * position2: new Float32Array([ 1, 1, 1 ]),\n * color: new Float32Array([ 1, 0, 0 ]),\n * color2: new Float32Array([ 0, 1, 0 ]),\n * radius: new Float32Array([ 1 ])\n * });\n */\nclass CylinderGeometryBuffer extends GeometryBuffer {\n updateNormals = true\n\n get defaultParameters() { return CylinderGeometryBufferDefaultParameters }\n parameters: CylinderGeometryBufferParameters\n\n __center: Float32Array\n _position: Float32Array\n _color: Float32Array\n _from: Float32Array\n _to: Float32Array\n _radius: Float32Array\n\n /**\n * @param {Object} data - buffer data\n * @param {Float32Array} data.position1 - from positions\n * @param {Float32Array} data.position2 - to positions\n * @param {Float32Array} data.color - from colors\n * @param {Float32Array} data.color2 - to colors\n * @param {Float32Array} data.radius - radii\n * @param {Picker} [data.picking] - picking ids\n * @param {BufferParameters} [params] - parameters object\n */\n constructor (data: CylinderBufferData, params: Partial = {}) {\n super(getData(data, params), params, getGeo(params))\n\n const n = data.position1.length\n const m = data.radius.length\n\n this.__center = new Float32Array(n)\n this._position = new Float32Array(n * 2)\n this._color = new Float32Array(n * 2)\n this._from = new Float32Array(n * 2)\n this._to = new Float32Array(n * 2)\n this._radius = new Float32Array(m * 2)\n\n this.setAttributes(data, true)\n }\n\n applyPositionTransform (matrix: Matrix4, i: number, i3: number) {\n eye.fromArray(this._from as any, i3)\n target.fromArray(this._to as any, i3)\n matrix.lookAt(eye, target, up)\n\n const r = this._radius[ i ]\n scale.set(r, r, eye.distanceTo(target))\n matrix.scale(scale)\n }\n\n setAttributes (data: Partial = {}, initNormals?: boolean) {\n const meshData: Partial = {}\n\n if (data.position1 && data.position2) {\n calculateCenterArray(\n data.position1, data.position2, this.__center\n )\n calculateCenterArray(\n data.position1, this.__center, this._position\n )\n calculateCenterArray(\n this.__center, data.position2, this._position, data.position1.length\n )\n this._from.set(data.position1)\n this._from.set(this.__center, data.position1.length)\n this._to.set(this.__center)\n this._to.set(data.position2, this.__center.length)\n meshData.position = this._position\n }\n\n if (data.color && data.color2) {\n this._color.set(data.color)\n this._color.set(data.color2, data.color.length)\n meshData.color = this._color\n }\n\n if (data.radius) {\n this._radius.set(data.radius)\n this._radius.set(data.radius, data.radius.length)\n meshData.radius = this._radius\n }\n\n super.setAttributes(meshData, initNormals)\n }\n}\n\nexport default CylinderGeometryBuffer\n","/**\n * @file Mapped Aligned Box Buffer\n * @author Alexander Rose \n * @private\n */\n\nimport { BufferParameters, BufferData } from './buffer'\nimport MappedBuffer from './mapped-buffer'\n\n// +Y /\n// 0**********2\n// * | / **\n// * |/ * *\n// -----------3---- +X\n// * /| * *\n// * / | * *\n// 1/**|******4\n// / | * *\n// / | ** \n// +Z | 5 \nconst mapping = new Float32Array([\n -1.0, 1.0, -1.0,\n -1.0, -1.0, -1.0,\n 1.0, 1.0, -1.0,\n 1.0, 1.0, 1.0,\n 1.0, -1.0, -1.0,\n 1.0, -1.0, 1.0\n])\n\nconst mappingIndices = new Uint16Array([\n 0, 1, 2,\n 1, 4, 2,\n 2, 4, 3,\n 4, 5, 3\n])\n\n/**\n * Mapped Aligned box buffer. Draws boxes where one side is always screen-space aligned.\n * Used to render cylinder imposters.\n * @interface\n */\nclass MappedAlignedBoxBuffer extends MappedBuffer {\n constructor(data: BufferData, params: Partial = {}) {\n super('v3', data, params)\n }\n get mapping () { return mapping }\n get mappingIndices () { return mappingIndices }\n get mappingIndicesSize () { return 12 }\n get mappingSize () { return 6 }\n get mappingItemSize () { return 3 }\n}\n\nexport default MappedAlignedBoxBuffer\n","/**\n * @file Cylinder Impostor Buffer\n * @author Alexander Rose \n * @private\n */\n\n// @ts-ignore: unused import Vector3 required for declaration only\nimport { Matrix4, Vector3 } from 'three'\n\nimport '../shader/CylinderImpostor.vert'\nimport '../shader/CylinderImpostor.frag'\n\nimport MappedAlignedBoxBuffer from './mappedalignedbox-buffer'\nimport { BufferDefaultParameters, BufferParameters, BufferParameterTypes, BufferTypes } from './buffer'\nimport { CylinderBufferData } from './cylinder-buffer'\n\nexport const CylinderImpostorBufferDefaultParameters = Object.assign({\n openEnded: false\n}, BufferDefaultParameters)\nexport type CylinderImpostorBufferParameters = BufferParameters & { openEnded: boolean }\n\nconst CylinderImpostorBufferParameterTypes = Object.assign({\n openEnded: { updateShader: true }\n}, BufferParameterTypes)\n\n/**\n * Cylinder impostor buffer.\n *\n * @example\n * var cylinderimpostorBuffer = new CylinderImpostorBuffer({\n * position1: new Float32Array([ 0, 0, 0 ]),\n * position2: new Float32Array([ 1, 1, 1 ]),\n * color: new Float32Array([ 1, 0, 0 ]),\n * color2: new Float32Array([ 0, 1, 0 ]),\n * radius: new Float32Array([ 1 ])\n * });\n */\nclass CylinderImpostorBuffer extends MappedAlignedBoxBuffer {\n parameterTypes = CylinderImpostorBufferParameterTypes\n get defaultParameters() { return CylinderImpostorBufferDefaultParameters }\n parameters: CylinderImpostorBufferParameters\n\n isImpostor = true\n vertexShader = 'CylinderImpostor.vert'\n fragmentShader = 'CylinderImpostor.frag'\n\n /**\n * make cylinder impostor buffer\n * @param {Object} data - attribute object\n * @param {Float32Array} data.position1 - from positions\n * @param {Float32Array} data.position2 - to positions\n * @param {Float32Array} data.color - from colors\n * @param {Float32Array} data.color2 - to colors\n * @param {Float32Array} data.radius - radii\n * @param {Picker} data.picking - picking ids\n * @param {BufferParameters} params - parameter object\n */\n constructor (data: CylinderBufferData, params: Partial = {}) {\n super(data, params)\n\n this.addUniforms({\n 'modelViewMatrixInverse': { value: new Matrix4() },\n 'ortho': { value: 0.0 }\n })\n\n this.addAttributes({\n 'position1': { type: 'v3', value: null },\n 'position2': { type: 'v3', value: null },\n 'color2': { type: 'c', value: null },\n 'radius': { type: 'f', value: null }\n })\n\n this.setAttributes(data)\n this.makeMapping()\n }\n\n getDefines (type?: BufferTypes) {\n const defines = MappedAlignedBoxBuffer.prototype.getDefines.call(this, type)\n\n if (!this.parameters.openEnded) {\n defines.CAP = 1\n }\n\n return defines\n }\n}\n\nexport default CylinderImpostorBuffer\n","/**\n * @file Cylinder Buffer\n * @author Alexander Rose \n * @private\n */\n\n// @ts-ignore: unused import required for declaration only\nimport { Vector3, Matrix4 } from 'three'\nimport { BufferRegistry, ExtensionFragDepth } from '../globals'\nimport CylinderGeometryBuffer, { CylinderGeometryBufferDefaultParameters, CylinderGeometryBufferParameters } from './cylindergeometry-buffer'\nimport CylinderImpostorBuffer, { CylinderImpostorBufferDefaultParameters, CylinderImpostorBufferParameters } from './cylinderimpostor-buffer'\nimport { BufferData } from './buffer'\n\nexport interface CylinderBufferData extends BufferData {\n position1: Float32Array\n position2: Float32Array\n color2: Float32Array\n radius: Float32Array\n}\n\nexport const CylinderBufferDefaultParameters = Object.assign({\n disableImpostor: false\n}, CylinderGeometryBufferDefaultParameters, CylinderImpostorBufferDefaultParameters)\nexport type CylinderBufferParameters = (CylinderGeometryBufferParameters & {disableImpostor: boolean}) | (CylinderImpostorBufferParameters & {disableImpostor: boolean})\n\nclass CylinderBufferImpl {\n constructor (data: CylinderBufferData, params: Partial = {}) {\n if (!data.color2 && data.color) data.color2 = data.color\n if (!ExtensionFragDepth || (params && params.disableImpostor)) {\n return new CylinderGeometryBuffer(data, params)\n } else {\n return new CylinderImpostorBuffer(data, params)\n }\n }\n}\n\n/**\n * Cylinder buffer. Depending on the value {@link ExtensionFragDepth} and\n * `params.disableImpostor` the constructor returns either a\n * {@link CylinderGeometryBuffer} or a {@link CylinderImpostorBuffer}\n * @implements {Buffer}\n *\n * @example\n * var cylinderBuffer = new CylinderBuffer({\n * position1: new Float32Array([ 0, 0, 0 ]),\n * position2: new Float32Array([ 1, 1, 1 ]),\n * color: new Float32Array([ 1, 0, 0 ]),\n * color2: new Float32Array([ 0, 1, 0 ]),\n * radius: new Float32Array([ 1 ])\n * });\n */\n//@ts-expect-error Incompatible constructor signatures\nconst CylinderBuffer: {\n new(data: CylinderBufferData, params: Partial): CylinderGeometryBuffer | CylinderImpostorBuffer;\n} = CylinderBufferImpl;\n\ntype CylinderBuffer = CylinderGeometryBuffer | CylinderImpostorBuffer;\n\nBufferRegistry.add('cylinder', CylinderBuffer)\n\nexport default CylinderBuffer\n","/**\n * @file Axes Representation\n * @author Alexander Rose \n * @private\n */\n\nimport { Color, Vector3 } from 'three'\n\nimport { RepresentationRegistry } from '../globals'\nimport { defaults } from '../utils'\nimport { AxesPicker } from '../utils/picker'\nimport { uniformArray, uniformArray3 } from '../math/array-utils'\nimport StructureRepresentation, { StructureRepresentationParameters, StructureRepresentationData } from './structure-representation'\nimport SphereBuffer, { SphereBufferData, SphereBufferParameters } from '../buffer/sphere-buffer'\nimport CylinderBuffer, { CylinderBufferData } from '../buffer/cylinder-buffer'\nimport StructureView from '../structure/structure-view';\nimport Viewer from '../viewer/viewer';\nimport { Structure } from '../ngl';\nimport { AtomDataFields } from '../structure/structure-data';\nimport SphereGeometryBuffer from '../buffer/spheregeometry-buffer';\nimport CylinderGeometryBuffer from '../buffer/cylindergeometry-buffer';\nimport PrincipalAxes from '../math/principal-axes';\n\nexport interface AxesRepresentationParameters extends StructureRepresentationParameters {\n showAxes: boolean\n showBox: boolean\n}\n\n/**\n * Axes representation. Show principal axes and/or a box aligned with them\n * that fits the structure or selection.\n *\n * __Name:__ _axes_\n *\n * @example\n * stage.loadFile( \"rcsb://3pqr\", {\n * assembly: \"BU1\"\n * } ).then( function( o ){\n * o.addRepresentation( \"cartoon\" );\n * o.addRepresentation( \"axes\", {\n * sele: \"RET\", showAxes: false, showBox: true, radius: 0.2\n * } );\n * o.addRepresentation( \"ball+stick\", { sele: \"RET\" } );\n * o.addRepresentation( \"axes\", {\n * sele: \":B and backbone\", showAxes: false, showBox: true, radius: 0.2\n * } );\n * stage.autoView();\n * var pa = o.structure.getPrincipalAxes();\n * stage.animationControls.rotate( pa.getRotationQuaternion(), 1500 );\n * } );\n */\nclass AxesRepresentation extends StructureRepresentation {\n \n protected showAxes: boolean\n protected showBox: boolean\n protected sphereBuffer: SphereBuffer\n protected cylinderBuffer: CylinderBuffer\n /**\n * @param {Structure} structure - the structure object\n * @param {Viewer} viewer - the viewer object\n * @param {StructureRepresentationParameters} params - parameters object\n */\n constructor (structure: Structure, viewer: Viewer, params: Partial) {\n super(structure, viewer, params)\n\n this.type = 'axes'\n\n this.parameters = Object.assign({\n\n radiusSize: {\n type: 'number', precision: 3, max: 10.0, min: 0.001\n },\n sphereDetail: true,\n radialSegments: true,\n disableImpostor: true,\n showAxes: {\n type: 'boolean', rebuild: true\n },\n showBox: {\n type: 'boolean', rebuild: true\n }\n\n }, this.parameters, {\n assembly: null\n })\n\n this.init(params)\n }\n\n init (params: Partial) {\n const p = params || {}\n p.radiusSize = defaults(p.radiusSize, 0.5)\n p.colorValue = defaults(p.colorValue, 'lightgreen')\n p.useInteriorColor = defaults(p.useInteriorColor, true)\n\n this.showAxes = defaults(p.showAxes, true)\n this.showBox = defaults(p.showBox, false)\n\n super.init(p)\n }\n\n getPrincipalAxes (): PrincipalAxes {\n let selection\n const assembly = this.getAssembly()\n\n if (assembly) {\n selection = assembly.partList[ 0 ].getSelection()\n }\n\n return this.structureView.getPrincipalAxes(selection)\n }\n\n getAxesData (sview: StructureView) {\n const pa = this.getPrincipalAxes()\n const c = new Color(this.colorValue)\n\n let vn = 0\n let en = 0\n\n if (this.showAxes) {\n vn += 6\n en += 3\n }\n\n if (this.showBox) {\n vn += 8\n en += 12\n }\n\n const vertexPosition = new Float32Array(3 * vn)\n const vertexColor = uniformArray3(vn, c.r, c.g, c.b)\n const vertexRadius = uniformArray(vn, this.radiusSize)\n\n const edgePosition1 = new Float32Array(3 * en)\n const edgePosition2 = new Float32Array(3 * en)\n const edgeColor = uniformArray3(en, c.r, c.g, c.b)\n const edgeRadius = uniformArray(en, this.radiusSize)\n\n let offset = 0\n\n if (this.showAxes) {\n const addAxis = function (v1: Vector3, v2: Vector3) {\n v1.toArray(vertexPosition as any, offset * 2)\n v2.toArray(vertexPosition as any, offset * 2 + 3)\n v1.toArray(edgePosition1 as any, offset)\n v2.toArray(edgePosition2 as any, offset)\n offset += 3\n }\n\n addAxis(pa.begA, pa.endA)\n addAxis(pa.begB, pa.endB)\n addAxis(pa.begC, pa.endC)\n }\n\n if (this.showBox) {\n const v = new Vector3()\n const { d1a, d2a, d3a, d1b, d2b, d3b } = pa.getProjectedScaleForAtoms(sview)\n\n // console.log(d1a, d2a, d3a, d1b, d2b, d3b)\n\n let offset2 = offset * 2\n const addCorner = function (d1: number, d2: number, d3: number) {\n v.copy(pa.center)\n .addScaledVector(pa.normVecA, d1)\n .addScaledVector(pa.normVecB, d2)\n .addScaledVector(pa.normVecC, d3)\n v.toArray(vertexPosition as any, offset2)\n offset2 += 3\n }\n addCorner(d1a, d2a, d3a)\n addCorner(d1a, d2a, d3b)\n addCorner(d1a, d2b, d3b)\n addCorner(d1a, d2b, d3a)\n addCorner(d1b, d2b, d3b)\n addCorner(d1b, d2b, d3a)\n addCorner(d1b, d2a, d3a)\n addCorner(d1b, d2a, d3b)\n\n let edgeOffset = offset\n const addEdge = function (a: number, b: number) {\n v.fromArray(vertexPosition as any, offset * 2 + a * 3)\n .toArray(edgePosition1 as any, edgeOffset)\n v.fromArray(vertexPosition as any, offset * 2 + b * 3)\n .toArray(edgePosition2 as any, edgeOffset)\n edgeOffset += 3\n }\n addEdge(0, 1)\n addEdge(0, 3)\n addEdge(0, 6)\n addEdge(1, 2)\n addEdge(1, 7)\n addEdge(2, 3)\n addEdge(2, 4)\n addEdge(3, 5)\n addEdge(4, 5)\n addEdge(4, 7)\n addEdge(5, 6)\n addEdge(6, 7)\n }\n\n const picker = new AxesPicker(pa)\n\n return {\n vertex: {\n position: vertexPosition,\n color: vertexColor,\n radius: vertexRadius,\n picking: picker\n },\n edge: {\n position1: edgePosition1,\n position2: edgePosition2,\n color: edgeColor,\n color2: edgeColor,\n radius: edgeRadius,\n picking: picker\n }\n }\n }\n\n create () {\n const axesData = this.getAxesData(this.structureView)\n\n this.sphereBuffer = new SphereBuffer(\n axesData.vertex as SphereBufferData,\n this.getBufferParams({\n sphereDetail: this.sphereDetail,\n disableImpostor: this.disableImpostor,\n dullInterior: true\n }) as SphereBufferParameters\n )\n\n this.cylinderBuffer = new CylinderBuffer(\n axesData.edge as CylinderBufferData,\n this.getBufferParams({\n openEnded: true,\n radialSegments: this.radialSegments,\n disableImpostor: this.disableImpostor,\n dullInterior: true\n })\n )\n\n this.dataList.push({\n sview: this.structureView,\n bufferList: [ this.sphereBuffer as SphereGeometryBuffer, this.cylinderBuffer as CylinderGeometryBuffer]\n })\n }\n\n createData (sview: StructureView): undefined {\n return\n }\n\n updateData (what: AtomDataFields, data: StructureRepresentationData) {\n const axesData = this.getAxesData(data.sview as StructureView)\n const sphereData = {}\n const cylinderData = {}\n\n if (!what || what.position) {\n Object.assign(sphereData, {\n position: axesData.vertex.position\n })\n Object.assign(cylinderData, {\n position1: axesData.edge.position1,\n position2: axesData.edge.position2\n })\n }\n\n if (!what || what.color) {\n Object.assign(sphereData, {\n color: axesData.vertex.color as Float32Array\n })\n Object.assign(cylinderData, {\n color: axesData.edge.color as Float32Array,\n color2: axesData.edge.color as Float32Array\n })\n }\n\n if (!what || what.radius) {\n Object.assign(sphereData, {\n radius: axesData.vertex.radius as Float32Array\n })\n Object.assign(cylinderData, {\n radius: axesData.edge.radius as Float32Array\n })\n }\n\n (this.sphereBuffer as SphereGeometryBuffer).setAttributes(sphereData);\n (this.cylinderBuffer as CylinderGeometryBuffer).setAttributes(cylinderData)\n }\n}\n\nRepresentationRegistry.add('axes', AxesRepresentation)\n\nexport default AxesRepresentation\n","/**\n * @file Ball And Stick Representation\n * @author Alexander Rose \n * @private\n */\n\nimport { defaults } from '../utils'\nimport { ExtensionFragDepth, RepresentationRegistry } from '../globals'\nimport StructureRepresentation, { StructureRepresentationParameters, StructureRepresentationData } from './structure-representation'\nimport SphereBuffer, { SphereBufferData, SphereBufferParameters } from '../buffer/sphere-buffer'\nimport CylinderBuffer, { CylinderBufferData } from '../buffer/cylinder-buffer'\nimport WideLineBuffer from '../buffer/wideline-buffer'\nimport Viewer from '../viewer/viewer';\n// @ts-ignore: unused import Volume required for declaration only\nimport { Structure, Volume } from '../ngl';\nimport AtomProxy from '../proxy/atom-proxy';\nimport { AtomDataParams, BondDataParams, BondDataFields, AtomDataFields, BondData, AtomData } from '../structure/structure-data';\nimport StructureView from '../structure/structure-view';\nimport CylinderGeometryBuffer from '../buffer/cylindergeometry-buffer';\nimport SphereGeometryBuffer from '../buffer/spheregeometry-buffer';\n// @ts-ignore: unused import Surface required for declaration only\nimport Surface from '../surface/surface';\n\nexport interface BallAndStickRepresentationParameters extends StructureRepresentationParameters {\n sphereDetail: number\n radialSegments: number\n openEnded: boolean\n disableImpostor: boolean\n aspectRatio: number\n lineOnly: boolean\n lineWidth: number\n cylinderOnly: boolean\n multipleBond: 'off' | 'symmetric' | 'offset'\n bondSpacing: number\n bondScale: number\n linewidth: number\n}\n\n/**\n * Ball And Stick representation parameter object. Extends {@link RepresentationParameters} and\n * {@link StructureRepresentationParameters}.\n *\n * @typedef {Object} BallAndStickRepresentationParameters - ball and stick representation parameters\n *\n * @property {Integer} sphereDetail - sphere quality (icosahedron subdivisions)\n * @property {Integer} radialSegments - cylinder quality (number of segments)\n * @property {Boolean} openEnded - capped or not\n * @property {Boolean} disableImpostor - disable use of raycasted impostors for rendering\n * @property {Float} aspectRatio - size difference between atom and bond radii\n * @property {Boolean} lineOnly - render only bonds, and only as lines\n * @property {Integer} linewidth - width of lines\n * @property {Boolean} cylinderOnly - render only bonds (no atoms)\n * @property {String} multipleBond - one off \"off\", \"symmetric\", \"offset\"\n * @property {Float} bondSpacing - spacing for multiple bond rendering\n * @property {Float} bondScale - scale/radius for multiple bond rendering\n */\n\n/**\n * Ball And Stick representation. Show atoms as spheres and bonds as cylinders.\n *\n * __Name:__ _ball+stick_\n *\n * @example\n * stage.loadFile( \"rcsb://1crn\" ).then( function( o ){\n * o.addRepresentation( \"ball+stick\" );\n * o.autoView();\n * } );\n */\nclass BallAndStickRepresentation extends StructureRepresentation {\n protected sphereDetail: number\n protected radialSegments: number\n protected openEnded: boolean\n protected disableImpostor: boolean\n protected aspectRatio: number\n protected lineOnly: boolean\n protected lineWidth: number\n protected cylinderOnly: boolean\n protected multipleBond: 'off' | 'symmetric' | 'offset'\n protected bondSpacing: number\n protected bondScale: number\n protected linewidth: number\n\n protected lineBuffer: WideLineBuffer\n /**\n * Create Ball And Stick representation object\n * @param {Structure} structure - the structure to be represented\n * @param {Viewer} viewer - a viewer object\n * @param {BallAndStickRepresentationParameters} params - ball and stick representation parameters\n */\n constructor (structure: Structure, viewer: Viewer, params: Partial) {\n super(structure, viewer, params)\n\n this.type = 'ball+stick'\n\n this.parameters = Object.assign({\n\n sphereDetail: true,\n radialSegments: true,\n openEnded: true,\n disableImpostor: true,\n aspectRatio: {\n type: 'number', precision: 1, max: 10.0, min: 1.0\n },\n lineOnly: {\n type: 'boolean', rebuild: true\n },\n cylinderOnly: {\n type: 'boolean', rebuild: true\n },\n multipleBond: {\n type: 'select',\n rebuild: true,\n options: {\n 'off': 'off',\n 'symmetric': 'symmetric',\n 'offset': 'offset'\n }\n },\n bondScale: {\n type: 'number', precision: 2, max: 1.0, min: 0.01\n },\n bondSpacing: {\n type: 'number', precision: 2, max: 2.0, min: 0.5\n },\n linewidth: {\n type: 'integer', max: 50, min: 1, buffer: true\n }\n\n }, this.parameters)\n\n this.init(params)\n }\n\n init (params: Partial) {\n var p = params || {}\n p.radiusType = defaults(p.radiusType, 'size')\n p.radiusSize = defaults(p.radiusSize, 0.15)\n p.useInteriorColor = defaults(p.useInteriorColor, true)\n\n this.aspectRatio = defaults(p.aspectRatio, 2.0)\n this.lineOnly = defaults(p.lineOnly, false)\n this.cylinderOnly = defaults(p.cylinderOnly, false)\n this.multipleBond = defaults(p.multipleBond, 'off')\n this.bondSpacing = defaults(p.bondSpacing, 1.0)\n this.bondScale = defaults(p.bondScale, 0.4)\n this.linewidth = defaults(p.linewidth, 2)\n\n super.init(p)\n }\n\n getAtomRadius (atom: AtomProxy) {\n return this.aspectRatio * super.getAtomRadius(atom)\n }\n\n getAtomParams (what?: AtomDataFields, params?: Partial) {\n var p = super.getAtomParams(what, params)\n p.radiusParams.scale *= this.aspectRatio\n\n return p\n }\n\n getAtomData (sview: StructureView, what?: AtomDataFields, params?: Partial): AtomData {\n return sview.getAtomData(this.getAtomParams(what, params))\n }\n\n getBondParams (what?: BondDataFields, params?: Partial) {\n params = Object.assign({\n multipleBond: this.multipleBond,\n bondSpacing: this.bondSpacing,\n bondScale: this.bondScale\n }, params)\n\n return super.getBondParams(what, params)\n }\n\n getBondData (sview: StructureView, what?: BondDataFields, params?: Partial): BondData {\n return sview.getBondData(this.getBondParams(what, params))\n }\n\n createData (sview: StructureView) {\n const bufferList: any[] = []\n\n if (this.lineOnly) {\n this.lineBuffer = new WideLineBuffer(\n this.getBondData(sview, { position: true, color: true, picking: true }),\n this.getBufferParams({ linewidth: this.linewidth })\n )\n\n bufferList.push(this.lineBuffer)\n } else {\n const cylinderBuffer = new CylinderBuffer(\n (this.getBondData(sview) as CylinderBufferData),\n this.getBufferParams({\n openEnded: this.openEnded,\n radialSegments: this.radialSegments,\n disableImpostor: this.disableImpostor,\n dullInterior: true\n })\n )\n\n bufferList.push(cylinderBuffer as CylinderGeometryBuffer)\n\n if (!this.cylinderOnly) {\n const sphereBuffer = new SphereBuffer(\n (this.getAtomData(sview) as SphereBufferData),\n (this.getBufferParams({\n sphereDetail: this.sphereDetail,\n disableImpostor: this.disableImpostor,\n dullInterior: true\n }) as SphereBufferParameters)\n )\n\n bufferList.push(sphereBuffer as SphereGeometryBuffer)\n }\n }\n\n return {\n bufferList: bufferList\n }\n }\n\n updateData (what: BondDataFields | AtomDataFields, data: StructureRepresentationData) {\n if (this.multipleBond !== 'off' && what && what.radius) {\n what.position = true\n }\n\n const bondData = this.getBondData(data.sview as StructureView, what)\n\n if (this.lineOnly) {\n const lineData:Partial = {}\n\n if (!what || what.position) {\n Object.assign(lineData, {\n position1: bondData.position1,\n position2: bondData.position2\n })\n }\n\n if (!what || what.color) {\n Object.assign(lineData, {\n color: bondData.color,\n color2: bondData.color2\n })\n }\n\n data.bufferList[ 0 ].setAttributes(lineData)\n } else {\n var cylinderData: Partial = {}\n\n if (!what || what.position) {\n Object.assign(cylinderData, {\n position1: bondData.position1,\n position2: bondData.position2\n })\n }\n\n if (!what || what.color) {\n Object.assign(cylinderData, {\n color: bondData.color,\n color2: bondData.color2\n })\n }\n\n if (!what || what.radius) {\n Object.assign(cylinderData, {\n radius: bondData.radius\n })\n }\n\n data.bufferList[ 0 ].setAttributes(cylinderData)\n\n if (!this.cylinderOnly) {\n var atomData = this.getAtomData(data.sview as StructureView, what)\n\n var sphereData: Partial = {}\n\n if (!what || what.position) {\n Object.assign(sphereData, {\n position: atomData.position\n })\n }\n\n if (!what || what.color) {\n Object.assign(sphereData, {\n color: atomData.color\n })\n }\n\n if (!what || what.radius) {\n Object.assign(sphereData, {\n radius: atomData.radius\n })\n }\n\n data.bufferList[ 1 ].setAttributes(sphereData)\n }\n }\n }\n\n setParameters (params: Partial = {}) {\n let rebuild = false\n const what: AtomDataFields = {}\n\n if (params.aspectRatio || params.bondSpacing || params.bondScale) {\n Object.assign(what, {radius: true})\n if (!ExtensionFragDepth || this.disableImpostor) {\n rebuild = true\n }\n }\n\n super.setParameters(params, what, rebuild)\n\n return this\n }\n}\n\nRepresentationRegistry.add('ball+stick', BallAndStickRepresentation)\n\nexport default BallAndStickRepresentation\n","/**\n * @file Backbone Representation\n * @author Alexander Rose \n * @private\n */\n\nimport { RepresentationRegistry } from '../globals'\nimport { defaults } from '../utils'\nimport BallAndStickRepresentation, { BallAndStickRepresentationParameters } from './ballandstick-representation'\nimport { Structure } from '../ngl';\nimport Viewer from '../viewer/viewer';\nimport AtomProxy from '../proxy/atom-proxy';\nimport StructureView from '../structure/structure-view';\nimport { AtomDataFields, AtomDataParams, BondDataFields, BondDataParams, BondData, AtomData } from '../structure/structure-data';\n\n/**\n * Backbone representation. Show cylinders (or lines) connecting .CA (protein)\n * or .C4'/.C3' (RNA/DNA) of polymers.\n *\n * __Name:__ _backbone_\n *\n * @example\n * stage.loadFile( \"rcsb://1sfi\" ).then( function( o ){\n * o.addRepresentation( \"backbone\" );\n * o.autoView();\n * } );\n */\nclass BackboneRepresentation extends BallAndStickRepresentation {\n /**\n * @param {Structure} structure - the structure object\n * @param {Viewer} viewer - the viewer object\n * @param {BallAndStickRepresentationParameters} params - parameters object\n */\n constructor (structure: Structure, viewer: Viewer, params: Partial) {\n super(structure, viewer, params)\n\n this.type = 'backbone'\n\n this.parameters = Object.assign({\n\n }, this.parameters, {\n\n multipleBond: null,\n bondSpacing: null\n\n })\n\n this.init(params)\n }\n\n init (params: Partial) {\n var p = params || {}\n p.aspectRatio = defaults(p.aspectRatio, 1.0)\n p.radiusSize = defaults(p.radiusSize, 0.25)\n\n super.init(p)\n }\n\n getAtomRadius (atom: AtomProxy) {\n return atom.isTrace() ? super.getAtomRadius(atom) : 0\n }\n\n getAtomData (sview: StructureView, what?: AtomDataFields, params?: Partial): AtomData {\n return sview.getBackboneAtomData(this.getAtomParams(what, params))\n }\n\n getBondData (sview: StructureView, what?: BondDataFields, params?: Partial): BondData {\n return sview.getBackboneBondData(this.getBondParams(what, params))\n }\n}\n\nRepresentationRegistry.add('backbone', BackboneRepresentation)\n\nexport default BackboneRepresentation\n","/**\n * @file Base Representation\n * @author Alexander Rose \n * @private\n */\n\nimport { RepresentationRegistry } from '../globals'\nimport { defaults } from '../utils'\nimport BallAndStickRepresentation, { BallAndStickRepresentationParameters } from './ballandstick-representation'\nimport { Structure } from '../ngl';\nimport Viewer from '../viewer/viewer';\nimport StructureView from '../structure/structure-view';\nimport { AtomDataFields, AtomDataParams, BondDataFields, BondDataParams, BondData, AtomData } from '../structure/structure-data';\n\n/**\n * Base representation. Show cylinders for RNA/DNA ladders.\n *\n * __Name:__ _base_\n *\n * @example\n * stage.loadFile( \"rcsb://1d66\" ).then( function( o ){\n * o.addRepresentation( \"cartoon\", { sele: \"nucleic\" } );\n * o.addRepresentation( \"base\", { color: \"resname\" } );\n * o.autoView( \"nucleic\" );\n * } );\n */\nclass BaseRepresentation extends BallAndStickRepresentation {\n /**\n * @param {Structure} structure - the structure object\n * @param {Viewer} viewer - the viewer object\n * @param {BallAndStickRepresentationParameters} params - parameters object\n */\n constructor (structure: Structure, viewer: Viewer, params: Partial) {\n super(structure, viewer, params)\n\n this.type = 'base'\n\n this.parameters = Object.assign({\n\n }, this.parameters, {\n\n multipleBond: null,\n bondSpacing: null\n\n })\n }\n\n init (params: Partial) {\n let p = params || {}\n p.aspectRatio = defaults(p.aspectRatio, 1.0)\n p.radiusSize = defaults(p.radiusSize, 0.3)\n\n super.init(p)\n }\n\n getAtomData (sview: StructureView, what?: AtomDataFields, params?: AtomDataParams): AtomData {\n return sview.getRungAtomData(this.getAtomParams(what, params))\n }\n\n getBondData (sview: StructureView, what?: BondDataFields, params?: BondDataParams): BondData {\n let p = this.getBondParams(what, params)\n Object.assign(p.colorParams, {rung: true})\n\n return sview.getRungBondData(p)\n }\n}\n\nRepresentationRegistry.add('base', BaseRepresentation)\n\nexport default BaseRepresentation\n","/**\n * @file Spline\n * @author Alexander Rose \n * @private\n */\n\nimport { Vector3 } from 'three'\n\nimport { ColormakerRegistry } from '../globals'\nimport { AtomPicker } from '../utils/picker'\nimport RadiusFactory, { RadiusParams } from '../utils/radius-factory'\nimport { copyArray } from '../math/array-utils'\nimport { spline } from '../math/math-utils'\nimport Polymer from '../proxy/polymer';\nimport AtomProxy from '../proxy/atom-proxy';\nimport { ColormakerParameters } from '../color/colormaker';\nimport { NumberArray } from '../types';\n\nexport class Interpolator {\n \n m: number\n tension: number\n dt: number\n delta: number\n vec1: Vector3\n vec2: Vector3\n vDir: Vector3 \n vTan: Vector3\n vNorm: Vector3\n vBin: Vector3\n m2: number\n\n constructor (m: number, tension: number) {\n this.m = m\n this.tension = tension\n this.dt = 1.0 / this.m\n this.delta = 0.0001\n\n this.vec1 = new Vector3()\n this.vec2 = new Vector3()\n\n this.vDir = new Vector3()\n this.vTan = new Vector3()\n this.vNorm = new Vector3()\n this.vBin = new Vector3()\n \n this.m2 = Math.ceil(this.m / 2)\n }\n\n private interpolateToArr (v0: Vector3, v1: Vector3, v2: Vector3, v3: Vector3, t: number, arr: Float32Array, offset: number) {\n arr[ offset + 0 ] = spline(v0.x, v1.x, v2.x, v3.x, t, this.tension)\n arr[ offset + 1 ] = spline(v0.y, v1.y, v2.y, v3.y, t, this.tension)\n arr[ offset + 2 ] = spline(v0.z, v1.z, v2.z, v3.z, t, this.tension)\n }\n\n private interpolateToVec (v0: Vector3, v1: Vector3, v2: Vector3, v3: Vector3, t: number, vec: Vector3) {\n vec.x = spline(v0.x, v1.x, v2.x, v3.x, t, this.tension)\n vec.y = spline(v0.y, v1.y, v2.y, v3.y, t, this.tension)\n vec.z = spline(v0.z, v1.z, v2.z, v3.z, t, this.tension)\n }\n\n private interpolatePosition (v0: Vector3, v1: Vector3, v2: Vector3, v3: Vector3, pos: Float32Array, offset: number) {\n for (var j = 0; j < this.m; ++j) {\n var l = offset + j * 3\n var d = this.dt * j\n this.interpolateToArr(v0, v1, v2, v3, d, pos, l)\n }\n }\n\n private interpolateTangent (v0: Vector3, v1: Vector3, v2: Vector3, v3: Vector3, tan: Float32Array, offset: number) {\n for (var j = 0; j < this.m; ++j) {\n var d = this.dt * j\n var d1 = d - this.delta\n var d2 = d + this.delta\n var l = offset + j * 3\n // capping as a precaution\n if (d1 < 0) d1 = 0\n if (d2 > 1) d2 = 1\n //\n this.interpolateToVec(v0, v1, v2, v3, d1, this.vec1)\n this.interpolateToVec(v0, v1, v2, v3, d2, this.vec2)\n //\n this.vec2.sub(this.vec1).normalize()\n this.vec2.toArray(tan as any, l)\n }\n }\n\n private vectorSubdivide (interpolationFn: (v0: Vector3, v1: Vector3, v2: Vector3, v3: Vector3, array: Float32Array, offset: number) => void,\n iterator: AtomIterator, array: Float32Array, offset: number, isCyclic: boolean) {\n let v0: Vector3\n let v1 = iterator.next()\n let v2 = iterator.next()\n let v3 = iterator.next()\n //\n const n = iterator.size\n const n1 = n - 1\n let k = offset || 0\n for (let i = 0; i < n1; ++i) {\n v0 = v1\n v1 = v2\n v2 = v3\n v3 = iterator.next()\n interpolationFn.apply(this, [v0, v1, v2, v3, array, k])\n k += 3 * this.m\n }\n if (isCyclic) {\n v0 = iterator.get(n - 2)\n v1 = iterator.get(n - 1)\n v2 = iterator.get(0)\n v3 = iterator.get(1)\n interpolationFn.apply(this, [v0, v1, v2, v3, array, k])\n k += 3 * this.m\n }\n }\n\n //\n\n public getPosition (iterator: AtomIterator, array: Float32Array, offset: number, isCyclic: boolean) {\n iterator.reset()\n this.vectorSubdivide(this.interpolatePosition, iterator, array, offset, isCyclic)\n var n1 = iterator.size - 1\n var k = n1 * this.m * 3\n if (isCyclic) k += this.m * 3\n var v = iterator.get(isCyclic ? 0 : n1)\n array[ k ] = v.x\n array[ k + 1 ] = v.y\n array[ k + 2 ] = v.z\n }\n\n public getTangent (iterator: AtomIterator, array: Float32Array, offset: number, isCyclic: boolean) {\n iterator.reset()\n this.vectorSubdivide(this.interpolateTangent, iterator, array, offset, isCyclic)\n const n1 = iterator.size - 1\n let k = n1 * this.m * 3\n if (isCyclic) k += this.m * 3\n copyArray(array, array, k - 3, k, 3)\n }\n\n private interpolateNormalDir (u0: Vector3, u1: Vector3, u2: Vector3, u3: Vector3,\n v0: Vector3, v1: Vector3, v2: Vector3, v3: Vector3,\n tan: Float32Array, norm: Float32Array, bin: Float32Array,\n offset: number, shift: boolean) {\n for (let j = 0; j < this.m; ++j) {\n let l = offset + j * 3\n if (shift) l += this.m2 * 3\n const d = this.dt * j\n this.interpolateToVec(u0, u1, u2, u3, d, this.vec1)\n this.interpolateToVec(v0, v1, v2, v3, d, this.vec2)\n this.vDir.subVectors(this.vec2, this.vec1).normalize()\n this.vTan.fromArray(tan as any, l)\n this.vBin.crossVectors(this.vDir, this.vTan).normalize()\n this.vBin.toArray(bin as any, l)\n this.vNorm.crossVectors(this.vTan, this.vBin).normalize()\n this.vNorm.toArray(norm as any, l)\n }\n }\n\n private interpolateNormal (vDir: Vector3, tan: Float32Array, norm: Float32Array, bin: Float32Array, offset: number) {\n for (var j = 0; j < this.m; ++j) {\n var l = offset + j * 3\n vDir.copy(this.vNorm)\n this.vTan.fromArray(tan as any, l)\n this.vBin.crossVectors(vDir, this.vTan).normalize()\n this.vBin.toArray(bin as any, l)\n this.vNorm.crossVectors(this.vTan, this.vBin).normalize()\n this.vNorm.toArray(norm as any, l)\n }\n }\n\n public getNormal (size: number, tan: Float32Array, norm: Float32Array, bin: Float32Array, offset: number, isCyclic: boolean) {\n this.vNorm.set(0, 0, 1)\n const n = size\n const n1 = n - 1\n let k = offset || 0\n for (var i = 0; i < n1; ++i) {\n this.interpolateNormal(this.vDir, tan, norm, bin, k)\n k += 3 * this.m\n }\n if (isCyclic) {\n this.interpolateNormal(this.vDir, tan, norm, bin, k)\n k += 3 * this.m\n }\n this.vBin.toArray(bin as any, k)\n this.vNorm.toArray(norm as any, k)\n }\n\n public getNormalDir (iterDir1: AtomIterator, iterDir2: AtomIterator, tan: Float32Array, norm: Float32Array, bin: Float32Array, offset: number, isCyclic: boolean, shift: boolean) {\n iterDir1.reset()\n iterDir2.reset()\n //\n const vSub1 = new Vector3()\n const vSub2 = new Vector3()\n const vSub3 = new Vector3()\n const vSub4 = new Vector3()\n //\n const d1v1 = new Vector3()\n const d1v2 = new Vector3().copy(iterDir1.next())\n const d1v3 = new Vector3().copy(iterDir1.next())\n const d1v4 = new Vector3().copy(iterDir1.next())\n const d2v1 = new Vector3()\n const d2v2 = new Vector3().copy(iterDir2.next())\n const d2v3 = new Vector3().copy(iterDir2.next())\n const d2v4 = new Vector3().copy(iterDir2.next())\n //\n this.vNorm.set(0, 0, 1)\n let n = iterDir1.size\n let n1 = n - 1\n let k = offset || 0\n for (var i = 0; i < n1; ++i) {\n d1v1.copy(d1v2)\n d1v2.copy(d1v3)\n d1v3.copy(d1v4)\n d1v4.copy(iterDir1.next())\n d2v1.copy(d2v2)\n d2v2.copy(d2v3)\n d2v3.copy(d2v4)\n d2v4.copy(iterDir2.next())\n //\n if (i === 0) {\n vSub1.subVectors(d2v1, d1v1)\n vSub2.subVectors(d2v2, d1v2)\n if (vSub1.dot(vSub2) < 0) {\n vSub2.multiplyScalar(-1)\n d2v2.addVectors(d1v2, vSub2)\n }\n vSub3.subVectors(d2v3, d1v3)\n if (vSub2.dot(vSub3) < 0) {\n vSub3.multiplyScalar(-1)\n d2v3.addVectors(d1v3, vSub3)\n }\n } else {\n vSub3.copy(vSub4)\n }\n vSub4.subVectors(d2v4, d1v4)\n if (vSub3.dot(vSub4) < 0) {\n vSub4.multiplyScalar(-1)\n d2v4.addVectors(d1v4, vSub4)\n }\n this.interpolateNormalDir(\n d1v1, d1v2, d1v3, d1v4,\n d2v1, d2v2, d2v3, d2v4,\n tan, norm, bin, k, shift\n )\n k += 3 * this.m\n }\n if (isCyclic) {\n d1v1.copy(iterDir1.get(n - 2))\n d1v2.copy(iterDir1.get(n - 1))\n d1v3.copy(iterDir1.get(0))\n d1v4.copy(iterDir1.get(1))\n d2v1.copy(iterDir2.get(n - 2))\n d2v2.copy(iterDir2.get(n - 1))\n d2v3.copy(iterDir2.get(0))\n d2v4.copy(iterDir2.get(1))\n //\n vSub3.copy(vSub4)\n vSub4.subVectors(d2v4, d1v4)\n if (vSub3.dot(vSub4) < 0) {\n vSub4.multiplyScalar(-1)\n d2v4.addVectors(d1v4, vSub4)\n }\n this.interpolateNormalDir(\n d1v1, d1v2, d1v3, d1v4,\n d2v1, d2v2, d2v3, d2v4,\n tan, norm, bin, k, shift\n )\n k += 3 * this.m\n }\n if (shift) {\n // FIXME shift requires data from one this.more preceeding residue\n this.vBin.fromArray(bin as any, this.m2 * 3)\n this.vNorm.fromArray(norm as any, this.m2 * 3)\n for (var j = 0; j < this.m2; ++j) {\n this.vBin.toArray(bin as any, j * 3)\n this.vNorm.toArray(norm as any, j * 3)\n }\n } else {\n this.vBin.toArray(bin as any, k)\n this.vNorm.toArray(norm as any, k)\n }\n }\n\n //\n\n private interpolateColor (item1: AtomProxy, item2: AtomProxy, colFn: (...arg: any[]) => void, col: any, offset: number) {\n var j, l\n for (j = 0; j < this.m2; ++j) {\n l = offset + j * 3\n colFn.apply(this, [item1, col, l]) // itemColorToArray\n }\n for (j = this.m2; j < this.m; ++j) {\n l = offset + j * 3\n colFn.apply(this, [item2, col, l]) // itemColorToArray\n }\n }\n\n public getColor (iterator: AtomIterator, colFn: (...arg: any[]) => void, col: any, offset: number, isCyclic: boolean) {\n iterator.reset()\n iterator.next() // first element not needed\n let i0: AtomProxy\n let i1 = iterator.next()\n //\n var n = iterator.size\n var n1 = n - 1\n var k = offset || 0\n for (var i = 0; i < n1; ++i) {\n i0 = i1\n i1 = iterator.next()\n this.interpolateColor(i0, i1, colFn, col, k)\n k += 3 * this.m\n }\n if (isCyclic) {\n i0 = iterator.get(n - 1)\n i1 = iterator.get(0)\n this.interpolateColor(i0, i1, colFn, col, k)\n k += 3 * this.m\n }\n //\n col[ k ] = col[ k - 3 ]\n col[ k + 1 ] = col[ k - 2 ]\n col[ k + 2 ] = col[ k - 1 ]\n }\n\n //\n\n private interpolatePicking (item1: AtomProxy, item2: AtomProxy, pickFn: (item: AtomProxy) => number, pick: Float32Array, offset: number) {\n var j\n for (j = 0; j < this.m2; ++j) {\n pick[ offset + j ] = pickFn.apply(this, [item1])\n }\n for (j = this.m2; j < this.m; ++j) {\n pick[ offset + j ] = pickFn.apply(this, [item2])\n }\n }\n\n public getPicking (iterator: AtomIterator, pickFn: (item: AtomProxy) => number, pick: Float32Array, offset: number, isCyclic: boolean) {\n iterator.reset()\n iterator.next() // first element not needed\n let i0: AtomProxy\n let i1 = iterator.next()\n //\n const n = iterator.size\n const n1 = n - 1\n let k = offset || 0\n for (var i = 0; i < n1; ++i) {\n i0 = i1\n i1 = iterator.next()\n this.interpolatePicking(i0, i1, pickFn, pick, k)\n k += this.m\n }\n if (isCyclic) {\n i0 = iterator.get(n - 1)\n i1 = iterator.get(0)\n this.interpolatePicking(i0, i1, pickFn, pick, k)\n k += this.m\n }\n //\n pick[ k ] = pick[ k - 1 ]\n }\n\n //\n\n private interpolateSize (item1: AtomProxy, item2: AtomProxy, sizeFn: (item: AtomProxy) => number, size: Float32Array, offset: number) {\n const s1: number = sizeFn.apply(this, [item1])\n const s2: number = sizeFn.apply(this, [item2])\n for (let j = 0; j < this.m; ++j) {\n // linear interpolation\n let t = j / this.m\n size[ offset + j ] = (1 - t) * s1 + t * s2\n }\n }\n\n public getSize (iterator: AtomIterator, sizeFn: (item: AtomProxy) => number, size: Float32Array, offset: number, isCyclic: boolean) {\n iterator.reset()\n iterator.next() // first element not needed\n let i0: AtomProxy\n let i1: AtomProxy = iterator.next()\n //\n const n = iterator.size\n const n1 = n - 1\n let k = offset || 0\n for (var i = 0; i < n1; ++i) {\n i0 = i1\n i1 = iterator.next()\n this.interpolateSize(i0, i1, sizeFn, size, k)\n k += this.m\n }\n if (isCyclic) {\n i0 = iterator.get(n - 1)\n i1 = iterator.get(0)\n this.interpolateSize(i0, i1, sizeFn, size, k)\n k += this.m\n }\n //\n size[ k ] = size[ k - 1 ]\n }\n}\n\nexport interface SplineParameters {\n directional?: boolean\n positionIterator?: boolean\n subdiv?: number\n smoothSheet?: boolean\n tension?: number\n}\nexport interface AtomIterator {\n size: number,\n next: () => AtomProxy | Vector3,\n get: (idx: number) => AtomProxy | Vector3,\n reset: () => void\n}\nclass Spline {\n\n polymer: Polymer\n size: number\n directional: boolean\n positionIterator: any\n subdiv: number\n smoothSheet: boolean\n tension: number\n interpolator: Interpolator\n\n constructor (polymer: Polymer, params?: SplineParameters) {\n this.polymer = polymer\n this.size = polymer.residueCount\n\n var p = params || {}\n this.directional = p.directional || false\n this.positionIterator = p.positionIterator || false\n this.subdiv = p.subdiv || 1\n this.smoothSheet = p.smoothSheet || false\n\n if (!p.tension) {\n this.tension = this.polymer.isNucleic() ? 0.5 : 0.9\n } else {\n this.tension = p.tension\n }\n\n this.interpolator = new Interpolator(this.subdiv, this.tension)\n }\n\n getAtomIterator (type: string, smooth?: boolean): AtomIterator {\n const polymer = this.polymer\n const structure = polymer.structure\n const n = polymer.residueCount\n\n let i = 0\n let j = -1\n\n const cache = [\n structure.getAtomProxy(),\n structure.getAtomProxy(),\n structure.getAtomProxy(),\n structure.getAtomProxy()\n ]\n\n const cache2 = [\n new Vector3(),\n new Vector3(),\n new Vector3(),\n new Vector3()\n ]\n\n function next () {\n var atomProxy = get(j)\n j += 1\n return atomProxy\n }\n\n var apPrev = structure.getAtomProxy()\n var apNext = structure.getAtomProxy()\n\n function get (idx: number) {\n var atomProxy = cache[ i % 4 ]\n atomProxy.index = polymer.getAtomIndexByType(idx, type) as number\n if (smooth && idx > 0 && idx < n && atomProxy.sstruc === 'e') {\n var vec = cache2[ i % 4 ]\n apPrev.index = polymer.getAtomIndexByType(idx + 1, type) as number\n apNext.index = polymer.getAtomIndexByType(idx - 1, type) as number\n vec.addVectors(apPrev as any, apNext as any)\n .add(atomProxy as any).add(atomProxy as any)\n .multiplyScalar(0.25)\n i += 1\n return vec\n }\n i += 1\n return atomProxy\n }\n\n function reset () {\n i = 0\n j = -1\n }\n\n return {\n size: n,\n next: next,\n get: get,\n reset: reset\n }\n }\n\n getSubdividedColor (params: {scheme: string, [k: string]: any } & ColormakerParameters) {\n var m = this.subdiv\n var polymer = this.polymer\n var n = polymer.residueCount\n var n1 = n - 1\n var nCol = n1 * m * 3 + 3\n if (polymer.isCyclic) nCol += m * 3\n\n var col = new Float32Array(nCol)\n var iterator = this.getAtomIterator('trace')\n\n var p = params || {}\n p.structure = polymer.structure\n\n var colormaker = ColormakerRegistry.getScheme(p)\n\n function colFn (item: AtomProxy, array: NumberArray, offset: number) {\n colormaker.atomColorToArray(item, array, offset)\n }\n\n this.interpolator.getColor(\n iterator, colFn, col, 0, polymer.isCyclic\n )\n\n return {\n 'color': col\n }\n }\n \n getSubdividedPicking () {\n var m = this.subdiv\n var polymer = this.polymer\n var n = polymer.residueCount\n var n1 = n - 1\n var nCol = n1 * m + 1\n if (polymer.isCyclic) nCol += m\n\n var structure = polymer.structure\n var iterator = this.getAtomIterator('trace')\n var pick = new Float32Array(nCol)\n\n function pickFn (item: AtomProxy) {\n return item.index\n }\n\n this.interpolator.getPicking(\n iterator, pickFn, pick, 0, polymer.isCyclic\n )\n\n return {\n 'picking': new AtomPicker(pick, structure)\n }\n }\n\n getSubdividedPosition () {\n var pos = this.getPosition()\n\n return {\n 'position': pos\n }\n }\n \n getSubdividedOrientation () {\n const tan = this.getTangent()\n const normals = this.getNormals(tan)\n\n return {\n 'tangent': tan,\n 'normal': normals.normal,\n 'binormal': normals.binormal\n }\n }\n\n getSubdividedSize (params: RadiusParams) {\n var m = this.subdiv\n var polymer = this.polymer\n var n = polymer.residueCount\n var n1 = n - 1\n var nSize = n1 * m + 1\n if (polymer.isCyclic) nSize += m\n\n var size = new Float32Array(nSize)\n var iterator = this.getAtomIterator('trace')\n\n var radiusFactory = new RadiusFactory(params)\n\n function sizeFn (item: AtomProxy) {\n return radiusFactory.atomRadius(item)\n }\n\n this.interpolator.getSize(\n iterator, sizeFn, size, 0, polymer.isCyclic\n )\n\n return {\n 'size': size\n }\n }\n\n getPosition () {\n const m = this.subdiv\n const polymer = this.polymer\n const n = polymer.residueCount\n const n1 = n - 1\n let nPos = n1 * m * 3 + 3\n if (polymer.isCyclic) nPos += m * 3\n\n const pos = new Float32Array(nPos)\n const iterator = this.positionIterator || this.getAtomIterator('trace', this.smoothSheet)\n\n this.interpolator.getPosition(iterator, pos, 0, polymer.isCyclic)\n\n return pos\n }\n\n getTangent () {\n const m = this.subdiv\n const polymer = this.polymer\n const n = this.size\n const n1 = n - 1\n let nTan = n1 * m * 3 + 3\n if (polymer.isCyclic) nTan += m * 3\n\n const tan = new Float32Array(nTan)\n const iterator = this.positionIterator || this.getAtomIterator('trace', this.smoothSheet)\n\n this.interpolator.getTangent(iterator, tan, 0, polymer.isCyclic)\n\n return tan\n }\n\n getNormals (tan: Float32Array) {\n const m = this.subdiv\n const polymer = this.polymer\n const isProtein = polymer.isProtein()\n const n = this.size\n const n1 = n - 1\n let nNorm = n1 * m * 3 + 3\n if (polymer.isCyclic) nNorm += m * 3\n\n const norm = new Float32Array(nNorm)\n const bin = new Float32Array(nNorm)\n\n if (this.directional && !this.polymer.isCg()) {\n const iterDir1 = this.getAtomIterator('direction1')\n const iterDir2 = this.getAtomIterator('direction2')\n this.interpolator.getNormalDir(\n iterDir1, iterDir2, tan, norm, bin, 0, polymer.isCyclic, isProtein\n )\n } else {\n this.interpolator.getNormal(\n n, tan, norm, bin, 0, polymer.isCyclic\n )\n }\n\n return {\n 'normal': norm,\n 'binormal': bin\n }\n }\n\n}\n\nexport default Spline\n","/**\n * @file Tube Mesh Buffer\n * @author Alexander Rose \n * @private\n */\n\n// @ts-ignore: unused import Matrix4 required for declaration only\nimport { Vector3, Matrix4 } from 'three'\n\nimport { defaults, getUintArray } from '../utils'\nimport { serialArray } from '../math/array-utils'\nimport MeshBuffer from './mesh-buffer'\nimport { BufferDefaultParameters, BufferData, BufferParameters } from './buffer'\nimport {Log} from \"../globals\";\n\nconst vTangent = new Vector3()\nconst vMeshNormal = new Vector3()\n\nexport interface TubeMeshBufferData extends BufferData {\n binormal: Float32Array\n tangent: Float32Array\n size: Float32Array\n}\n\nexport const TubeMeshBufferDefaultParameters = Object.assign({\n radialSegments: 4,\n capped: false,\n aspectRatio: 1.0\n}, BufferDefaultParameters)\nexport type TubeMeshBufferParameters = BufferParameters & {\n radialSegments: number,\n capped: boolean,\n aspectRatio: number\n}\n\nfunction getData (data: TubeMeshBufferData, params: Partial = {}) {\n const radialSegments = defaults(params.radialSegments, 4)\n const capped = defaults(params.capped, false)\n\n const capVertices = capped ? radialSegments : 0\n const capTriangles = capped ? radialSegments - 2 : 0\n\n const n = data.position!.length / 3\n const n1 = n - 1\n const x = n * radialSegments * 3 + 2 * capVertices * 3\n const xi = n1 * 2 * radialSegments * 3 + 2 * capTriangles * 3\n\n return {\n position: new Float32Array(x),\n color: new Float32Array(x),\n index: getUintArray(xi, x / 3),\n normal: new Float32Array(x),\n picking: data.picking\n }\n}\n\n/**\n * Tube mesh buffer. Draws a tube.\n */\nclass TubeMeshBuffer extends MeshBuffer {\n get defaultParameters() { return TubeMeshBufferDefaultParameters }\n parameters: TubeMeshBufferParameters\n\n capVertices: number\n capTriangles: number\n size2: number\n\n /**\n * @param {Object} data - attribute object\n * @param {Float32Array} data.position - positions\n * @param {Float32Array} data.normal - normals\n * @param {Float32Array} data.binormal - binormals\n * @param {Float32Array} data.tangent - tangents\n * @param {Float32Array} data.color - colors\n * @param {Float32Array} data.size - sizes\n * @param {Picker} data.picking - picking ids\n * @param {BufferParameters} params - parameter object\n */\n constructor (data: TubeMeshBufferData, params: Partial = {}) {\n super(getData(data, params), params)\n\n this.capVertices = this.parameters.capped ? this.parameters.radialSegments : 0\n this.capTriangles = this.parameters.capped ? this.parameters.radialSegments - 2 : 0\n\n this.size2 = data.position!.length / 3\n data.primitiveId = serialArray(this.size2)\n\n this.setAttributes(data)\n this.makeIndex()\n }\n\n setAttributes (data: Partial = {}) {\n const aspectRatio = this.parameters.aspectRatio\n\n const n = this.size2\n const n1 = n - 1\n const radialSegments = this.parameters.radialSegments\n\n const attributes = this.geometry.attributes as any\n\n let position, normal, binormal, tangent, color, size, primitiveId\n let meshPosition, meshColor, meshNormal, meshPrimitiveId\n\n if (data.position) {\n position = data.position\n normal = data.normal\n binormal = data.binormal\n tangent = data.tangent\n size = data.size\n\n meshPosition = attributes.position.array\n meshNormal = attributes.normal.array\n\n attributes.position.needsUpdate = true\n attributes.normal.needsUpdate = true\n }\n\n if (data.color) {\n color = data.color\n meshColor = attributes.color.array\n attributes.color.needsUpdate = true\n }\n\n if (data.primitiveId) {\n primitiveId = data.primitiveId\n meshPrimitiveId = attributes.primitiveId.array\n attributes.primitiveId.needsUpdate = true\n }\n\n let k, l\n let radius = 0\n\n let normX = 0\n let normY = 0\n let normZ = 0\n let biX = 0\n let biY = 0\n let biZ = 0\n let posX = 0\n let posY = 0\n let posZ = 0\n\n const cxArr = []\n const cyArr = []\n const cx1Arr = []\n const cy1Arr = []\n const cx2Arr = []\n const cy2Arr = []\n\n if (position) {\n for (let j = 0; j < radialSegments; ++j) {\n const v = (j / radialSegments) * 2 * Math.PI\n\n cxArr[ j ] = aspectRatio * Math.cos(v)\n cyArr[ j ] = Math.sin(v)\n\n cx1Arr[ j ] = aspectRatio * Math.cos(v - 0.01)\n cy1Arr[ j ] = Math.sin(v - 0.01)\n cx2Arr[ j ] = aspectRatio * Math.cos(v + 0.01)\n cy2Arr[ j ] = Math.sin(v + 0.01)\n }\n }\n\n for (let i = 0; i < n; ++i) {\n k = i * 3\n l = k * radialSegments\n\n if (position && tangent && normal && binormal && size) {\n vTangent.set(\n tangent[ k ], tangent[ k + 1 ], tangent[ k + 2 ]\n )\n\n normX = normal[ k ]\n normY = normal[ k + 1 ]\n normZ = normal[ k + 2 ]\n\n biX = binormal[ k ]\n biY = binormal[ k + 1 ]\n biZ = binormal[ k + 2 ]\n\n posX = position[ k ]\n posY = position[ k + 1 ]\n posZ = position[ k + 2 ]\n\n radius = size[ i ]\n }\n\n for (let j = 0; j < radialSegments; ++j) {\n const s = l + j * 3\n\n if (position) {\n const cx = -radius * cxArr[ j ] // TODO: Hack: Negating it so it faces outside.\n const cy = radius * cyArr[ j ]\n\n const cx1 = -radius * cx1Arr[ j ]\n const cy1 = radius * cy1Arr[ j ]\n const cx2 = -radius * cx2Arr[ j ]\n const cy2 = radius * cy2Arr[ j ]\n\n meshPosition[ s ] = posX + cx * normX + cy * biX\n meshPosition[ s + 1 ] = posY + cx * normY + cy * biY\n meshPosition[ s + 2 ] = posZ + cx * normZ + cy * biZ\n\n // TODO half of these are symmetric\n vMeshNormal.set(\n // ellipse tangent approximated as vector from/to adjacent points\n (cx2 * normX + cy2 * biX) - (cx1 * normX + cy1 * biX),\n (cx2 * normY + cy2 * biY) - (cx1 * normY + cy1 * biY),\n (cx2 * normZ + cy2 * biZ) - (cx1 * normZ + cy1 * biZ)\n ).cross(vTangent)\n\n meshNormal[ s ] = vMeshNormal.x\n meshNormal[ s + 1 ] = vMeshNormal.y\n meshNormal[ s + 2 ] = vMeshNormal.z\n }\n\n if (color) {\n meshColor[ s ] = color[ k ]\n meshColor[ s + 1 ] = color[ k + 1 ]\n meshColor[ s + 2 ] = color[ k + 2 ]\n }\n\n if (primitiveId) {\n meshPrimitiveId[ i * radialSegments + j ] = primitiveId[ i ]\n }\n }\n }\n\n // front cap\n\n k = 0\n l = n * 3 * radialSegments\n\n for (let j = 0; j < radialSegments; ++j) {\n const s = k + j * 3\n const t = l + j * 3\n\n if (position && tangent) {\n meshPosition[ t ] = meshPosition[ s ]\n meshPosition[ t + 1 ] = meshPosition[ s + 1 ]\n meshPosition[ t + 2 ] = meshPosition[ s + 2 ]\n\n meshNormal[ t ] = tangent[ k ]\n meshNormal[ t + 1 ] = tangent[ k + 1 ]\n meshNormal[ t + 2 ] = tangent[ k + 2 ]\n }\n\n if (color) {\n meshColor[ t ] = meshColor[ s ]\n meshColor[ t + 1 ] = meshColor[ s + 1 ]\n meshColor[ t + 2 ] = meshColor[ s + 2 ]\n }\n\n if (primitiveId) {\n meshPrimitiveId[ n * radialSegments + j ] = meshPrimitiveId[ 0 + j ]\n }\n }\n\n // back cap\n\n k = (n - 1) * 3 * radialSegments\n l = (n + 1) * 3 * radialSegments\n\n for (let j = 0; j < radialSegments; ++j) {\n const s = k + j * 3\n const t = l + j * 3\n\n if (position && tangent) {\n meshPosition[ t ] = meshPosition[ s ]\n meshPosition[ t + 1 ] = meshPosition[ s + 1 ]\n meshPosition[ t + 2 ] = meshPosition[ s + 2 ]\n\n meshNormal[ t ] = tangent[ n1 * 3 ]\n meshNormal[ t + 1 ] = tangent[ n1 * 3 + 1 ]\n meshNormal[ t + 2 ] = tangent[ n1 * 3 + 2 ]\n }\n\n if (color) {\n meshColor[ t ] = meshColor[ s ]\n meshColor[ t + 1 ] = meshColor[ s + 1 ]\n meshColor[ t + 2 ] = meshColor[ s + 2 ]\n }\n\n if (primitiveId) {\n meshPrimitiveId[ (n + 1) * radialSegments + j ] = meshPrimitiveId[ (n - 1) * radialSegments + j ]\n }\n }\n }\n\n makeIndex () {\n const index = this.geometry.getIndex()\n if (!index) { Log.error('Index is null'); return; }\n const meshIndex = index.array as Uint32Array|Uint16Array\n\n const n = this.size2\n const n1 = n - 1\n const capTriangles = this.capTriangles\n const radialSegments = this.parameters.radialSegments\n const radialSegments1 = this.parameters.radialSegments + 1\n\n let k, l\n\n for (let i = 0; i < n1; ++i) {\n const k = i * radialSegments * 3 * 2\n\n const irs = i * radialSegments\n const irs1 = (i + 1) * radialSegments\n\n for (let j = 0; j < radialSegments; ++j) {\n l = k + j * 3 * 2\n\n // meshIndex[ l + 0 ] = irs + ( ( j + 0 ) % radialSegments );\n meshIndex[ l ] = irs + j\n meshIndex[ l + 1 ] = irs + ((j + 1) % radialSegments)\n // meshIndex[ l + 2 ] = irs1 + ( ( j + 0 ) % radialSegments );\n meshIndex[ l + 2 ] = irs1 + j\n\n // meshIndex[ l + 3 ] = irs1 + ( ( j + 0 ) % radialSegments );\n meshIndex[ l + 3 ] = irs1 + j\n meshIndex[ l + 4 ] = irs + ((j + 1) % radialSegments)\n meshIndex[ l + 5 ] = irs1 + ((j + 1) % radialSegments)\n }\n }\n\n // capping\n\n const strip = [ 0 ]\n\n for (let j = 1; j < radialSegments1 / 2; ++j) {\n strip.push(j)\n if (radialSegments - j !== j) {\n strip.push(radialSegments - j)\n }\n }\n\n // front cap\n\n l = n1 * radialSegments * 3 * 2\n k = n * radialSegments\n\n for (let j = 0; j < strip.length - 2; ++j) {\n if (j % 2 === 0) {\n meshIndex[ l + j * 3 + 0 ] = k + strip[ j + 0 ]\n meshIndex[ l + j * 3 + 1 ] = k + strip[ j + 1 ]\n meshIndex[ l + j * 3 + 2 ] = k + strip[ j + 2 ]\n } else {\n meshIndex[ l + j * 3 + 0 ] = k + strip[ j + 2 ]\n meshIndex[ l + j * 3 + 1 ] = k + strip[ j + 1 ]\n meshIndex[ l + j * 3 + 2 ] = k + strip[ j + 0 ]\n }\n }\n\n // back cap\n\n l = n1 * radialSegments * 3 * 2 + 3 * capTriangles\n k = n * radialSegments + radialSegments\n\n for (let j = 0; j < strip.length - 2; ++j) {\n if (j % 2 === 0) {\n meshIndex[ l + j * 3 + 0 ] = k + strip[ j + 0 ]\n meshIndex[ l + j * 3 + 1 ] = k + strip[ j + 1 ]\n meshIndex[ l + j * 3 + 2 ] = k + strip[ j + 2 ]\n } else {\n meshIndex[ l + j * 3 + 0 ] = k + strip[ j + 2 ]\n meshIndex[ l + j * 3 + 1 ] = k + strip[ j + 1 ]\n meshIndex[ l + j * 3 + 2 ] = k + strip[ j + 0 ]\n }\n }\n }\n}\n\nexport default TubeMeshBuffer\n","/**\n * @file Cartoon Representation\n * @author Alexander Rose \n * @private\n */\n\nimport { defaults } from '../utils'\nimport { Debug, Log, RepresentationRegistry } from '../globals'\nimport Spline from '../geometry/spline'\nimport StructureRepresentation, { StructureRepresentationParameters, StructureRepresentationData } from './structure-representation'\nimport TubeMeshBuffer from '../buffer/tubemesh-buffer'\nimport { Structure } from '../ngl';\nimport Viewer from '../viewer/viewer';\nimport Polymer from '../proxy/polymer';\nimport AtomProxy from '../proxy/atom-proxy';\nimport StructureView from '../structure/structure-view';\nimport Buffer from '../buffer/buffer';\n\nexport interface CartoonRepresentationParameters extends StructureRepresentationParameters {\n aspectRatio: number\n subdiv: number\n radialSegments: number\n tension: number\n capped: boolean\n smoothSheet: boolean\n}\n\n/**\n * Cartoon representation. Show a thick ribbon that\n * smoothly connecting backbone atoms in polymers.\n *\n * __Name:__ _cartoon_\n *\n * @example\n * stage.loadFile( \"rcsb://1crn\" ).then( function( o ){\n * o.addRepresentation( \"cartoon\" );\n * o.autoView();\n * } );\n */\nclass CartoonRepresentation extends StructureRepresentation {\n protected aspectRatio: number\n protected tension: number\n protected capped: boolean\n protected smoothSheet: boolean\n protected subdiv: number\n \n /**\n * Create Cartoon representation object\n * @param {Structure} structure - the structure to be represented\n * @param {Viewer} viewer - a viewer object\n * @param {StructureRepresentationParameters} params - representation parameters\n */\n constructor (structure: Structure, viewer: Viewer, params: Partial) {\n super(structure, viewer, params)\n\n this.type = 'cartoon'\n\n this.parameters = Object.assign({\n\n aspectRatio: {\n type: 'number', precision: 1, max: 10.0, min: 1.0, rebuild: true\n },\n subdiv: {\n type: 'integer', max: 50, min: 1, rebuild: true\n },\n radialSegments: {\n type: 'integer', max: 50, min: 1, rebuild: true\n },\n tension: {\n type: 'number', precision: 1, max: 1.0, min: 0.1\n },\n capped: {\n type: 'boolean', rebuild: true\n },\n smoothSheet: {\n type: 'boolean', rebuild: true\n }\n\n }, this.parameters)\n\n this.init(params)\n }\n\n init (params: Partial) {\n var p = params || {}\n p.colorScheme = defaults(p.colorScheme, 'chainname')\n p.colorScale = defaults(p.colorScale, 'RdYlBu')\n p.radiusType = defaults(p.radiusType, 'sstruc')\n p.radiusScale = defaults(p.radiusScale, 0.7)\n p.useInteriorColor = defaults(p.useInteriorColor, true)\n\n this.aspectRatio = defaults(p.aspectRatio, 5.0)\n this.tension = defaults(p.tension, NaN)\n this.capped = defaults(p.capped, true)\n this.smoothSheet = defaults(p.smoothSheet, false)\n\n if (p.quality === 'low') {\n this.subdiv = 3\n this.radialSegments = 6\n } else if (p.quality === 'medium') {\n this.subdiv = 6\n } else if (p.quality === 'high') {\n this.subdiv = 12\n } else {\n this.subdiv = defaults(p.subdiv, 6)\n }\n\n super.init(p)\n }\n\n getSplineParams (params?: Partial) {\n return Object.assign({\n subdiv: this.subdiv,\n tension: this.tension,\n directional: this.aspectRatio !== 1.0,\n smoothSheet: this.smoothSheet\n }, params)\n }\n\n getSpline (polymer: Polymer): Spline {\n return new Spline(polymer, this.getSplineParams())\n }\n\n getAspectRatio (polymer: Polymer): number {\n return polymer.isCg() ? 1.0 : this.aspectRatio\n }\n\n getAtomRadius (atom: AtomProxy): number {\n return atom.isTrace() ? super.getAtomRadius(atom) : 0\n }\n\n createData (sview: StructureView) {\n let bufferList: Buffer[] = []\n let polymerList: Polymer[] = []\n\n this.structure.eachPolymer(polymer => {\n if (polymer.residueCount < 4) return\n polymerList.push(polymer)\n\n const spline = this.getSpline(polymer)\n const aspectRatio = this.getAspectRatio(polymer)\n\n const subPos = spline.getSubdividedPosition()\n const subOri = spline.getSubdividedOrientation()\n const subCol = spline.getSubdividedColor(this.getColorParams())\n const subPick = spline.getSubdividedPicking()\n const subSize = spline.getSubdividedSize(this.getRadiusParams())\n\n bufferList.push(\n new TubeMeshBuffer(\n Object.assign({}, subPos, subOri, subCol, subPick, subSize),\n this.getBufferParams({\n radialSegments: this.radialSegments,\n aspectRatio: aspectRatio,\n capped: this.capped\n })\n )\n )\n }, sview.getSelection())\n\n return {\n bufferList: bufferList,\n polymerList: polymerList\n }\n }\n\n updateData (what: any, data: StructureRepresentationData) {\n if (Debug) Log.time(this.type + ' repr update')\n\n what = what || {}\n\n for (var i = 0, il = data.polymerList!.length; i < il; ++i) {\n var bufferData: {[key: string]: any} = {}\n var polymer = data.polymerList![ i ]\n var spline = this.getSpline(polymer)\n var aspectRatio = this.getAspectRatio(polymer)\n\n Object.assign(data.bufferList[ i ], {aspectRatio: aspectRatio})\n\n if (what.position || what.radius) {\n var subPos = spline.getSubdividedPosition()\n var subOri = spline.getSubdividedOrientation()\n var subSize = spline.getSubdividedSize(this.getRadiusParams(aspectRatio))\n\n bufferData.position = subPos.position\n bufferData.normal = subOri.normal\n bufferData.binormal = subOri.binormal\n bufferData.tangent = subOri.tangent\n bufferData.size = subSize.size\n }\n\n if (what.color) {\n var subCol = spline.getSubdividedColor(this.getColorParams())\n bufferData.color = subCol.color\n }\n\n if (what.picking) {\n var subPick = spline.getSubdividedPicking()\n bufferData.picking = subPick.picking\n }\n\n data.bufferList[ i ].setAttributes(bufferData)\n }\n\n if (Debug) Log.timeEnd(this.type + ' repr update')\n }\n\n setParameters (params: Partial) {\n const rebuild = false\n var what: {[k: string]: any} = {}\n\n if (params && params.aspectRatio) {\n what.radius = true\n }\n\n if (params && params.tension) {\n what.position = true\n }\n\n super.setParameters(params, what, rebuild)\n\n return this\n }\n}\n\nRepresentationRegistry.add('cartoon', CartoonRepresentation)\n\nexport default CartoonRepresentation\n","/**\n * @file Contact Representation\n * @author Alexander Rose \n * @private\n */\n\nimport { defaults } from '../utils'\nimport { RepresentationRegistry } from '../globals'\nimport StructureRepresentation, { StructureRepresentationParameters } from './structure-representation'\nimport { calculateContacts, getContactData, getLabelData } from '../chemistry/interactions/contact'\nimport CylinderBuffer from '../buffer/cylinder-buffer'\nimport TextBuffer from '../buffer/text-buffer'\nimport { getFixedCountDashData } from '../geometry/dash'\nimport Viewer from '../viewer/viewer';\nimport { Structure } from '../ngl';\nimport StructureView from '../structure/structure-view';\nimport CylinderGeometryBuffer from '../buffer/cylindergeometry-buffer';\nimport CylinderImpostorBuffer from '../buffer/cylinderimpostor-buffer';\n// @ts-ignore: unused import ContactPicker required for declaration only\nimport { ContactPicker } from '../utils/picker';\n\nexport interface ContactRepresentationParameters extends StructureRepresentationParameters {\n hydrogenBond: boolean\n weakHydrogenBond: boolean\n waterHydrogenBond: boolean\n backboneHydrogenBond: boolean\n hydrophobic: boolean\n halogenBond: boolean\n ionicInteraction: boolean\n metalCoordination: boolean\n cationPi: boolean\n piStacking: boolean\n filterSele: string|[string, string]\n maxHydrophobicDist: number\n maxHbondDist: number\n maxHbondSulfurDist: number\n maxHbondAccAngle: number\n maxHbondDonAngle: number\n maxHbondAccPlaneAngle: number\n maxHbondDonPlaneAngle: number\n maxPiStackingDist: number\n maxPiStackingOffset: number\n maxPiStackingAngle: number\n maxCationPiDist: number\n maxCationPiOffset: number\n maxIonicDist: number\n maxHalogenBondDist: number\n maxHalogenBondAngle: number\n maxMetalDist: number\n refineSaltBridges: boolean\n masterModelIndex: number\n lineOfSightDistFactor: number\n}\n\n/**\n * Contact representation.\n */\nclass ContactRepresentation extends StructureRepresentation {\n protected hydrogenBond: boolean\n protected weakHydrogenBond: boolean\n protected waterHydrogenBond: boolean\n protected backboneHydrogenBond: boolean\n protected hydrophobic: boolean\n protected halogenBond: boolean\n protected ionicInteraction: boolean\n protected metalCoordination: boolean\n protected cationPi: boolean\n protected piStacking: boolean\n protected filterSele: string|[string, string]\n protected maxHydrophobicDist: number\n protected maxHbondDist: number\n protected maxHbondSulfurDist: number\n protected maxHbondAccAngle: number\n protected maxHbondDonAngle: number\n protected maxHbondAccPlaneAngle: number\n protected maxHbondDonPlaneAngle: number\n protected maxPiStackingDist: number\n protected maxPiStackingOffset: number\n protected maxPiStackingAngle: number\n protected maxCationPiDist: number\n protected maxCationPiOffset: number\n protected maxIonicDist: number\n protected maxHalogenBondDist: number\n protected maxHalogenBondAngle: number\n protected maxMetalDist: number\n protected refineSaltBridges: boolean\n protected masterModelIndex: number\n protected lineOfSightDistFactor: number\n\n constructor (structure: Structure, viewer: Viewer, params: Partial) {\n super(structure, viewer, params)\n\n this.type = 'contact'\n\n this.parameters = Object.assign({\n hydrogenBond: {\n type: 'boolean', rebuild: true\n },\n weakHydrogenBond: {\n type: 'boolean', rebuild: true\n },\n waterHydrogenBond: {\n type: 'boolean', rebuild: true\n },\n backboneHydrogenBond: {\n type: 'boolean', rebuild: true\n },\n hydrophobic: {\n type: 'boolean', rebuild: true\n },\n halogenBond: {\n type: 'boolean', rebuild: true\n },\n ionicInteraction: {\n type: 'boolean', rebuild: true\n },\n metalCoordination: {\n type: 'boolean', rebuild: true\n },\n cationPi: {\n type: 'boolean', rebuild: true\n },\n piStacking: {\n type: 'boolean', rebuild: true\n },\n\n filterSele: {\n type: 'text', rebuild: true\n },\n\n labelVisible: {\n type: 'boolean', rebuild: true\n },\n\n labelFixedSize: {\n type: 'boolean', buffer: 'fixedSize'\n },\n\n labelSize: {\n type: 'number', precision: 3, max: 10.0, min: 0.001, rebuild: true\n },\n\n labelUnit: {\n type: 'select',\n rebuild: true,\n options: { '': '', angstrom: 'angstrom', nm: 'nm' }\n },\n\n maxHydrophobicDist: {\n type: 'number', precision: 1, max: 10, min: 0.1, rebuild: true\n },\n maxHbondDist: {\n type: 'number', precision: 1, max: 10, min: 0.1, rebuild: true\n },\n maxHbondSulfurDist: {\n type: 'number', precision: 1, max: 10, min: 0.1, rebuild: true\n },\n maxHbondAccAngle: {\n type: 'integer', max: 180, min: 0, rebuild: true\n },\n maxHbondDonAngle: {\n type: 'integer', max: 180, min: 0, rebuild: true\n },\n maxHbondAccPlaneAngle: {\n type: 'integer', max: 90, min: 0, rebuild: true\n },\n maxHbondDonPlaneAngle: {\n type: 'integer', max: 90, min: 0, rebuild: true\n },\n maxPiStackingDist: {\n type: 'number', precision: 1, max: 10, min: 0.1, rebuild: true\n },\n maxPiStackingOffset: {\n type: 'number', precision: 1, max: 10, min: 0.1, rebuild: true\n },\n maxPiStackingAngle: {\n type: 'integer', max: 180, min: 0, rebuild: true\n },\n maxCationPiDist: {\n type: 'number', precision: 1, max: 10, min: 0.1, rebuild: true\n },\n maxCationPiOffset: {\n type: 'number', precision: 1, max: 10, min: 0.1, rebuild: true\n },\n maxIonicDist: {\n type: 'number', precision: 1, max: 10, min: 0.1, rebuild: true\n },\n maxHalogenBondDist: {\n type: 'number', precision: 1, max: 10, min: 0.1, rebuild: true\n },\n maxHalogenBondAngle: {\n type: 'integer', max: 180, min: 0, rebuild: true\n },\n maxMetalDist: {\n type: 'number', precision: 1, max: 10, min: 0.1, rebuild: true\n },\n refineSaltBridges: {\n type: 'boolean', rebuild: true\n },\n masterModelIndex: {\n type: 'integer', max: 1000, min: -1, rebuild: true\n },\n lineOfSightDistFactor: {\n type: 'number', precision: 1, max: 10, min: 0.0, rebuild: true\n },\n\n radialSegments: true,\n disableImpostor: true\n }, this.parameters)\n\n this.init(params)\n }\n\n init (params: Partial) {\n var p = params || {}\n p.radiusSize = defaults(p.radiusSize, 0.05)\n p.useInteriorColor = defaults(p.useInteriorColor, true)\n\n this.hydrogenBond = defaults(p.hydrogenBond, true)\n this.weakHydrogenBond = defaults(p.weakHydrogenBond, false)\n this.waterHydrogenBond = defaults(p.waterHydrogenBond, false)\n this.backboneHydrogenBond = defaults(p.backboneHydrogenBond, false)\n this.hydrophobic = defaults(p.hydrophobic, false)\n this.halogenBond = defaults(p.halogenBond, true)\n this.ionicInteraction = defaults(p.ionicInteraction, true)\n this.metalCoordination = defaults(p.metalCoordination, true)\n this.cationPi = defaults(p.cationPi, true)\n this.piStacking = defaults(p.piStacking, true)\n\n this.filterSele = defaults(p.filterSele, '')\n this.labelVisible = defaults(p.labelVisible, false)\n this.labelFixedSize = defaults(p.labelFixedSize, false)\n this.labelSize = defaults(p.labelSize, 2.0)\n this.labelUnit = defaults(p.labelUnit, '')\n\n this.maxHydrophobicDist = defaults(p.maxHydrophobicDist, 4.0)\n this.maxHbondDist = defaults(p.maxHbondDist, 3.5)\n this.maxHbondSulfurDist = defaults(p.maxHbondSulfurDist, 4.1)\n this.maxHbondAccAngle = defaults(p.maxHbondAccAngle, 45)\n this.maxHbondDonAngle = defaults(p.maxHbondDonAngle, 45)\n this.maxHbondAccPlaneAngle = defaults(p.maxHbondAccPlaneAngle, 90)\n this.maxHbondDonPlaneAngle = defaults(p.maxHbondDonPlaneAngle, 30)\n this.maxPiStackingDist = defaults(p.maxPiStackingDist, 5.5)\n this.maxPiStackingOffset = defaults(p.maxPiStackingOffset, 2.0)\n this.maxPiStackingAngle = defaults(p.maxPiStackingAngle, 30)\n this.maxCationPiDist = defaults(p.maxCationPiDist, 6.0)\n this.maxCationPiOffset = defaults(p.maxCationPiOffset, 2.0)\n this.maxIonicDist = defaults(p.maxIonicDist, 5.0)\n this.maxHalogenBondDist = defaults(p.maxHalogenBondDist, 3.5)\n this.maxHalogenBondAngle = defaults(p.maxHalogenBondAngle, 30)\n this.maxMetalDist = defaults(p.maxMetalDist, 3.0)\n this.refineSaltBridges = defaults(p.refineSaltBridges, true)\n this.masterModelIndex = defaults(p.masterModelIndex, -1)\n this.lineOfSightDistFactor = defaults(p.lineOfSightDistFactor, 1.0)\n\n super.init(p)\n }\n\n getAtomRadius () {\n return 0\n }\n\n getContactData (sview: StructureView) {\n const params = {\n maxHydrophobicDist: this.maxHydrophobicDist,\n maxHbondDist: this.maxHbondDist,\n maxHbondSulfurDist: this.maxHbondSulfurDist,\n maxHbondAccAngle: this.maxHbondAccAngle,\n maxHbondDonAngle: this.maxHbondDonAngle,\n maxHbondAccPlaneAngle: this.maxHbondAccPlaneAngle,\n maxHbondDonPlaneAngle: this.maxHbondDonPlaneAngle,\n maxPiStackingDist: this.maxPiStackingDist,\n maxPiStackingOffset: this.maxPiStackingOffset,\n maxPiStackingAngle: this.maxPiStackingAngle,\n maxCationPiDist: this.maxCationPiDist,\n maxCationPiOffset: this.maxCationPiOffset,\n maxIonicDist: this.maxIonicDist,\n maxHalogenBondDist: this.maxHalogenBondDist,\n maxHalogenBondAngle: this.maxHalogenBondAngle,\n maxMetalDist: this.maxMetalDist,\n refineSaltBridges: this.refineSaltBridges,\n masterModelIndex: this.masterModelIndex,\n lineOfSightDistFactor: this.lineOfSightDistFactor\n }\n\n const dataParams = {\n hydrogenBond: this.hydrogenBond,\n weakHydrogenBond: this.weakHydrogenBond,\n waterHydrogenBond: this.waterHydrogenBond,\n backboneHydrogenBond: this.backboneHydrogenBond,\n hydrophobic: this.hydrophobic,\n halogenBond: this.halogenBond,\n ionicInteraction: this.ionicInteraction,\n metalCoordination: this.metalCoordination,\n cationPi: this.cationPi,\n piStacking: this.piStacking,\n radius: this.radiusSize * this.radiusScale,\n filterSele: this.filterSele\n }\n\n const contacts = calculateContacts(sview, params)\n return getContactData(contacts, sview, dataParams)\n }\n\n createData (sview: StructureView) {\n const contactData = this.getContactData(sview)\n\n const bufferList = [\n new CylinderBuffer(\n getFixedCountDashData(contactData),\n this.getBufferParams({\n sphereDetail: 1,\n dullInterior: true,\n disableImpostor: this.disableImpostor\n })\n ) as (CylinderGeometryBuffer | CylinderImpostorBuffer | TextBuffer)\n ]\n\n if (this.labelVisible) {\n const labelParams = {\n size: this.labelSize,\n unit: this.labelUnit\n }\n bufferList.push(new TextBuffer(\n getLabelData(contactData, labelParams),\n this.getBufferParams({fixedSize: this.labelFixedSize})\n ))\n }\n\n return { bufferList }\n }\n}\n\nRepresentationRegistry.add('contact', ContactRepresentation)\n\nexport default ContactRepresentation\n","/**\n * @file Dihedral Representation\n * @author Fred Ludlow \n * @private\n */\nimport { Color } from 'three'\n\nimport { RepresentationRegistry } from '../globals'\nimport MeasurementRepresentation, { calcArcPoint, parseNestedAtoms, MeasurementRepresentationParameters, LabelDataField } from './measurement-representation'\nimport { defaults } from '../utils'\n\nimport MeshBuffer from '../buffer/mesh-buffer'\nimport TextBuffer, { TextBufferData } from '../buffer/text-buffer'\nimport WideLineBuffer from '../buffer/wideline-buffer'\n\nimport { copyArray, uniformArray, uniformArray3 } from '../math/array-utils'\nimport { v3add, v3angle, v3cross, v3dot, v3multiplyScalar, v3fromArray, v3length,\n v3negate, v3new, v3normalize, v3sub, v3toArray } from '../math/vector-utils'\nimport { RAD2DEG } from '../math/math-constants'\nimport { getFixedLengthWrappedDashData } from '../geometry/dash'\nimport { Structure } from '../ngl';\nimport Viewer from '../viewer/viewer';\nimport StructureView from '../structure/structure-view';\nimport { CylinderBufferData } from '../buffer/cylinder-buffer';\nimport { BufferData } from '../buffer/buffer';\nimport { StructureRepresentationData } from './structure-representation';\n\n/**\n * @typedef {Object} DihedralRepresentationParameters - dihedral representation parameters\n * @mixes RepresentationParameters\n * @mixes StructureRepresentationParameters\n * @mixes MeasurementRepresentationParameters\n *\n * @property {String} atomQuad - list of quadruplets of selection strings\n * or atom indices\n * @property {Boolean} extendLine - Extend lines in planes\n * @property {Number} lineOpacity - Opacity for the line part of the representation\n * @property {Boolean} lineVisible - Display the line part of the representation\n * @property {Number} linewidth - width for line part of representation\n * @property {Boolean} planeVisible - Display the two planes corresponding to dihedral\n * @property {Boolean} sectorVisible - Display the filled arc for each angle\n */\n\nexport interface DihedralRepresentationParameters extends MeasurementRepresentationParameters {\n atomQuad: (number|string)[][]\n extendLine: boolean\n lineOpacity: number\n lineVisible: boolean\n linewidth: number\n planeVisible: boolean\n sectorVisible: boolean\n}\n\n/**\n * Dihedral representation object\n *\n * Reperesentation consists of three parts, visibility can be set for each\n * label - text label indicating dihedral angle\n * line - line indicating four positions that define the dihedral\n * sector - filled arc section\n *\n * @param {Structure} structure - the structure to measure angles in\n * @param {Viewer} viewer - a viewer object\n * @param {AngleRepresentationParameters} params - angle representation parameters\n */\nclass DihedralRepresentation extends MeasurementRepresentation {\n protected atomQuad: (number|string)[][]\n protected extendLine: boolean\n protected lineOpacity: number\n protected lineVisible: boolean\n protected linewidth: number\n protected planeVisible: boolean\n protected sectorVisible: boolean\n\n protected lineLength: number\n protected planeLength: number\n protected sectorLength: number\n\n protected lineBuffer: WideLineBuffer\n protected planeBuffer: MeshBuffer\n protected sectorBuffer: MeshBuffer\n\n constructor (structure: Structure, viewer: Viewer, params: Partial) {\n super(structure, viewer, params)\n\n this.type = 'dihedral'\n\n this.parameters = Object.assign({\n atomQuad: {\n type: 'hidden', rebuild: true\n },\n extendLine: {\n type: 'boolean', rebuild: true, default: true\n },\n lineVisible: {\n type: 'boolean', default: true\n },\n planeVisible: {\n type: 'boolean', default: true\n },\n sectorVisible: {\n type: 'boolean', default: true\n }\n }, this.parameters)\n\n this.init(params)\n }\n\n init (params: Partial) {\n const p = params || {}\n p.side = defaults(p.side, 'double')\n p.opacity = defaults(p.opacity, 0.5)\n\n this.atomQuad = defaults(p.atomQuad, [])\n this.extendLine = defaults(p.extendLine, true)\n this.lineVisible = defaults(p.lineVisible, true)\n this.planeVisible = defaults(p.planeVisible, true)\n this.sectorVisible = defaults(p.sectorVisible, true)\n\n super.init(p)\n }\n\n createData (sview: StructureView) {\n if (!sview.atomCount || !this.atomQuad.length) return\n\n const atomPosition = parseNestedAtoms(sview, this.atomQuad)\n const dihedralData = getDihedralData(\n atomPosition, {\n extendLine: this.extendLine\n }\n )\n\n const n = this.n = dihedralData.labelText.length\n const labelColor = new Color(this.labelColor)\n\n this.textBuffer = new TextBuffer({\n position: dihedralData.labelPosition,\n size: uniformArray(n, this.labelSize),\n color: uniformArray3(n, labelColor.r, labelColor.g, labelColor.b),\n text: dihedralData.labelText\n } as TextBufferData, this.getLabelBufferParams())\n\n const c = new Color(this.colorValue)\n this.lineLength = dihedralData.linePosition1.length / 3\n const lineColor = uniformArray3(this.lineLength, c.r, c.g, c.b)\n\n this.lineBuffer = new WideLineBuffer(\n getFixedLengthWrappedDashData({\n position1: dihedralData.linePosition1,\n position2: dihedralData.linePosition2,\n color: lineColor,\n color2: lineColor\n } as CylinderBufferData) ,\n this.getBufferParams({\n linewidth: this.linewidth,\n visible: this.lineVisible,\n opacity: this.lineOpacity\n })\n )\n\n this.planeLength = dihedralData.planePosition.length / 3\n this.planeBuffer = new MeshBuffer({\n position: dihedralData.planePosition,\n color: uniformArray3(this.planeLength, c.r, c.g, c.b)\n } as BufferData, this.getBufferParams({\n visible: this.planeVisible\n }))\n\n this.sectorLength = dihedralData.sectorPosition.length / 3\n this.sectorBuffer = new MeshBuffer({\n position: dihedralData.sectorPosition,\n color: uniformArray3(this.sectorLength, c.r, c.g, c.b)\n } as BufferData, this.getBufferParams({\n visible: this.sectorVisible\n }))\n\n return {\n bufferList: [\n this.textBuffer,\n this.lineBuffer,\n this.planeBuffer,\n this.sectorBuffer\n ]\n }\n }\n\n updateData (what: LabelDataField & {color?: boolean}, data: StructureRepresentationData) {\n super.updateData(what, data)\n const lineData = {}\n const planeData = {}\n const sectorData = {}\n\n if (what.color) {\n const c = new Color(this.colorValue)\n Object.assign(lineData, {\n color: uniformArray3(this.lineLength, c.r, c.g, c.b),\n color2: uniformArray3(this.lineLength, c.r, c.g, c.b)\n })\n Object.assign(planeData, {\n color: uniformArray3(this.planeLength, c.r, c.g, c.b)\n })\n Object.assign(sectorData, {\n color: uniformArray3(this.sectorLength, c.r, c.g, c.b)\n })\n }\n\n this.lineBuffer.setAttributes(lineData)\n this.planeBuffer.setAttributes(planeData)\n this.sectorBuffer.setAttributes(sectorData)\n }\n\n setParameters (params: Partial) {\n var rebuild = false\n var what = {}\n\n super.setParameters(params, what, rebuild)\n\n if (params && (\n params.lineVisible !== undefined ||\n params.sectorVisible !== undefined ||\n params.planeVisible !== undefined)) {\n this.setVisibility(this.visible)\n }\n\n if (params && params.lineOpacity) {\n this.lineBuffer.setParameters({ opacity: params.lineOpacity })\n }\n\n if (params && params.opacity !== undefined) {\n this.lineBuffer.setParameters({ opacity: this.lineOpacity })\n }\n\n if (params && params.linewidth) {\n this.lineBuffer.setParameters({ linewidth: params.linewidth })\n }\n\n return this\n }\n\n setVisibility (value: boolean, noRenderRequest?: boolean) {\n super.setVisibility(value, true)\n\n if (this.lineBuffer) {\n this.lineBuffer.setVisibility(this.lineVisible && this.visible)\n }\n\n if (this.planeBuffer) {\n this.planeBuffer.setVisibility(this.planeVisible && this.visible)\n }\n\n if (this.sectorBuffer) {\n this.sectorBuffer.setVisibility(this.sectorVisible && this.visible)\n }\n\n if (!noRenderRequest) this.viewer.requestRender()\n\n return this\n }\n}\n\n/**\n * Build the data required to create {Buffer} objects, given positions\n * @param {Float32Array} atomPosition 3*4*nDihedral array of coordinates\n * @return {Object} Arrays for building buffers\n */\nfunction getDihedralData (position: Float32Array, params: Partial = {}) {\n const angleStep = defaults(params.angleStep, Math.PI / 90)\n const nPos = position.length\n const n = position.length / 12\n const angles = new Float32Array(n)\n const labelPosition = new Float32Array(n * 3)\n const labelText = new Array(n)\n\n // Temporary arrays as don't know output length yet\n const lineTmp1 = new Array(n)\n const lineTmp2 = new Array(n)\n const sectorTmp = new Array(n)\n const planeTmp = new Array(n)\n\n // Eventual sizes of output arrays\n let totalLines = 0\n let totalSegments = 0\n let totalPlanes = 0\n\n const p1 = v3new()\n const p2 = v3new()\n const p3 = v3new()\n const p4 = v3new()\n\n const v21 = v3new()\n const v23 = v3new()\n const v34 = v3new()\n\n const tmp = v3new()\n const mid = v3new()\n const inPlane1 = v3new()\n const inPlane2 = v3new()\n const start = v3new()\n const end = v3new()\n\n const cross = v3new()\n const arcPoint = v3new()\n\n let i = 0 // Actual output index (after skipping inappropriate)\n\n for (var p = 0; p < nPos; p += 12) {\n // Set Positions\n v3fromArray(p1, position, p)\n v3fromArray(p2, position, p + 3)\n v3fromArray(p3, position, p + 6)\n v3fromArray(p4, position, p + 9)\n\n // Vectors between points\n v3sub(v21, p1, p2)\n v3sub(v23, p3, p2)\n if (v3length(v23) === 0.0) {\n continue // Can't define axis\n }\n\n v3sub(v34, p4, p3)\n\n v3multiplyScalar(tmp, v23, 0.5)\n v3add(mid, p2, tmp)\n\n v3normalize(v21, v21)\n v3normalize(v23, v23)\n v3normalize(v34, v34)\n\n // Which side of plane are p1, p4 (are we measuring something that\n // looks more like an improper? e.g. C, CA, CB, N)\n v3sub(tmp, p1, mid)\n const improperStart = v3dot(tmp, v23) > 0.0\n v3sub(tmp, p4, mid)\n const improperEnd = v3dot(tmp, v23) < 0.0\n\n // Calculate vectors perp to v23 (lying in plane (1,2,3) and (2,3,4))\n v3multiplyScalar(tmp, v23, v3dot(v23, v21))\n v3sub(inPlane1, v21, tmp)\n\n v3multiplyScalar(tmp, v23, v3dot(v23, v34))\n v3sub(inPlane2, v34, tmp)\n\n if (v3length(inPlane1) === 0.0 || v3length(inPlane2) === 0.0) {\n continue // Indeterminate angle\n }\n\n v3normalize(inPlane1, inPlane1)\n v3normalize(inPlane2, inPlane2)\n\n const angle = angles[ i ] = v3angle(inPlane1, inPlane2)\n labelText[ i ] = (RAD2DEG * angle).toFixed(1) + String.fromCharCode(0x00B0)\n\n v3cross(cross, inPlane1, v23)\n v3normalize(cross, cross)\n if (v3dot(cross, inPlane2) < 0.0) {\n v3negate(cross, cross) // Ensure cp faces correct way\n }\n\n calcArcPoint(tmp, mid, inPlane1, cross, angle / 2.0)\n v3toArray(tmp, labelPosition, 3 * i)\n\n const nSegments = Math.ceil(angle / angleStep)\n // For extended display mode, 4 straight lines plus arc/segment edge\n // For non-extended, 2 straight lines plus segment edge\n const nLines = nSegments + ((params.extendLine) ? 4 : 2)\n\n // Don't draw planes if not extending lines\n const nPlanes = params.extendLine ? 36 : 0\n\n const line1 = new Float32Array(nLines * 3)\n const line2 = new Float32Array(nLines * 3)\n const sector = new Float32Array(nSegments * 9)\n // 2 planes, 2 triangles each per dihedral (2*2*9)\n const plane = new Float32Array(nPlanes)\n\n lineTmp1[ i ] = line1\n lineTmp2[ i ] = line2\n sectorTmp[ i ] = sector\n planeTmp[ i ] = plane\n\n // Start points for lines/planes, only required\n // if extending lines\n if (params.extendLine) {\n if (improperStart) { // We'll start on the v3->1 line (tmp)\n v3sub(tmp, p1, p3)\n v3normalize(tmp, tmp)\n v3multiplyScalar(start, tmp, 1.0 / v3dot(inPlane1, tmp))\n v3add(start, start, p3)\n } else { // start on the 2->1 line\n v3multiplyScalar(start, v21, 1.0 / v3dot(inPlane1, v21))\n v3add(start, start, p2)\n }\n\n if (improperEnd) { // Finish on 2->4 line\n v3sub(tmp, p4, p2)\n v3normalize(tmp, tmp)\n v3multiplyScalar(end, tmp, 1.0 / v3dot(inPlane2, tmp))\n v3add(end, end, p2)\n } else { // end on the 3->4 line\n v3multiplyScalar(end, v34, 1.0 / v3dot(inPlane2, v34))\n v3add(end, end, p3)\n }\n }\n\n v3add(arcPoint, mid, inPlane1)\n\n // index into line1, line2\n let li = 0\n // If extending lines, there's a bit of stuff to do here\n // figuring out start and end positions\n if (params.extendLine) {\n v3toArray(p1, line1, li)\n v3toArray(start, line2, li)\n li += 3\n v3toArray(start, line1, li)\n v3toArray(arcPoint, line2, li)\n li += 3\n\n // Construct plane at start, if not extening lines\n // this is skipped\n v3toArray(start, plane, 0)\n v3toArray(arcPoint, plane, 3)\n v3toArray(improperStart ? p3 : p2, plane, 6)\n v3toArray(improperStart ? p3 : p2, plane, 9)\n v3toArray(arcPoint, plane, 12)\n v3toArray(mid, plane, 15)\n } else {\n // Not extending lines\n v3toArray(mid, line1, li)\n v3toArray(arcPoint, line2, li)\n li += 3\n }\n\n const appendArcSection = function (a: number, j: number) {\n const si = j * 9\n\n v3toArray(mid, sector, si)\n v3toArray(arcPoint, sector, si + 3)\n v3toArray(arcPoint, line1, li)\n\n calcArcPoint(arcPoint, mid, inPlane1, cross, a)\n\n v3toArray(arcPoint, sector, si + 6)\n v3toArray(arcPoint, line2, li)\n li += 3\n }\n\n let j = 0\n for (let a = angleStep; a < angle; a += angleStep) {\n appendArcSection(a, j++)\n }\n appendArcSection(angle, j++)\n\n if (params.extendLine) {\n v3toArray(arcPoint, line1, (nLines - 2) * 3)\n v3toArray(end, line2, (nLines - 2) * 3)\n v3toArray(end, line1, (nLines - 1) * 3)\n v3toArray(p4, line2, (nLines - 1) * 3)\n\n // Construct plane at end\n v3toArray(end, plane, 18)\n v3toArray(arcPoint, plane, 21)\n v3toArray(improperEnd ? p2 : p3, plane, 24)\n v3toArray(improperEnd ? p2 : p3, plane, 27)\n v3toArray(arcPoint, plane, 30)\n v3toArray(mid, plane, 33)\n } else {\n v3toArray(arcPoint, line1, li)\n v3toArray(mid, line2, li)\n li += 3\n }\n\n totalLines += nLines * 3\n totalSegments += nSegments * 9\n totalPlanes += nPlanes\n i += 1\n }\n\n const nSuccess = i\n\n const linePosition1 = new Float32Array(totalLines)\n const linePosition2 = new Float32Array(totalLines)\n const sectorPosition = new Float32Array(totalSegments)\n const planePosition = new Float32Array(totalPlanes)\n\n let lineOffset = 0\n let sectorOffset = 0\n let planeOffset = 0\n\n for (let i = 0; i < nSuccess; i++) {\n const lp1 = lineTmp1[ i ]\n const lp2 = lineTmp2[ i ]\n const sp = sectorTmp[ i ]\n const pp = planeTmp[ i ]\n\n copyArray(lp1, linePosition1, 0, lineOffset, lp1.length)\n copyArray(lp2, linePosition2, 0, lineOffset, lp2.length)\n copyArray(sp, sectorPosition, 0, sectorOffset, sp.length)\n copyArray(pp, planePosition, 0, planeOffset, pp.length)\n\n lineOffset += lp1.length\n sectorOffset += sp.length\n planeOffset += pp.length\n }\n\n return {\n labelPosition: labelPosition.subarray(0, nSuccess * 3),\n labelText: labelText.slice(0, nSuccess),\n linePosition1,\n linePosition2,\n planePosition,\n sectorPosition\n }\n}\n\nRepresentationRegistry.add('dihedral', DihedralRepresentation)\n\nexport default DihedralRepresentation\n","/**\n * @file Dihedral Histogram Representation\n * @author Rudolfs Petrovs \n * @private\n */\nimport { Color } from 'three'\n\nimport { calcArcPoint, parseNestedAtoms } from './measurement-representation'\nimport StructureRepresentation, { StructureRepresentationParameters } from './structure-representation'\n\nimport { RepresentationRegistry } from '../globals'\nimport { Structure } from '../ngl'\nimport { defaults } from '../utils'\n\nimport { BufferData } from '../buffer/buffer'\nimport MeshBuffer from '../buffer/mesh-buffer'\nimport WideLineBuffer, { WideLineBufferData } from '../buffer/wideline-buffer'\n\nimport { copyArray, uniformArray3, arraySum } from '../math/array-utils'\nimport {\n v3add, v3cross, v3dot, v3multiplyScalar, v3fromArray,\n v3negate, v3new, v3normalize, v3sub, v3toArray, v3length\n} from '../math/vector-utils'\n\nimport StructureView from '../structure/structure-view'\n\nimport Viewer from '../viewer/viewer'\n\n\nconst pointLength = 3 // One Point Length (number of coordinates of one point in 3D)\nconst pointsInTriangle = 3\n\ntype ColorDefinition = Color | string | number | undefined\n\ninterface HistogramColorParameters {\n histogramBinBorderColor: ColorDefinition\n adjacentBondArrowColor: ColorDefinition\n distantBondArrowColor: ColorDefinition\n frontHistogramColor: ColorDefinition\n backHistogramColor: ColorDefinition\n opaqueMiddleDiscColor: ColorDefinition\n}\n\ninterface HistogramInputData extends Partial {\n atomQuad: (number | string)[]\n histogram360: number[]\n}\n\ninterface HistogramData extends HistogramInputData {\n atomPositions: Float32Array\n histogram360Scaled: number[]\n}\n\ninterface WideLineData {\n startPoints: Float32Array\n endPoints: Float32Array\n startColors: Float32Array\n endColors: Float32Array\n}\n\ninterface MeshData {\n triangles: Float32Array\n triangleColors: Float32Array\n}\n\nfunction createUpdatedObject(o: Object, updateSource: Object) {\n function hasKey(obj: O, key: keyof any): key is keyof O {\n return key in obj\n }\n\n const result = { ...o } // Shallow copy\n for (const key in result) {\n if (hasKey(result, key) && hasKey(updateSource, key)) {\n result[key] = defaults(updateSource[key], result[key])\n }\n }\n return result\n}\n\nfunction createColorArray(color: ColorDefinition, arrayLength: number) {\n const colorValue = new Color(color)\n const targetArray = new Float32Array(arrayLength * 3)\n uniformArray3(arrayLength, colorValue.r, colorValue.g, colorValue.b, targetArray)\n return targetArray\n}\n\n/**\n * @typedef {Object} DihedralHistogramRepresentationParameters - dihedral representation parameters\n * @mixes RepresentationParameters\n * @mixes StructureRepresentationParameters\n *\n * @property {HistogramInputData[]} histogramsData\n * List of HistogramInputData objects, which properties specifies each particular\n * histogram, and can contain particular histogram-specific parameters.\n * Obligatory properties are:\n * atomQuad - Quadruplet of selection strings or atom indices\n * histogram360 - List of values, representing histogram from 0 to 360 degrees.\n * @property {Boolean} histogramBinBorderVisible - Display the lines that separate circular histogram bins\n * @property {Boolean} scaleBinToSectorArea - Should sector-based histogram bins'\n * area be proportional to the bins' value\n */\n\nexport interface DihedralHistogramRepresentationParameters extends StructureRepresentationParameters {\n histogramsData: HistogramInputData[]\n\n histogramBinBorderVisible: boolean\n scaleBinToSectorArea: boolean\n}\n\n/**\n * Dihedral Histogram representation object\n *\n * Reperesentation consists of several parts:\n * opaqueMiddleDisc - opaque disc in the middle of the dihedral between front and back histograms\n * frontHistogram - circular histogram from the adjacent bond viewpoint\n * backHistogram - circular histogram from the distant bond viewpoint\n * histogramBinBorder - lines, which separate histogram bins\n * bondArrows - lines, which show the actual angle on the histogram disc\n *\n * @param {Structure} structure - the structure to measure angles in\n * @param {Viewer} viewer - a viewer object\n * @param {DihedralHistogramRepresentationParameters} params - Dihedral histogram representation parameters\n */\nclass DihedralHistogramRepresentation extends StructureRepresentation {\n protected histogramsData: HistogramData[]\n\n protected histogramBinBorderVisible: boolean\n protected histogramBinBorderWidth: number\n protected histogramBinBorderColor: ColorDefinition\n protected histogramBinBorderOpacity: number\n\n protected bondArrowVisible: boolean\n protected bondArrowWidth: number\n protected bondArrowOpacity: number\n\n protected adjacentBondArrowColor: ColorDefinition\n protected distantBondArrowColor: ColorDefinition\n\n protected histogramOpacity: number\n protected frontHistogramColor: ColorDefinition\n protected backHistogramColor: ColorDefinition\n\n protected opaqueMiddleDiscVisible: boolean\n protected opaqueMiddleDiscColor: ColorDefinition\n protected opaqueMiddleDiscOpacity: number\n\n protected scaleBinToSectorArea: boolean\n\n constructor(structure: Structure, viewer: Viewer, params: DihedralHistogramRepresentationParameters) {\n super(structure, viewer, params)\n\n this.type = 'dihedral-histogram'\n\n this.parameters = Object.assign({\n histogramsData: {\n type: 'hidden', rebuild: true\n },\n histogramBinBorderVisible: {\n type: 'boolean', default: true\n },\n scaleBinToSectorArea: {\n type: 'boolean',\n rebuild: true,\n default: false\n }\n }, this.parameters)\n\n this.init(params)\n }\n\n init(params: Partial) {\n const p = params || {}\n\n const defaultColorData = {\n histogramBinBorderColor: 'grey',\n adjacentBondArrowColor: 'black',\n distantBondArrowColor: 'magenta',\n frontHistogramColor: 'green',\n backHistogramColor: 'blue',\n opaqueMiddleDiscColor: 'white'\n }\n\n const colorData = createUpdatedObject(defaultColorData, p)\n Object.assign(this, colorData)\n\n const defaultParameters = {\n histogramsData: [],\n histogramOpacity: 1.0,\n\n opaqueMiddleDiscVisible: true,\n opaqueMiddleDiscOpacity: 1.0,\n\n histogramBinBorderVisible: true,\n histogramBinBorderWidth: 1,\n histogramBinBorderOpacity: 0.5,\n\n bondArrowVisible: true,\n bondArrowWidth: 2,\n bondArrowOpacity: 1.0,\n\n scaleBinToSectorArea: false,\n }\n const parameters = createUpdatedObject(defaultParameters, p)\n Object.assign(this, parameters)\n\n this.histogramsData.forEach(x => {\n const specificColorData = createUpdatedObject(colorData, x)\n Object.assign(x, specificColorData)\n })\n\n p.side = defaults(p.side, 'double')\n p.opacity = defaults(p.opacity, 0.5)\n p.radiusType = defaults(p.radiusType, 'size')\n p.radiusSize = defaults(p.radiusSize, 0.15)\n\n super.init(p)\n }\n\n getHistogramBinBorderBufferParameters() {\n return this.getBufferParams({\n linewidth: this.histogramBinBorderWidth,\n visible: this.histogramBinBorderVisible,\n opacity: this.histogramBinBorderOpacity,\n })\n }\n\n getBondArrowsBufferParameters() {\n return this.getBufferParams({\n linewidth: this.bondArrowWidth,\n visible: this.bondArrowVisible,\n opacity: this.bondArrowOpacity,\n })\n }\n\n getOpaqueMiddleDiscBufferParameters() {\n return this.getBufferParams({\n visible: this.opaqueMiddleDiscVisible,\n opacity: this.opaqueMiddleDiscOpacity\n })\n }\n\n getHistogramBufferParameters() {\n return this.getBufferParams({\n visible: true,\n opacity: this.histogramOpacity,\n side: \"double\"\n })\n }\n\n createData(sview: StructureView) {\n if (!sview.atomCount || !this.histogramsData.length) return\n this.histogramsData.forEach(x => x.atomPositions = parseNestedAtoms(sview, [x.atomQuad]))\n const scaleData = this.scaleBinToSectorArea ? function (y: number) { return Math.sqrt(y) } : function (y: number) { return y }\n this.histogramsData.forEach(x => x.histogram360Scaled = x.histogram360.map(scaleData))\n function Float32Concat(arrays: Float32Array[]) {\n const lengths = arrays.map(x => x.length)\n const result = new Float32Array(arraySum(lengths))\n let accumulatedOffset = 0\n for (let i = 0; i < arrays.length; i++) {\n result.set(arrays[i], accumulatedOffset)\n accumulatedOffset += arrays[i].length\n }\n return result\n }\n\n function createWideLineBuffer(linesList: WideLineData[], params: {}) {\n return new WideLineBuffer(\n {\n position1: Float32Concat(linesList.map(x => x.startPoints)),\n position2: Float32Concat(linesList.map(x => x.endPoints)),\n color: Float32Concat(linesList.map(x => x.startColors)),\n color2: Float32Concat(linesList.map(x => x.endColors)),\n } as WideLineBufferData,\n params)\n }\n\n function createMeshBuffer(mesh: MeshData[], params: {}) {\n return new MeshBuffer(\n {\n position: Float32Concat(mesh.map(x => x.triangles)),\n color: Float32Concat(mesh.map(x => x.triangleColors))\n } as BufferData,\n params)\n }\n\n const dihedralDataArray = []\n\n for (let i = 0; i < this.histogramsData.length; i++) {\n let dihedralData = undefined\n let currentHistogramData = this.histogramsData[i]\n let currentHistogram360 = currentHistogramData.histogram360\n if (currentHistogram360.length >= 3) {\n dihedralData = calculateDihedralHistogram(currentHistogramData)\n }\n if (typeof dihedralData === \"undefined\") continue\n dihedralDataArray.push(dihedralData)\n }\n\n this.frontHistogramBinBordersBuffer = createWideLineBuffer(\n dihedralDataArray.map(x => x.frontHistogramBinBorders),\n this.getHistogramBinBorderBufferParameters()\n )\n\n this.backHistogramBinBordersBuffer = createWideLineBuffer(\n dihedralDataArray.map(x => x.backHistogramBinBorders),\n this.getHistogramBinBorderBufferParameters()\n )\n\n this.adjacentBondArrowsBuffer = createWideLineBuffer(\n dihedralDataArray.map(x => x.adjacentBondArrows),\n this.getBondArrowsBufferParameters()\n )\n\n this.distantBondArrowsBuffer = createWideLineBuffer(\n dihedralDataArray.map(x => x.distantBondArrows),\n this.getBondArrowsBufferParameters()\n )\n\n this.opaqueMiddleDiscBuffer = createMeshBuffer(\n dihedralDataArray.map(x => x.opaqueMiddleDisc),\n this.getOpaqueMiddleDiscBufferParameters()\n )\n\n this.frontHistogramBuffer = createMeshBuffer(\n dihedralDataArray.map(x => x.frontHistogram),\n this.getHistogramBufferParameters()\n )\n\n this.backHistogramBuffer = createMeshBuffer(\n dihedralDataArray.map(x => x.backHistogram),\n this.getHistogramBufferParameters()\n )\n\n return {\n bufferList: [].concat(\n this.frontHistogramBinBordersBuffer,\n this.backHistogramBinBordersBuffer,\n this.adjacentBondArrowsBuffer,\n this.distantBondArrowsBuffer,\n this.opaqueMiddleDiscBuffer,\n this.frontHistogramBuffer,\n this.backHistogramBuffer\n )\n }\n }\n\n setParameters(params: Partial) {\n const rebuild = false\n const what = {}\n super.setParameters(params, what, rebuild)\n\n if (params && (params.histogramBinBorderVisible !== undefined)) {\n this.setVisibility(this.visible)\n }\n return this\n }\n\n setVisibility(value: boolean, noRenderRequest?: boolean) {\n super.setVisibility(value, true)\n if (this.frontHistogramBinBordersBuffer) {\n this.frontHistogramBinBordersBuffer.setVisibility(this.histogramBinBorderVisible)\n }\n if (this.backHistogramBinBordersBuffer) {\n this.backHistogramBinBordersBuffer.setVisibility(this.histogramBinBorderVisible)\n }\n if (!noRenderRequest) this.viewer.requestRender()\n return this\n }\n}\n\n/**\n * Calculates the data required to create {Buffer} objects for one histogram, given positions\n * @param Float32Array positionOfDihedralAtoms 3*4 array of coordinates\n * @param NumberArray histogram array of coordinates\n * @return Arrays for building buffers\n */\nfunction calculateDihedralHistogram(histogramData: HistogramData) {\n const positionOfDihedralAtoms = histogramData.atomPositions\n const histogram = histogramData.histogram360Scaled;\n const totalSectorTrianglesInOpaqueMiddleDisc = histogram.length <= 180 ? 360 : histogram.length * 2\n const frontAndBack = 2\n\n const opaqueMiddleDisc = {\n triangles: new Float32Array(totalSectorTrianglesInOpaqueMiddleDisc * pointsInTriangle * pointLength),\n triangleColors: createColorArray(histogramData.opaqueMiddleDiscColor, totalSectorTrianglesInOpaqueMiddleDisc * pointsInTriangle)\n }\n\n const frontHistogram = {\n triangles: new Float32Array(histogram.length * pointsInTriangle * pointLength),\n triangleColors: createColorArray(histogramData.frontHistogramColor, histogram.length * pointsInTriangle)\n }\n\n const backHistogram = {\n triangles: new Float32Array(histogram.length * pointsInTriangle * pointLength),\n triangleColors: createColorArray(histogramData.backHistogramColor, histogram.length * pointsInTriangle)\n }\n\n const frontHistogramBinBorders = {\n startPoints: new Float32Array(histogram.length * pointLength),\n endPoints: new Float32Array(histogram.length * pointLength),\n startColors: createColorArray(histogramData.histogramBinBorderColor, histogram.length),\n endColors: createColorArray(histogramData.histogramBinBorderColor, histogram.length)\n }\n\n const backHistogramBinBorders = {\n startPoints: new Float32Array(histogram.length * pointLength),\n endPoints: new Float32Array(histogram.length * pointLength),\n startColors: createColorArray(histogramData.histogramBinBorderColor, histogram.length),\n endColors: createColorArray(histogramData.histogramBinBorderColor, histogram.length)\n }\n\n const adjacentBondArrows = {\n startPoints: new Float32Array(frontAndBack * pointLength),\n endPoints: new Float32Array(frontAndBack * pointLength),\n startColors: createColorArray(histogramData.adjacentBondArrowColor, histogram.length),\n endColors: createColorArray(histogramData.adjacentBondArrowColor, histogram.length)\n }\n const distantBondArrows = {\n startPoints: new Float32Array(frontAndBack * pointLength),\n endPoints: new Float32Array(frontAndBack * pointLength),\n startColors: createColorArray(histogramData.distantBondArrowColor, histogram.length),\n endColors: createColorArray(histogramData.distantBondArrowColor, histogram.length)\n }\n\n const p1 = v3new()\n const p2 = v3new()\n const p3 = v3new()\n const p4 = v3new()\n\n const v21 = v3new()\n const v23 = v3new()\n const v32 = v3new()\n const v34 = v3new()\n\n const mid = v3new()\n const inPlane1 = v3new()\n const inPlane2 = v3new()\n\n const cross1 = v3new()\n const cross2 = v3new()\n\n const arcPoint = v3new()\n const tmp = v3new()\n const tmp2 = v3new()\n\n // Set Atom Coordinates\n const dihedralAtomVectors = [p1, p2, p3, p4]\n\n for (let i = 0; i < dihedralAtomVectors.length; i++) {\n v3fromArray(dihedralAtomVectors[i], positionOfDihedralAtoms, i * pointLength)\n }\n\n // Vectors between points\n v3sub(v21, p1, p2)\n v3sub(v23, p3, p2)\n v3sub(v34, p4, p3)\n if (v3length(v23) === 0.0) {\n return // Can't define axis\n }\n\n v3multiplyScalar(tmp, v23, 0.5)\n v3add(mid, p2, tmp)\n\n v3normalize(v21, v21)\n v3normalize(v23, v23)\n v3normalize(v34, v34)\n\n v3negate(v32, v23)\n // Calculate vectors perp to v23 (lying in plane (1,2,3) and (2,3,4))\n v3multiplyScalar(tmp, v32, v3dot(v32, v21))\n v3sub(inPlane1, v21, tmp)\n\n v3multiplyScalar(tmp, v23, v3dot(v23, v34))\n v3sub(inPlane2, v34, tmp)\n\n if (v3length(inPlane1) === 0.0 || v3length(inPlane2) === 0.0) {\n return // Indeterminate angle\n }\n\n v3normalize(inPlane1, inPlane1)\n v3normalize(inPlane2, inPlane2)\n\n // Can use acos as normalized and non-zero\n const absAngle = Math.acos(v3dot(inPlane1, inPlane2))\n\n v3cross(cross1, v32, inPlane1)\n v3cross(cross2, v23, inPlane2)\n v3normalize(cross1, cross1)\n v3normalize(cross2, cross2)\n\n let angle = absAngle\n if (v3dot(cross1, inPlane2) < 0.0) {\n angle = -absAngle\n }\n\n v3add(arcPoint, mid, inPlane1)\n\n // Calculate necessary constants\n const maxHist = Math.max.apply(null, histogram)\n const histBinAngleStep = (Math.PI * 2) / histogram.length\n\n function setHistogramBinCoordinates(out: Float32Array, ind: number, zeroDegreeVector: Float32Array, crossVector: Float32Array, histBinAngleStep: number) {\n const startOffset = ind * pointsInTriangle * pointLength\n v3toArray(mid, out, startOffset)\n const scalingFactor = Number(histogram[ind]) / maxHist\n v3multiplyScalar(tmp, zeroDegreeVector, scalingFactor)\n v3multiplyScalar(tmp2, crossVector, scalingFactor)\n calcArcPoint(arcPoint, mid, tmp, tmp2, ind * histBinAngleStep)\n v3toArray(arcPoint, out, startOffset + 1 * pointLength)\n calcArcPoint(arcPoint, mid, tmp, tmp2, (ind + 1) * histBinAngleStep)\n v3toArray(arcPoint, out, startOffset + 2 * pointLength)\n }\n\n function setOneSideHistogram(discHistogram: MeshData, binBorders: { startPoints: Float32Array, endPoints: Float32Array }, ind: number, zeroDegreeVector: Float32Array, crossVector: Float32Array) {\n // Set Bond Arrows\n\n copyArray(mid, adjacentBondArrows.startPoints, 0, ind * pointLength, mid.length)\n calcArcPoint(tmp, mid, zeroDegreeVector, crossVector, 0 + histBinAngleStep * 0)\n copyArray(tmp, adjacentBondArrows.endPoints, 0, ind * pointLength, mid.length)\n\n copyArray(mid, distantBondArrows.startPoints, 0, ind * pointLength, mid.length)\n calcArcPoint(tmp, mid, zeroDegreeVector, crossVector, angle)\n copyArray(tmp, distantBondArrows.endPoints, 0, ind * pointLength, mid.length)\n\n // Set Histogram Bin Borders\n\n for (let i = 0; i < histogram.length; i++) {\n copyArray(mid, binBorders.startPoints, 0, i * 3, mid.length)\n calcArcPoint(tmp, mid, zeroDegreeVector, crossVector, 0 + histBinAngleStep * i)\n copyArray(tmp, binBorders.endPoints, 0, i * 3, tmp.length)\n }\n\n // Set Histogram Bins\n\n for (let sectionIndex = 0; sectionIndex < histogram.length; sectionIndex++) {\n setHistogramBinCoordinates(discHistogram.triangles, sectionIndex, zeroDegreeVector, crossVector, histBinAngleStep)\n }\n }\n\n // Opaque disc\n const opaqueCircleSectorAngleStep = Math.PI * 2 / totalSectorTrianglesInOpaqueMiddleDisc\n\n for (let sectionIndex = 0; sectionIndex < totalSectorTrianglesInOpaqueMiddleDisc; sectionIndex++) {\n const startOffset = sectionIndex * pointsInTriangle * pointLength\n v3toArray(mid, opaqueMiddleDisc.triangles, startOffset)\n calcArcPoint(arcPoint, mid, inPlane1, cross1, sectionIndex * opaqueCircleSectorAngleStep)\n v3toArray(arcPoint, opaqueMiddleDisc.triangles, startOffset + 1 * pointLength)\n calcArcPoint(arcPoint, mid, inPlane1, cross1, (sectionIndex + 1) * opaqueCircleSectorAngleStep)\n v3toArray(arcPoint, opaqueMiddleDisc.triangles, startOffset + 2 * pointLength)\n }\n\n // Front Histogram\n const distanceToOpaqueDisc = 0.01\n v3multiplyScalar(tmp, v23, -distanceToOpaqueDisc) // Get a vector to move \"mid\" just a bit from opaque disc\n v3add(mid, mid, tmp)\n setOneSideHistogram(frontHistogram, frontHistogramBinBorders, 0, inPlane1, cross1)\n\n // Back Histogram\n v3multiplyScalar(tmp, v23, 2 * distanceToOpaqueDisc) // Get a vector to move \"mid\" back and plus just a bit from opaque disc the other way\n v3add(mid, mid, tmp)\n setOneSideHistogram(backHistogram, backHistogramBinBorders, 1, inPlane2, cross2)\n\n return {\n opaqueMiddleDisc,\n frontHistogram,\n backHistogram,\n frontHistogramBinBorders,\n backHistogramBinBorders,\n adjacentBondArrows,\n distantBondArrows\n }\n}\n\nRepresentationRegistry.add('dihedral-histogram', DihedralHistogramRepresentation)\n\nexport default DihedralHistogramRepresentation\n","/**\n * @file Distance Representation\n * @author Alexander Rose \n * @author Fred Ludlow \n * @private\n */\n\nimport { Color } from 'three'\n\nimport { RepresentationRegistry } from '../globals'\nimport { defaults } from '../utils'\nimport { DistancePicker } from '../utils/picker'\nimport { uniformArray, uniformArray3 } from '../math/array-utils'\nimport BitArray from '../utils/bitarray'\nimport MeasurementRepresentation, { MeasurementRepresentationParameters } from './measurement-representation'\nimport Selection from '../selection/selection'\nimport BondStore from '../store/bond-store'\nimport TextBuffer, { TextBufferData, TextBufferParameters } from '../buffer/text-buffer'\nimport WideLineBuffer from '../buffer/wideline-buffer'\nimport CylinderBuffer, { CylinderBufferData } from '../buffer/cylinder-buffer'\nimport { getFixedLengthDashData } from '../geometry/dash'\nimport Viewer from '../viewer/viewer';\nimport { Structure } from '../ngl';\nimport StructureView from '../structure/structure-view';\nimport { BondDataFields, BondDataParams, BondData } from '../structure/structure-data';\nimport { StructureRepresentationData } from './structure-representation';\nimport CylinderGeometryBuffer from '../buffer/cylindergeometry-buffer';\n\n/**\n * Distance representation parameter object.\n * @typedef {Object} DistanceRepresentationParameters - distance representation parameters\n * @mixes RepresentationParameters\n * @mixes StructureRepresentationParameters\n * @mixes MeasurementRepresentationParameters\n *\n * @property {String} labelUnit - distance unit (e.g. \"angstrom\" or \"nm\"). If set, a distance\n * symbol is appended to the label (i.e. 'nm' or '\\u00C5'). In case of 'nm', the\n * distance value is computed in nanometers instead of Angstroms.\n * @property {Array[]} atomPair - list of pairs of selection strings (see {@link Selection})\n * or pairs of atom indices. Using atom indices is much more\n * efficient when the representation is updated often, e.g. by\n * changing the selection or the atom positions, as there\n * are no selection strings to be evaluated.\n */\nexport interface DistanceRepresentationParameters extends MeasurementRepresentationParameters {\n labelUnit: string\n atomPair: AtomPair\n useCylinder: boolean\n}\nexport type AtomPair = (number|string)[][]\n/**\n * Distance representation\n */\nclass DistanceRepresentation extends MeasurementRepresentation {\n protected labelUnit: string\n protected atomPair: AtomPair\n protected useCylinder: boolean\n protected distanceBuffer: WideLineBuffer|CylinderGeometryBuffer\n /**\n * Create Distance representation object\n * @example\n * stage.loadFile( \"rcsb://1crn\" ).then( function( o ){\n * o.addRepresentation( \"cartoon\" );\n * // either give selections (uses first selected atom) ...\n * var atomPair = [ [ \"1.CA\", \"4.CA\" ], [ \"7.CA\", \"13.CA\" ] ];\n * // or atom indices\n * var atomPair = [ [ 8, 28 ], [ 173, 121 ] ];\n * o.addRepresentation( \"distance\", { atomPair: atomPair } );\n * stage.autoView();\n * } );\n * @param {Structure} structure - the structure to be represented\n * @param {Viewer} viewer - a viewer object\n * @param {DistanceRepresentationParameters} params - distance representation parameters\n */\n constructor (structure: Structure, viewer: Viewer, params: Partial) {\n super(structure, viewer, params)\n\n this.type = 'distance'\n\n this.parameters = Object.assign({\n radialSegments: true,\n openEnded: true,\n disableImpostor: true,\n labelUnit: {\n type: 'select',\n rebuild: true,\n options: { '': '', angstrom: 'angstrom', nm: 'nm' }\n },\n useCylinder: {\n type: 'boolean', rebuild: true\n },\n atomPair: {\n type: 'hidden', rebuild: true\n }\n }, this.parameters)\n\n this.init(params)\n }\n\n init (params: Partial) {\n const p = params || {}\n p.linewidth = defaults(p.linewidth, 5.0)\n p.radiusType = defaults(p.radiusType, 'size')\n p.radiusSize = defaults(p.radiusSize, 0.2)\n\n this.labelUnit = defaults(p.labelUnit, '')\n this.useCylinder = defaults(p.useCylinder, false)\n this.atomPair = defaults(p.atomPair, [])\n\n super.init(p)\n }\n\n getDistanceData (sview: StructureView, atomPair: AtomPair) {\n let n = atomPair.length\n const text = new Array(n)\n let position = new Float32Array(n * 3)\n const sele1 = new Selection()\n const sele2 = new Selection()\n\n const bondStore = new BondStore()\n\n const ap1 = sview.getAtomProxy()\n const ap2 = sview.getAtomProxy()\n\n let j = 0 // Skipped pairs\n const selected = sview.getAtomSet()\n\n atomPair.forEach((pair, i) => {\n let v1 = pair[ 0 ]\n let v2 = pair[ 1 ]\n\n if (typeof(v1) === 'number' && Number.isInteger(v1) && typeof(v2) === 'number' && Number.isInteger(v2)) {\n if (selected.get(v1) && selected.get(v2)) {\n ap1.index = v1\n ap2.index = v2\n } else {\n j += 1\n return\n }\n } else {\n sele1.setString(v1 as string)\n sele2.setString(v2 as string)\n\n var atomIndices1 = sview.getAtomIndices(sele1)\n var atomIndices2 = sview.getAtomIndices(sele2)\n\n if (atomIndices1!.length && atomIndices2!.length) {\n ap1.index = atomIndices1![ 0 ]\n ap2.index = atomIndices2![ 0 ]\n } else {\n j += 1\n return\n }\n }\n\n bondStore.addBond(ap1, ap2, 1)\n\n i -= j\n var d = ap1.distanceTo(ap2)\n switch (this.labelUnit) {\n case 'angstrom':\n text[ i ] = d.toFixed(2) + ' ' + String.fromCharCode(0x212B)\n break\n case 'nm':\n text[ i ] = (d / 10).toFixed(2) + ' nm'\n break\n default:\n text[ i ] = d.toFixed(2)\n break\n }\n\n var i3 = i * 3\n position[ i3 + 0 ] = (ap1.x + ap2.x) / 2\n position[ i3 + 1 ] = (ap1.y + ap2.y) / 2\n position[ i3 + 2 ] = (ap1.z + ap2.z) / 2\n })\n\n if (j > 0) {\n n -= j\n position = position.subarray(0, n * 3)\n }\n\n var bondSet = new BitArray(bondStore.count, true)\n\n return {\n text: text,\n position: position,\n bondSet: bondSet,\n bondStore: bondStore\n }\n }\n\n getBondData (sview: StructureView, what: BondDataFields, params: BondDataParams): BondData {\n const bondData = sview.getBondData(this.getBondParams(what, params))\n if (bondData.picking) {\n bondData.picking = new DistancePicker(\n bondData.picking.array,\n bondData.picking.structure,\n params.bondStore!\n ) as any\n }\n return bondData\n }\n\n createData (sview: StructureView) {\n if (!sview.atomCount || !this.atomPair.length) return\n\n const n = this.atomPair.length\n const c = new Color(this.labelColor)\n const distanceData = this.getDistanceData(sview, this.atomPair)\n\n this.textBuffer = new TextBuffer({\n position: distanceData.position,\n size: uniformArray(n, this.labelSize),\n color: uniformArray3(n, c.r, c.g, c.b),\n text: distanceData.text\n } as TextBufferData, this.getLabelBufferParams() as TextBufferParameters)\n\n const bondParams = {\n bondSet: distanceData.bondSet,\n bondStore: distanceData.bondStore\n }\n\n const bondData = this.getBondData(\n sview,\n { position: true, color: true, picking: true, radius: this.useCylinder },\n bondParams\n )\n\n if (this.useCylinder) {\n this.distanceBuffer = new CylinderBuffer(\n bondData as CylinderBufferData,\n this.getBufferParams({\n openEnded: this.openEnded,\n radialSegments: this.radialSegments,\n disableImpostor: this.disableImpostor,\n dullInterior: true\n }) \n ) as CylinderGeometryBuffer\n } else {\n this.distanceBuffer = new WideLineBuffer(\n getFixedLengthDashData(bondData as CylinderBufferData),\n this.getBufferParams({\n linewidth: this.linewidth,\n visible: this.lineVisible,\n opacity: this.lineOpacity\n })\n )\n }\n\n return {\n bondSet: distanceData.bondSet,\n bondStore: distanceData.bondStore,\n position: distanceData.position,\n bufferList: [ this.textBuffer, this.distanceBuffer ]\n }\n }\n\n updateData (what: BondDataFields, data: StructureRepresentationData) {\n super.updateData(what, data)\n\n const bondParams = {\n bondSet: data.bondSet,\n bondStore: data.bondStore\n }\n\n const bondData = this.getBondData(data.sview as StructureView, what, bondParams)\n const distanceData = {}\n\n if (!what || what.color) {\n Object.assign( distanceData, {\n color: bondData.color,\n color2: bondData.color2\n })\n }\n\n if (!what || what.radius) {\n Object.assign( distanceData, {radius: bondData.radius})\n }\n\n (this.distanceBuffer as CylinderGeometryBuffer).setAttributes(distanceData)\n }\n\n setParameters (params: Partial) {\n let rebuild = false\n const what = {}\n\n super.setParameters(params, what, rebuild)\n\n if (!this.useCylinder) {\n if (params && params.lineOpacity) {\n (this.distanceBuffer as WideLineBuffer).setParameters({ opacity: params.lineOpacity })\n }\n if (params && params.opacity !== undefined) {\n (this.distanceBuffer as WideLineBuffer).setParameters({ opacity: this.lineOpacity })\n }\n if (params && params.linewidth) {\n (this.distanceBuffer as WideLineBuffer).setParameters({ linewidth: params.linewidth })\n }\n }\n\n return this\n }\n}\n\nRepresentationRegistry.add('distance', DistanceRepresentation)\n\nexport default DistanceRepresentation\n","/**\n * @file Vector Buffer\n * @author Alexander Rose \n * @private\n */\n\n// @ts-ignore: unused import Vector3, Matrix4 required for declaration only\nimport { Color, Matrix4, Vector3 } from 'three'\n\nimport '../shader/Line.vert'\nimport '../shader/Line.frag'\n\nimport { uniformArray3 } from '../math/array-utils'\nimport Buffer, { BufferDefaultParameters, BufferData, BufferParameters } from './buffer'\nimport { GenericColor } from '../types'\n\nfunction getSize(data: BufferData){\n const n = data.position!.length / 3\n return n * 2 * 3\n}\n\nexport interface VectorBufferData extends BufferData {\n vector: Float32Array\n}\n\nexport const VectorBufferDefaultParameters = Object.assign({\n scale: 1,\n color: 'grey'\n}, BufferDefaultParameters)\nexport type VectorBufferParameters = BufferParameters & { scale: number, color: GenericColor }\n\n/**\n * Vector buffer. Draws vectors as lines.\n */\nclass VectorBuffer extends Buffer {\n get defaultParameters() { return VectorBufferDefaultParameters }\n parameters: VectorBufferParameters\n\n isLine = true\n vertexShader = 'Line.vert'\n fragmentShader = 'Line.frag'\n\n /**\n * @param {Object} data - attribute object\n * @param {Float32Array} data.position - positions\n * @param {Float32Array} data.vector - vectors\n * @param {BufferParameters} params - parameter object\n */\n constructor (data: VectorBufferData, params: Partial = {}) {\n super({\n position: new Float32Array(getSize(data)),\n color: new Float32Array(getSize(data))\n }, params)\n\n const color = new Color(this.parameters.color)\n const attributes = this.geometry.attributes as any // TODO\n uniformArray3(getSize(data) / 3, color.r, color.g, color.b, attributes.color.array)\n\n this.setAttributes(data)\n }\n\n setAttributes (data: Partial = {}) {\n const attributes = this.geometry.attributes as any // TODO\n\n let position, vector\n let aPosition\n\n if (data.position && data.vector) {\n position = data.position\n vector = data.vector\n aPosition = attributes.position.array\n attributes.position.needsUpdate = true\n }\n\n const n = this.size / 2\n const scale = this.parameters.scale\n\n if (position && vector) {\n for (let v = 0; v < n; v++) {\n const i = v * 2 * 3\n const j = v * 3\n\n aPosition[ i + 0 ] = position[ j + 0 ]\n aPosition[ i + 1 ] = position[ j + 1 ]\n aPosition[ i + 2 ] = position[ j + 2 ]\n aPosition[ i + 3 ] = position[ j + 0 ] + vector[ j + 0 ] * scale\n aPosition[ i + 4 ] = position[ j + 1 ] + vector[ j + 1 ] * scale\n aPosition[ i + 5 ] = position[ j + 2 ] + vector[ j + 2 ] * scale\n }\n }\n }\n}\n\nexport default VectorBuffer\n","/**\n * @file Helixorient Representation\n * @author Alexander Rose \n * @private\n */\n\nimport { Debug, Log, RepresentationRegistry } from '../globals'\nimport { defaults } from '../utils'\nimport Helixorient from '../geometry/helixorient'\nimport StructureRepresentation, { StructureRepresentationParameters, StructureRepresentationData } from './structure-representation'\nimport SphereBuffer, { SphereBufferParameters } from '../buffer/sphere-buffer'\nimport VectorBuffer from '../buffer/vector-buffer'\nimport Viewer from '../viewer/viewer';\nimport { Structure } from '../ngl';\nimport StructureView from '../structure/structure-view';\nimport Polymer from '../proxy/polymer';\nimport { AtomDataFields } from '../structure/structure-data';\nimport SphereGeometryBuffer from '../buffer/spheregeometry-buffer';\nimport SphereImpostorBuffer from '../buffer/sphereimpostor-buffer';\nimport { BufferData } from '../buffer/buffer';\n\n/**\n * Helixorient Representation\n */\nclass HelixorientRepresentation extends StructureRepresentation {\n constructor (structure: Structure, viewer: Viewer, params: Partial) {\n super(structure, viewer, params)\n\n this.type = 'helixorient'\n\n this.parameters = Object.assign({\n sphereDetail: true,\n disableImpostor: true\n }, this.parameters)\n\n this.init(params)\n }\n\n init (params: Partial) {\n const p = params || {}\n p.colorScheme = defaults(p.colorScheme, 'sstruc')\n p.radiusType = defaults(p.radiusType, 'size')\n p.radiusSize = defaults(p.radiusSize, 0.15)\n p.radiusScale = defaults(p.radiusScale, 1.0)\n p.useInteriorColor = defaults(p.useInteriorColor, true)\n\n super.init(p)\n }\n\n createData (sview: StructureView) {\n const bufferList: (SphereBuffer|VectorBuffer)[] = []\n const polymerList: Polymer[] = []\n\n this.structure.eachPolymer(polymer => {\n if (polymer.residueCount < 4) return\n polymerList.push(polymer)\n\n const helixorient = new Helixorient(polymer)\n const position = helixorient.getPosition()\n const color = helixorient.getColor(this.getColorParams())\n const size = helixorient.getSize(this.getRadiusParams())\n const picking = helixorient.getPicking()\n\n bufferList.push(\n new SphereBuffer(\n {\n position: position.center,\n color: color.color,\n radius: size.size,\n picking: picking.picking\n },\n this.getBufferParams({\n sphereDetail: this.sphereDetail,\n disableImpostor: this.disableImpostor,\n dullInterior: true\n }) as SphereBufferParameters\n ),\n new VectorBuffer(\n {\n position: position.center,\n vector: position.axis\n },\n this.getBufferParams({\n color: 'skyblue',\n scale: 1\n })\n ),\n new VectorBuffer(\n {\n position: position.center,\n vector: position.resdir\n },\n this.getBufferParams({\n color: 'lightgreen',\n scale: 1\n })\n )\n )\n }, sview.getSelection())\n\n return {\n bufferList: bufferList as (SphereGeometryBuffer|SphereImpostorBuffer|VectorBuffer)[],\n polymerList: polymerList\n }\n }\n\n updateData (what: AtomDataFields, data: StructureRepresentationData) {\n if (Debug) Log.time(this.type + ' repr update')\n\n what = what || {}\n\n for (let i = 0, il = data.polymerList!.length; i < il; ++i) {\n const j = i * 3\n\n const bufferData: Partial = {}\n const polymer = data.polymerList![ i ]\n const helixorient = new Helixorient(polymer)\n\n if (what.position) {\n const position = helixorient.getPosition()\n\n Object.assign(bufferData, {position: position.center})\n\n data.bufferList[ j + 1 ].setAttributes({\n 'position': position.center,\n 'vector': position.axis\n })\n data.bufferList[ j + 2 ].setAttributes({\n 'position': position.center,\n 'vector': position.resdir\n })\n }\n\n data.bufferList[ j ].setAttributes(bufferData)\n }\n\n if (Debug) Log.timeEnd(this.type + ' repr update')\n }\n}\n\nRepresentationRegistry.add('helixorient', HelixorientRepresentation)\n\nexport default HelixorientRepresentation\n","/**\n * @file Licorice Representation\n * @author Alexander Rose \n * @private\n */\n\nimport { RepresentationRegistry } from '../globals'\nimport BallAndStickRepresentation, { BallAndStickRepresentationParameters } from './ballandstick-representation'\nimport { Structure } from '../ngl';\nimport Viewer from '../viewer/viewer';\n\n/**\n * Licorice representation object ({@link BallAndStickRepresentation} with `aspectRatio` fixed at 1.0)\n */\nclass LicoriceRepresentation extends BallAndStickRepresentation {\n /**\n * Create Licorice representation object\n * @param {Structure} structure - the structure to be represented\n * @param {Viewer} viewer - a viewer object\n * @param {BallAndStickRepresentationParameters} params - ball and stick representation parameters\n */\n constructor (structure: Structure, viewer: Viewer, params: Partial) {\n super(structure, viewer, params)\n\n this.type = 'licorice'\n\n this.parameters = Object.assign(\n {}, this.parameters, { aspectRatio: null }\n )\n }\n\n init (params: Partial) {\n var p = params || {}\n p.aspectRatio = 1.0\n\n super.init(p)\n }\n}\n\nRepresentationRegistry.add('licorice', LicoriceRepresentation)\n\nexport default LicoriceRepresentation\n","/**\n * @file Mapped Box Buffer\n * @author Alexander Rose \n * @private\n */\n\nimport { BufferParameters, BufferData } from './buffer'\nimport MappedBuffer from './mapped-buffer'\n\nconst mapping = new Float32Array([\n -1.0, -1.0, -1.0,\n 1.0, -1.0, -1.0,\n 1.0, -1.0, 1.0,\n -1.0, -1.0, 1.0,\n -1.0, 1.0, -1.0,\n 1.0, 1.0, -1.0,\n 1.0, 1.0, 1.0,\n -1.0, 1.0, 1.0\n])\n\nconst mappingIndices = new Uint16Array([\n 0, 1, 2,\n 0, 2, 3,\n 1, 5, 6,\n 1, 6, 2,\n 4, 6, 5,\n 4, 7, 6,\n 0, 7, 4,\n 0, 3, 7,\n 0, 5, 1,\n 0, 4, 5,\n 3, 2, 6,\n 3, 6, 7\n])\n\n/**\n * Mapped Box buffer. Draws boxes. Used to render general imposters.\n * @interface\n */\nclass MappedBoxBuffer extends MappedBuffer {\n constructor(data: BufferData, params: Partial = {}) {\n super('v3', data, params)\n }\n get mapping () { return mapping }\n get mappingIndices () { return mappingIndices }\n get mappingIndicesSize () { return 36 }\n get mappingSize () { return 8 }\n get mappingItemSize () { return 3 }\n}\n\nexport default MappedBoxBuffer\n","/**\n * @file Hyperball Stick Impostor Buffer\n * @author Alexander Rose \n * @private\n */\n\n// @ts-ignore: unused import Vector3 required for declaration only\nimport { Matrix4, Vector3 } from 'three'\n\nimport '../shader/HyperballStickImpostor.vert'\nimport '../shader/HyperballStickImpostor.frag'\n\nimport MappedBoxBuffer from './mappedbox-buffer'\nimport { BufferDefaultParameters, BufferParameterTypes, BufferData, BufferParameters } from './buffer'\n\nexport interface HyperballStickImpostorBufferData extends BufferData {\n position1: Float32Array\n position2: Float32Array\n color2: Float32Array\n radius: Float32Array\n radius2: Float32Array\n}\n\nexport const HyperballStickImpostorBufferDefaultParameters = Object.assign({\n shrink: 0.14\n}, BufferDefaultParameters)\nexport type HyperballStickImpostorBufferParameters = BufferParameters & { shrink: number }\n\nconst HyperballStickImpostorBufferParameterTypes = Object.assign({\n shrink: { uniform: true }\n}, BufferParameterTypes)\n\n/**\n * Hyperball stick impostor buffer.\n *\n * @example\n * var hyperballStickImpostorBuffer = new HyperballStickImpostorBuffer({\n * position1: new Float32Array([ 0, 0, 0 ]),\n * position2: new Float32Array([ 2, 2, 2 ]),\n * color: new Float32Array([ 1, 0, 0 ]),\n * color2: new Float32Array([ 0, 1, 0 ]),\n * radius: new Float32Array([ 1 ]),\n * radius2: new Float32Array([ 2 ])\n * });\n */\nclass HyperballStickImpostorBuffer extends MappedBoxBuffer {\n parameterTypes = HyperballStickImpostorBufferParameterTypes\n get defaultParameters() { return HyperballStickImpostorBufferDefaultParameters }\n parameters: HyperballStickImpostorBufferParameters\n\n isImpostor = true\n vertexShader = 'HyperballStickImpostor.vert'\n fragmentShader = 'HyperballStickImpostor.frag'\n\n constructor (data: HyperballStickImpostorBufferData, params: Partial = {}) {\n super(data, params)\n\n this.addUniforms({\n 'modelViewProjectionMatrix': { value: new Matrix4() },\n 'modelViewProjectionMatrixInverse': { value: new Matrix4() },\n 'modelViewMatrixInverseTranspose': { value: new Matrix4() },\n 'shrink': { value: this.parameters.shrink }\n })\n\n this.addAttributes({\n 'position1': { type: 'v3', value: null },\n 'position2': { type: 'v3', value: null },\n 'color2': { type: 'c', value: null },\n 'radius': { type: 'f', value: null },\n 'radius2': { type: 'f', value: null }\n })\n\n this.setAttributes(data)\n this.makeMapping()\n }\n}\n\nexport default HyperballStickImpostorBuffer\n","/**\n * @file Hyperball Stick Buffer\n * @author Alexander Rose \n * @private\n */\n\n// @ts-ignore: unused import required for declaration only\nimport { Vector3, Matrix4 } from 'three'\nimport { ExtensionFragDepth } from '../globals'\nimport { calculateMinArray } from '../math/array-utils'\nimport CylinderGeometryBuffer, { CylinderGeometryBufferDefaultParameters, CylinderGeometryBufferParameters } from './cylindergeometry-buffer'\nimport HyperballStickImpostorBuffer, { HyperballStickImpostorBufferDefaultParameters, HyperballStickImpostorBufferParameters } from './hyperballstickimpostor-buffer'\nimport { BufferData } from './buffer'\n\nexport interface HyperballStickBufferData extends BufferData {\n position1: Float32Array\n position2: Float32Array\n color2: Float32Array\n radius: Float32Array\n radius2: Float32Array\n}\n\nexport const HyperballStickBufferDefaultParameters = Object.assign({\n disableImpostor: false\n}, CylinderGeometryBufferDefaultParameters, HyperballStickImpostorBufferDefaultParameters)\nexport type HyperballStickBufferParameters = HyperballStickImpostorBufferParameters & CylinderGeometryBufferParameters & { disableImpostor: boolean }\n\nclass HyperballStickBufferImpl {\n /**\n * @param {Object} data - attribute object\n * @param {Float32Array} data.position1 - from positions\n * @param {Float32Array} data.position2 - to positions\n * @param {Float32Array} data.color - from colors\n * @param {Float32Array} data.color2 - to colors\n * @param {Float32Array} data.radius - from radii\n * @param {Float32Array} data.radius2 - to radii\n * @param {Float32Array} data.picking - picking ids\n * @param {BufferParameters} params - parameter object\n */\n constructor (data: HyperballStickBufferData, params: Partial = {}) {\n if (!ExtensionFragDepth || (params && params.disableImpostor)) {\n data.radius = calculateMinArray(data.radius, data.radius2)\n return new CylinderGeometryBuffer(data, params)\n } else {\n return new HyperballStickImpostorBuffer(data, params)\n }\n }\n}\n\n/**\n * Hyperball stick buffer. Depending on the value {@link ExtensionFragDepth} and\n * `params.disableImpostor` the constructor returns either a\n * {@link CylinderGeometryBuffer} or a {@link HyperballStickImpostorBuffer}\n * @implements {Buffer}\n *\n * @example\n * var hyperballStickBuffer = new HyperballStickBuffer({\n * position1: new Float32Array([ 0, 0, 0 ]),\n * position2: new Float32Array([ 2, 2, 2 ]),\n * color: new Float32Array([ 1, 0, 0 ]),\n * color2: new Float32Array([ 0, 1, 0 ]),\n * radius: new Float32Array([ 1 ]),\n * radius2: new Float32Array([ 2 ])\n * });\n */\n//@ts-expect-error Incompatible constructor signatures\nconst HyperballStickBuffer: {\n new(data: HyperballStickBufferData, params: Partial): CylinderGeometryBuffer | HyperballStickImpostorBuffer;\n} = HyperballStickBufferImpl;\n\ntype HyperballStickBuffer = CylinderGeometryBuffer | HyperballStickImpostorBuffer;\n\nexport default HyperballStickBuffer\n","/**\n * @file Hyperball Representation\n * @author Alexander Rose \n * @private\n */\n\nimport { RepresentationRegistry } from '../globals'\nimport { defaults } from '../utils'\nimport { calculateCenterArray } from '../math/array-utils'\nimport LicoriceRepresentation from './licorice-representation'\nimport SphereBuffer, { SphereBufferData, SphereBufferParameters } from '../buffer/sphere-buffer'\nimport HyperballStickBuffer, { HyperballStickBufferData } from '../buffer/hyperballstick-buffer'\nimport { BallAndStickRepresentationParameters } from './ballandstick-representation';\n// @ts-ignore: unused import Volume required for declaration only\nimport { Structure, Volume } from '../ngl';\nimport Viewer from '../viewer/viewer';\nimport { BondDataParams, BondDataFields, AtomDataFields } from '../structure/structure-data';\nimport StructureView from '../structure/structure-view';\nimport { StructureRepresentationData } from './structure-representation';\nimport SphereGeometryBuffer from '../buffer/spheregeometry-buffer';\n// @ts-ignore: unused import Surface required for declaration only\nimport Surface from '../surface/surface';\n\nexport interface HyperballRepresentationParameters extends BallAndStickRepresentationParameters {\n shrink: number\n}\n\n/**\n * Hyperball Representation\n */\nclass HyperballRepresentation extends LicoriceRepresentation {\n protected shrink: number\n protected __center: Float32Array\n \n constructor (structure: Structure, viewer: Viewer, params: Partial) {\n super(structure, viewer, params)\n\n this.type = 'hyperball'\n\n this.parameters = Object.assign({\n\n shrink: {\n type: 'number', precision: 3, max: 1.0, min: 0.001, buffer: true\n }\n\n }, this.parameters, {\n\n multipleBond: null,\n bondSpacing: null\n\n })\n }\n\n init (params: Partial) {\n var p = params || {}\n p.radiusScale = defaults(p.radiusScale, 0.2)\n p.radiusType = defaults(p.radiusType, 'vdw')\n p.useInteriorColor = defaults(p.useInteriorColor, true)\n\n this.shrink = defaults(p.shrink, 0.12)\n\n super.init(p)\n }\n\n getBondParams (what?: BondDataFields, params?: BondDataParams) {\n if (!what || what.radius) {\n params = Object.assign({ radius2: true }, params)\n }\n\n return super.getBondParams(what, params)\n }\n\n createData (sview: StructureView) {\n var sphereBuffer = new SphereBuffer(\n (sview.getAtomData(this.getAtomParams()) as SphereBufferData),\n this.getBufferParams({\n sphereDetail: this.sphereDetail,\n disableImpostor: this.disableImpostor,\n dullInterior: true\n }) as SphereBufferParameters\n ) as SphereGeometryBuffer\n\n this.__center = new Float32Array(sview.bondCount * 3)\n\n var stickBuffer = new HyperballStickBuffer(\n sview.getBondData(this.getBondParams()) as HyperballStickBufferData,\n this.getBufferParams({\n shrink: this.shrink,\n radialSegments: this.radialSegments,\n disableImpostor: this.disableImpostor,\n dullInterior: true\n })\n )\n\n return {\n bufferList: [ sphereBuffer, stickBuffer ]\n }\n }\n\n updateData (what: AtomDataFields, data: StructureRepresentationData) {\n var atomData = data.sview!.getAtomData(this.getAtomParams())\n var bondData = data.sview!.getBondData(this.getBondParams())\n var sphereData = {}\n var stickData = {}\n\n if (!what || what.position) {\n Object.assign(sphereData, {position: atomData.position})\n var from = bondData.position1\n var to = bondData.position2\n Object.assign(stickData, {\n position: calculateCenterArray(from!, to!, this.__center),\n position1: from,\n position2: to\n })\n }\n\n if (!what || what.color) {\n Object.assign(sphereData, {color: atomData.color})\n Object.assign(stickData, {\n color: bondData.color,\n color2: bondData.color2\n })\n }\n\n if (!what || what.radius) {\n Object.assign(sphereData, {radius: atomData.radius})\n Object.assign(stickData, {\n radius: bondData.radius,\n radius2: bondData.radius2\n })\n }\n\n data.bufferList[ 0 ].setAttributes(sphereData)\n data.bufferList[ 1 ].setAttributes(stickData)\n }\n}\n\nRepresentationRegistry.add('hyperball', HyperballRepresentation)\n\nexport default HyperballRepresentation\n","/**\n * @file Label Factory\n * @author Alexander Rose \n * @private\n */\n\nimport { AA1 } from '../structure/structure-constants'\nimport AtomProxy from '../proxy/atom-proxy'\nimport { sprintf } from 'sprintf-js'\n\nexport const LabelFactoryTypes = {\n '': '',\n 'atomname': 'atom name',\n 'atomindex': 'atom index',\n 'occupancy': 'occupancy',\n 'bfactor': 'b-factor',\n 'serial': 'serial',\n 'element': 'element',\n 'atom': 'atom name + index',\n 'resname': 'residue name',\n 'resno': 'residue no',\n 'res': 'one letter code + no',\n 'residue': '[residue name] + no + inscode',\n 'text': 'text',\n 'format': 'format',\n 'qualified': 'qualified name'\n}\nexport type LabelType = keyof typeof LabelFactoryTypes\n\nclass LabelFactory {\n\n static types = LabelFactoryTypes\n errorLogged: boolean = false\n\n constructor(readonly type: LabelType, readonly text: { [k: number]: string } = {},\n readonly format: string = '') {}\n\n atomLabel (a: AtomProxy) {\n const type = this.type\n\n let l\n\n switch (type) {\n case 'atomname':\n l = a.atomname\n break\n\n case 'atomindex':\n l = `${a.index}`\n break\n\n case 'occupancy':\n l = a.occupancy.toFixed(2)\n break\n\n case 'bfactor':\n l = a.bfactor.toFixed(2)\n break\n\n case 'serial':\n l = `${a.serial}`\n break\n\n case 'element':\n l = a.element\n break\n\n case 'atom':\n l = `${a.atomname}|${a.index}`\n break\n\n case 'resname':\n l = a.resname\n break\n\n case 'resno':\n l = `${a.resno}`\n break\n\n case 'res':\n l = `${(AA1[ a.resname.toUpperCase() ] || a.resname)}${a.resno}`\n break\n\n case 'residue':\n const aa1 = AA1[ a.resname.toUpperCase() ]\n if (aa1 && !a.inscode) {\n l = `${aa1}${a.resno}`\n } else {\n l = `[${a.resname}]${a.resno}${a.inscode}`\n }\n break\n\n case 'text':\n l = this.text[ a.index ]\n break\n\n case 'format':\n try {\n l = sprintf(this.format, a)\n } catch (e) {\n if (!this.errorLogged) {\n this.errorLogged = true\n console.log(e.message)\n }\n }\n break\n\n // case \"qualified\":\n default:\n l = a.qualifiedName()\n break\n }\n\n return l === undefined ? '' : l\n }\n}\n\nexport default LabelFactory\n","/**\n * @file Label Representation\n * @author Alexander Rose \n * @private\n */\n\nimport { RepresentationRegistry, ColormakerRegistry } from '../globals'\nimport { defaults } from '../utils'\nimport LabelFactory, { LabelType } from '../utils/label-factory'\nimport RadiusFactory from '../utils/radius-factory'\nimport StructureRepresentation, { StructureRepresentationData } from './structure-representation'\nimport TextBuffer, { TextBufferData } from '../buffer/text-buffer'\nimport { RepresentationParameters } from './representation';\nimport { Structure } from '../ngl';\nimport Viewer from '../viewer/viewer';\nimport StructureView from '../structure/structure-view';\nimport { GenericColor } from '../types'\n\nexport interface TextDataField {\n position?: boolean\n color?: boolean\n radius?: boolean\n text?: boolean\n}\n\n/**\n * Label representation parameter object. Extends {@link RepresentationParameters} and\n * {@link StructureRepresentationParameters}.\n *\n * @typedef {Object} LabelRepresentationParameters - label representation parameters\n *\n * @property {Integer} clipNear - position of camera near/front clipping plane\n * in percent of scene bounding box\n * @property {Float} opacity - translucency: 1 is fully opaque, 0 is fully transparent\n * @property {String} labelType - type of the label, one of:\n * \"atomname\", \"atomindex\", \"occupancy\", \"bfactor\",\n * \"serial\", \"element\", \"atom\", \"resname\", \"resno\",\n * \"res\", \"text\", \"qualified\". When set to \"text\", the\n * `labelText` list is used.\n * @property {String[]} labelText - list of label strings, must set `labelType` to \"text\"\n * to take effect\n * @property {String} labelFormat - sprintf-js format string, any attribute of\n * {@link AtomProxy} can be used\n * @property {String} labelGrouping - grouping of the label, one of:\n * \"atom\", \"residue\".\n * @property {String} fontFamily - font family, one of: \"sans-serif\", \"monospace\", \"serif\"\n * @property {String} fontStyle - font style, \"normal\" or \"italic\"\n * @property {String} fontWeight - font weight, \"normal\" or \"bold\"\n * @property {Float} xOffset - offset in x-direction\n * @property {Float} yOffset - offset in y-direction\n * @property {Float} zOffset - offset in z-direction (i.e. in camera direction)\n * @property {String} attachment - attachment of the label, one of:\n * \"bottom-left\", \"bottom-center\", \"bottom-right\",\n * \"middle-left\", \"middle-center\", \"middle-right\",\n * \"top-left\", \"top-center\", \"top-right\"\n * @property {Boolean} showBorder - show border/outline\n * @property {Color} borderColor - color of the border/outline\n * @property {Float} borderWidth - width of the border/outline\n * @property {Boolean} showBackground - show background rectangle\n * @property {Color} backgroundColor - color of the background\n * @property {Float} backgroundMargin - width of the background\n * @property {Float} backgroundOpacity - opacity of the background\n * @property {Boolean} fixedSize - show text with a fixed pixel size\n */\nexport interface LabelRepresentationParameters extends RepresentationParameters {\n labelType: LabelType\n labelText: string[]\n labelFormat: string\n labelGrouping: 'atom'|'residue'\n fontFamily: 'sans-serif'|'monospace'|'serif'\n fontStyle: 'normal'|'italic'\n fontWeight: 'normal'|'bold'\n xOffset: number\n yOffset: number\n zOffset: number\n attachment: 'bottom-left'|'bottom-center'|'bottom-right'|'middle-left'|'middle-center'|'middle-right'|'top-left'|'top-center'|'top-right'\n showBorder: boolean\n borderColor: GenericColor\n borderWidth: number\n showBackground: boolean\n backgroundColor: GenericColor\n backgroundMargin: number\n backgroundOpacity: number\n fixedSize: boolean\n}\n/**\n * Label representation\n */\nclass LabelRepresentation extends StructureRepresentation {\n\n protected labelType: LabelType\n protected labelText: string[]\n protected labelFormat: string\n protected labelGrouping: 'atom'|'residue'\n protected fontFamily: 'sans-serif'|'monospace'|'serif'\n protected fontStyle: 'normal'|'italic'\n protected fontWeight: 'normal'|'bold'\n protected xOffset: number\n protected yOffset: number\n protected zOffset: number\n protected attachment: 'bottom-left'|'bottom-center'|'bottom-right'|'middle-left'|'middle-center'|'middle-right'|'top-left'|'top-center'|'top-right'\n protected showBorder: boolean\n protected borderColor: GenericColor\n protected borderWidth: number\n protected showBackground: boolean\n protected backgroundColor: GenericColor\n protected backgroundMargin: number\n protected backgroundOpacity: number\n protected fixedSize: boolean\n\n /**\n * Create Label representation object\n * @param {Structure} structure - the structure to be represented\n * @param {Viewer} viewer - a viewer object\n * @param {LabelRepresentationParameters} params - label representation parameters\n */\n constructor (structure: Structure, viewer: Viewer, params: Partial) {\n super(structure, viewer, params)\n\n this.type = 'label'\n\n this.parameters = Object.assign({\n\n labelType: {\n type: 'select', options: LabelFactory.types, rebuild: true\n },\n labelText: {\n type: 'hidden', rebuild: true\n },\n labelFormat: {\n type: 'text', rebuild: true\n },\n labelGrouping: {\n type: 'select',\n options: {\n 'atom': 'atom',\n 'residue': 'residue'\n },\n rebuild: true\n },\n fontFamily: {\n type: 'select',\n options: {\n 'sans-serif': 'sans-serif',\n 'monospace': 'monospace',\n 'serif': 'serif'\n },\n buffer: true\n },\n fontStyle: {\n type: 'select',\n options: {\n 'normal': 'normal',\n 'italic': 'italic'\n },\n buffer: true\n },\n fontWeight: {\n type: 'select',\n options: {\n 'normal': 'normal',\n 'bold': 'bold'\n },\n buffer: true\n },\n xOffset: {\n type: 'number', precision: 1, max: 20, min: -20, buffer: true\n },\n yOffset: {\n type: 'number', precision: 1, max: 20, min: -20, buffer: true\n },\n zOffset: {\n type: 'number', precision: 1, max: 20, min: -20, buffer: true\n },\n attachment: {\n type: 'select',\n options: {\n 'bottom-left': 'bottom-left',\n 'bottom-center': 'bottom-center',\n 'bottom-right': 'bottom-right',\n 'middle-left': 'middle-left',\n 'middle-center': 'middle-center',\n 'middle-right': 'middle-right',\n 'top-left': 'top-left',\n 'top-center': 'top-center',\n 'top-right': 'top-right'\n },\n rebuild: true\n },\n showBorder: {\n type: 'boolean', buffer: true\n },\n borderColor: {\n type: 'color', buffer: true\n },\n borderWidth: {\n type: 'number', precision: 2, max: 0.3, min: 0, buffer: true\n },\n showBackground: {\n type: 'boolean', rebuild: true\n },\n backgroundColor: {\n type: 'color', buffer: true\n },\n backgroundMargin: {\n type: 'number', precision: 2, max: 2, min: 0, rebuild: true\n },\n backgroundOpacity: {\n type: 'range', step: 0.01, max: 1, min: 0, buffer: true\n },\n fixedSize: {\n type: 'boolean', buffer: true\n }\n\n }, this.parameters, {\n\n side: null,\n flatShaded: null,\n wireframe: null,\n linewidth: null,\n\n roughness: null,\n metalness: null,\n diffuse: null\n\n })\n\n this.init(params)\n }\n\n init (params: Partial) {\n const p = params || {}\n\n this.labelType = defaults(p.labelType, 'res')\n this.labelText = defaults(p.labelText, {})\n this.labelFormat = defaults(p.labelFormat, '')\n this.labelGrouping = defaults(p.labelGrouping, 'atom')\n this.fontFamily = defaults(p.fontFamily, 'sans-serif')\n this.fontStyle = defaults(p.fontStyle, 'normal')\n this.fontWeight = defaults(p.fontWeight, 'bold')\n this.xOffset = defaults(p.xOffset, 0.0)\n this.yOffset = defaults(p.yOffset, 0.0)\n this.zOffset = defaults(p.zOffset, 0.5)\n this.attachment = defaults(p.attachment, 'bottom-left')\n this.showBorder = defaults(p.showBorder, false)\n this.borderColor = defaults(p.borderColor, 'lightgrey')\n this.borderWidth = defaults(p.borderWidth, 0.15)\n this.showBackground = defaults(p.showBackground, false)\n this.backgroundColor = defaults(p.backgroundColor, 'lightgrey')\n this.backgroundMargin = defaults(p.backgroundMargin, 0.5)\n this.backgroundOpacity = defaults(p.backgroundOpacity, 1.0)\n this.fixedSize = defaults(p.fixedSize, false)\n\n super.init(p)\n }\n\n getTextData (sview: StructureView, what?: TextDataField) {\n const p = this.getAtomParams(what)\n const labelFactory = new LabelFactory(this.labelType, this.labelText, this.labelFormat)\n let position: Float32Array, size: Float32Array, color: Float32Array, text: string[],\n positionN: number[], sizeN: number[], colorN: number[]\n if (this.labelGrouping === 'atom') {\n const atomData = sview.getAtomData(p)\n position = atomData.position as Float32Array\n size = atomData.radius as Float32Array\n color = atomData.color as Float32Array\n if (!what || what.text) {\n text = []\n sview.eachAtom(ap => text.push(labelFactory.atomLabel(ap)))\n }\n } else if (this.labelGrouping === 'residue') {\n if (!what || what.position) positionN = []\n if (!what || what.color) colorN = []\n if (!what || what.radius) sizeN = []\n if (!what || what.text) text = []\n if (p.colorParams) p.colorParams.structure = sview.getStructure()\n const colormaker = ColormakerRegistry.getScheme(p.colorParams)\n const radiusFactory = new RadiusFactory(p.radiusParams)\n const ap1 = sview.getAtomProxy()\n\n let i = 0\n sview.eachResidue(rp => {\n const i3 = i * 3\n if (rp.isProtein() || rp.isNucleic()) {\n ap1.index = rp.traceAtomIndex\n if (!what || what.position) {\n ap1.positionToArray(positionN, i3)\n }\n } else {\n ap1.index = rp.atomOffset\n if (!what || what.position) {\n rp.positionToArray(positionN, i3)\n }\n }\n if (!what || what.color) {\n colormaker.atomColorToArray(ap1, colorN, i3)\n }\n if (!what || what.radius) {\n sizeN[ i ] = radiusFactory.atomRadius(ap1)\n }\n if (!what || what.text) {\n text.push(labelFactory.atomLabel(ap1))\n }\n ++i\n })\n\n if (!what || what.position) position = new Float32Array(positionN!)\n if (!what || what.color) color = new Float32Array(colorN!)\n if (!what || what.radius) size = new Float32Array(sizeN!)\n }\n\n return { position: position!, size: size!, color: color!, text: text! }\n }\n\n createData (sview: StructureView) {\n const what: TextDataField = { position: true, color: true, radius: true, text: true }\n\n const textBuffer = new TextBuffer(\n this.getTextData(sview, what) as TextBufferData,\n this.getBufferParams({\n fontFamily: this.fontFamily,\n fontStyle: this.fontStyle,\n fontWeight: this.fontWeight,\n xOffset: this.xOffset,\n yOffset: this.yOffset,\n zOffset: this.zOffset,\n attachment: this.attachment,\n showBorder: this.showBorder,\n borderColor: this.borderColor,\n borderWidth: this.borderWidth,\n showBackground: this.showBackground,\n backgroundColor: this.backgroundColor,\n backgroundMargin: this.backgroundMargin,\n backgroundOpacity: this.backgroundOpacity,\n fixedSize: this.fixedSize\n })\n )\n\n return { bufferList: [ textBuffer ] }\n }\n\n updateData (what: TextDataField, data: StructureRepresentationData) {\n data.bufferList[ 0 ].setAttributes(this.getTextData(data.sview as StructureView, what))\n }\n\n getAtomRadius () {\n return 0\n }\n}\n\nRepresentationRegistry.add('label', LabelRepresentation)\n\nexport default LabelRepresentation\n","/**\n * @file Line Representation\n * @author Alexander Rose \n * @private\n */\n\nimport { defaults } from '../utils'\nimport { RepresentationRegistry } from '../globals'\nimport StructureRepresentation, { StructureRepresentationParameters, StructureRepresentationData } from './structure-representation'\nimport WideLineBuffer from '../buffer/wideline-buffer'\nimport { AtomPicker } from '../utils/picker'\n// @ts-ignore: unused import Volume required for declaration only\nimport { Structure, Volume } from '../ngl';\nimport StructureView from '../structure/structure-view';\nimport Viewer from '../viewer/viewer';\nimport AtomProxy from '../proxy/atom-proxy';\n// @ts-ignore: unused import Surface required for declaration only\nimport Surface from '../surface/surface';\n// @ts-ignore: unused import BondDataFields, BondDataParams required for declaration only\nimport { BondDataFields, BondDataParams } from '../structure/structure-data';\n\n/**\n * Determine which atoms in a Structure[View] form no bonds to any other atoms\n * in that Structure.\n *\n * This differs from setting the selection to \"nonbonded\" as it finds atoms\n * that have no bonds within the current selection.\n * @param {Structure} structure - The Structure or StructureView object\n * @return {AtomSet} AtomSet of lone atoms\n */\nfunction getLoneAtomSet (structure: Structure | StructureView) {\n const atomSet = structure.getAtomSet()\n const bondSet = structure.getBondSet()\n const bp = structure.getBondProxy()\n bondSet.forEach(function (idx) {\n bp.index = idx\n atomSet.clear(bp.atomIndex1)\n atomSet.clear(bp.atomIndex2)\n })\n return atomSet\n}\n\nexport interface LineRepresentationParameters extends StructureRepresentationParameters {\n multipleBond: 'off' | 'symmetric' | 'offset'\n bondSpacing: number\n linewidth: number\n lines: boolean\n crosses: 'off' | 'all' | 'lone'\n crossSize: number\n}\n\nexport interface CrossData {\n position1?: Float32Array\n position2?: Float32Array\n color?: Float32Array\n color2?: Float32Array\n picking?: AtomPicker\n}\n\n/**\n * Line representation\n */\nclass LineRepresentation extends StructureRepresentation {\n protected multipleBond: 'off' | 'symmetric' | 'offset'\n protected bondSpacing: number\n protected linewidth: number\n protected lines: boolean\n protected crosses: 'off' | 'all' | 'lone'\n protected crossSize: number\n /**\n * Create Line representation object\n * @param {Structure} structure - the structure to be represented\n * @param {Viewer} viewer - a viewer object\n * @param {RepresentationParameters} params - representation parameters, plus the properties listed below\n * @property {String} multipleBond - one off \"off\", \"symmetric\", \"offset\"\n * @param {Float} params.bondSpacing - spacing for multiple bond rendering\n * @param {Integer} params.linewidth - width of lines\n * @param {Boolean} params.lines - render bonds as lines\n * @param {String} params.crosses - render atoms as crosses: \"off\", \"all\" or \"lone\" (default)\n * @param {Float} params.crossSize - size of cross\n * @param {null} params.flatShaded - not available\n * @param {null} params.side - not available\n * @param {null} params.wireframe - not available\n * @param {null} params.roughness - not available\n * @param {null} params.metalness - not available\n * @param {null} params.diffuse - not available\n */\n constructor (structure: Structure, viewer: Viewer, params: Partial) {\n super(structure, viewer, params)\n\n this.type = 'line'\n\n this.parameters = Object.assign({\n\n multipleBond: {\n type: 'select',\n rebuild: true,\n options: {\n 'off': 'off',\n 'symmetric': 'symmetric',\n 'offset': 'offset'\n }\n },\n bondSpacing: {\n type: 'number', precision: 2, max: 2.0, min: 0.5\n },\n linewidth: {\n type: 'integer', max: 50, min: 1, buffer: true\n },\n lines: {\n type: 'boolean', rebuild: true\n },\n crosses: {\n type: 'select',\n rebuild: true,\n options: {\n 'off': 'off',\n 'lone': 'lone',\n 'all': 'all'\n }\n },\n crossSize: {\n type: 'number', precision: 2, max: 2.0, min: 0.1\n }\n\n }, this.parameters, {\n\n flatShaded: null,\n side: null,\n wireframe: null,\n\n roughness: null,\n metalness: null\n\n })\n\n this.init(params)\n }\n\n init (params: Partial) {\n var p = params || {}\n\n this.multipleBond = defaults(p.multipleBond, 'off')\n this.bondSpacing = defaults(p.bondSpacing, 1.0)\n this.linewidth = defaults(p.linewidth, 2)\n this.lines = defaults(p.lines, true)\n this.crosses = defaults(p.crosses, 'lone')\n this.crossSize = defaults(p.crossSize, 0.4)\n\n super.init(p)\n }\n\n getAtomRadius (atom:AtomProxy) {\n return 0.1\n }\n\n getBondParams (what: any, params?: Partial) {\n params = Object.assign({\n multipleBond: this.multipleBond,\n bondSpacing: this.bondSpacing,\n radiusParams: { 'type': 'size', 'size': 0.1, 'scale': 1 }\n }, params)\n\n return super.getBondParams(what, params)\n }\n\n _crossData (what: any, sview: StructureView) {\n if (what) {\n if (!what.position && !what.color) return\n }\n\n const p = {}\n if (this.crosses === 'lone') {\n Object.assign(p, {atomSet : getLoneAtomSet(sview)})\n }\n\n const atomData = sview.getAtomData(this.getAtomParams(what, p))\n const crossData: CrossData = {}\n const position = atomData.position\n const color = atomData.color\n const picking = atomData.picking\n\n const size = (position! || color).length\n const attrSize = size * 3\n\n let cPosition1 = new Float32Array(0)\n let cPosition2 = new Float32Array(0)\n let cColor = new Float32Array(0)\n let cColor2 = new Float32Array(0)\n let cOffset: number = 0\n\n let pickingArray = new Float32Array(0)\n\n if (!what || what.position) {\n cPosition1 = crossData.position1 = new Float32Array(attrSize)\n cPosition2 = crossData.position2 = new Float32Array(attrSize)\n cOffset = this.crossSize / 2\n }\n if (!what || what.color) {\n cColor = crossData.color = new Float32Array(attrSize)\n cColor2 = crossData.color2 = new Float32Array(attrSize)\n }\n if (!what || what.picking) {\n pickingArray = new Float32Array(atomData.picking!.array!.length * 3) // Needs padding??\n }\n\n for (let v = 0; v < size; v++) {\n const j = v * 3\n const i = j * 3\n\n if (!what || what.position) {\n const x = position![ j ]\n const y = position![ j + 1 ]\n const z = position![ j + 2 ]\n\n cPosition1[ i ] = x - cOffset!\n cPosition1[ i + 1 ] = y\n cPosition1[ i + 2 ] = z\n cPosition2[ i ] = x + cOffset\n cPosition2[ i + 1 ] = y\n cPosition2[ i + 2 ] = z\n\n cPosition1[ i + 3 ] = x\n cPosition1[ i + 4 ] = y - cOffset\n cPosition1[ i + 5 ] = z\n cPosition2[ i + 3 ] = x\n cPosition2[ i + 4 ] = y + cOffset\n cPosition2[ i + 5 ] = z\n\n cPosition1[ i + 6 ] = x\n cPosition1[ i + 7 ] = y\n cPosition1[ i + 8 ] = z - cOffset\n cPosition2[ i + 6 ] = x\n cPosition2[ i + 7 ] = y\n cPosition2[ i + 8 ] = z + cOffset\n }\n\n if (!what || what.color) {\n const cimax = i + 9\n for (let ci = i; ci < cimax; ci += 3) {\n cColor[ ci ] = cColor2[ ci ] = color![ j ]\n cColor[ ci + 1 ] = cColor2[ ci + 1 ] = color![ j + 1 ]\n cColor[ ci + 2 ] = cColor2[ ci + 2 ] = color![ j + 2 ]\n }\n }\n\n if (!what || what.picking) {\n pickingArray[ j ] =\n pickingArray[ j + 1 ] =\n pickingArray[ j + 2 ] = picking!.array![ v ]\n }\n }\n\n if (!what || what.picking) {\n crossData.picking = new AtomPicker(\n pickingArray, picking!.structure\n )\n }\n\n return crossData\n }\n\n createData (sview: StructureView) {\n const what = { position: true, color: true, picking: true }\n\n const bufferList = []\n\n if (this.lines) {\n const bondData = sview.getBondData(this.getBondParams(what))\n\n const lineBuffer = new WideLineBuffer(\n bondData, this.getBufferParams({ linewidth: this.linewidth })\n )\n\n bufferList.push(lineBuffer)\n }\n\n if (this.crosses !== 'off') {\n const crossBuffer = new WideLineBuffer(\n (this._crossData(what, sview) as CrossData),\n this.getBufferParams({linewidth: this.linewidth})\n )\n bufferList.push(crossBuffer)\n }\n\n return {\n bufferList: bufferList\n }\n }\n\n updateData (what: any, data: StructureRepresentationData) {\n let bufferIdx = 0\n\n if (this.lines) {\n const bondData = data.sview!.getBondData(this.getBondParams(what))\n const lineAttributes = {}\n\n if (!what || what.position) {\n Object.assign(lineAttributes, {\n position1: bondData.position1,\n position2: bondData.position2\n })\n }\n\n if (!what || what.color) {\n Object.assign(lineAttributes, {\n color: bondData.color,\n color2: bondData.color2\n })\n }\n\n data.bufferList[ bufferIdx++ ].setAttributes(lineAttributes)\n }\n\n if (this.crosses !== 'off') {\n const crossData = this._crossData(what, (data.sview as StructureView))\n const crossAttributes = {}\n\n if (!what || what.position) {\n Object.assign(crossAttributes, {\n position1: crossData!.position1,\n position2: crossData!.position2\n })\n }\n if (!what || what.color) {\n Object.assign(crossAttributes, {\n color: crossData!.color,\n color2: crossData!.color2\n })\n }\n\n data.bufferList[ bufferIdx++ ].setAttributes(crossAttributes)\n }\n }\n\n setParameters (params: Partial) {\n var rebuild = false\n var what = {}\n\n if (params && (params.bondSpacing || params.crossSize)) {\n Object.assign(what, { position: true })\n }\n\n super.setParameters(params, what, rebuild)\n\n return this\n }\n}\n\nRepresentationRegistry.add('line', LineRepresentation)\n\nexport default LineRepresentation\n","import { NumberArray, TypedArray } from \"../types\";\n\n/**\n * @file Grid\n * @author Alexander Rose \n * @private\n */\nexport interface iGrid {\n data: TypedArray\n index: (x: number, y: number, z: number) => number\n set: (x: number, y: number, z: number, ...arg: number[]) => void\n toArray: (x: number, y: number, z: number, array?: NumberArray, offset?: number) => void\n fromArray: (x: number, y: number, z: number, array: NumberArray, offset?: number) => void\n copy: (grid: iGrid) => void\n // clone: () => iGrid\n}\n\nfunction makeGrid (length: number, width: number, height: number, DataCtor: any, elemSize: number) : iGrid {\n DataCtor = DataCtor || Int32Array\n elemSize = elemSize || 1\n\n const data = new DataCtor(length * width * height * elemSize)\n\n function index (x: number, y: number, z: number) {\n return ((((x * width) + y) * height) + z) * elemSize\n }\n\n function set (x: number, y: number, z: number, ...args: number[]) {\n const i = index(x, y, z)\n\n for (let j = 0; j < elemSize; ++j) {\n data[ i + j ] = args[ j ]\n }\n }\n\n function toArray (x: number, y: number, z: number, array: NumberArray = [], offset: number = 0) {\n const i = index(x, y, z)\n\n for (let j = 0; j < elemSize; ++j) {\n array[ offset + j ] = data[ i + j ]\n }\n }\n\n function fromArray(x: number, y: number, z: number, array: NumberArray, offset: number = 0) {\n const i = index(x, y, z)\n\n for (let j = 0; j < elemSize; ++j) {\n data[ i + j ] = array[ offset + j ]\n }\n }\n\n function copy(grid: iGrid) {\n data.set(grid.data)\n }\n\n // function clone() {\n // return makeGrid(\n // length, width, height, DataCtor, elemSize\n // ).copy(this)\n // }\n return { data, index, set, toArray, fromArray, copy }\n}\n\nexport { makeGrid }","/**\n * @file EDT Surface\n * @author Alexander Rose \n * @private\n */\n\nimport { VolumeSurface } from './volume'\nimport { iGrid, makeGrid } from '../geometry/grid'\nimport { computeBoundingBox } from '../math/vector-utils'\nimport { getRadiusDict, getSurfaceGrid } from './surface-utils'\nimport { TypedArray } from '../types';\n\ninterface EDTSurface {\n getVolume: (type: string, probeRadius: number, scaleFactor: number, cutoff: number, setAtomID: boolean) => {\n data: TypedArray\n nx: number\n ny: number\n nz: number\n atomindex: TypedArray\n }\n getSurface: (type: string, probeRadius: number, scaleFactor: number, cutoff: number, setAtomID: boolean, smooth: number, contour: boolean) => any\n}\n\nfunction EDTSurface (this: EDTSurface, coordList: Float32Array, radiusList: Float32Array, indexList: Uint16Array|Uint32Array) {\n // based on D. Xu, Y. Zhang (2009) Generating Triangulated Macromolecular\n // Surfaces by Euclidean Distance Transform. PLoS ONE 4(12): e8140.\n //\n // Permission to use, copy, modify, and distribute this program for\n // any purpose, with or without fee, is hereby granted, provided that\n // the notices on the head, the reference information, and this\n // copyright notice appear in all copies or substantial portions of\n // the Software. It is provided \"as is\" without express or implied\n // warranty.\n //\n // ported to JavaScript by biochem_fan (http://webglmol.sourceforge.jp/)\n // refactored by dkoes (https://github.com/dkoes)\n //\n // adapted to NGL by Alexander Rose\n\n var radiusDict = getRadiusDict(radiusList as any)\n var bbox = computeBoundingBox(coordList)\n if (coordList.length === 0) {\n bbox[ 0 ].set([ 0, 0, 0 ])\n bbox[ 1 ].set([ 0, 0, 0 ])\n }\n var min = bbox[ 0 ]\n var max = bbox[ 1 ]\n\n var probeRadius: number, scaleFactor: number, cutoff: number\n var pLength: number, pWidth: number, pHeight: number\n var matrix: Float32Array, ptran: Float32Array\n var depty: {[k: string]: TypedArray}, widxz: {[k: string]: number}\n var cutRadius: number\n var setAtomID: boolean\n var vpBits: TypedArray, vpDistance: TypedArray, vpAtomID: TypedArray\n\n function init (btype: boolean, _probeRadius: number, _scaleFactor: number, _cutoff: number, _setAtomID: boolean) {\n probeRadius = _probeRadius || 1.4\n scaleFactor = _scaleFactor || 2.0\n setAtomID = _setAtomID || true\n\n var maxRadius = 0\n for (var radius in radiusDict) {\n maxRadius = Math.max(maxRadius, radius as any)\n }\n\n var grid = getSurfaceGrid(\n min, max, maxRadius, scaleFactor, btype ? probeRadius : 0\n )\n\n pLength = grid.dim[0]\n pWidth = grid.dim[1]\n pHeight = grid.dim[2]\n\n matrix = grid.matrix\n ptran = grid.tran\n scaleFactor = grid.scaleFactor\n\n // boundingatom caches\n depty = {}\n widxz = {}\n boundingatom(btype)\n\n cutRadius = probeRadius * scaleFactor\n\n if (_cutoff) {\n cutoff = _cutoff\n } else {\n // cutoff = Math.max( 0.1, -1.2 + scaleFactor * probeRadius );\n cutoff = probeRadius / scaleFactor\n }\n\n vpBits = new Uint8Array(pLength * pWidth * pHeight)\n if (btype) {\n vpDistance = new Float64Array(pLength * pWidth * pHeight)\n }\n if (setAtomID) {\n vpAtomID = new Int32Array(pLength * pWidth * pHeight)\n }\n }\n\n // constants for vpBits bitmasks\n var INOUT = 1\n var ISDONE = 2\n var ISBOUND = 4\n\n var nb = [\n new Int32Array([ 1, 0, 0 ]), new Int32Array([ -1, 0, 0 ]),\n new Int32Array([ 0, 1, 0 ]), new Int32Array([ 0, -1, 0 ]),\n new Int32Array([ 0, 0, 1 ]), new Int32Array([ 0, 0, -1 ]),\n new Int32Array([ 1, 1, 0 ]), new Int32Array([ 1, -1, 0 ]),\n new Int32Array([ -1, 1, 0 ]), new Int32Array([ -1, -1, 0 ]),\n new Int32Array([ 1, 0, 1 ]), new Int32Array([ 1, 0, -1 ]),\n new Int32Array([ -1, 0, 1 ]), new Int32Array([ -1, 0, -1 ]),\n new Int32Array([ 0, 1, 1 ]), new Int32Array([ 0, 1, -1 ]),\n new Int32Array([ 0, -1, 1 ]), new Int32Array([ 0, -1, -1 ]),\n new Int32Array([ 1, 1, 1 ]), new Int32Array([ 1, 1, -1 ]),\n new Int32Array([ 1, -1, 1 ]), new Int32Array([ -1, 1, 1 ]),\n new Int32Array([ 1, -1, -1 ]), new Int32Array([ -1, -1, 1 ]),\n new Int32Array([ -1, 1, -1 ]), new Int32Array([ -1, -1, -1 ])\n ]\n\n //\n\n this.getVolume = function (type: string, probeRadius: number, scaleFactor: number, cutoff: number, setAtomID: boolean) {\n console.time('EDTSurface.getVolume')\n\n var btype = type !== 'vws'\n\n init(btype, probeRadius, scaleFactor, cutoff, setAtomID)\n\n fillvoxels(btype)\n buildboundary()\n\n if (type === 'ms' || type === 'ses') {\n fastdistancemap()\n }\n\n if (type === 'ses') {\n boundingatom(false)\n fillvoxelswaals()\n }\n\n marchingcubeinit(type)\n\n // set atomindex in the volume data\n for (var i = 0, il = vpAtomID.length; i < il; ++i) {\n vpAtomID[ i ] = indexList[ vpAtomID[ i ] ]\n }\n\n console.timeEnd('EDTSurface.getVolume')\n\n return {\n data: vpBits,\n nx: pHeight,\n ny: pWidth,\n nz: pLength,\n atomindex: vpAtomID\n }\n }\n\n this.getSurface = function (type: string, probeRadius: number, scaleFactor: number, cutoff: number, setAtomID: boolean, smooth: number, contour: boolean) {\n var vd = this.getVolume(\n type, probeRadius, scaleFactor, cutoff, setAtomID\n )\n\n var volsurf = new (VolumeSurface as any)(\n vd.data, vd.nx, vd.ny, vd.nz, vd.atomindex\n ) as VolumeSurface\n\n return (volsurf!.getSurface as any)(1, smooth, undefined, matrix, contour)\n }\n\n function boundingatom (btype: boolean) {\n var r\n var j\n var k\n var txz\n var tdept\n var sradius\n var tradius\n var widxzR\n var deptyName\n var indx\n\n for (var name in radiusDict) {\n r = parseFloat(name)\n\n if (depty[ name ]) continue\n\n if (!btype) {\n tradius = r * scaleFactor + 0.5\n } else {\n tradius = (r + probeRadius) * scaleFactor + 0.5\n }\n\n sradius = tradius * tradius\n widxzR = Math.floor(tradius) + 1\n deptyName = new Int32Array(widxzR * widxzR)\n indx = 0\n\n for (j = 0; j < widxzR; ++j) {\n for (k = 0; k < widxzR; ++k) {\n txz = j * j + k * k\n\n if (txz > sradius) {\n deptyName[ indx ] = -1\n } else {\n tdept = Math.sqrt(sradius - txz)\n deptyName[ indx ] = Math.floor(tdept)\n }\n\n ++indx\n }\n }\n\n widxz[ name ] = widxzR\n depty[ name ] = deptyName\n }\n }\n\n function fillatom (idx: number) {\n var ci = idx * 3\n var ri = idx\n\n var cx, cy, cz, ox, oy, oz, mi, mj, mk, i, j, k, si, sj, sk\n var ii, jj, kk\n\n cx = Math.floor(0.5 + scaleFactor * (coordList[ ci ] + ptran[0]))\n cy = Math.floor(0.5 + scaleFactor * (coordList[ ci + 1 ] + ptran[1]))\n cz = Math.floor(0.5 + scaleFactor * (coordList[ ci + 2 ] + ptran[2]))\n\n var at = radiusList[ ri ]\n var deptyAt = depty[ at ]\n var nind = 0\n var pWH = pWidth * pHeight\n var n = widxz[ at ]\n\n var deptyAtNind\n\n for (i = 0; i < n; ++i) {\n for (j = 0; j < n; ++j) {\n deptyAtNind = deptyAt[ nind ]\n\n if (deptyAtNind !== -1) {\n for (ii = -1; ii < 2; ++ii) {\n for (jj = -1; jj < 2; ++jj) {\n for (kk = -1; kk < 2; ++kk) {\n if (ii !== 0 && jj !== 0 && kk !== 0) {\n mi = ii * i\n mk = kk * j\n\n for (k = 0; k <= deptyAtNind; ++k) {\n mj = k * jj\n si = cx + mi\n sj = cy + mj\n sk = cz + mk\n\n if (si < 0 || sj < 0 || sk < 0 ||\n si >= pLength || sj >= pWidth || sk >= pHeight\n ) {\n continue\n }\n\n var index = si * pWH + sj * pHeight + sk\n\n if (!setAtomID) {\n vpBits[ index ] |= INOUT\n } else {\n if (!(vpBits[ index ] & INOUT)) {\n vpBits[ index ] |= INOUT\n vpAtomID[ index ] = idx\n } else if (vpBits[ index ] & INOUT) {\n var ci2 = vpAtomID[ index ]\n\n if (ci2 !== ci) {\n ox = cx + mi - Math.floor(0.5 + scaleFactor * (coordList[ci2] + ptran[0]))\n oy = cy + mj - Math.floor(0.5 + scaleFactor * (coordList[ci2 + 1] + ptran[1]))\n oz = cz + mk - Math.floor(0.5 + scaleFactor * (coordList[ci2 + 2] + ptran[2]))\n\n if (mi * mi + mj * mj + mk * mk < ox * ox + oy * oy + oz * oz) {\n vpAtomID[ index ] = idx\n }\n }\n }\n }\n } // k\n } // if\n } // kk\n } // jj\n } // ii\n } // if\n\n nind++\n } // j\n } // i\n }\n\n function fillvoxels (btype: boolean) {\n console.time('EDTSurface fillvoxels')\n\n var i, il\n\n for (i = 0, il = vpBits.length; i < il; ++i) {\n vpBits[ i ] = 0\n if (btype) vpDistance[ i ] = -1.0\n if (setAtomID) vpAtomID[ i ] = -1\n }\n\n for (i = 0, il = coordList.length / 3; i < il; ++i) {\n fillatom(i)\n }\n\n for (i = 0, il = vpBits.length; i < il; ++i) {\n if (vpBits[ i ] & INOUT) {\n vpBits[ i ] |= ISDONE\n }\n }\n\n console.timeEnd('EDTSurface fillvoxels')\n }\n\n function fillAtomWaals (idx: number) {\n var ci = idx * 3\n var ri = idx\n\n var cx\n var cy\n var cz\n var ox\n var oy\n var oz\n var nind = 0\n\n var mi\n var mj\n var mk\n var si\n var sj\n var sk\n var i\n var j\n var k\n var ii\n var jj\n var kk\n var n\n\n cx = Math.floor(0.5 + scaleFactor * (coordList[ ci ] + ptran[0]))\n cy = Math.floor(0.5 + scaleFactor * (coordList[ ci + 1 ] + ptran[1]))\n cz = Math.floor(0.5 + scaleFactor * (coordList[ ci + 2 ] + ptran[2]))\n\n var at = radiusList[ ri ]\n var pWH = pWidth * pHeight\n\n for (i = 0, n = widxz[at]; i < n; ++i) {\n for (j = 0; j < n; ++j) {\n if (depty[ at ][ nind ] !== -1) {\n for (ii = -1; ii < 2; ++ii) {\n for (jj = -1; jj < 2; ++jj) {\n for (kk = -1; kk < 2; ++kk) {\n if (ii !== 0 && jj !== 0 && kk !== 0) {\n mi = ii * i\n mk = kk * j\n\n for (k = 0; k <= depty[ at ][ nind ]; ++k) {\n mj = k * jj\n si = cx + mi\n sj = cy + mj\n sk = cz + mk\n\n if (si < 0 || sj < 0 || sk < 0 ||\n si >= pLength || sj >= pWidth || sk >= pHeight\n ) {\n continue\n }\n\n var index = si * pWH + sj * pHeight + sk\n\n if (!(vpBits[ index ] & ISDONE)) {\n vpBits[ index ] |= ISDONE\n if (setAtomID) vpAtomID[ index ] = idx\n } else if (setAtomID) {\n var ci2 = vpAtomID[ index ]\n\n ox = Math.floor(0.5 + scaleFactor * (coordList[ ci2 ] + ptran[0]))\n oy = Math.floor(0.5 + scaleFactor * (coordList[ ci2 + 1 ] + ptran[1]))\n oz = Math.floor(0.5 + scaleFactor * (coordList[ ci2 + 2 ] + ptran[2]))\n\n if (mi * mi + mj * mj + mk * mk < ox * ox + oy * oy + oz * oz) {\n vpAtomID[ index ] = idx\n }\n }\n } // k\n } // if\n } // kk\n } // jj\n } // ii\n } // if\n\n nind++\n } // j\n } // i\n }\n\n function fillvoxelswaals () {\n var i, il\n\n for (i = 0, il = vpBits.length; i < il; ++i) {\n vpBits[ i ] &= ~ISDONE // not isdone\n }\n\n for (i = 0, il = coordList.length / 3; i < il; ++i) {\n fillAtomWaals(i)\n }\n }\n\n function buildboundary () {\n var i, j, k\n var pWH = pWidth * pHeight\n\n for (i = 0; i < pLength; ++i) {\n for (j = 0; j < pHeight; ++j) {\n for (k = 0; k < pWidth; ++k) {\n var index = i * pWH + k * pHeight + j\n\n if (vpBits[ index ] & INOUT) {\n // var flagbound = false;\n var ii = 0\n\n // while( !flagbound && ii < 26 ){\n while (ii < 26) {\n var ti = i + nb[ ii ][ 0 ]\n var tj = j + nb[ ii ][ 2 ]\n var tk = k + nb[ ii ][ 1 ]\n\n if (ti > -1 && ti < pLength &&\n tk > -1 && tk < pWidth &&\n tj > -1 && tj < pHeight &&\n !(vpBits[ ti * pWH + tk * pHeight + tj ] & INOUT)\n ) {\n vpBits[ index ] |= ISBOUND\n // flagbound = true;\n break\n } else {\n ii++\n }\n }\n }\n } // k\n } // j\n } // i\n }\n\n function fastdistancemap () {\n console.time('EDTSurface fastdistancemap')\n\n var i, j, k, n\n\n var boundPoint = makeGrid(\n pLength, pWidth, pHeight, Uint16Array, 3\n )\n var pWH = pWidth * pHeight\n var cutRSq = cutRadius * cutRadius\n\n var totalsurfacevox = 0\n // var totalinnervox = 0;\n\n var index\n\n for (i = 0; i < pLength; ++i) {\n for (j = 0; j < pWidth; ++j) {\n for (k = 0; k < pHeight; ++k) {\n index = i * pWH + j * pHeight + k\n\n vpBits[ index ] &= ~ISDONE\n\n if (vpBits[ index ] & INOUT) {\n if (vpBits[ index ] & ISBOUND) {\n boundPoint.set(\n i, j, k,\n i, j, k\n )\n\n vpDistance[ index ] = 0\n vpBits[ index ] |= ISDONE\n\n totalsurfacevox += 1\n }/* else{\n totalinnervox += 1;\n } */\n }\n }\n }\n }\n\n var inarray = new Int32Array(3 * totalsurfacevox)\n var positin = 0\n var outarray = new Int32Array(3 * totalsurfacevox)\n var positout = 0\n\n for (i = 0; i < pLength; ++i) {\n for (j = 0; j < pWidth; ++j) {\n for (k = 0; k < pHeight; ++k) {\n index = i * pWH + j * pHeight + k\n\n if (vpBits[ index ] & ISBOUND) {\n inarray[ positin ] = i\n inarray[ positin + 1 ] = j\n inarray[ positin + 2 ] = k\n positin += 3\n\n vpBits[ index ] &= ~ISBOUND\n }\n }\n }\n }\n\n do {\n positout = fastoneshell(inarray, boundPoint, positin, outarray)\n positin = 0\n\n for (i = 0, n = positout; i < n; i += 3) {\n index = pWH * outarray[ i ] + pHeight * outarray[ i + 1 ] + outarray[ i + 2 ]\n vpBits[ index ] &= ~ISBOUND\n\n if (vpDistance[ index ] <= 1.0404 * cutRSq) {\n // if( vpDistance[ index ] <= 1.02 * cutRadius ){\n\n inarray[ positin ] = outarray[ i ]\n inarray[ positin + 1 ] = outarray[ i + 1 ]\n inarray[ positin + 2 ] = outarray[ i + 2 ]\n positin += 3\n }\n }\n } while (positin > 0)\n\n // var cutsf = Math.max( 0, scaleFactor - 0.5 );\n // cutoff = cutRadius - 0.5 / ( 0.1 + cutsf );\n var cutoffSq = cutoff * cutoff\n\n var index2\n var bp = new Uint16Array(3)\n\n for (i = 0; i < pLength; ++i) {\n for (j = 0; j < pWidth; ++j) {\n for (k = 0; k < pHeight; ++k) {\n index = i * pWH + j * pHeight + k\n vpBits[ index ] &= ~ISBOUND\n\n // ses solid\n\n if (vpBits[ index ] & INOUT) {\n if (!(vpBits[ index ] & ISDONE) ||\n ((vpBits[ index ] & ISDONE) && vpDistance[ index ] >= cutoffSq)\n ) {\n vpBits[ index ] |= ISBOUND\n\n if (setAtomID && (vpBits[ index ] & ISDONE)) {\n boundPoint.toArray(i, j, k, bp)\n index2 = bp[ 0 ] * pWH + bp[ 1 ] * pHeight + bp[ 2 ]\n\n vpAtomID[ index ] = vpAtomID[ index2 ]\n }\n }\n }\n }\n }\n }\n\n console.timeEnd('EDTSurface fastdistancemap')\n }\n\n function fastoneshell (inarray: Int32Array, boundPoint: iGrid, positin: number, outarray: Int32Array) {\n // *allocout,voxel2\n // ***boundPoint, int*\n // outnum, int *elimi)\n var tx, ty, tz\n var dx, dy, dz\n var i, j, n\n var square\n var index\n var nbj\n var bp = new Uint16Array(3)\n var positout = 0\n\n if (positin === 0) {\n return positout\n }\n\n var tnvix = -1\n var tnviy = -1\n var tnviz = -1\n\n var pWH = pWidth * pHeight\n\n for (i = 0, n = positin; i < n; i += 3) {\n tx = inarray[ i ]\n ty = inarray[ i + 1 ]\n tz = inarray[ i + 2 ]\n boundPoint.toArray(tx, ty, tz, bp)\n\n for (j = 0; j < 6; ++j) {\n nbj = nb[ j ]\n tnvix = tx + nbj[ 0 ]\n tnviy = ty + nbj[ 1 ]\n tnviz = tz + nbj[ 2 ]\n\n if (tnvix < pLength && tnvix > -1 &&\n tnviy < pWidth && tnviy > -1 &&\n tnviz < pHeight && tnviz > -1\n ) {\n index = tnvix * pWH + pHeight * tnviy + tnviz\n\n if ((vpBits[ index ] & INOUT) && !(vpBits[ index ] & ISDONE)) {\n boundPoint.fromArray(tnvix, tnviy, tnviz, bp)\n dx = tnvix - bp[ 0 ]\n dy = tnviy - bp[ 1 ]\n dz = tnviz - bp[ 2 ]\n square = dx * dx + dy * dy + dz * dz\n // square = Math.sqrt( square );\n\n vpDistance[ index ] = square\n vpBits[ index ] |= ISDONE\n vpBits[ index ] |= ISBOUND\n\n outarray[ positout ] = tnvix\n outarray[ positout + 1 ] = tnviy\n outarray[ positout + 2 ] = tnviz\n positout += 3\n } else if ((vpBits[ index ] & INOUT) && (vpBits[ index ] & ISDONE)) {\n dx = tnvix - bp[ 0 ]\n dy = tnviy - bp[ 1 ]\n dz = tnviz - bp[ 2 ]\n square = dx * dx + dy * dy + dz * dz\n // square = Math.sqrt( square );\n\n if (square < vpDistance[ index ]) {\n boundPoint.fromArray(tnvix, tnviy, tnviz, bp)\n vpDistance[ index ] = square\n\n if (!(vpBits[ index ] & ISBOUND)) {\n vpBits[ index ] |= ISBOUND\n\n outarray[ positout ] = tnvix\n outarray[ positout + 1 ] = tnviy\n outarray[ positout + 2 ] = tnviz\n positout += 3\n }\n }\n }\n }\n }\n }\n\n for (i = 0, n = positin; i < n; i += 3) {\n tx = inarray[ i ]\n ty = inarray[ i + 1 ]\n tz = inarray[ i + 2 ]\n boundPoint.toArray(tx, ty, tz, bp)\n\n for (j = 6; j < 18; j++) {\n nbj = nb[ j ]\n tnvix = tx + nbj[ 0 ]\n tnviy = ty + nbj[ 1 ]\n tnviz = tz + nbj[ 2 ]\n\n if (tnvix < pLength && tnvix > -1 &&\n tnviy < pWidth && tnviy > -1 &&\n tnviz < pHeight && tnviz > -1\n ) {\n index = tnvix * pWH + pHeight * tnviy + tnviz\n\n if ((vpBits[index] & INOUT) && !(vpBits[index] & ISDONE)) {\n boundPoint.fromArray(tnvix, tnviy, tnviz, bp)\n dx = tnvix - bp[ 0 ]\n dy = tnviy - bp[ 1 ]\n dz = tnviz - bp[ 2 ]\n square = dx * dx + dy * dy + dz * dz\n // square = Math.sqrt( square );\n\n vpDistance[index] = square\n vpBits[index] |= ISDONE\n vpBits[index] |= ISBOUND\n\n outarray[ positout ] = tnvix\n outarray[ positout + 1 ] = tnviy\n outarray[ positout + 2 ] = tnviz\n positout += 3\n } else if ((vpBits[index] & INOUT) && (vpBits[index] & ISDONE)) {\n dx = tnvix - bp[ 0 ]\n dy = tnviy - bp[ 1 ]\n dz = tnviz - bp[ 2 ]\n square = dx * dx + dy * dy + dz * dz\n // square = Math.sqrt( square );\n\n if (square < vpDistance[index]) {\n boundPoint.fromArray(tnvix, tnviy, tnviz, bp)\n vpDistance[index] = square\n\n if (!(vpBits[index] & ISBOUND)) {\n vpBits[index] |= ISBOUND\n\n outarray[ positout ] = tnvix\n outarray[ positout + 1 ] = tnviy\n outarray[ positout + 2 ] = tnviz\n positout += 3\n }\n }\n }\n }\n }\n }\n\n for (i = 0, n = positin; i < n; i += 3) {\n tx = inarray[ i ]\n ty = inarray[ i + 1 ]\n tz = inarray[ i + 2 ]\n boundPoint.toArray(tx, ty, tz, bp)\n\n for (j = 18; j < 26; j++) {\n nbj = nb[ j ]\n tnvix = tx + nbj[ 0 ]\n tnviy = ty + nbj[ 1 ]\n tnviz = tz + nbj[ 2 ]\n\n if (tnvix < pLength && tnvix > -1 &&\n tnviy < pWidth && tnviy > -1 &&\n tnviz < pHeight && tnviz > -1\n ) {\n index = tnvix * pWH + pHeight * tnviy + tnviz\n\n if ((vpBits[index] & INOUT) && !(vpBits[index] & ISDONE)) {\n boundPoint.fromArray(tnvix, tnviy, tnviz, bp)\n dx = tnvix - bp[ 0 ]\n dy = tnviy - bp[ 1 ]\n dz = tnviz - bp[ 2 ]\n square = dx * dx + dy * dy + dz * dz\n // square = Math.sqrt( square );\n\n vpDistance[index] = square\n vpBits[index] |= ISDONE\n vpBits[index] |= ISBOUND\n\n outarray[ positout ] = tnvix\n outarray[ positout + 1 ] = tnviy\n outarray[ positout + 2 ] = tnviz\n positout += 3\n } else if ((vpBits[index] & INOUT) && (vpBits[index] & ISDONE)) {\n dx = tnvix - bp[ 0 ]\n dy = tnviy - bp[ 1 ]\n dz = tnviz - bp[ 2 ]\n square = dx * dx + dy * dy + dz * dz\n // square = Math.sqrt( square );\n\n if (square < vpDistance[index]) {\n boundPoint.fromArray(tnvix, tnviy, tnviz, bp)\n vpDistance[index] = square\n\n if (!(vpBits[index] & ISBOUND)) {\n vpBits[index] |= ISBOUND\n\n outarray[ positout ] = tnvix\n outarray[ positout + 1 ] = tnviy\n outarray[ positout + 2 ] = tnviz\n positout += 3\n }\n }\n }\n }\n }\n }\n\n return positout\n }\n\n function marchingcubeinit (stype: string) {\n var i\n var n = vpBits.length\n\n if (stype === 'vws') {\n for (i = 0; i < n; ++i) {\n vpBits[ i ] &= ~ISBOUND\n vpBits[ i ] = (vpBits[ i ] & ISDONE) ? 1 : 0\n }\n } else if (stype === 'ms') { // ses without vdw => ms\n for (i = 0; i < n; ++i) {\n vpBits[ i ] &= ~ISDONE\n if (vpBits[ i ] & ISBOUND) {\n vpBits[ i ] |= ISDONE\n }\n vpBits[ i ] &= ~ISBOUND\n vpBits[ i ] = (vpBits[ i ] & ISDONE) ? 1 : 0\n }\n } else if (stype === 'ses') {\n for (i = 0; i < n; ++i) {\n if ((vpBits[ i ] & ISBOUND) && (vpBits[ i ] & ISDONE)) {\n vpBits[ i ] &= ~ISBOUND\n } else if ((vpBits[ i ] & ISBOUND) && !(vpBits[ i ] & ISDONE)) {\n vpBits[ i ] |= ISDONE\n }\n vpBits[ i ] = (vpBits[ i ] & ISDONE) ? 1 : 0\n }\n } else if (stype === 'sas') {\n for (i = 0; i < n; ++i) {\n vpBits[ i ] &= ~ISBOUND\n vpBits[ i ] = (vpBits[ i ] & ISDONE) ? 1 : 0\n }\n }\n }\n}\nObject.assign(EDTSurface, {__deps: [\n getSurfaceGrid, getRadiusDict, VolumeSurface, computeBoundingBox, makeGrid\n]})\n\nexport default EDTSurface\n","/**\n * @file AV Surface\n * @author Fred Ludlow \n * @private\n */\n\nimport { getSurfaceGrid } from './surface-utils'\nimport { VolumeSurface } from './volume'\nimport { uniformArray } from '../math/array-utils'\nimport {\n computeBoundingBox, v3multiplyScalar, v3cross, v3normalize\n} from '../math/vector-utils'\nimport { defaults } from '../utils'\nimport { NumberArray } from '../types';\n\n\n/**\n * Modifed from SpatialHash\n *\n * Main differences are:\n * - Optimized grid size to ensure we only ever need to look +/-1 cell\n * - Aware of atomic radii and will only output atoms within rAtom + rExtra\n * (see withinRadii method)\n *\n * (Uses rounding rather than bitshifting as consequence of arbitrary grid size)\n * @class\n * @param {Float32Array} atomsX - x coordinates\n * @param {Float32Array} atomsY - y coordinates\n * @param {Float32Array} atomsZ - z coordinates\n * @param {Float32Array} atomsR - atom radii\n * @param {Float32Array} min - xyz min coordinates\n * @param {Float32Array} max - xyz max coordinates\n * @param {Float} maxDistance - max distance\n */\nexport interface iAVHash {\n neighbourListLength: number\n withinRadii: (x: number, y: number, z: number, rExtra: number, out: Int32Array) => void\n}\n\n\nfunction makeAVHash (atomsX: Float32Array, atomsY: Float32Array, atomsZ: Float32Array, atomsR: Float32Array, min: Float32Array, max: Float32Array, maxDistance: number): iAVHash {\n maxDistance = Math.max(0.1, maxDistance) // Avoid maxDistance of zero, see #802\n var nAtoms = atomsX.length\n\n var minX = min[ 0 ]\n var minY = min[ 1 ]\n var minZ = min[ 2 ]\n\n var maxX = max[ 0 ]\n var maxY = max[ 1 ]\n var maxZ = max[ 2 ]\n\n function hashFunc (w: number, minW: number) {\n return Math.floor((w - minW) / maxDistance)\n }\n\n var iDim = hashFunc(maxX, minX) + 1\n var jDim = hashFunc(maxY, minY) + 1\n var kDim = hashFunc(maxZ, minZ) + 1\n\n var nCells = iDim * jDim * kDim\n\n var jkDim = jDim * kDim\n\n /* Get cellID for cartesian x,y,z */\n var cellID = function (x: number, y: number, z: number) {\n return (((hashFunc(x, minX) * jDim) + hashFunc(y, minY)) * kDim) + hashFunc(z, minZ)\n }\n\n /* Initial building, could probably be optimized further */\n var preHash = [] // preHash[ cellID ] = [ atomId1, atomId2 ];\n\n for (var i = 0; i < nAtoms; i++) {\n var cid = cellID(atomsX[ i ], atomsY[ i ], atomsZ[ i ])\n\n if (preHash[ cid ] === undefined) {\n preHash[ cid ] = [ i ]\n } else {\n preHash[ cid ].push(i)\n }\n }\n\n var cellOffsets = new Uint32Array(nCells)\n var cellLengths = new Uint16Array(nCells)\n var data = new Uint32Array(nAtoms)\n\n var offset = 0\n var maxCellLength = 0\n\n for (i = 0; i < nCells; i++) {\n var start = cellOffsets[ i ] = offset\n\n var subArray = preHash[ i ]\n\n if (subArray !== undefined) {\n for (var j = 0; j < subArray.length; j++) {\n data[ offset ] = subArray[ j ]\n offset++\n }\n }\n\n var cellLength = offset - start\n cellLengths[ i ] = cellLength\n\n if (cellLength > maxCellLength) { maxCellLength = cellLength }\n }\n\n // Maximum number of neighbours we could ever produce (27 adjacent cells of equal population)\n const neighbourListLength = (27 * maxCellLength) + 1\n\n /**\n * Populate the supplied out array with atom indices that are within rAtom + rExtra\n * of x,y,z\n *\n * -1 in out array indicates the end of the list\n *\n * @param {Float} x - x coordinate\n * @param {Float} y - y coordinate\n * @param {Float} z - z coordinate\n * @param {Float} rExtra - additional radius\n * @param {Float32Array} out - pre-allocated output array\n * @return {undefined}\n */\n const withinRadii = function (x: number, y: number, z: number, rExtra: number, out: Int32Array) {\n var outIdx = 0\n\n var nearI = hashFunc(x, minX)\n var nearJ = hashFunc(y, minY)\n var nearK = hashFunc(z, minZ)\n\n var loI = Math.max(0, nearI - 1)\n var loJ = Math.max(0, nearJ - 1)\n var loK = Math.max(0, nearK - 1)\n\n var hiI = Math.min(iDim, nearI + 2)\n var hiJ = Math.min(jDim, nearJ + 2)\n var hiK = Math.min(kDim, nearK + 2)\n\n for (var i = loI; i < hiI; ++i) {\n var iOffset = i * jkDim\n\n for (var j = loJ; j < hiJ; ++j) {\n var jOffset = j * kDim\n\n for (var k = loK; k < hiK; ++k) {\n var cid = iOffset + jOffset + k\n\n var cellStart = cellOffsets[ cid ]\n var cellEnd = cellStart + cellLengths[ cid ]\n\n for (var dataIndex = cellStart; dataIndex < cellEnd; dataIndex++) {\n var atomIndex = data[ dataIndex ]\n var dx = atomsX[ atomIndex ] - x\n var dy = atomsY[ atomIndex ] - y\n var dz = atomsZ[ atomIndex ] - z\n var rSum = atomsR[ atomIndex ] + rExtra\n\n if ((dx * dx + dy * dy + dz * dz) <= (rSum * rSum)) {\n out[ outIdx++ ] = data[ dataIndex ]\n }\n }\n }\n }\n }\n // Add terminator\n out[ outIdx ] = -1\n }\n return {\n neighbourListLength: neighbourListLength,\n withinRadii: withinRadii\n }\n}\n\ninterface AVSurface {\n getSurface: (type: string, probeRadius: number, scaleFactor: number, cutoff: number, setAtomID: boolean, smooth: number, contour: boolean) => any\n}\nfunction AVSurface (this: AVSurface, coordList: Float32Array, radiusList: Float32Array, indexList: Uint16Array|Uint32Array) {\n // Field generation method adapted from AstexViewer (Mike Hartshorn)\n // by Fred Ludlow.\n // Other parts based heavily on NGL (Alexander Rose) EDT Surface class\n //\n // Should work as a drop-in alternative to EDTSurface (though some of\n // the EDT paramters are not relevant in this method).\n\n const nAtoms = radiusList.length\n\n const x = new Float32Array(nAtoms)\n const y = new Float32Array(nAtoms)\n const z = new Float32Array(nAtoms)\n\n for (let i = 0; i < nAtoms; i++) {\n const ci = 3 * i\n x[ i ] = coordList[ ci ]\n y[ i ] = coordList[ ci + 1 ]\n z[ i ] = coordList[ ci + 2 ]\n }\n\n let bbox = computeBoundingBox(coordList)\n if (coordList.length === 0) {\n bbox[ 0 ].set([ 0, 0, 0 ])\n bbox[ 1 ].set([ 0, 0, 0 ])\n }\n const min = bbox[0]\n const max = bbox[1]\n\n let r: Float32Array, r2: Float32Array // Atom positions, expanded radii (squared)\n let maxRadius: number\n\n // Parameters\n let probeRadius: number, scaleFactor: number, setAtomID: boolean, probePositions: number\n\n // Cache last value for obscured test\n let lastClip = -1\n\n // Grid params\n let dim: Float32Array, matrix: Float32Array, grid: NumberArray, atomIndex: Int32Array\n\n // grid indices -> xyz coords\n let gridx: Float32Array, gridy: Float32Array, gridz: Float32Array\n\n // Lookup tables:\n let sinTable: Float32Array, cosTable: Float32Array\n\n // Spatial Hash\n let hash: iAVHash\n\n // Neighbour array to be filled by hash\n let neighbours: Int32Array\n\n // Vectors for Torus Projection\n const atob = new Float32Array([ 0.0, 0.0, 0.0 ])\n const mid = new Float32Array([ 0.0, 0.0, 0.0 ])\n const n1 = new Float32Array([ 0.0, 0.0, 0.0 ])\n const n2 = new Float32Array([ 0.0, 0.0, 0.0 ])\n\n let ngTorus: number\n\n function init (_probeRadius?: number, _scaleFactor?: number, _setAtomID?: boolean, _probePositions?: number) {\n probeRadius = defaults(_probeRadius, 1.4)\n scaleFactor = defaults(_scaleFactor, 2.0)\n setAtomID = defaults(_setAtomID, true)\n probePositions = defaults(_probePositions, 30)\n\n r = new Float32Array(nAtoms)\n r2 = new Float32Array(nAtoms)\n\n for (let i = 0; i < r.length; ++i) {\n var rExt = radiusList[ i ] + probeRadius\n r[ i ] = rExt\n r2[ i ] = rExt * rExt\n }\n\n maxRadius = 0\n for (let j = 0; j < r.length; ++j) {\n if (r[ j ] > maxRadius) maxRadius = r[ j ]\n }\n\n initializeGrid()\n initializeAngleTables()\n initializeHash()\n\n lastClip = -1\n }\n\n function fillGridDim (a: Float32Array, start: number, step: number) {\n for (let i = 0; i < a.length; i++) {\n a[i] = start + (step * i)\n }\n }\n\n function initializeGrid () {\n const surfGrid = getSurfaceGrid(\n min, max, maxRadius, scaleFactor, 0.0\n )\n\n scaleFactor = surfGrid.scaleFactor\n dim = surfGrid.dim\n matrix = surfGrid.matrix\n\n ngTorus = Math.max(5, 2 + Math.floor(probeRadius * scaleFactor))\n\n grid = uniformArray(dim[0] * dim[1] * dim[2], -1001.0)\n\n atomIndex = new Int32Array(grid.length)\n\n gridx = new Float32Array(dim[0])\n gridy = new Float32Array(dim[1])\n gridz = new Float32Array(dim[2])\n\n fillGridDim(gridx, min[0], 1 / scaleFactor)\n fillGridDim(gridy, min[1], 1 / scaleFactor)\n fillGridDim(gridz, min[2], 1 / scaleFactor)\n }\n\n function initializeAngleTables () {\n var theta = 0.0\n var step = 2 * Math.PI / probePositions\n\n cosTable = new Float32Array(probePositions)\n sinTable = new Float32Array(probePositions)\n for (var i = 0; i < probePositions; i++) {\n cosTable[ i ] = Math.cos(theta)\n sinTable[ i ] = Math.sin(theta)\n theta += step\n }\n }\n\n function initializeHash () {\n hash = makeAVHash(x, y, z, r, min, max, 2.01 * maxRadius)\n neighbours = new Int32Array(hash.neighbourListLength)\n }\n\n function obscured (x: number, y: number, z: number, a: number, b: number) {\n // Is the point at x,y,z obscured by any of the atoms\n // specifeid by indices in neighbours. Ignore indices\n // a and b (these are the relevant atoms in projectPoints/Torii)\n\n // Cache the last clipped atom (as very often the same one in\n // subsequent calls)\n let ai: number\n\n if (lastClip !== -1) {\n ai = lastClip\n if (ai !== a && ai !== b && singleAtomObscures(ai, x, y, z)) {\n return ai\n } else {\n lastClip = -1\n }\n }\n\n var ni = 0\n ai = neighbours[ ni ]\n while (ai >= 0) {\n if (ai !== a && ai !== b && singleAtomObscures(ai, x, y, z)) {\n lastClip = ai\n return ai\n }\n ai = neighbours[ ++ni ]\n }\n\n lastClip = -1\n\n return -1\n }\n\n function singleAtomObscures (ai: number, x: number, y: number, z: number) {\n var ci = 3 * ai\n var ra2 = r2[ ai ]\n var dx = coordList[ ci ] - x\n var dy = coordList[ ci + 1 ] - y\n var dz = coordList[ ci + 2 ] - z\n var d2 = dx * dx + dy * dy + dz * dz\n\n return d2 < ra2\n }\n\n function projectPoints () {\n // For each atom:\n // Iterate over a subsection of the grid, for each point:\n // If current value < 0.0, unvisited, set positive\n //\n // In any case: Project this point onto surface of the atomic sphere\n // If this projected point is not obscured by any other atom\n // Calcualte delta distance and set grid value to minimum of\n // itself and delta\n\n // Should we alias frequently accessed closure variables??\n // Assume JS engine capable of optimizing this\n // anyway...\n\n for (var i = 0; i < nAtoms; i++) {\n var ax = x[ i ]\n var ay = y[ i ]\n var az = z[ i ]\n var ar = r[ i ]\n var ar2 = r2[ i ]\n\n hash.withinRadii(ax, ay, az, ar, neighbours)\n\n // Number of grid points, round this up...\n var ng = Math.ceil(ar * scaleFactor)\n\n // Center of the atom, mapped to grid points (take floor)\n var iax = Math.floor(scaleFactor * (ax - min[ 0 ]))\n var iay = Math.floor(scaleFactor * (ay - min[ 1 ]))\n var iaz = Math.floor(scaleFactor * (az - min[ 2 ]))\n\n // Extents of grid to consider for this atom\n var minx = Math.max(0, iax - ng)\n var miny = Math.max(0, iay - ng)\n var minz = Math.max(0, iaz - ng)\n\n // Add two to these points:\n // - iax are floor'd values so this ensures coverage\n // - these are loop limits (exclusive)\n var maxx = Math.min(dim[ 0 ], iax + ng + 2)\n var maxy = Math.min(dim[ 1 ], iay + ng + 2)\n var maxz = Math.min(dim[ 2 ], iaz + ng + 2)\n\n for (var ix = minx; ix < maxx; ix++) {\n var dx = gridx[ ix ] - ax\n var xoffset = dim[ 1 ] * dim[ 2 ] * ix\n\n for (var iy = miny; iy < maxy; iy++) {\n var dy = gridy[ iy ] - ay\n var dxy2 = dx * dx + dy * dy\n var xyoffset = xoffset + dim[ 2 ] * iy\n\n for (var iz = minz; iz < maxz; iz++) {\n var dz = gridz[ iz ] - az\n var d2 = dxy2 + dz * dz\n\n if (d2 < ar2) {\n var idx = iz + xyoffset\n\n if (grid[idx] < 0.0) {\n // Unvisited, make positive\n grid[ idx ] = -grid[ idx ]\n }\n // Project on to the surface of the sphere\n // sp is the projected point ( dx, dy, dz ) * ( ra / d )\n var d = Math.sqrt(d2)\n var ap = ar / d\n var spx = dx * ap\n var spy = dy * ap\n var spz = dz * ap\n\n spx += ax\n spy += ay\n spz += az\n\n if (obscured(spx, spy, spz, i, -1) === -1) {\n var dd = ar - d\n if (dd < grid[ idx ]) {\n grid[ idx ] = dd\n if (setAtomID) atomIndex[ idx ] = i\n }\n }\n }\n }\n }\n }\n }\n }\n\n function projectTorii () {\n for (var i = 0; i < nAtoms; i++) {\n hash.withinRadii(x[ i ], y[ i ], z[ i ], r[ i ], neighbours)\n var ia = 0\n var ni = neighbours[ ia ]\n while (ni >= 0) {\n if (i < ni) {\n projectTorus(i, ni)\n }\n ni = neighbours[ ++ia ]\n }\n }\n }\n\n function projectTorus (a: number, b: number) {\n var r1 = r[ a ]\n var r2 = r[ b ]\n var dx = atob[ 0 ] = x[ b ] - x[ a ]\n var dy = atob[ 1 ] = y[ b ] - y[ a ]\n var dz = atob[ 2 ] = z[ b ] - z[ a ]\n var d2 = dx * dx + dy * dy + dz * dz\n\n // This check now redundant as already done in AVHash.withinRadii\n // if( d2 > (( r1 + r2 ) * ( r1 + r2 )) ){ return; }\n\n var d = Math.sqrt(d2)\n\n // Find angle between a->b vector and the circle\n // of their intersection by cosine rule\n var cosA = (r1 * r1 + d * d - r2 * r2) / (2.0 * r1 * d)\n\n // distance along a->b at intersection\n var dmp = r1 * cosA\n\n v3normalize(atob, atob)\n\n // Create normal to line\n normalToLine(n1 as any, atob)\n v3normalize(n1, n1)\n\n // Cross together for second normal vector\n v3cross(n2, atob, n1)\n v3normalize(n2, n2)\n\n // r is radius of circle of intersection\n var rInt = Math.sqrt(r1 * r1 - dmp * dmp)\n\n v3multiplyScalar(n1, n1, rInt)\n v3multiplyScalar(n2, n2, rInt)\n v3multiplyScalar(atob, atob, dmp)\n\n mid[ 0 ] = atob[ 0 ] + x[ a ]\n mid[ 1 ] = atob[ 1 ] + y[ a ]\n mid[ 2 ] = atob[ 2 ] + z[ a ]\n\n lastClip = -1\n\n var ng = ngTorus\n\n for (var i = 0; i < probePositions; i++) {\n var cost = cosTable[ i ]\n var sint = sinTable[ i ]\n\n var px = mid[ 0 ] + cost * n1[ 0 ] + sint * n2[ 0 ]\n var py = mid[ 1 ] + cost * n1[ 1 ] + sint * n2[ 1 ]\n var pz = mid[ 2 ] + cost * n1[ 2 ] + sint * n2[ 2 ]\n\n if (obscured(px, py, pz, a, b) === -1) {\n // As above, iterate over our grid...\n // px, py, pz in grid coords\n var iax = Math.floor(scaleFactor * (px - min[ 0 ]))\n var iay = Math.floor(scaleFactor * (py - min[ 1 ]))\n var iaz = Math.floor(scaleFactor * (pz - min[ 2 ]))\n\n var minx = Math.max(0, iax - ng)\n var miny = Math.max(0, iay - ng)\n var minz = Math.max(0, iaz - ng)\n\n var maxx = Math.min(dim[ 0 ], iax + ng + 2)\n var maxy = Math.min(dim[ 1 ], iay + ng + 2)\n var maxz = Math.min(dim[ 2 ], iaz + ng + 2)\n\n for (var ix = minx; ix < maxx; ix++) {\n dx = px - gridx[ ix ]\n var xoffset = dim[ 1 ] * dim[ 2 ] * ix\n\n for (var iy = miny; iy < maxy; iy++) {\n dy = py - gridy[ iy ]\n var dxy2 = dx * dx + dy * dy\n var xyoffset = xoffset + dim[ 2 ] * iy\n\n for (var iz = minz; iz < maxz; iz++) {\n dz = pz - gridz[ iz ]\n d2 = dxy2 + dz * dz\n var idx = iz + xyoffset\n var current = grid[ idx ]\n\n if (current > 0.0 && d2 < (current * current)) {\n grid[ idx ] = Math.sqrt(d2)\n if (setAtomID) {\n // Is this grid point closer to a or b?\n // Take dot product of atob and gridpoint->p (dx, dy, dz)\n const dp = dx * atob[ 0 ] + dy * atob [ 1 ] + dz * atob[ 2 ]\n atomIndex[ idx ] = dp < 0.0 ? b : a\n }\n }\n }\n }\n }\n }\n }\n }\n\n function normalToLine (out: Int32Array, p: Float32Array) {\n out[ 0 ] = out[ 1 ] = out[ 2 ] = 1.0\n if (p[ 0 ] !== 0) {\n out[ 0 ] = (p[ 1 ] + p[ 2 ]) / -p[ 0 ]\n } else if (p[ 1 ] !== 0) {\n out[ 1 ] = (p[ 0 ] + p[ 2 ]) / -p[ 1 ]\n } else if (p[ 2 ] !== 0) {\n out[ 2 ] = (p[ 0 ] + p[ 1 ]) / -p[ 2 ]\n }\n return out\n }\n\n function fixNegatives () {\n for (var i = 0; i < grid.length; i++) {\n if (grid[ i ] < 0) grid[ i ] = 0\n }\n }\n\n function fixAtomIDs () {\n for (var i = 0; i < atomIndex.length; i++) {\n atomIndex[ i ] = indexList[ atomIndex[ i ] ]\n }\n }\n\n function getVolume (probeRadius: number, scaleFactor: number, setAtomID: boolean) {\n // Basic steps are:\n // 1) Initialize\n // 2) Project points\n // 3) Project torii\n\n console.time('AVSurface.getVolume')\n\n console.time('AVSurface.init')\n init(probeRadius, scaleFactor, setAtomID)\n console.timeEnd('AVSurface.init')\n\n console.time('AVSurface.projectPoints')\n projectPoints()\n console.timeEnd('AVSurface.projectPoints')\n\n console.time('AVSurface.projectTorii')\n projectTorii()\n console.timeEnd('AVSurface.projectTorii')\n fixNegatives()\n fixAtomIDs()\n\n console.timeEnd('AVSurface.getVolume')\n }\n\n this.getSurface = function (type: string, probeRadius: number, scaleFactor: number, cutoff: number, setAtomID: boolean, smooth: number, contour: boolean) {\n // type and cutoff left in for compatibility with EDTSurface.getSurface\n // function signature\n\n getVolume(probeRadius, scaleFactor, setAtomID)\n\n var volsurf = new (VolumeSurface as any)(\n grid, dim[ 2 ], dim[ 1 ], dim[ 0 ], atomIndex\n ) as VolumeSurface\n\n return volsurf.getSurface!(probeRadius, false, undefined, matrix, contour)\n }\n}\nObject.assign(AVSurface, {__deps: [\n getSurfaceGrid, VolumeSurface, uniformArray, computeBoundingBox,\n v3multiplyScalar, v3cross, v3normalize,\n makeAVHash,\n defaults\n]})\n\nexport { AVSurface, makeAVHash }\n","/**\n * @file Molecular Surface\n * @author Alexander Rose \n * @private\n */\n\nimport { WorkerRegistry } from '../globals'\nimport { defaults } from '../utils'\nimport Worker from '../worker/worker'\nimport EDTSurface from './edt-surface'\nimport { AVSurface } from './av-surface'\nimport Surface, { SurfaceData } from './surface'\nimport { Structure } from '../ngl';\nimport { AtomData, RadiusParams } from '../structure/structure-data';\n\nWorkerRegistry.add('molsurf', function func (e: any, callback: (data: any, buffers: any[])=> void) {\n const a = e.data.args\n const p = e.data.params\n if (a && p) {\n const SurfClass = (p.type === 'av') ? AVSurface : EDTSurface\n const surf = new (SurfClass as any)(a.coordList, a.radiusList, a.indexList) as AVSurface|EDTSurface\n const sd = surf.getSurface(\n p.type, p.probeRadius, p.scaleFactor, p.cutoff, true, p.smooth, p.contour\n ) as SurfaceData\n const transferList = [ sd.position.buffer, sd.index!.buffer ]\n if (sd.normal) transferList.push(sd.normal.buffer)\n if (sd.atomindex) transferList.push(sd.atomindex.buffer)\n const data = {\n sd: sd,\n p: p\n }\n callback(data, transferList)\n }\n}, [ EDTSurface, AVSurface ])\n\n/**\n * Molecular surface parameter object.\n * @typedef {Object} MolecularSurfaceParameters - stage parameters\n * @property {String} type - \"av\" or \"edt\"\n * @property {Number} probeRadius - probe radius\n * @property {Number} scaleFactor - higher for better quality\n * @property {Integer} smooth - number of smoothing cycles to apply\n * @property {String} name - name for created surface\n */\nexport interface MolecularSurfaceParameters {\n type: 'av'|'edt'\n probeRadius: number\n scaleFactor: number\n smooth: number\n name: string\n cutoff: number\n contour: boolean,\n radiusParams: RadiusParams\n}\n/**\n * Create Molecular surfaces\n */\nclass MolecularSurface {\n structure: Structure\n worker: Worker|undefined\n\n constructor (structure: Structure) {\n this.structure = structure\n }\n\n _getAtomData (params: Partial): AtomData {\n return this.structure.getAtomData({\n what: { position: true, radius: true, index: true },\n radiusParams: defaults(params.radiusParams, {\n type: 'vdw', scale: 1.0\n })\n })\n }\n\n _makeSurface (sd: SurfaceData, p: Partial) {\n var surface = new Surface(p.name!, '', sd)\n\n surface.info.type = p.type\n surface.info.probeRadius = p.probeRadius\n surface.info.scaleFactor = p.scaleFactor\n surface.info.smooth = p.smooth\n surface.info.cutoff = p.cutoff\n\n return surface\n }\n\n /**\n * Get molecular surface\n * @param {MolecularSurfaceParameters} params - parameters for surface creation\n * @return {Surface} the surface\n */\n getSurface (params: Partial) {\n const p = params || {}\n\n const atomData = this._getAtomData(params)\n const coordList = atomData.position\n const radiusList = atomData.radius\n const indexList = atomData.index\n\n const SurfClass = (p.type === 'av') ? AVSurface : EDTSurface\n const surf = new (SurfClass as any)(coordList, radiusList, indexList) as AVSurface|EDTSurface\n const sd = surf.getSurface(\n p.type!, p.probeRadius!, p.scaleFactor!, p.cutoff!, true, p.smooth!, p.contour!\n )\n\n return this._makeSurface(sd, p)\n }\n\n /**\n * Get molecular surface asynchronous\n * @param {MolecularSurfaceParameters} params - parameters for surface creation\n * @param {function(surface: Surface)} callback - function to be called after surface is created\n * @return {undefined}\n */\n getSurfaceWorker (params: MolecularSurfaceParameters, callback: (s: Surface) => void) {\n const p = Object.assign({}, params)\n\n if (window.hasOwnProperty('Worker')) {\n if (this.worker === undefined) {\n this.worker = new Worker('molsurf')\n }\n\n const atomData = this._getAtomData(params)\n const coordList = atomData.position\n const radiusList = atomData.radius\n const indexList = atomData.index\n\n const msg = {\n args: {\n coordList: coordList,\n radiusList: radiusList,\n indexList: indexList\n },\n params: p\n }\n\n const transferList = [\n coordList!.buffer, radiusList!.buffer, indexList!.buffer\n ]\n\n this.worker.post(msg, transferList,\n\n (e: any) => {\n callback(this._makeSurface(e.data.sd, p))\n },\n\n (e: string) => {\n console.warn(\n 'MolecularSurface.getSurfaceWorker error - trying without worker', e\n )\n this.worker!.terminate()\n this.worker = undefined\n const surface = this.getSurface(p)\n callback(surface)\n }\n\n )\n } else {\n const surface = this.getSurface(p)\n callback(surface)\n }\n }\n\n /**\n * Cleanup\n * @return {undefined}\n */\n dispose () {\n if (this.worker) this.worker.terminate()\n }\n}\n\nexport default MolecularSurface\n","/**\n * @file Molecular Surface Representation\n * @author Alexander Rose \n * @private\n */\n\nimport { RepresentationRegistry } from '../globals'\nimport { defaults } from '../utils'\nimport StructureRepresentation, { StructureRepresentationParameters, StructureRepresentationData } from './structure-representation'\nimport MolecularSurface, { MolecularSurfaceParameters } from '../surface/molecular-surface'\nimport SurfaceBuffer from '../buffer/surface-buffer'\nimport ContourBuffer from '../buffer/contour-buffer'\nimport DoubleSidedBuffer from '../buffer/doublesided-buffer'\nimport Selection from '../selection/selection'\nimport Viewer from '../viewer/viewer';\n// @ts-ignore: unused import Volume required for declaration only\nimport { Structure, Vector3, Volume } from '../ngl';\nimport StructureView from '../structure/structure-view';\nimport { SurfaceDataFields } from './surface-representation';\nimport Surface, {SurfaceData} from '../surface/surface';\n\nexport interface MolecularSurfaceRepresentationParameters extends StructureRepresentationParameters {\n surfaceType: 'vws'|'sas'|'ms'|'ses'|'av'\n probeRadius: number\n smooth: number\n scaleFactor: number\n cutoff: number\n contour: boolean\n background: boolean\n opaqueBack: boolean\n filterSele: string\n colorVolume: any\n useWorker: boolean\n}\n\nexport interface MolecularSurfaceInfo {\n molsurf?: MolecularSurface\n sele?: string\n surface?: Surface\n}\n\n/**\n * Molecular Surface Representation\n */\nclass MolecularSurfaceRepresentation extends StructureRepresentation {\n protected surfaceType: 'vws'|'sas'|'ms'|'ses'|'av'\n protected probeRadius: number\n protected smooth: number\n protected scaleFactor: number\n protected cutoff: number\n protected contour: boolean\n protected background: boolean\n protected opaqueBack: boolean\n protected filterSele: string\n protected colorVolume: any\n protected useWorker: boolean\n\n protected __infoList: MolecularSurfaceInfo[]\n protected __forceNewMolsurf: boolean\n protected __sele: string\n protected __surfaceParams: string\n\n constructor (structure: Structure, viewer: Viewer, params: Partial) {\n super(structure, viewer, params)\n\n this.type = 'surface'\n\n this.parameters = Object.assign({\n\n surfaceType: {\n type: 'select',\n rebuild: true,\n options: {\n 'vws': 'vws',\n 'sas': 'sas',\n 'ms': 'ms',\n 'ses': 'ses',\n 'av': 'av'\n }\n },\n probeRadius: {\n type: 'number',\n precision: 1,\n max: 20,\n min: 0,\n rebuild: true\n },\n smooth: {\n type: 'integer',\n precision: 1,\n max: 10,\n min: 0,\n rebuild: true\n },\n scaleFactor: {\n type: 'number',\n precision: 1,\n max: 5,\n min: 0,\n rebuild: true\n },\n cutoff: {\n type: 'number',\n precision: 2,\n max: 50,\n min: 0,\n rebuild: true\n },\n contour: {\n type: 'boolean', rebuild: true\n },\n background: {\n type: 'boolean', rebuild: true // FIXME\n },\n opaqueBack: {\n type: 'boolean', buffer: true\n },\n filterSele: {\n type: 'text', rebuild: true\n },\n colorVolume: {\n type: 'hidden'\n },\n useWorker: {\n type: 'boolean', rebuild: true\n }\n\n }, this.parameters, {\n\n radius: null,\n scale: null\n\n })\n\n this.__infoList = []\n\n // TODO find a more direct way\n this.structure.signals.refreshed.add(() => {\n this.__forceNewMolsurf = true\n })\n\n this.toBePrepared = true\n\n this.init(params)\n }\n\n init (params: Partial) {\n const p = params || {}\n p.colorScheme = defaults(p.colorScheme, 'uniform')\n p.colorValue = defaults(p.colorValue, 0xDDDDDD)\n p.disablePicking = defaults(p.disablePicking, true)\n\n this.surfaceType = defaults(p.surfaceType, 'ms')\n this.probeRadius = defaults(p.probeRadius, 1.4)\n this.smooth = defaults(p.smooth, 2)\n this.scaleFactor = defaults(p.scaleFactor, 2.0)\n this.cutoff = defaults(p.cutoff, 0.0)\n this.contour = defaults(p.contour, false)\n this.background = defaults(p.background, false)\n this.opaqueBack = defaults(p.opaqueBack, true)\n this.filterSele = defaults(p.filterSele, '')\n this.colorVolume = defaults(p.colorVolume, undefined)\n this.useWorker = defaults(p.useWorker, true)\n\n super.init(params)\n }\n\n prepareData (sview: StructureView, i: number, callback: (i: number) => void) {\n let info: MolecularSurfaceInfo = this.__infoList[ i ]\n if (!info) {\n info = {}\n this.__infoList[ i ] = info\n }\n\n if (!info.molsurf || info.sele !== sview.selection.string) {\n if (this.filterSele) {\n const sviewFilter = sview.structure.getView(new Selection(this.filterSele))\n const bbSize = sviewFilter.boundingBox.getSize(new Vector3())\n const maxDim = Math.max(bbSize.x, bbSize.y, bbSize.z)\n const asWithin = sview.getAtomSetWithinPoint(sviewFilter.center, (maxDim / 2) + 6.0)\n sview = sview.getView(\n new Selection(sview.getAtomSetWithinSelection(asWithin, 3).toSeleString())\n )\n if (sview.atomCount === 0) {\n callback(i)\n return\n }\n }\n\n info.sele = sview.selection.string\n info.molsurf = new MolecularSurface(sview)\n\n const p = this.getSurfaceParams()\n const onSurfaceFinish = (surface: Surface) => {\n info.surface = surface\n callback(i)\n }\n\n if (this.useWorker) {\n info.molsurf.getSurfaceWorker(p as MolecularSurfaceParameters, onSurfaceFinish)\n } else {\n onSurfaceFinish(info.molsurf.getSurface(p as {name: string, type: 'av'|'edt' } & MolecularSurfaceRepresentationParameters))\n }\n } else {\n callback(i)\n }\n }\n\n prepare (callback: () => void) {\n if (this.__forceNewMolsurf || this.__sele !== this.selection.string ||\n this.__surfaceParams !== JSON.stringify(this.getSurfaceParams())) {\n this.__infoList.forEach((info: MolecularSurfaceInfo) => {\n if (info && info.molsurf) {\n info.molsurf.dispose()\n }\n })\n this.__infoList.length = 0\n }\n\n if (this.structureView.atomCount === 0) {\n callback()\n return\n }\n\n const after = () => {\n this.__sele = this.selection.string\n this.__surfaceParams = JSON.stringify(this.getSurfaceParams())\n this.__forceNewMolsurf = false\n callback()\n }\n\n const name = this.assembly === 'default' ? this.defaultAssembly : this.assembly\n const assembly = this.structure.biomolDict[ name ]\n\n if (assembly) {\n assembly.partList.forEach((part, i) => {\n const sview = part.getView(this.structureView)\n this.prepareData(sview as StructureView, i, (_i) => {\n if (_i === assembly.partList.length - 1) after()\n })\n })\n } else {\n this.prepareData(this.structureView, 0, after)\n }\n }\n\n createData (sview: StructureView, i: number) {\n const info = this.__infoList[ i ]\n const surface = info.surface\n\n if (!surface) {\n // Surface creation bailed (no surface generated for this sview)\n return\n }\n\n const surfaceData = {\n position: surface!.getPosition(),\n color: surface!.getColor(this.getColorParams()),\n index: surface!.getFilteredIndex(this.filterSele, sview)\n }\n\n const bufferList = []\n\n if (surface.contour) {\n const contourBuffer = new ContourBuffer(\n surfaceData,\n this.getBufferParams({\n wireframe: false\n })\n )\n\n bufferList.push(contourBuffer)\n } else {\n Object.assign(surfaceData, {\n normal: surface.getNormal(),\n picking: surface.getPicking(sview.getStructure())\n })\n\n const surfaceBuffer = new SurfaceBuffer(\n surfaceData,\n this.getBufferParams({\n background: this.background,\n opaqueBack: this.opaqueBack,\n dullInterior: false\n })\n )\n\n if (this.getBufferParams().side == 'double') {\n const doubleSidedBuffer = new DoubleSidedBuffer(surfaceBuffer)\n bufferList.push(doubleSidedBuffer)\n }\n else {\n bufferList.push(surfaceBuffer)\n }\n }\n\n return { bufferList, info } as StructureRepresentationData\n }\n\n updateData (what: SurfaceDataFields, data: StructureRepresentationData) {\n const surfaceData: Partial = {}\n\n if (what.position || what.radius) {\n this.__forceNewMolsurf = true\n this.build()\n return\n }\n\n if (what.color) {\n surfaceData.color = data.info.surface.getColor(this.getColorParams())\n }\n\n if (what.index) {\n surfaceData.index = data.info.surface.getFilteredIndex(this.filterSele, data.sview)\n }\n\n data.bufferList[ 0 ].setAttributes(surfaceData)\n }\n\n setParameters (params: Partial, what: Partial = {}, rebuild?: boolean) {\n if (params && params.filterSele) {\n what.index = true\n }\n\n if (params && params.colorVolume !== undefined) {\n what.color = true\n }\n\n // forbid setting wireframe to true when contour is true\n if (params && params.wireframe && (\n params.contour || (params.contour === undefined && this.contour)\n )\n ) {\n params.wireframe = false\n }\n\n super.setParameters(params, what, rebuild)\n\n return this\n }\n\n getSurfaceParams (params: Partial = {}) {\n const p = Object.assign({\n type: this.surfaceType as string,\n probeRadius: this.probeRadius as number,\n scaleFactor: this.scaleFactor as number,\n smooth: this.smooth && !this.contour,\n cutoff: this.cutoff as number,\n contour: this.contour as boolean,\n useWorker: this.useWorker as boolean,\n radiusParams: this.getRadiusParams()\n }, params)\n\n return p\n }\n\n getColorParams () {\n const p = super.getColorParams()\n\n p.volume = this.colorVolume\n\n return p\n }\n\n getAtomRadius () {\n return 0\n }\n\n clear () {\n super.clear()\n }\n\n dispose () {\n this.__infoList.forEach((info: MolecularSurfaceInfo) => {\n if (info && info.molsurf) {\n info.molsurf.dispose()\n }\n })\n this.__infoList.length = 0\n\n super.dispose()\n }\n}\n\nRepresentationRegistry.add('surface', MolecularSurfaceRepresentation)\n\nexport default MolecularSurfaceRepresentation\n","/**\n * @file Point Representation\n * @author Alexander Rose \n * @private\n */\n\nimport { RepresentationRegistry } from '../globals'\nimport { defaults } from '../utils'\nimport StructureRepresentation, { StructureRepresentationParameters, StructureRepresentationData } from './structure-representation'\nimport PointBuffer from '../buffer/point-buffer'\nimport { Structure } from '../ngl';\nimport Viewer from '../viewer/viewer';\nimport StructureView from '../structure/structure-view';\nimport { AtomDataFields } from '../structure/structure-data';\n\nexport interface PointRepresentationParameters extends StructureRepresentationParameters {\n pointSize: number\n sizeAttenuation: boolean\n sortParticles: boolean\n useTexture: boolean\n alphaTest: number\n forceTransparent: boolean\n edgeBleach: number\n}\n\n/**\n * Point Representation\n */\nclass PointRepresentation extends StructureRepresentation {\n protected pointSize: number\n protected sizeAttenuation: boolean\n protected sortParticles: boolean\n protected useTexture: boolean\n protected alphaTest: number\n protected forceTransparent: boolean\n protected edgeBleach: number\n\n constructor (structure: Structure, viewer: Viewer, params: Partial) {\n super(structure, viewer, params)\n\n this.type = 'point'\n\n this.parameters = Object.assign({\n\n pointSize: {\n type: 'number', precision: 1, max: 100, min: 0, buffer: true\n },\n sizeAttenuation: {\n type: 'boolean', buffer: true\n },\n sortParticles: {\n type: 'boolean', rebuild: true\n },\n useTexture: {\n type: 'boolean', buffer: true\n },\n alphaTest: {\n type: 'range', step: 0.001, max: 1, min: 0, buffer: true\n },\n forceTransparent: {\n type: 'boolean', buffer: true\n },\n edgeBleach: {\n type: 'range', step: 0.001, max: 1, min: 0, buffer: true\n }\n\n }, this.parameters, {\n\n flatShaded: null,\n wireframe: null,\n linewidth: null,\n side: null,\n\n roughness: null,\n metalness: null\n\n })\n\n this.init(params)\n }\n\n init (params: Partial) {\n var p = params || {}\n\n this.pointSize = defaults(p.pointSize, 1)\n this.sizeAttenuation = defaults(p.sizeAttenuation, true)\n this.sortParticles = defaults(p.sortParticles, false)\n this.useTexture = defaults(p.useTexture, false)\n this.alphaTest = defaults(p.alphaTest, 0.5)\n this.forceTransparent = defaults(p.forceTransparent, false)\n this.edgeBleach = defaults(p.edgeBleach, 0.0)\n\n super.init(p)\n }\n\n createData (sview: StructureView) {\n var what = { position: true, color: true, picking: true }\n var atomData = sview.getAtomData(this.getAtomParams(what))\n\n var pointBuffer = new PointBuffer(\n atomData,\n this.getBufferParams({\n pointSize: this.pointSize,\n sizeAttenuation: this.sizeAttenuation,\n sortParticles: this.sortParticles,\n useTexture: this.useTexture,\n alphaTest: this.alphaTest,\n forceTransparent: this.forceTransparent,\n edgeBleach: this.edgeBleach\n })\n )\n\n return {\n bufferList: [ pointBuffer ]\n }\n }\n\n updateData (what: AtomDataFields, data: StructureRepresentationData) {\n var atomData = data.sview!.getAtomData(this.getAtomParams(what))\n var pointData = {}\n\n if (!what || what.position) {\n Object.assign(pointData, {position: atomData.position})\n }\n\n if (!what || what.color) {\n Object.assign(pointData, {color: atomData.color})\n }\n\n data.bufferList[ 0 ].setAttributes(pointData)\n }\n\n getAtomRadius () {\n return 0.1\n }\n}\n\nRepresentationRegistry.add('point', PointRepresentation)\n\nexport default PointRepresentation\n","/**\n * @file Ribbon Buffer\n * @author Alexander Rose \n * @private\n */\n\nimport '../shader/Ribbon.vert'\n\nimport { getUintArray } from '../utils'\nimport { serialArray } from '../math/array-utils'\nimport MeshBuffer from './mesh-buffer'\nimport { BufferParameters, BufferData } from './buffer'\nimport {Log} from \"../globals\";\n\nconst quadIndices = new Uint16Array([\n 0, 1, 2,\n 1, 3, 2\n])\n\nexport interface RibbonBufferData extends BufferData {\n normal: Float32Array\n dir: Float32Array\n size: Float32Array\n}\n\nfunction getSize(data: RibbonBufferData){\n const n = (data.position!.length / 3) - 1\n const n4 = n * 4\n const x = n4 * 3\n return x\n}\n\n/**\n * Ribbon buffer. Draws a thin ribbon.\n */\nclass RibbonBuffer extends MeshBuffer {\n vertexShader = 'Ribbon.vert'\n\n /**\n * @param {Object} data - attribute object\n * @param {Float32Array} data.position - positions\n * @param {Float32Array} data.normal - normals\n * @param {Float32Array} data.dir - binormals\n * @param {Float32Array} data.color - colors\n * @param {Float32Array} data.size - sizes\n * @param {Picker} data.picking - picking ids\n * @param {BufferParameters} params - parameter object\n */\n constructor (data: RibbonBufferData, params: Partial = {}) {\n super({\n position: new Float32Array(getSize(data)),\n color: new Float32Array(getSize(data)),\n index: getUintArray(getSize(data), getSize(data) / 3),\n normal: new Float32Array(getSize(data)),\n picking: data.picking\n }, params)\n\n const n = (data.position!.length / 3) - 1\n const n4 = n * 4\n const x = n4 * 3\n\n this.addAttributes({\n 'dir': { type: 'v3', value: new Float32Array(x) }\n })\n this.addAttributes({\n 'size': { type: 'f', value: new Float32Array(n4) }\n })\n\n data.primitiveId = serialArray(n)\n this.setAttributes(data)\n\n this.makeIndex()\n }\n\n setAttributes (data: Partial = {}) {\n const n4 = this.size\n const n = n4 / 4\n\n const attributes = this.geometry.attributes as any // TODO\n\n let position, normal, size, dir, color, primitiveId\n let aPosition, aNormal, aSize, aDir, aColor, aPrimitiveId\n\n if (data.position) {\n position = data.position\n aPosition = attributes.position.array\n attributes.position.needsUpdate = true\n }\n\n if (data.normal) {\n normal = data.normal\n aNormal = attributes.normal.array\n attributes.normal.needsUpdate = true\n }\n\n if (data.size) {\n size = data.size\n aSize = attributes.size.array\n attributes.size.needsUpdate = true\n }\n\n if (data.dir) {\n dir = data.dir\n aDir = attributes.dir.array\n attributes.dir.needsUpdate = true\n }\n\n if (data.color) {\n color = data.color\n aColor = attributes.color.array\n attributes.color.needsUpdate = true\n }\n\n if (data.primitiveId) {\n primitiveId = data.primitiveId\n aPrimitiveId = attributes.primitiveId.array\n attributes.primitiveId.needsUpdate = true\n }\n\n let v, i, k, p, l, v3\n let currSize\n let prevSize = size ? size[ 0 ] : null\n\n for (v = 0; v < n; ++v) {\n v3 = v * 3\n k = v * 3 * 4\n l = v * 4\n\n if (position) {\n aPosition[ k ] = aPosition[ k + 3 ] = position[ v3 ]\n aPosition[ k + 1 ] = aPosition[ k + 4 ] = position[ v3 + 1 ]\n aPosition[ k + 2 ] = aPosition[ k + 5 ] = position[ v3 + 2 ]\n\n aPosition[ k + 6 ] = aPosition[ k + 9 ] = position[ v3 + 3 ]\n aPosition[ k + 7 ] = aPosition[ k + 10 ] = position[ v3 + 4 ]\n aPosition[ k + 8 ] = aPosition[ k + 11 ] = position[ v3 + 5 ]\n }\n\n if (normal) {\n aNormal[ k ] = aNormal[ k + 3 ] = -normal[ v3 ]\n aNormal[ k + 1 ] = aNormal[ k + 4 ] = -normal[ v3 + 1 ]\n aNormal[ k + 2 ] = aNormal[ k + 5 ] = -normal[ v3 + 2 ]\n\n aNormal[ k + 6 ] = aNormal[ k + 9 ] = -normal[ v3 + 3 ]\n aNormal[ k + 7 ] = aNormal[ k + 10 ] = -normal[ v3 + 4 ]\n aNormal[ k + 8 ] = aNormal[ k + 11 ] = -normal[ v3 + 5 ]\n }\n\n for (i = 0; i < 4; ++i) {\n p = k + 3 * i\n\n if (color) {\n aColor[ p ] = color[ v3 ]\n aColor[ p + 1 ] = color[ v3 + 1 ]\n aColor[ p + 2 ] = color[ v3 + 2 ]\n }\n\n if (primitiveId) {\n aPrimitiveId[ l + i ] = primitiveId[ v ]\n }\n }\n\n if (size) {\n currSize = size[ v ]\n\n if (prevSize !== size[ v ]) {\n aSize[ l ] = prevSize\n aSize[ l + 1 ] = prevSize\n aSize[ l + 2 ] = currSize\n aSize[ l + 3 ] = currSize\n } else {\n aSize[ l ] = currSize\n aSize[ l + 1 ] = currSize\n aSize[ l + 2 ] = currSize\n aSize[ l + 3 ] = currSize\n }\n\n prevSize = currSize\n }\n\n if (dir) {\n aDir[ k ] = dir[ v3 ]\n aDir[ k + 1 ] = dir[ v3 + 1 ]\n aDir[ k + 2 ] = dir[ v3 + 2 ]\n\n aDir[ k + 3 ] = -dir[ v3 ]\n aDir[ k + 4 ] = -dir[ v3 + 1 ]\n aDir[ k + 5 ] = -dir[ v3 + 2 ]\n\n aDir[ k + 6 ] = dir[ v3 + 3 ]\n aDir[ k + 7 ] = dir[ v3 + 4 ]\n aDir[ k + 8 ] = dir[ v3 + 5 ]\n\n aDir[ k + 9 ] = -dir[ v3 + 3 ]\n aDir[ k + 10 ] = -dir[ v3 + 4 ]\n aDir[ k + 11 ] = -dir[ v3 + 5 ]\n }\n }\n }\n\n makeIndex () {\n const index = this.geometry.getIndex()\n if (!index) { Log.error('Index is null'); return; }\n const meshIndex = index.array as Uint32Array|Uint16Array\n const n = meshIndex.length / 4 / 3\n\n for (let v = 0; v < n; ++v) {\n const ix = v * 6\n const it = v * 4\n\n meshIndex.set(quadIndices, ix)\n for (let s = 0; s < 6; ++s) {\n meshIndex[ ix + s ] += it\n }\n }\n }\n}\n\nexport default RibbonBuffer\n","/**\n * @file Ribbon Representation\n * @author Alexander Rose \n * @private\n */\n\nimport { RepresentationRegistry } from '../globals'\nimport { defaults } from '../utils'\nimport Spline, { SplineParameters } from '../geometry/spline'\nimport StructureRepresentation, { StructureRepresentationParameters } from './structure-representation'\nimport RibbonBuffer from '../buffer/ribbon-buffer'\nimport { Structure } from '../ngl';\nimport Viewer from '../viewer/viewer';\nimport AtomProxy from '../proxy/atom-proxy';\nimport StructureView from '../structure/structure-view';\nimport Polymer from '../proxy/polymer';\n\nexport interface RibbonRepresentationParameters extends StructureRepresentationParameters {\n subdiv: number\n tension: number\n smoothSheet: boolean\n}\n\n/**\n * Ribbon Representation\n */\nclass RibbonRepresentation extends StructureRepresentation {\n protected subdiv: number\n protected tension: number\n protected smoothSheet: boolean\n \n constructor (structure: Structure, viewer: Viewer, params: Partial) {\n super(structure, viewer, params)\n\n this.type = 'ribbon'\n\n this.parameters = Object.assign({\n\n subdiv: {\n type: 'integer', max: 50, min: 1, rebuild: true\n },\n tension: {\n type: 'number', precision: 1, max: 1.0, min: 0.1\n },\n smoothSheet: {\n type: 'boolean', rebuild: true\n }\n\n }, this.parameters, {\n\n side: null,\n wireframe: null,\n linewidth: null\n\n })\n\n this.init(params)\n }\n\n init (params: Partial) {\n var p = params || {}\n p.colorScheme = defaults(p.colorScheme, 'chainname')\n p.colorScale = defaults(p.colorScale, 'RdYlBu')\n p.radiusType = defaults(p.radiusType, 'sstruc')\n p.radiusScale = defaults(p.radiusScale, 4.0)\n\n if (p.quality === 'low') {\n this.subdiv = 3\n } else if (p.quality === 'medium') {\n this.subdiv = 6\n } else if (p.quality === 'high') {\n this.subdiv = 12\n } else {\n this.subdiv = defaults(p.subdiv, 6)\n }\n\n this.tension = defaults(p.tension, NaN)\n this.smoothSheet = defaults(p.smoothSheet, false)\n\n super.init(p)\n }\n\n getSplineParams (params?: Partial) {\n return Object.assign({\n subdiv: this.subdiv,\n tension: this.tension,\n directional: true,\n smoothSheet: this.smoothSheet\n }, params)\n }\n\n getAtomRadius (atom: AtomProxy) {\n return atom.isTrace() ? super.getAtomRadius(atom) : 0\n }\n\n createData (sview: StructureView) {\n var bufferList: RibbonBuffer[] = []\n var polymerList: Polymer[] = []\n\n this.structure.eachPolymer(polymer => {\n if (polymer.residueCount < 4) return\n polymerList.push(polymer)\n\n var spline = new Spline(polymer, this.getSplineParams())\n var subPos = spline.getSubdividedPosition()\n var subOri = spline.getSubdividedOrientation()\n var subCol = spline.getSubdividedColor(this.getColorParams())\n var subPick = spline.getSubdividedPicking()\n var subSize = spline.getSubdividedSize(this.getRadiusParams())\n\n bufferList.push(\n new RibbonBuffer(\n ({\n position: subPos.position,\n normal: subOri.binormal,\n dir: subOri.normal,\n color: subCol.color,\n size: subSize.size,\n picking: subPick.picking\n }),\n this.getBufferParams()\n )\n )\n }, sview.getSelection())\n\n return {\n bufferList: bufferList,\n polymerList: polymerList\n }\n }\n\n updateData (what: {position?: boolean, radius?: boolean, scale?: boolean, color?: boolean}, data: {polymerList: Polymer[], bufferList: RibbonBuffer[]}) {\n what = what || {}\n\n var i = 0\n var n = data.polymerList.length\n\n for (i = 0; i < n; ++i) {\n var bufferData = {}\n var spline = new Spline(data.polymerList[ i ], this.getSplineParams())\n\n if (what.position) {\n var subPos = spline.getSubdividedPosition()\n var subOri = spline.getSubdividedOrientation()\n Object.assign(bufferData, {\n position: subPos.position,\n normal: subOri.binormal,\n dir: subOri.normal\n })\n }\n\n if (what.radius || what.scale) {\n var subSize = spline.getSubdividedSize(this.getRadiusParams())\n Object.assign(bufferData, {size: subSize.size})\n }\n\n if (what.color) {\n var subCol = spline.getSubdividedColor(this.getColorParams())\n Object.assign(bufferData, {color: subCol.color})\n }\n\n data.bufferList[ i ].setAttributes(bufferData)\n }\n }\n\n setParameters (params: Partial) {\n var rebuild = false\n var what = {}\n\n if (params && params.tension) {\n Object.assign(what, {position: true})\n }\n\n super.setParameters(params, what, rebuild)\n\n return this\n }\n}\n\nRepresentationRegistry.add('ribbon', RibbonRepresentation)\n\nexport default RibbonRepresentation\n","/**\n * @file Rocket Representation\n * @author Alexander Rose \n * @private\n */\n\nimport { RepresentationRegistry } from '../globals'\nimport { defaults } from '../utils'\nimport { AtomPicker } from '../utils/picker'\nimport StructureRepresentation, { StructureRepresentationParameters } from './structure-representation'\nimport Helixbundle, { Axis } from '../geometry/helixbundle'\nimport CylinderBuffer from '../buffer/cylinder-buffer'\nimport { Structure } from '../ngl';\nimport Viewer from '../viewer/viewer';\nimport StructureView from '../structure/structure-view';\nimport CylinderGeometryBuffer from '../buffer/cylindergeometry-buffer';\nimport CylinderImpostorBuffer from '../buffer/cylinderimpostor-buffer';\n\nexport interface RocketRepresentationParameters extends StructureRepresentationParameters {\n localAngle: number\n centerDist: number\n ssBorder: boolean\n radialSegments: number\n openEnded: boolean\n disableImpostor: boolean\n}\n\nexport interface AxisData {\n begin: Float32Array\n end: Float32Array\n size: Float32Array\n color: Float32Array\n picking: AtomPicker\n}\n\n/**\n * Rocket Representation\n */\nclass RocketRepresentation extends StructureRepresentation {\n\n protected localAngle: number\n protected centerDist: number\n protected ssBorder: boolean\n protected radialSegments: number\n protected openEnded: boolean\n protected disableImpostor: boolean\n // protected helixbundleList: Helixbundle[]\n\n constructor (structure: Structure, viewer: Viewer, params: Partial) {\n super(structure, viewer, params)\n\n this.type = 'rocket'\n\n this.parameters = Object.assign({\n\n localAngle: {\n type: 'integer', max: 180, min: 0, rebuild: true\n },\n centerDist: {\n type: 'number', precision: 1, max: 10, min: 0, rebuild: true\n },\n ssBorder: {\n type: 'boolean', rebuild: true\n },\n radialSegments: true,\n openEnded: true,\n disableImpostor: true\n\n }, this.parameters)\n\n // this.helixbundleList = []\n\n this.init(params)\n }\n\n init (params: Partial) {\n let p = params || {}\n p.colorScheme = defaults(p.colorScheme, 'sstruc')\n p.radiusSize = defaults(p.radiusSize, 1.5)\n p.radiusScale = defaults(p.radiusScale, 1.0)\n p.openEnded = defaults(p.openEnded, false)\n p.useInteriorColor = defaults(p.useInteriorColor, true)\n\n this.localAngle = defaults(p.localAngle, 30)\n this.centerDist = defaults(p.centerDist, 2.5)\n this.ssBorder = defaults(p.ssBorder, false)\n\n super.init(p)\n }\n\n createData (sview: StructureView) {\n let length = 0\n const axisList:Axis[] = []\n const helixbundleList:Helixbundle[] = []\n\n this.structure.eachPolymer(polymer => {\n if (polymer.residueCount < 4 || polymer.isNucleic()) return\n\n const helixbundle = new Helixbundle(polymer)\n const axis = helixbundle.getAxis(\n this.localAngle, this.centerDist, this.ssBorder,\n this.getColorParams(), this.getRadiusParams()\n )\n\n length += axis.size.length\n axisList.push(axis)\n helixbundleList.push(helixbundle)\n }, sview.getSelection())\n\n const axisData = {\n begin: new Float32Array(length * 3),\n end: new Float32Array(length * 3),\n size: new Float32Array(length),\n color: new Float32Array(length * 3),\n picking: {}\n }\n\n let picking = new Float32Array(length)\n\n let offset = 0\n\n axisList.forEach(function (axis) {\n axisData.begin.set(axis.begin, offset * 3)\n axisData.end.set(axis.end, offset * 3)\n axisData.size.set(axis.size, offset)\n axisData.color.set(axis.color, offset * 3)\n picking.set(axis.picking.array!, offset)\n offset += axis.size.length\n })\n\n if (length) {\n axisData.picking = new AtomPicker(\n picking, sview.getStructure()\n )\n }\n\n const cylinderBuffer = new CylinderBuffer(\n {\n position1: axisData.begin,\n position2: axisData.end,\n color: axisData.color,\n color2: axisData.color,\n radius: axisData.size,\n picking: axisData.picking\n },\n this.getBufferParams({\n openEnded: this.openEnded,\n radialSegments: this.radialSegments,\n disableImpostor: this.disableImpostor,\n dullInterior: true\n })\n )\n\n return {\n bufferList: [ cylinderBuffer as CylinderGeometryBuffer|CylinderImpostorBuffer ],\n axisList: axisList,\n helixbundleList: helixbundleList,\n axisData: axisData\n }\n }\n\n \n updateData (what: any, data: {bufferList: CylinderBuffer[], helixbundleList: Helixbundle[], axisList: Axis[], axisData: AxisData}) {\n what = what || {}\n\n if (what.position) {\n this.build()\n return\n }\n\n var cylinderData = {}\n\n if (what.color || what.radius) {\n var offset = 0\n\n data.helixbundleList.forEach((helixbundle) => {\n var axis = helixbundle.getAxis(\n this.localAngle, this.centerDist, this.ssBorder,\n this.getColorParams(), this.getRadiusParams()\n )\n if (what.color) {\n data.axisData.color.set(axis.color, offset * 3)\n }\n if (what.radius || what.scale) {\n data.axisData.size.set(axis.size, offset)\n }\n offset += axis.size.length\n })\n\n if (what.color) {\n Object.assign(cylinderData, {\n color: data.axisData.color,\n color2: data.axisData.color\n })\n }\n\n if (what.radius || what.scale) {\n Object.assign(cylinderData, {\n radius: data.axisData.size\n })\n }\n }\n\n (data.bufferList[ 0 ] as CylinderGeometryBuffer).setAttributes(cylinderData)\n }\n}\n\nRepresentationRegistry.add('rocket', RocketRepresentation)\n\nexport default RocketRepresentation\n","/**\n * @file Rope Representation\n * @author Alexander Rose \n * @private\n */\n\nimport { RepresentationRegistry } from '../globals'\nimport { defaults } from '../utils'\nimport CartoonRepresentation, { CartoonRepresentationParameters } from './cartoon-representation'\nimport Helixorient from '../geometry/helixorient'\nimport Spline from '../geometry/spline'\nimport { Structure } from '../ngl';\nimport Viewer from '../viewer/viewer';\nimport Polymer from '../proxy/polymer';\n\n/**\n * Rope Representation\n */\nclass RopeRepresentation extends CartoonRepresentation {\n protected smooth: number\n \n constructor (structure: Structure, viewer: Viewer, params: Partial&{smooth: number}) {\n super(structure, viewer, params)\n\n this.type = 'rope'\n\n this.parameters = Object.assign({\n\n smooth: {\n type: 'integer', max: 15, min: 0, rebuild: true\n }\n\n }, this.parameters, {\n aspectRatio: null,\n smoothSheet: null\n })\n }\n\n init (params: Partial) {\n var p = params || {}\n p.aspectRatio = 1.0\n p.tension = defaults(p.tension, 0.5)\n p.radiusScale = defaults(p.radiusScale, 5.0)\n p.smoothSheet = false\n\n this.smooth = defaults(p.smooth, 2)\n\n super.init(p)\n }\n\n getSpline (polymer: Polymer) {\n var helixorient = new Helixorient(polymer)\n\n return new Spline(polymer, this.getSplineParams({\n directional: false,\n positionIterator: helixorient.getCenterIterator(this.smooth)\n }))\n }\n}\n\nRepresentationRegistry.add('rope', RopeRepresentation)\n\nexport default RopeRepresentation\n","/**\n * @file Spacefill Representation\n * @author Alexander Rose \n * @private\n */\n\nimport { defaults } from '../utils'\nimport { RepresentationRegistry } from '../globals'\nimport StructureRepresentation, { StructureRepresentationParameters, StructureRepresentationData } from './structure-representation'\nimport SphereBuffer, { SphereBufferData, SphereBufferParameters } from '../buffer/sphere-buffer'\nimport { Structure } from '../ngl';\nimport Viewer from '../viewer/viewer';\nimport StructureView from '../structure/structure-view';\nimport SphereGeometryBuffer from '../buffer/spheregeometry-buffer';\nimport { AtomDataFields } from '../structure/structure-data';\nimport SphereImpostorBuffer from '../buffer/sphereimpostor-buffer';\n\n/**\n * Spacefill Representation\n */\nclass SpacefillRepresentation extends StructureRepresentation {\n constructor (structure: Structure, viewer: Viewer, params: Partial) {\n super(structure, viewer, params)\n\n this.type = 'spacefill'\n\n this.parameters = Object.assign({\n sphereDetail: true,\n disableImpostor: true\n }, this.parameters)\n\n this.init(params)\n }\n\n init (params: Partial) {\n var p = params || {}\n p.useInteriorColor = defaults(p.useInteriorColor, true)\n\n super.init(p)\n }\n\n createData (sview: StructureView) {\n var sphereBuffer = new SphereBuffer(\n (sview.getAtomData(this.getAtomParams()) as SphereBufferData),\n (this.getBufferParams({\n sphereDetail: this.sphereDetail,\n dullInterior: true,\n disableImpostor: this.disableImpostor\n }) as SphereBufferParameters)\n )\n\n return {\n bufferList: [ sphereBuffer as SphereGeometryBuffer|SphereImpostorBuffer ]\n }\n }\n\n updateData (what: AtomDataFields, data: StructureRepresentationData) {\n var atomData = data.sview!.getAtomData(this.getAtomParams(what))\n var sphereData: Partial = {}\n\n if (!what || what.position) {\n Object.assign(sphereData, {position: atomData.position})\n }\n\n if (!what || what.color) {\n Object.assign(sphereData, {color: atomData.color})\n }\n\n if (!what || what.radius) {\n Object.assign(sphereData, {radius: atomData.radius})\n }\n\n data.bufferList[ 0 ].setAttributes(sphereData)\n }\n}\n\nRepresentationRegistry.add('spacefill', SpacefillRepresentation)\n\nexport default SpacefillRepresentation\n","/**\n * @file Trace Buffer\n * @author Alexander Rose \n * @private\n */\n\nimport '../shader/Line.vert'\nimport '../shader/Line.frag'\n\nimport { Log } from '../globals'\nimport Buffer, { BufferParameters, BufferData } from './buffer'\n\nfunction getSize(data: BufferData){\n const n = data.position!.length / 3\n const n1 = n - 1\n return n1 * 3 * 2\n}\n\n/**\n * Trace buffer. Draws a series of lines.\n */\nclass TraceBuffer extends Buffer {\n isLine = true\n vertexShader = 'Line.vert'\n fragmentShader = 'Line.frag'\n\n /**\n * @param {Object} data - attribute object\n * @param {Float32Array} data.position - positions\n * @param {Float32Array} data.color - colors\n * @param {BufferParameters} params - parameter object\n */\n constructor (data: BufferData, params: Partial = {}) {\n super({\n position: new Float32Array(getSize(data)),\n color: new Float32Array(getSize(data))\n }, params)\n\n this.setAttributes(data)\n }\n\n setAttributes (data: Partial) {\n let position, color\n let linePosition, lineColor\n\n const attributes = this.geometry.attributes as any // TODO\n\n if (data.position) {\n position = data.position\n linePosition = attributes.position.array\n attributes.position.needsUpdate = true\n }\n\n if (data.color) {\n color = data.color\n lineColor = attributes.color.array\n attributes.color.needsUpdate = true\n }\n\n if (!position && !color) {\n Log.warn('TraceBuffer.prototype.setAttributes no data')\n return\n }\n\n let v, v2\n const n = this.size\n const n1 = n - 1\n\n for (let i = 0; i < n1; ++i) {\n v = 3 * i\n v2 = 3 * i * 2\n\n if (position) {\n linePosition[ v2 ] = position[ v ]\n linePosition[ v2 + 1 ] = position[ v + 1 ]\n linePosition[ v2 + 2 ] = position[ v + 2 ]\n\n linePosition[ v2 + 3 ] = position[ v + 3 ]\n linePosition[ v2 + 4 ] = position[ v + 4 ]\n linePosition[ v2 + 5 ] = position[ v + 5 ]\n }\n\n if (color) {\n lineColor[ v2 ] = color[ v ]\n lineColor[ v2 + 1 ] = color[ v + 1 ]\n lineColor[ v2 + 2 ] = color[ v + 2 ]\n\n lineColor[ v2 + 3 ] = color[ v + 3 ]\n lineColor[ v2 + 4 ] = color[ v + 4 ]\n lineColor[ v2 + 5 ] = color[ v + 5 ]\n }\n }\n }\n}\n\nexport default TraceBuffer\n","/**\n * @file Trace Representation\n * @author Alexander Rose \n * @private\n */\n\nimport { RepresentationRegistry } from '../globals'\nimport { defaults } from '../utils'\nimport Spline from '../geometry/spline'\nimport StructureRepresentation, { StructureRepresentationParameters, StructureRepresentationData } from './structure-representation'\nimport TraceBuffer from '../buffer/trace-buffer'\nimport { Structure } from '../ngl';\nimport Viewer from '../viewer/viewer';\nimport AtomProxy from '../proxy/atom-proxy';\nimport StructureView from '../structure/structure-view';\nimport Polymer from '../proxy/polymer';\n\nexport interface TraceRepresentationParameters extends StructureRepresentationParameters {\n subdiv: number\n tension: number\n smoothSheet: boolean\n}\n/**\n * Trace Representation\n */\nclass TraceRepresentation extends StructureRepresentation {\n protected subdiv: number\n protected tension: number\n protected smoothSheet: boolean\n \n constructor (structure: Structure, viewer: Viewer, params: Partial) {\n super(structure, viewer, params)\n\n this.type = 'trace'\n\n this.parameters = Object.assign({\n\n subdiv: {\n type: 'integer', max: 50, min: 1, rebuild: true\n },\n tension: {\n type: 'number', precision: 1, max: 1.0, min: 0.1\n },\n smoothSheet: {\n type: 'boolean', rebuild: true\n }\n\n }, this.parameters, {\n\n flatShaded: null,\n side: null,\n wireframe: null\n\n })\n\n this.init(params)\n }\n\n init (params: Partial) {\n var p = params || {}\n p.colorScheme = defaults(p.colorScheme, 'chainname')\n p.colorScale = defaults(p.colorScale, 'RdYlBu')\n\n if (p.quality === 'low') {\n this.subdiv = 3\n } else if (p.quality === 'medium') {\n this.subdiv = 6\n } else if (p.quality === 'high') {\n this.subdiv = 12\n } else {\n this.subdiv = defaults(p.subdiv, 6)\n }\n\n this.tension = defaults(p.tension, NaN)\n this.smoothSheet = defaults(p.smoothSheet, false)\n\n super.init(p)\n }\n\n getSplineParams (params?: {[k:string]: any}) {\n return Object.assign({\n subdiv: this.subdiv,\n tension: this.tension,\n directional: false,\n smoothSheet: this.smoothSheet\n }, params)\n }\n\n getAtomRadius (atom: AtomProxy) {\n return atom.isTrace() ? 0.1 : 0\n }\n\n createData (sview: StructureView) {\n var bufferList: TraceBuffer[] = []\n var polymerList: Polymer[] = []\n\n this.structure.eachPolymer(polymer => {\n if (polymer.residueCount < 4) return\n polymerList.push(polymer)\n\n var spline = new Spline(polymer, this.getSplineParams())\n var subPos = spline.getSubdividedPosition()\n var subCol = spline.getSubdividedColor(this.getColorParams())\n\n bufferList.push(\n new TraceBuffer(\n Object.assign({}, subPos, subCol),\n this.getBufferParams()\n )\n )\n }, sview.getSelection())\n\n return {\n bufferList: bufferList,\n polymerList: polymerList\n }\n }\n\n updateData (what: any, data: StructureRepresentationData) {\n what = what || {}\n\n var i = 0\n var n = data.polymerList!.length\n\n for (i = 0; i < n; ++i) {\n var bufferData = {}\n var spline = new Spline(data.polymerList![ i ], this.getSplineParams())\n\n if (what.position) {\n var subPos = spline.getSubdividedPosition()\n Object.assign(bufferData, { position: subPos.position })\n }\n\n if (what.color) {\n var subCol = spline.getSubdividedColor(this.getColorParams())\n Object.assign(bufferData, { color: subCol.color })\n }\n\n data.bufferList[ i ].setAttributes(bufferData)\n }\n }\n\n setParameters (params: Partial) {\n var rebuild = false\n var what = {}\n\n if (params && params.tension) {\n Object.assign(what, {position: true})\n }\n\n super.setParameters(params, what, rebuild)\n\n return this\n }\n}\n\nRepresentationRegistry.add('trace', TraceRepresentation)\n\nexport default TraceRepresentation\n","/**\n * @file Tube Representation\n * @author Alexander Rose \n * @private\n */\n\nimport { RepresentationRegistry } from '../globals'\nimport { defaults } from '../utils'\nimport CartoonRepresentation, {CartoonRepresentationParameters} from './cartoon-representation'\nimport { Structure } from '../ngl';\nimport Viewer from '../viewer/viewer';\n\n/**\n * Tube Representation\n */\nclass TubeRepresentation extends CartoonRepresentation {\n constructor (structure: Structure, viewer: Viewer, params: Partial) {\n super(structure, viewer, params)\n\n this.type = 'tube'\n\n this.parameters = Object.assign(\n {}, this.parameters, { aspectRatio: null }\n )\n }\n\n init (params: Partial) {\n var p = params || {}\n p.aspectRatio = 1.0\n p.radiusScale = defaults(p.radiusScale, 2.0)\n\n if (p.quality === 'low') {\n this.radialSegments = 5\n }\n\n super.init(p)\n }\n\n getSplineParams (/* params */) {\n return super.getSplineParams({\n directional: false\n })\n }\n}\n\nRepresentationRegistry.add('tube', TubeRepresentation)\n\nexport default TubeRepresentation\n","/**\n * @file Unitcell Representation\n * @author Alexander Rose \n * @private\n */\n\nimport { RepresentationRegistry } from '../globals'\nimport { defaults } from '../utils'\nimport StructureRepresentation, { StructureRepresentationParameters, StructureRepresentationData } from './structure-representation'\nimport SphereBuffer, { SphereBufferData, SphereBufferParameters } from '../buffer/sphere-buffer'\nimport CylinderBuffer, { CylinderBufferData } from '../buffer/cylinder-buffer'\nimport { Structure } from '../ngl';\nimport Viewer from '../viewer/viewer';\nimport { AtomDataFields } from '../structure/structure-data';\nimport StructureView from '../structure/structure-view';\nimport SphereGeometryBuffer from '../buffer/spheregeometry-buffer';\nimport CylinderGeometryBuffer from '../buffer/cylindergeometry-buffer';\n// @ts-ignore: unused import UnitcellPicker required for declaration only\nimport { UnitcellPicker } from '../utils/picker';\n\nexport interface UnitcellRepresentationParameters extends StructureRepresentationParameters {\n radiusSize: number\n sphereDetail: number\n radialSegments: number\n disableImpostor: boolean\n}\n\n/**\n * Unitcell Representation\n */\nclass UnitcellRepresentation extends StructureRepresentation {\n sphereBuffer: SphereBuffer\n cylinderBuffer: CylinderBuffer\n\n constructor (structure: Structure, viewer: Viewer, params: Partial) {\n super(structure, viewer, params)\n\n this.type = 'unitcell'\n\n this.parameters = Object.assign({\n\n radiusSize: {\n type: 'number', precision: 3, max: 10.0, min: 0.001\n },\n sphereDetail: true,\n radialSegments: true,\n disableImpostor: true\n\n }, this.parameters, {\n assembly: null\n })\n\n this.init(params)\n }\n\n init (params: Partial) {\n const p = params || {}\n\n let defaultRadius = 0.5\n if (this.structure.unitcell) {\n defaultRadius = Math.cbrt(this.structure.unitcell.volume) / 200\n }\n\n p.radiusSize = defaults(p.radiusSize, defaultRadius)\n p.colorValue = defaults(p.colorValue, 'orange')\n p.useInteriorColor = defaults(p.useInteriorColor, true)\n\n super.init(p)\n }\n\n getUnitcellData (structure: Structure) {\n return structure.unitcell!.getData(structure)\n }\n\n create () {\n const structure = this.structureView.getStructure()\n if (!structure.unitcell) return\n const unitcellData = this.getUnitcellData(structure)\n\n this.sphereBuffer = new SphereBuffer(\n unitcellData.vertex as SphereBufferData,\n this.getBufferParams({\n sphereDetail: this.sphereDetail,\n disableImpostor: this.disableImpostor,\n dullInterior: true\n }) as SphereBufferParameters\n )\n\n this.cylinderBuffer = new CylinderBuffer(\n unitcellData.edge as CylinderBufferData,\n this.getBufferParams({\n openEnded: true,\n radialSegments: this.radialSegments,\n disableImpostor: this.disableImpostor,\n dullInterior: true\n })\n )\n\n this.dataList.push({\n sview: this.structureView,\n bufferList: [ this.sphereBuffer as SphereGeometryBuffer, this.cylinderBuffer as CylinderGeometryBuffer ]\n })\n }\n\n createData (sview: StructureView): undefined {\n return\n }\n\n updateData (what: AtomDataFields, data: StructureRepresentationData) {\n const structure = data.sview!.getStructure()\n if (!structure.unitcell) return\n const unitcellData = this.getUnitcellData(structure)\n const sphereData: Partial = {}\n const cylinderData: Partial = {}\n\n if (!what || what.position) {\n Object.assign(sphereData, {position: unitcellData.vertex.position})\n Object.assign(cylinderData, {\n position1: unitcellData.edge.position1,\n position2: unitcellData.edge.position2\n })\n }\n\n if (!what || what.color) {\n Object.assign(sphereData, {color: unitcellData.vertex.color})\n Object.assign(cylinderData, {\n color: unitcellData.edge.color,\n color2: unitcellData.edge.color2\n })\n }\n\n if (!what || what.radius) {\n Object.assign(sphereData, {radius: unitcellData.vertex.radius})\n Object.assign(cylinderData, {radius: unitcellData.edge.radius})\n }\n\n (this.sphereBuffer as SphereGeometryBuffer).setAttributes(sphereData);\n (this.cylinderBuffer as CylinderGeometryBuffer).setAttributes(cylinderData)\n }\n}\n\nRepresentationRegistry.add('unitcell', UnitcellRepresentation)\n\nexport default UnitcellRepresentation\n","/**\n * @file Validation Representation\n * @author Alexander Rose \n * @private\n */\n\nimport { RepresentationRegistry } from '../globals'\nimport { defaults } from '../utils'\nimport StructureRepresentation, { StructureRepresentationParameters } from './structure-representation'\nimport CylinderBuffer from '../buffer/cylinder-buffer'\nimport { Structure } from '../ngl';\nimport Viewer from '../viewer/viewer';\nimport StructureView from '../structure/structure-view';\nimport CylinderGeometryBuffer from '../buffer/cylindergeometry-buffer';\nimport CylinderImpostorBuffer from '../buffer/cylinderimpostor-buffer';\n\n/**\n * Validation representation\n */\nclass ValidationRepresentation extends StructureRepresentation {\n constructor (structure: Structure, viewer: Viewer, params: Partial) {\n super(structure, viewer, params)\n\n this.type = 'validation'\n\n this.parameters = Object.assign({\n\n }, this.parameters, {\n radiusType: null,\n radiusSize: null,\n radiusScale: null\n })\n\n this.init(params)\n }\n\n init (params: Partial) {\n const p = params || {}\n p.colorValue = defaults(p.colorValue, '#f0027f')\n p.useInteriorColor = defaults(p.useInteriorColor, true)\n\n super.init(p)\n }\n\n createData (sview: StructureView) {\n if (!sview.validation) return\n\n const clashData = sview.validation.getClashData({\n structure: sview,\n color: this.colorValue\n })\n\n const cylinderBuffer = new CylinderBuffer(\n clashData, this.getBufferParams({ openEnded: false })\n )\n\n return {\n bufferList: [ cylinderBuffer as CylinderGeometryBuffer|CylinderImpostorBuffer ]\n }\n }\n}\n\nRepresentationRegistry.add('validation', ValidationRepresentation)\n\nexport default ValidationRepresentation\n","/**\n * @file Cone Buffer\n * @author Alexander Rose \n * @private\n */\n\nimport { Matrix4, Vector3, ConeGeometry } from 'three'\n\nimport { BufferRegistry } from '../globals'\nimport { defaults } from '../utils'\nimport { calculateCenterArray } from '../math/array-utils'\nimport GeometryBuffer from './geometry-buffer'\nimport { BufferData, BufferDefaultParameters } from './buffer'\n\nconst scale = new Vector3()\nconst eye = new Vector3()\nconst target = new Vector3()\nconst up = new Vector3(0, 1, 0)\n\nfunction getGeo (params: Partial = {}) {\n const geo = new ConeGeometry(\n 1, // radius\n 1, // height\n defaults(params.radialSegments, 60), // radialSegments\n 1, // heightSegments\n defaults(params.openEnded, false) // openEnded\n )\n geo.applyMatrix4(new Matrix4().makeRotationX(-Math.PI / 2))\n\n return geo\n}\n\nexport interface ConeBufferData extends BufferData {\n position1: Float32Array\n position2: Float32Array\n radius: Float32Array\n}\n\nexport const ConeBufferDefaultParameters = Object.assign({\n radialSegments: 60,\n openEnded: false\n}, BufferDefaultParameters)\nexport type ConeBufferParameters = typeof ConeBufferDefaultParameters\n\n\n/**\n * Cone geometry buffer.\n *\n * @example\n * var coneBuffer = new ConeBuffer({\n * position1: new Float32Array([ 0, 0, 0 ]),\n * position2: new Float32Array([ 1, 1, 1 ]),\n * color: new Float32Array([ 1, 0, 0 ]),\n * color2: new Float32Array([ 0, 1, 0 ]),\n * radius: new Float32Array([ 1 ])\n * });\n */\nclass ConeBuffer extends GeometryBuffer {\n updateNormals = true\n\n get defaultParameters() { return ConeBufferDefaultParameters }\n parameters: ConeBufferParameters\n\n _position: Float32Array\n _position1: Float32Array\n _position2: Float32Array\n _radius: Float32Array\n\n /**\n * @param {Object} data - buffer data\n * @param {Float32Array} data.position1 - from positions\n * @param {Float32Array} data.position2 - to positions\n * @param {Float32Array} data.color - colors\n * @param {Float32Array} data.radius - radii\n * @param {Picker} [data.picking] - picking ids\n * @param {BufferParameters} [params] - parameters object\n */\n constructor (data: ConeBufferData, params: Partial = {}) {\n super({\n position: new Float32Array(data.position1.length),\n color: data.color,\n picking: data.picking\n }, params, getGeo(params))\n\n this._position = new Float32Array(data.position1.length)\n\n this.setAttributes(data, true)\n }\n\n applyPositionTransform (matrix: Matrix4, i: number, i3: number) {\n eye.fromArray(this._position1 as any, i3)\n target.fromArray(this._position2 as any, i3)\n matrix.lookAt(eye, target, up)\n\n const r = this._radius[ i ]\n scale.set(r, r, eye.distanceTo(target))\n matrix.scale(scale)\n }\n\n setAttributes (data: Partial = {}, initNormals?: boolean) {\n if (data.position1 && data.position2) {\n calculateCenterArray(data.position1, data.position2, this._position)\n this._position1 = data.position1\n this._position2 = data.position2\n data.position = this._position\n }\n if (data.radius) this._radius = data.radius\n\n super.setAttributes(data, initNormals)\n }\n}\n\nBufferRegistry.add('cone', ConeBuffer)\n\nexport default ConeBuffer\n","/**\n * @file Geometry Group\n * @author Alexander Rose \n * @private\n */\n\nimport { Box3, BufferGeometry } from 'three'\n\nclass GeometryGroup {\n geometryList: BufferGeometry[]\n boundingBox: Box3\n\n constructor (geometryList: BufferGeometry[] = []) {\n this.geometryList = geometryList\n }\n\n computeBoundingBox () {\n if (!this.boundingBox) {\n this.boundingBox = new Box3()\n } else {\n this.boundingBox.empty()\n }\n\n this.geometryList.forEach(geo => {\n if (!geo.boundingBox) geo.computeBoundingBox()\n this.boundingBox.union(geo.boundingBox as Box3)\n })\n }\n}\n\nexport default GeometryGroup\n","/**\n * @file Arrow Buffer\n * @author Alexander Rose \n * @private\n */\n\nimport { Matrix4, Vector3, Group } from 'three'\n\nimport { BufferRegistry } from '../globals'\nimport { createParams, defaults } from '../utils'\nimport { Picker } from '../utils/picker'\nimport Buffer from './buffer'\nimport CylinderBuffer, { CylinderBufferData } from './cylinder-buffer'\nimport CylinderGeometryBuffer from './cylindergeometry-buffer'\nimport ConeBuffer, { ConeBufferData } from './cone-buffer'\nimport GeometryGroup from '../viewer/geometry-group'\nimport { BufferData, BufferDefaultParameters } from './buffer'\n\nexport interface ArrowBufferData extends BufferData {\n position1: Float32Array\n position2: Float32Array\n radius: Float32Array\n}\n\nexport const ArrowBufferDefaultParameters = Object.assign({\n aspectRatio: 1.5,\n radialSegments: 50,\n openEnded: false,\n disableImpostor: false\n}, BufferDefaultParameters)\nexport type ArrowBufferParameters = typeof ArrowBufferDefaultParameters\n\n/**\n * Arrow buffer. Draws arrows made from a cylinder and a cone.\n * @implements {Buffer}\n *\n * @example\n * var arrowBuffer = new ArrowBuffer({\n * position1: new Float32Array([ 0, 0, 0 ]),\n * position2: new Float32Array([ 10, 1, 1 ]),\n * color: new Float32Array([ 1, 0, 0 ]),\n * radius: new Float32Array([ 1 ])\n * });\n */\nclass ArrowBuffer {\n parameters: ArrowBufferParameters\n get defaultParameters() { return ArrowBufferDefaultParameters }\n\n cylinderBuffer: CylinderGeometryBuffer\n coneBuffer: ConeBuffer\n\n splitPosition: Float32Array\n cylinderRadius: Float32Array\n\n geometry: GeometryGroup\n picking?: Picker\n\n group = new Group()\n wireframeGroup = new Group()\n pickingGroup = new Group()\n\n visible = true\n\n /**\n * @param {Object} data - buffer data\n * @param {Float32Array} data.position1 - from positions\n * @param {Float32Array} data.position2 - to positions\n * @param {Float32Array} data.color - colors\n * @param {Float32Array} data.radius - radii\n * @param {Picker} [data.picking] - picking ids\n * @param {BufferParameters} [params] - parameters object\n */\n constructor (data: ArrowBufferData, params: Partial = {}) {\n this.parameters = createParams(params, this.defaultParameters)\n\n this.splitPosition = new Float32Array(data.position1.length)\n this.cylinderRadius = new Float32Array(data.radius.length)\n\n const attr = this.makeAttributes(data)\n const bufferParams = {\n radialSegments: this.parameters.radialSegments,\n openEnded: this.parameters.openEnded,\n disableImpostor: this.parameters.disableImpostor\n }\n\n this.cylinderBuffer = new CylinderBuffer(\n attr.cylinder as CylinderBufferData, bufferParams\n ) as CylinderGeometryBuffer\n this.coneBuffer = new ConeBuffer(\n attr.cone as ConeBufferData, bufferParams\n )\n\n this.geometry = new GeometryGroup([\n this.cylinderBuffer.geometry,\n this.coneBuffer.geometry\n ])\n\n // requires Group objects to be present\n this.matrix = defaults(params.matrix, new Matrix4())\n\n this.picking = data.picking\n }\n\n set matrix (m) {\n Buffer.prototype.setMatrix.call(this, m)\n }\n get matrix () {\n return this.group.matrix.clone()\n }\n\n get pickable () {\n return !!this.picking\n }\n\n makeAttributes (data: Partial = {}) {\n const splitPosition = this.splitPosition\n const cylinderRadius = this.cylinderRadius\n\n const aspectRatio = this.parameters.aspectRatio\n\n let i, il\n const cylinder: Partial = {}\n const cone: Partial = {}\n\n if (data.radius) {\n for (i = 0, il = cylinderRadius.length; i < il; ++i) {\n cylinderRadius[ i ] = data.radius[ i ] / aspectRatio\n }\n cylinder.radius = cylinderRadius\n cone.radius = data.radius\n }\n\n if (data.position1 && data.position2) {\n const vFrom = new Vector3()\n const vTo = new Vector3()\n const vDir = new Vector3()\n const vSplit = new Vector3()\n for (i = 0, il = splitPosition.length; i < il; i += 3) {\n vFrom.fromArray(data.position1 as any, i)\n vTo.fromArray(data.position2 as any, i)\n vDir.subVectors(vFrom, vTo)\n const fullLength = vDir.length()\n const coneLength = cylinderRadius[ i / 3 ] * aspectRatio * 2\n const length = Math.min(fullLength, coneLength)\n vDir.setLength(length)\n vSplit.copy(vTo).add(vDir)\n vSplit.toArray(splitPosition as any, i)\n }\n cylinder.position1 = data.position1\n cylinder.position2 = splitPosition\n cone.position1 = splitPosition\n cone.position2 = data.position2\n }\n\n if (data.color) {\n cylinder.color = data.color\n cylinder.color2 = data.color\n cone.color = data.color\n }\n\n return {\n cylinder: cylinder,\n cone: cone\n }\n }\n\n getMesh () {\n return new Group().add(\n this.cylinderBuffer.getMesh(),\n this.coneBuffer.getMesh()\n )\n }\n\n getWireframeMesh () {\n return new Group().add(\n this.cylinderBuffer.getWireframeMesh(),\n this.coneBuffer.getWireframeMesh()\n )\n }\n\n getPickingMesh () {\n return new Group().add(\n this.cylinderBuffer.getPickingMesh(),\n this.coneBuffer.getPickingMesh()\n )\n }\n\n setAttributes (data: Partial = {}) {\n const attr = this.makeAttributes(data)\n\n this.cylinderBuffer.setAttributes(attr.cylinder)\n this.coneBuffer.setAttributes(attr.cone)\n }\n\n /**\n * Set buffer parameters\n * @param {BufferParameters} params - buffer parameters object\n * @return {undefined}\n */\n setParameters (params: Partial = {}) {\n params = Object.assign({}, params)\n\n if (params && params.matrix !== undefined) {\n this.matrix = params.matrix\n }\n delete params.matrix\n\n if (params && params.wireframe !== undefined) {\n this.parameters.wireframe = params.wireframe\n this.setVisibility(this.visible)\n }\n\n this.cylinderBuffer.setParameters(params)\n this.coneBuffer.setParameters(params)\n }\n\n setVisibility (value: boolean) {\n Buffer.prototype.setVisibility.call(this, value)\n }\n\n dispose () {\n this.cylinderBuffer.dispose()\n this.coneBuffer.dispose()\n }\n}\n\nBufferRegistry.add('arrow', ArrowBuffer)\n\nexport default ArrowBuffer\n","/**\n * @file Box Buffer\n * @author Alexander Rose \n * @private\n */\n\nimport { BoxGeometry, Vector3, Matrix4 } from 'three'\n\nimport { BufferRegistry } from '../globals'\nimport GeometryBuffer from './geometry-buffer'\nimport { BufferData, BufferParameters } from './buffer'\n\nconst scale = new Vector3()\nconst target = new Vector3()\nconst up = new Vector3()\nconst eye = new Vector3(0, 0, 0)\n\nexport interface BoxBufferData extends BufferData {\n heightAxis: Float32Array\n depthAxis: Float32Array\n size: Float32Array\n}\n\n/**\n * Box buffer. Draws boxes.\n *\n * @example\n * var boxBuffer = new BoxBuffer({\n * position: new Float32Array([ 0, 3, 0, -2, 0, 0 ]),\n * color: new Float32Array([ 1, 0, 1, 0, 1, 0 ]),\n * size: new Float32Array([ 2, 1.5 ]),\n * heightAxis: new Float32Array([ 0, 1, 1, 0, 2, 0 ]),\n * depthAxis: new Float32Array([ 1, 0, 1, 0, 0, 2 ])\n * })\n */\nclass BoxBuffer extends GeometryBuffer {\n updateNormals = true\n\n _heightAxis: Float32Array\n _depthAxis: Float32Array\n _size: Float32Array\n\n constructor (data: BoxBufferData, params: Partial = {}) {\n super(data, params, new BoxGeometry(1, 1, 1))\n\n this.setAttributes(data, true)\n }\n\n applyPositionTransform (matrix: Matrix4, i: number, i3: number) {\n target.fromArray(this._heightAxis as any, i3)\n up.fromArray(this._depthAxis as any, i3)\n matrix.lookAt(eye, target, up)\n\n scale.set(this._size[ i ], up.length(), target.length())\n matrix.scale(scale)\n }\n\n setAttributes (data: Partial = {}, initNormals?: boolean) {\n if (data.size) this._size = data.size\n if (data.heightAxis) this._heightAxis = data.heightAxis\n if (data.depthAxis) this._depthAxis = data.depthAxis\n\n super.setAttributes(data, initNormals)\n }\n}\n\nBufferRegistry.add('box', BoxBuffer)\n\nexport default BoxBuffer\n","/**\n * @file Ellipsoid Geometry Buffer\n * @author Alexander Rose \n * @private\n */\n\nimport { IcosahedronGeometry, Vector3, Matrix4 } from 'three'\n\nimport { BufferRegistry } from '../globals'\nimport { defaults } from '../utils'\nimport GeometryBuffer from './geometry-buffer'\nimport { BufferData, BufferDefaultParameters } from './buffer'\n\nconst scale = new Vector3()\nconst target = new Vector3()\nconst up = new Vector3()\nconst eye = new Vector3(0, 0, 0)\n\nexport interface EllipsoidBufferData extends BufferData {\n majorAxis: Float32Array\n minorAxis: Float32Array\n radius: Float32Array\n}\n\nexport const EllipsoidBufferDefaultParameters = Object.assign({\n sphereDetail: 2,\n}, BufferDefaultParameters)\nexport type EllipsoidBufferParameters = typeof EllipsoidBufferDefaultParameters\n\n/**\n * Ellipsoid buffer. Draws ellipsoids.\n *\n * @example\n * var ellipsoidBuffer = new EllipsoidBuffer({\n * position: new Float32Array([ 0, 0, 0 ]),\n * color: new Float32Array([ 1, 0, 0 ]),\n * radius: new Float32Array([ 1 ]),\n * majorAxis: new Float32Array([ 1, 1, 0 ]),\n * minorAxis: new Float32Array([ 0.5, 0, 0.5 ]),\n * });\n */\nclass EllipsoidBuffer extends GeometryBuffer {\n updateNormals = true\n\n get defaultParameters() { return EllipsoidBufferDefaultParameters }\n parameters: EllipsoidBufferParameters\n\n _majorAxis: Float32Array\n _minorAxis: Float32Array\n _radius: Float32Array\n\n constructor (data: EllipsoidBufferData, params: Partial = {}) {\n super(data, params, new IcosahedronGeometry(1, defaults(params.sphereDetail, 2)))\n\n this.setAttributes(data, true)\n }\n\n applyPositionTransform (matrix: Matrix4, i: number, i3: number) {\n target.fromArray(this._majorAxis as any, i3)\n up.fromArray(this._minorAxis as any, i3)\n matrix.lookAt(eye, target, up)\n\n scale.set(this._radius[ i ], up.length(), target.length())\n matrix.scale(scale)\n }\n\n setAttributes (data: Partial = {}, initNormals?: boolean) {\n if (data.radius) this._radius = data.radius\n if (data.majorAxis) this._majorAxis = data.majorAxis\n if (data.minorAxis) this._minorAxis = data.minorAxis\n\n super.setAttributes(data, initNormals)\n }\n}\n\nBufferRegistry.add('ellipsoid', EllipsoidBuffer)\n\nexport default EllipsoidBuffer\n","/**\n * @file Octahedron Buffer\n * @author Alexander Rose \n * @private\n */\n\nimport { OctahedronGeometry, Vector3, Matrix4 } from 'three'\nimport { BufferRegistry } from '../globals'\nimport GeometryBuffer from './geometry-buffer'\nimport { BufferData, BufferParameters } from './buffer'\n\nconst scale = new Vector3()\nconst target = new Vector3()\nconst up = new Vector3()\nconst eye = new Vector3(0, 0, 0)\n\nexport interface OctahedronBufferData extends BufferData {\n heightAxis: Float32Array\n depthAxis: Float32Array\n size: Float32Array\n}\n\n/**\n * Octahedron buffer. Draws octahedrons.\n *\n * @example\n * var octahedronBuffer = new OctahedronBuffer({\n * position: new Float32Array([ 0, 3, 0, -2, 0, 0 ]),\n * color: new Float32Array([ 1, 0, 1, 0, 1, 0 ]),\n * size: new Float32Array([ 2, 1.5 ]),\n * heightAxis: new Float32Array([ 0, 1, 1, 0, 2, 0 ]),\n * depthAxis: new Float32Array([ 1, 0, 1, 0, 0, 2 ])\n * })\n */\nclass OctahedronBuffer extends GeometryBuffer {\n updateNormals = true\n\n _heightAxis: Float32Array\n _depthAxis: Float32Array\n _size: Float32Array\n\n constructor (data: OctahedronBufferData, params: Partial = {}) {\n super(data, params, new OctahedronGeometry(1, 0))\n\n this.setAttributes(data, true)\n }\n\n applyPositionTransform (matrix: Matrix4, i: number, i3: number) {\n target.fromArray(this._heightAxis as any, i3)\n up.fromArray(this._depthAxis as any, i3)\n matrix.lookAt(eye, target, up)\n\n scale.set(this._size[ i ], up.length(), target.length())\n matrix.scale(scale)\n }\n\n setAttributes (data: Partial = {}, initNormals?: boolean) {\n if (data.size) this._size = data.size\n if (data.heightAxis) this._heightAxis = data.heightAxis\n if (data.depthAxis) this._depthAxis = data.depthAxis\n\n super.setAttributes(data, initNormals)\n }\n}\n\nBufferRegistry.add('octahedron', OctahedronBuffer)\n\nexport default OctahedronBuffer\n","/**\n * @file Tetrahedron Buffer\n * @author Alexander Rose \n * @private\n */\n\nimport { TetrahedronGeometry, Vector3, Matrix4 } from 'three'\nimport { BufferRegistry } from '../globals'\nimport GeometryBuffer from './geometry-buffer'\nimport { BufferData, BufferParameters } from './buffer'\n\nconst scale = new Vector3()\nconst target = new Vector3()\nconst up = new Vector3()\nconst eye = new Vector3(0, 0, 0)\n\nexport interface TetrahedronBufferData extends BufferData {\n heightAxis: Float32Array\n depthAxis: Float32Array\n size: Float32Array\n}\n\n/**\n * Tetrahedron buffer. Draws tetrahedrons.\n *\n * @example\n * var tetrahedronBuffer = new TetrahedronBuffer({\n * position: new Float32Array([ 0, 3, 0, -2, 0, 0 ]),\n * color: new Float32Array([ 1, 0, 1, 0, 1, 0 ]),\n * size: new Float32Array([ 2, 1.5 ]),\n * heightAxis: new Float32Array([ 0, 1, 1, 0, 2, 0 ]),\n * depthAxis: new Float32Array([ 1, 0, 1, 0, 0, 2 ])\n * })\n */\nclass TetrahedronBuffer extends GeometryBuffer {\n updateNormals = true\n\n _heightAxis: Float32Array\n _depthAxis: Float32Array\n _size: Float32Array\n\n constructor (data: TetrahedronBufferData, params: Partial = {}) {\n super(data, params, new TetrahedronGeometry(1, 0))\n\n this.setAttributes(data, true)\n }\n\n applyPositionTransform (matrix: Matrix4, i: number, i3: number) {\n target.fromArray(this._heightAxis as any, i3)\n up.fromArray(this._depthAxis as any, i3)\n matrix.lookAt(eye, target, up)\n\n scale.set(this._size[ i ], up.length(), target.length())\n matrix.scale(scale)\n }\n\n setAttributes (data: Partial = {}, initNormals?: boolean) {\n if (data.size) this._size = data.size\n if (data.heightAxis) this._heightAxis = data.heightAxis\n if (data.depthAxis) this._depthAxis = data.depthAxis\n\n super.setAttributes(data, initNormals)\n }\n}\n\nBufferRegistry.add('tetrahedron', TetrahedronBuffer)\n\nexport default TetrahedronBuffer\n","/**\n * @file Tetrahedron Geometry Buffer\n * @author Alexander Rose \n * @private\n */\n\nimport { TorusGeometry, Vector3, Matrix4 } from 'three'\n\nimport { BufferRegistry } from '../globals'\nimport { defaults } from '../utils'\nimport GeometryBuffer from './geometry-buffer'\nimport { BufferDefaultParameters, BufferData } from './buffer'\n\nconst scale = new Vector3()\nconst target = new Vector3()\nconst up = new Vector3()\nconst eye = new Vector3(0, 0, 0)\n\nexport interface TorusBufferData extends BufferData {\n majorAxis: Float32Array\n minorAxis: Float32Array\n radius: Float32Array\n}\n\nexport const TorusBufferDefaultParameters = Object.assign({\n radiusRatio: 0.2,\n radialSegments: 16,\n tubularSegments: 32\n}, BufferDefaultParameters)\nexport type TorusBufferParameters = typeof TorusBufferDefaultParameters\n\n/**\n * Torus geometry buffer. Draws torii.\n *\n * @example\n * var torusBuffer = new TorusBuffer({\n * position: new Float32Array([ 0, 0, 0 ]),\n * color: new Float32Array([ 1, 0, 0 ]),\n * radius: new Float32Array([ 1 ]),\n * majorAxis: new Float32Array([ 1, 1, 0 ]),\n * minorAxis: new Float32Array([ 0.5, 0, 0.5 ]),\n * });\n */\nclass TorusBuffer extends GeometryBuffer {\n updateNormals = true\n\n get defaultParameters() { return TorusBufferDefaultParameters }\n parameters: TorusBufferParameters\n\n _majorAxis: Float32Array\n _minorAxis: Float32Array\n _radius: Float32Array\n\n constructor (data: TorusBufferData, params: Partial = {}) {\n super(data, params, new TorusGeometry(\n 1,\n defaults(params.radiusRatio, 0.2),\n defaults(params.radialSegments, 16),\n defaults(params.tubularSegments, 32)\n ))\n\n this.setAttributes(data, true)\n }\n\n applyPositionTransform (matrix: Matrix4, i: number, i3: number) {\n target.fromArray(this._majorAxis as any, i3)\n up.fromArray(this._minorAxis as any, i3)\n matrix.lookAt(eye, target, up)\n\n const r = this._radius[ i ]\n scale.set(r, r, r)\n matrix.scale(scale)\n }\n\n setAttributes (data: Partial = {}, initNormals?: boolean) {\n if (data.radius) this._radius = data.radius\n if (data.majorAxis) this._majorAxis = data.majorAxis\n if (data.minorAxis) this._minorAxis = data.minorAxis\n\n super.setAttributes(data, initNormals)\n }\n}\n\nBufferRegistry.add('torus', TorusBuffer)\n\nexport default TorusBuffer\n","/**\n * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.\n *\n * @author David Sehnal \n */\nexport function getArrayBounds(rowCount, params) {\n const start = params && typeof params.start !== 'undefined' ? Math.max(Math.min(params.start, rowCount - 1), 0) : 0;\n const end = params && typeof params.end !== 'undefined' ? Math.min(params.end, rowCount) : rowCount;\n return { start, end };\n}\nexport function createArray(rowCount, params) {\n const c = params && typeof params.array !== 'undefined' ? params.array : Array;\n const { start, end } = getArrayBounds(rowCount, params);\n return { array: new c(end - start), start, end };\n}\nexport function fillArrayValues(value, target, start) {\n for (let i = 0, _e = target.length; i < _e; i++)\n target[i] = value(start + i);\n return target;\n}\nexport function createAndFillArray(rowCount, value, params) {\n const { array, start } = createArray(rowCount, params);\n return fillArrayValues(value, array, start);\n}\nexport function isTypedArray(data) {\n return !!data.buffer && typeof data.byteLength === 'number' && typeof data.BYTES_PER_ELEMENT === 'number';\n}\nexport function typedArrayWindow(data, params) {\n const { constructor, buffer, length, byteOffset, BYTES_PER_ELEMENT } = data;\n const { start, end } = getArrayBounds(length, params);\n if (start === 0 && end === length)\n return data;\n return new constructor(buffer, byteOffset + BYTES_PER_ELEMENT * start, Math.min(length, end - start));\n}\n","/**\n * Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.\n *\n * @author David Sehnal \n * @author Alexander Rose \n */\n/*\n * This code has been modified from https://github.com/toji/gl-matrix/,\n * copyright (c) 2015, Brandon Jones, Colin MacKenzie IV.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n */\nexport const EPSILON = 0.000001;\nexport function equalEps(a, b, eps) {\n return Math.abs(a - b) <= eps;\n}\n","/**\n * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.\n *\n * @author Alexander Rose \n */\nexport function normalize(value, min, max) {\n return (value - min) / (max - min);\n}\nexport function clamp(value, min, max) {\n return Math.max(min, Math.min(max, value));\n}\nexport function pclamp(value) {\n return clamp(value, 0, 100);\n}\nexport function saturate(value) {\n return clamp(value, 0, 1);\n}\nexport function damp(value, dampingFactor) {\n const dampedValue = value * dampingFactor;\n return Math.abs(dampedValue) < 0.1 ? 0 : dampedValue;\n}\nexport function lerp(start, stop, alpha) {\n return start + (stop - start) * alpha;\n}\n/** Catmul-Rom spline */\nexport function spline(p0, p1, p2, p3, t, tension) {\n const v0 = (p2 - p0) * tension;\n const v1 = (p3 - p1) * tension;\n const t2 = t * t;\n const t3 = t * t2;\n return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1;\n}\nexport function quadraticBezier(p0, p1, p2, t) {\n const k = 1 - t;\n return (k * k * p0) + (2 * k * t * p1) + (t * t * p2);\n}\nexport function smoothstep(min, max, x) {\n x = saturate(normalize(x, min, max));\n return x * x * (3 - 2 * x);\n}\nexport function smootherstep(min, max, x) {\n x = saturate(normalize(x, min, max));\n return x * x * x * (x * (x * 6 - 15) + 10);\n}\nexport function smootheststep(min, max, x) {\n x = saturate(normalize(x, min, max));\n return -20 * Math.pow(x, 7) + 70 * Math.pow(x, 6) - 84 * Math.pow(x, 5) + 35 * Math.pow(x, 4);\n}\nexport function almostIdentity(value, start, stop) {\n if (value > start)\n return value;\n const a = 2 * stop - start;\n const b = 2 * start - 3 * stop;\n const t = value / start;\n return (a * t + b) * t * t + stop;\n}\n","/**\n * Copyright (c) 2017-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.\n *\n * @author David Sehnal \n * @author Alexander Rose \n */\n/*\n * This code has been modified from https://github.com/toji/gl-matrix/,\n * copyright (c) 2015, Brandon Jones, Colin MacKenzie IV.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n */\nimport { Mat4 } from './mat4';\nimport { spline as _spline, quadraticBezier as _quadraticBezier, clamp as _clamp } from '../../interpolate';\nimport { EPSILON } from './common';\nconst _isFinite = isFinite;\nfunction Vec3() {\n return Vec3.zero();\n}\n(function (Vec3) {\n function zero() {\n const out = [0.1, 0.0, 0.0]; // ensure backing array of type double\n out[0] = 0;\n return out;\n }\n Vec3.zero = zero;\n function clone(a) {\n const out = zero();\n out[0] = a[0];\n out[1] = a[1];\n out[2] = a[2];\n return out;\n }\n Vec3.clone = clone;\n function isFinite(a) {\n return _isFinite(a[0]) && _isFinite(a[1]) && _isFinite(a[2]);\n }\n Vec3.isFinite = isFinite;\n function hasNaN(a) {\n return isNaN(a[0]) || isNaN(a[1]) || isNaN(a[2]);\n }\n Vec3.hasNaN = hasNaN;\n function setNaN(out) {\n out[0] = NaN;\n out[1] = NaN;\n out[2] = NaN;\n return out;\n }\n Vec3.setNaN = setNaN;\n function fromObj(v) {\n return create(v.x, v.y, v.z);\n }\n Vec3.fromObj = fromObj;\n function toObj(v) {\n return { x: v[0], y: v[1], z: v[2] };\n }\n Vec3.toObj = toObj;\n function fromArray(v, array, offset) {\n v[0] = array[offset + 0];\n v[1] = array[offset + 1];\n v[2] = array[offset + 2];\n return v;\n }\n Vec3.fromArray = fromArray;\n function toArray(v, out, offset) {\n out[offset + 0] = v[0];\n out[offset + 1] = v[1];\n out[offset + 2] = v[2];\n return out;\n }\n Vec3.toArray = toArray;\n function create(x, y, z) {\n const out = zero();\n out[0] = x;\n out[1] = y;\n out[2] = z;\n return out;\n }\n Vec3.create = create;\n function ofArray(array) {\n const out = zero();\n out[0] = array[0];\n out[1] = array[1];\n out[2] = array[2];\n return out;\n }\n Vec3.ofArray = ofArray;\n function set(out, x, y, z) {\n out[0] = x;\n out[1] = y;\n out[2] = z;\n return out;\n }\n Vec3.set = set;\n function copy(out, a) {\n out[0] = a[0];\n out[1] = a[1];\n out[2] = a[2];\n return out;\n }\n Vec3.copy = copy;\n function add(out, a, b) {\n out[0] = a[0] + b[0];\n out[1] = a[1] + b[1];\n out[2] = a[2] + b[2];\n return out;\n }\n Vec3.add = add;\n function sub(out, a, b) {\n out[0] = a[0] - b[0];\n out[1] = a[1] - b[1];\n out[2] = a[2] - b[2];\n return out;\n }\n Vec3.sub = sub;\n function mul(out, a, b) {\n out[0] = a[0] * b[0];\n out[1] = a[1] * b[1];\n out[2] = a[2] * b[2];\n return out;\n }\n Vec3.mul = mul;\n function div(out, a, b) {\n out[0] = a[0] / b[0];\n out[1] = a[1] / b[1];\n out[2] = a[2] / b[2];\n return out;\n }\n Vec3.div = div;\n function scale(out, a, b) {\n out[0] = a[0] * b;\n out[1] = a[1] * b;\n out[2] = a[2] * b;\n return out;\n }\n Vec3.scale = scale;\n /** Scales b, then adds a and b together */\n function scaleAndAdd(out, a, b, scale) {\n out[0] = a[0] + (b[0] * scale);\n out[1] = a[1] + (b[1] * scale);\n out[2] = a[2] + (b[2] * scale);\n return out;\n }\n Vec3.scaleAndAdd = scaleAndAdd;\n /** Scales b, then subtracts b from a */\n function scaleAndSub(out, a, b, scale) {\n out[0] = a[0] - (b[0] * scale);\n out[1] = a[1] - (b[1] * scale);\n out[2] = a[2] - (b[2] * scale);\n return out;\n }\n Vec3.scaleAndSub = scaleAndSub;\n function addScalar(out, a, b) {\n out[0] = a[0] + b;\n out[1] = a[1] + b;\n out[2] = a[2] + b;\n return out;\n }\n Vec3.addScalar = addScalar;\n function subScalar(out, a, b) {\n out[0] = a[0] - b;\n out[1] = a[1] - b;\n out[2] = a[2] - b;\n return out;\n }\n Vec3.subScalar = subScalar;\n /**\n * Math.round the components of a Vec3\n */\n function round(out, a) {\n out[0] = Math.round(a[0]);\n out[1] = Math.round(a[1]);\n out[2] = Math.round(a[2]);\n return out;\n }\n Vec3.round = round;\n /**\n * Math.ceil the components of a Vec3\n */\n function ceil(out, a) {\n out[0] = Math.ceil(a[0]);\n out[1] = Math.ceil(a[1]);\n out[2] = Math.ceil(a[2]);\n return out;\n }\n Vec3.ceil = ceil;\n /**\n * Math.floor the components of a Vec3\n */\n function floor(out, a) {\n out[0] = Math.floor(a[0]);\n out[1] = Math.floor(a[1]);\n out[2] = Math.floor(a[2]);\n return out;\n }\n Vec3.floor = floor;\n /**\n * Math.trunc the components of a Vec3\n */\n function trunc(out, a) {\n out[0] = Math.trunc(a[0]);\n out[1] = Math.trunc(a[1]);\n out[2] = Math.trunc(a[2]);\n return out;\n }\n Vec3.trunc = trunc;\n /**\n * Math.abs the components of a Vec3\n */\n function abs(out, a) {\n out[0] = Math.abs(a[0]);\n out[1] = Math.abs(a[1]);\n out[2] = Math.abs(a[2]);\n return out;\n }\n Vec3.abs = abs;\n /**\n * Returns the minimum of two Vec3's\n */\n function min(out, a, b) {\n out[0] = Math.min(a[0], b[0]);\n out[1] = Math.min(a[1], b[1]);\n out[2] = Math.min(a[2], b[2]);\n return out;\n }\n Vec3.min = min;\n /**\n * Returns the maximum of two Vec3's\n */\n function max(out, a, b) {\n out[0] = Math.max(a[0], b[0]);\n out[1] = Math.max(a[1], b[1]);\n out[2] = Math.max(a[2], b[2]);\n return out;\n }\n Vec3.max = max;\n /**\n * Assumes min < max, componentwise\n */\n function clamp(out, a, min, max) {\n out[0] = Math.max(min[0], Math.min(max[0], a[0]));\n out[1] = Math.max(min[1], Math.min(max[1], a[1]));\n out[2] = Math.max(min[2], Math.min(max[2], a[2]));\n return out;\n }\n Vec3.clamp = clamp;\n function distance(a, b) {\n const x = b[0] - a[0], y = b[1] - a[1], z = b[2] - a[2];\n return Math.sqrt(x * x + y * y + z * z);\n }\n Vec3.distance = distance;\n function squaredDistance(a, b) {\n const x = b[0] - a[0], y = b[1] - a[1], z = b[2] - a[2];\n return x * x + y * y + z * z;\n }\n Vec3.squaredDistance = squaredDistance;\n function magnitude(a) {\n const x = a[0], y = a[1], z = a[2];\n return Math.sqrt(x * x + y * y + z * z);\n }\n Vec3.magnitude = magnitude;\n function squaredMagnitude(a) {\n const x = a[0], y = a[1], z = a[2];\n return x * x + y * y + z * z;\n }\n Vec3.squaredMagnitude = squaredMagnitude;\n function setMagnitude(out, a, l) {\n return scale(out, normalize(out, a), l);\n }\n Vec3.setMagnitude = setMagnitude;\n /**\n * Negates the components of a vec3\n */\n function negate(out, a) {\n out[0] = -a[0];\n out[1] = -a[1];\n out[2] = -a[2];\n return out;\n }\n Vec3.negate = negate;\n /**\n * Returns the inverse of the components of a Vec3\n */\n function inverse(out, a) {\n out[0] = 1.0 / a[0];\n out[1] = 1.0 / a[1];\n out[2] = 1.0 / a[2];\n return out;\n }\n Vec3.inverse = inverse;\n function normalize(out, a) {\n const x = a[0], y = a[1], z = a[2];\n let len = x * x + y * y + z * z;\n if (len > 0) {\n len = 1 / Math.sqrt(len);\n out[0] = a[0] * len;\n out[1] = a[1] * len;\n out[2] = a[2] * len;\n }\n return out;\n }\n Vec3.normalize = normalize;\n function dot(a, b) {\n return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];\n }\n Vec3.dot = dot;\n function cross(out, a, b) {\n const ax = a[0], ay = a[1], az = a[2], bx = b[0], by = b[1], bz = b[2];\n out[0] = ay * bz - az * by;\n out[1] = az * bx - ax * bz;\n out[2] = ax * by - ay * bx;\n return out;\n }\n Vec3.cross = cross;\n /**\n * Performs a linear interpolation between two Vec3's\n */\n function lerp(out, a, b, t) {\n const ax = a[0], ay = a[1], az = a[2];\n out[0] = ax + t * (b[0] - ax);\n out[1] = ay + t * (b[1] - ay);\n out[2] = az + t * (b[2] - az);\n return out;\n }\n Vec3.lerp = lerp;\n const slerpRelVec = zero();\n function slerp(out, a, b, t) {\n const d = _clamp(dot(a, b), -1, 1);\n const theta = Math.acos(d) * t;\n scaleAndAdd(slerpRelVec, b, a, -d);\n normalize(slerpRelVec, slerpRelVec);\n return add(out, scale(out, a, Math.cos(theta)), scale(slerpRelVec, slerpRelVec, Math.sin(theta)));\n }\n Vec3.slerp = slerp;\n /**\n * Performs a hermite interpolation with two control points\n */\n function hermite(out, a, b, c, d, t) {\n const factorTimes2 = t * t;\n const factor1 = factorTimes2 * (2 * t - 3) + 1;\n const factor2 = factorTimes2 * (t - 2) + t;\n const factor3 = factorTimes2 * (t - 1);\n const factor4 = factorTimes2 * (3 - 2 * t);\n out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4;\n out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4;\n out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4;\n return out;\n }\n Vec3.hermite = hermite;\n /**\n * Performs a bezier interpolation with two control points\n */\n function bezier(out, a, b, c, d, t) {\n const inverseFactor = 1 - t;\n const inverseFactorTimesTwo = inverseFactor * inverseFactor;\n const factorTimes2 = t * t;\n const factor1 = inverseFactorTimesTwo * inverseFactor;\n const factor2 = 3 * t * inverseFactorTimesTwo;\n const factor3 = 3 * factorTimes2 * inverseFactor;\n const factor4 = factorTimes2 * t;\n out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4;\n out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4;\n out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4;\n return out;\n }\n Vec3.bezier = bezier;\n function quadraticBezier(out, a, b, c, t) {\n out[0] = _quadraticBezier(a[0], b[0], c[0], t);\n out[1] = _quadraticBezier(a[1], b[1], c[1], t);\n out[2] = _quadraticBezier(a[2], b[2], c[2], t);\n return out;\n }\n Vec3.quadraticBezier = quadraticBezier;\n /**\n * Performs a spline interpolation with two control points and a tension parameter\n */\n function spline(out, a, b, c, d, t, tension) {\n out[0] = _spline(a[0], b[0], c[0], d[0], t, tension);\n out[1] = _spline(a[1], b[1], c[1], d[1], t, tension);\n out[2] = _spline(a[2], b[2], c[2], d[2], t, tension);\n return out;\n }\n Vec3.spline = spline;\n /**\n * Generates a random vector with the given scale\n */\n function random(out, scale) {\n const r = Math.random() * 2.0 * Math.PI;\n const z = (Math.random() * 2.0) - 1.0;\n const zScale = Math.sqrt(1.0 - z * z) * scale;\n out[0] = Math.cos(r) * zScale;\n out[1] = Math.sin(r) * zScale;\n out[2] = z * scale;\n return out;\n }\n Vec3.random = random;\n /**\n * Transforms the Vec3 with a Mat4. 4th vector component is implicitly '1'\n */\n function transformMat4(out, a, m) {\n const x = a[0], y = a[1], z = a[2], w = 1 / ((m[3] * x + m[7] * y + m[11] * z + m[15]) || 1.0);\n out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) * w;\n out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) * w;\n out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) * w;\n return out;\n }\n Vec3.transformMat4 = transformMat4;\n function transformDirection(out, a, m) {\n const x = a[0], y = a[1], z = a[2];\n out[0] = m[0] * x + m[4] * y + m[8] * z;\n out[1] = m[1] * x + m[5] * y + m[9] * z;\n out[2] = m[2] * x + m[6] * y + m[10] * z;\n return normalize(out, out);\n }\n Vec3.transformDirection = transformDirection;\n /**\n * Like `transformMat4` but with offsets into arrays\n */\n function transformMat4Offset(out, a, m, outO, aO, oM) {\n const x = a[0 + aO], y = a[1 + aO], z = a[2 + aO], w = 1 / ((m[3 + oM] * x + m[7 + oM] * y + m[11 + oM] * z + m[15 + oM]) || 1.0);\n out[0 + outO] = (m[0 + oM] * x + m[4 + oM] * y + m[8 + oM] * z + m[12 + oM]) * w;\n out[1 + outO] = (m[1 + oM] * x + m[5 + oM] * y + m[9 + oM] * z + m[13 + oM]) * w;\n out[2 + outO] = (m[2 + oM] * x + m[6 + oM] * y + m[10 + oM] * z + m[14 + oM]) * w;\n return out;\n }\n Vec3.transformMat4Offset = transformMat4Offset;\n /**\n * Transforms the direction vector with a Mat4. 4th vector component is implicitly '0'\n * This means the translation components of the matrix are ignored.\n * Assumes that m is already the transpose of the inverse matrix suitable for normal transformation.\n */\n function transformDirectionOffset(out, a, m, outO, aO, oM) {\n const x = a[0 + aO], y = a[1 + aO], z = a[2 + aO];\n out[0 + outO] = m[0 + oM] * x + m[4 + oM] * y + m[8 + oM] * z;\n out[1 + outO] = m[1 + oM] * x + m[5 + oM] * y + m[9 + oM] * z;\n out[2 + outO] = m[2 + oM] * x + m[6 + oM] * y + m[10 + oM] * z;\n // Normalize the output vector to handle non-uniform scaling\n const len = Math.hypot(out[0 + outO], out[1 + outO], out[2 + outO]);\n if (len > 0) {\n out[0 + outO] /= len;\n out[1 + outO] /= len;\n out[2 + outO] /= len;\n }\n return out;\n }\n Vec3.transformDirectionOffset = transformDirectionOffset;\n /**\n * Transforms the Vec3 with a Mat3.\n */\n function transformMat3(out, a, m) {\n const x = a[0], y = a[1], z = a[2];\n out[0] = x * m[0] + y * m[3] + z * m[6];\n out[1] = x * m[1] + y * m[4] + z * m[7];\n out[2] = x * m[2] + y * m[5] + z * m[8];\n return out;\n }\n Vec3.transformMat3 = transformMat3;\n /** Transforms the Vec3 with a quat */\n function transformQuat(out, a, q) {\n // benchmarks: http://jsperf.com/quaternion-transform-vec3-implementations\n const x = a[0], y = a[1], z = a[2];\n const qx = q[0], qy = q[1], qz = q[2], qw = q[3];\n // calculate quat * vec\n const ix = qw * x + qy * z - qz * y;\n const iy = qw * y + qz * x - qx * z;\n const iz = qw * z + qx * y - qy * x;\n const iw = -qx * x - qy * y - qz * z;\n // calculate result * inverse quat\n out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy;\n out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz;\n out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx;\n return out;\n }\n Vec3.transformQuat = transformQuat;\n /** Computes the angle between 2 vectors, reports in radians. */\n function angle(a, b) {\n const denominator = Math.sqrt(squaredMagnitude(a) * squaredMagnitude(b));\n if (denominator === 0)\n return Math.PI / 2;\n const theta = dot(a, b) / denominator;\n return Math.acos(_clamp(theta, -1, 1)); // clamp to avoid numerical problems\n }\n Vec3.angle = angle;\n const tmp_dh_ab = zero();\n const tmp_dh_cb = zero();\n const tmp_dh_bc = zero();\n const tmp_dh_dc = zero();\n const tmp_dh_abc = zero();\n const tmp_dh_bcd = zero();\n const tmp_dh_cross = zero();\n /**\n * Computes the dihedral angles of 4 points, reports in radians.\n */\n function dihedralAngle(a, b, c, d) {\n sub(tmp_dh_ab, a, b);\n sub(tmp_dh_cb, c, b);\n sub(tmp_dh_bc, b, c);\n sub(tmp_dh_dc, d, c);\n cross(tmp_dh_abc, tmp_dh_ab, tmp_dh_cb);\n cross(tmp_dh_bcd, tmp_dh_bc, tmp_dh_dc);\n const _angle = angle(tmp_dh_abc, tmp_dh_bcd);\n cross(tmp_dh_cross, tmp_dh_abc, tmp_dh_bcd);\n return dot(tmp_dh_cb, tmp_dh_cross) > 0 ? _angle : -_angle;\n }\n Vec3.dihedralAngle = dihedralAngle;\n /**\n * @param inclination in radians [0, PI]\n * @param azimuth in radians [0, 2 * PI]\n * @param radius [0, +Inf]\n */\n function directionFromSpherical(out, inclination, azimuth, radius) {\n return Vec3.set(out, radius * Math.cos(azimuth) * Math.sin(inclination), radius * Math.sin(azimuth) * Math.sin(inclination), radius * Math.cos(inclination));\n }\n Vec3.directionFromSpherical = directionFromSpherical;\n /**\n * Returns whether or not the vectors have exactly the same elements in the same position (when compared with ===)\n */\n function exactEquals(a, b) {\n return a[0] === b[0] && a[1] === b[1] && a[2] === b[2];\n }\n Vec3.exactEquals = exactEquals;\n /**\n * Returns whether or not the vectors have approximately the same elements in the same position.\n */\n function equals(a, b) {\n const a0 = a[0], a1 = a[1], a2 = a[2];\n const b0 = b[0], b1 = b[1], b2 = b[2];\n return (Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&\n Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&\n Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)));\n }\n Vec3.equals = equals;\n const rotTemp = zero();\n function makeRotation(mat, a, b) {\n const by = angle(a, b);\n if (Math.abs(by) < 0.0001)\n return Mat4.setIdentity(mat);\n if (Math.abs(by - Math.PI) < EPSILON) {\n // here, axis can be [0,0,0] but the rotation is a simple flip\n return Mat4.fromScaling(mat, Vec3.negUnit);\n }\n const axis = cross(rotTemp, a, b);\n return Mat4.fromRotation(mat, by, axis);\n }\n Vec3.makeRotation = makeRotation;\n function isZero(v) {\n return v[0] === 0 && v[1] === 0 && v[2] === 0;\n }\n Vec3.isZero = isZero;\n /** Project `point` onto `vector` starting from `origin` */\n function projectPointOnVector(out, point, vector, origin) {\n sub(out, point, origin);\n const scalar = dot(vector, out) / squaredMagnitude(vector);\n return add(out, scale(out, vector, scalar), origin);\n }\n Vec3.projectPointOnVector = projectPointOnVector;\n const tmpProjectPlane = zero();\n /** Project `point` onto `plane` defined by `normal` starting from `origin` */\n function projectPointOnPlane(out, point, normal, origin) {\n normalize(tmpProjectPlane, normal);\n sub(out, point, origin);\n return sub(out, point, scale(tmpProjectPlane, tmpProjectPlane, dot(out, tmpProjectPlane)));\n }\n Vec3.projectPointOnPlane = projectPointOnPlane;\n function projectOnVector(out, p, vector) {\n const scalar = dot(vector, p) / squaredMagnitude(vector);\n return scale(out, vector, scalar);\n }\n Vec3.projectOnVector = projectOnVector;\n const tmpProject = zero();\n function projectOnPlane(out, p, normal) {\n projectOnVector(tmpProject, p, normal);\n return sub(out, p, tmpProject);\n }\n Vec3.projectOnPlane = projectOnPlane;\n /** Get a vector that is similar to `b` but orthogonal to `a` */\n function orthogonalize(out, a, b) {\n return normalize(out, cross(out, cross(out, a, b), a));\n }\n Vec3.orthogonalize = orthogonalize;\n /**\n * Get a vector like `a` that point into the same general direction as `b`,\n * i.e. where the dot product is > 0\n */\n function matchDirection(out, a, b) {\n if (dot(a, b) > 0)\n copy(out, a);\n else\n negate(out, copy(out, a));\n return out;\n }\n Vec3.matchDirection = matchDirection;\n const triangleNormalTmpAB = zero();\n const triangleNormalTmpAC = zero();\n /** Calculate normal for the triangle defined by `a`, `b` and `c` */\n function triangleNormal(out, a, b, c) {\n sub(triangleNormalTmpAB, b, a);\n sub(triangleNormalTmpAC, c, a);\n return normalize(out, cross(out, triangleNormalTmpAB, triangleNormalTmpAC));\n }\n Vec3.triangleNormal = triangleNormal;\n function toString(a, precision) {\n return `[${a[0].toPrecision(precision)} ${a[1].toPrecision(precision)} ${a[2].toPrecision(precision)}]`;\n }\n Vec3.toString = toString;\n Vec3.origin = create(0, 0, 0);\n Vec3.unit = create(1, 1, 1);\n Vec3.negUnit = create(-1, -1, -1);\n Vec3.unitX = create(1, 0, 0);\n Vec3.unitY = create(0, 1, 0);\n Vec3.unitZ = create(0, 0, 1);\n Vec3.negUnitX = create(-1, 0, 0);\n Vec3.negUnitY = create(0, -1, 0);\n Vec3.negUnitZ = create(0, 0, -1);\n})(Vec3 || (Vec3 = {}));\nexport { Vec3 };\n","/**\n * Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.\n *\n * @author Alexander Rose \n */\nexport const halfPI = Math.PI / 2;\nexport const PiDiv180 = Math.PI / 180;\nexport function degToRad(deg) {\n return deg * PiDiv180; // deg * Math.PI / 180\n}\nexport function radToDeg(rad) {\n return rad / PiDiv180; // rad * 180 / Math.PI\n}\nexport function isPowerOfTwo(x) {\n return (x !== 0) && (x & (x - 1)) === 0;\n}\n/** return the value that has the largest absolute value */\nexport function absMax(...values) {\n let max = 0;\n let absMax = 0;\n for (let i = 0, il = values.length; i < il; ++i) {\n const value = values[i];\n const abs = Math.abs(value);\n if (abs > absMax) {\n max = value;\n absMax = abs;\n }\n }\n return max;\n}\n/** Length of an arc with angle in radians */\nexport function arcLength(angle, radius) {\n return angle * radius;\n}\n/** Create an outward spiral of given `radius` on a 2d grid */\nexport function spiral2d(radius) {\n let x = 0;\n let y = 0;\n const delta = [0, -1];\n const size = radius * 2 + 1;\n const halfSize = size / 2;\n const out = [];\n for (let i = Math.pow(size, 2); i > 0; --i) {\n if ((-halfSize < x && x <= halfSize) && (-halfSize < y && y <= halfSize)) {\n out.push([x, y]);\n }\n if (x === y || (x < 0 && x === -y) || (x > 0 && x === 1 - y)) {\n [delta[0], delta[1]] = [-delta[1], delta[0]]; // change direction\n }\n x += delta[0];\n y += delta[1];\n }\n return out;\n}\n","/**\n * Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.\n *\n * @author David Sehnal \n * @author Alexander Rose \n */\n/*\n * This code has been modified from https://github.com/toji/gl-matrix/,\n * copyright (c) 2015, Brandon Jones, Colin MacKenzie IV.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n */\nimport { EPSILON, equalEps } from './common';\nimport { Vec3 } from './vec3';\nimport { degToRad } from '../../misc';\nfunction Mat4() {\n return Mat4.zero();\n}\n/**\n * Stores a 4x4 matrix in a column major (j * 4 + i indexing) format.\n */\n(function (Mat4) {\n function zero() {\n // force double backing array by 0.1.\n const ret = [0.1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];\n ret[0] = 0.0;\n return ret;\n }\n Mat4.zero = zero;\n function identity() {\n const out = zero();\n out[0] = 1;\n out[1] = 0;\n out[2] = 0;\n out[3] = 0;\n out[4] = 0;\n out[5] = 1;\n out[6] = 0;\n out[7] = 0;\n out[8] = 0;\n out[9] = 0;\n out[10] = 1;\n out[11] = 0;\n out[12] = 0;\n out[13] = 0;\n out[14] = 0;\n out[15] = 1;\n return out;\n }\n Mat4.identity = identity;\n function setIdentity(mat) {\n mat[0] = 1;\n mat[1] = 0;\n mat[2] = 0;\n mat[3] = 0;\n mat[4] = 0;\n mat[5] = 1;\n mat[6] = 0;\n mat[7] = 0;\n mat[8] = 0;\n mat[9] = 0;\n mat[10] = 1;\n mat[11] = 0;\n mat[12] = 0;\n mat[13] = 0;\n mat[14] = 0;\n mat[15] = 1;\n return mat;\n }\n Mat4.setIdentity = setIdentity;\n function setZero(mat) {\n for (let i = 0; i < 16; i++)\n mat[i] = 0;\n return mat;\n }\n Mat4.setZero = setZero;\n function ofRows(rows) {\n const out = zero();\n for (let i = 0; i < 4; i++) {\n const r = rows[i];\n for (let j = 0; j < 4; j++) {\n out[4 * j + i] = r[j];\n }\n }\n return out;\n }\n Mat4.ofRows = ofRows;\n const _id = identity();\n function isIdentity(m, eps) {\n return areEqual(m, _id, typeof eps === 'undefined' ? EPSILON : eps);\n }\n Mat4.isIdentity = isIdentity;\n function hasNaN(m) {\n for (let i = 0; i < 16; i++)\n if (isNaN(m[i]))\n return true;\n return false;\n }\n Mat4.hasNaN = hasNaN;\n function areEqual(a, b, eps) {\n for (let i = 0; i < 16; i++) {\n if (Math.abs(a[i] - b[i]) > eps)\n return false;\n }\n return true;\n }\n Mat4.areEqual = areEqual;\n function setValue(a, i, j, value) {\n a[4 * j + i] = value;\n }\n Mat4.setValue = setValue;\n function getValue(a, i, j) {\n return a[4 * j + i];\n }\n Mat4.getValue = getValue;\n function toArray(a, out, offset) {\n out[offset + 0] = a[0];\n out[offset + 1] = a[1];\n out[offset + 2] = a[2];\n out[offset + 3] = a[3];\n out[offset + 4] = a[4];\n out[offset + 5] = a[5];\n out[offset + 6] = a[6];\n out[offset + 7] = a[7];\n out[offset + 8] = a[8];\n out[offset + 9] = a[9];\n out[offset + 10] = a[10];\n out[offset + 11] = a[11];\n out[offset + 12] = a[12];\n out[offset + 13] = a[13];\n out[offset + 14] = a[14];\n out[offset + 15] = a[15];\n return out;\n }\n Mat4.toArray = toArray;\n function fromArray(a, array, offset) {\n a[0] = array[offset + 0];\n a[1] = array[offset + 1];\n a[2] = array[offset + 2];\n a[3] = array[offset + 3];\n a[4] = array[offset + 4];\n a[5] = array[offset + 5];\n a[6] = array[offset + 6];\n a[7] = array[offset + 7];\n a[8] = array[offset + 8];\n a[9] = array[offset + 9];\n a[10] = array[offset + 10];\n a[11] = array[offset + 11];\n a[12] = array[offset + 12];\n a[13] = array[offset + 13];\n a[14] = array[offset + 14];\n a[15] = array[offset + 15];\n return a;\n }\n Mat4.fromArray = fromArray;\n function fromBasis(a, x, y, z) {\n setZero(a);\n setValue(a, 0, 0, x[0]);\n setValue(a, 1, 0, x[1]);\n setValue(a, 2, 0, x[2]);\n setValue(a, 0, 1, y[0]);\n setValue(a, 1, 1, y[1]);\n setValue(a, 2, 1, y[2]);\n setValue(a, 0, 2, z[0]);\n setValue(a, 1, 2, z[1]);\n setValue(a, 2, 2, z[2]);\n setValue(a, 3, 3, 1);\n return a;\n }\n Mat4.fromBasis = fromBasis;\n function copy(out, a) {\n out[0] = a[0];\n out[1] = a[1];\n out[2] = a[2];\n out[3] = a[3];\n out[4] = a[4];\n out[5] = a[5];\n out[6] = a[6];\n out[7] = a[7];\n out[8] = a[8];\n out[9] = a[9];\n out[10] = a[10];\n out[11] = a[11];\n out[12] = a[12];\n out[13] = a[13];\n out[14] = a[14];\n out[15] = a[15];\n return out;\n }\n Mat4.copy = copy;\n function clone(a) {\n return copy(zero(), a);\n }\n Mat4.clone = clone;\n /**\n * Returns the translation vector component of a transformation matrix.\n */\n function getTranslation(out, mat) {\n out[0] = mat[12];\n out[1] = mat[13];\n out[2] = mat[14];\n return out;\n }\n Mat4.getTranslation = getTranslation;\n /**\n * Returns the scaling factor component of a transformation matrix.\n */\n function getScaling(out, mat) {\n const m11 = mat[0];\n const m12 = mat[1];\n const m13 = mat[2];\n const m21 = mat[4];\n const m22 = mat[5];\n const m23 = mat[6];\n const m31 = mat[8];\n const m32 = mat[9];\n const m33 = mat[10];\n out[0] = Math.sqrt(m11 * m11 + m12 * m12 + m13 * m13);\n out[1] = Math.sqrt(m21 * m21 + m22 * m22 + m23 * m23);\n out[2] = Math.sqrt(m31 * m31 + m32 * m32 + m33 * m33);\n return out;\n }\n Mat4.getScaling = getScaling;\n /**\n * Returns a quaternion representing the rotational component of a transformation matrix.\n */\n function getRotation(out, mat) {\n // Algorithm taken from http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm\n const trace = mat[0] + mat[5] + mat[10];\n let S = 0;\n if (trace > 0) {\n S = Math.sqrt(trace + 1.0) * 2;\n out[3] = 0.25 * S;\n out[0] = (mat[6] - mat[9]) / S;\n out[1] = (mat[8] - mat[2]) / S;\n out[2] = (mat[1] - mat[4]) / S;\n }\n else if ((mat[0] > mat[5]) && (mat[0] > mat[10])) {\n S = Math.sqrt(1.0 + mat[0] - mat[5] - mat[10]) * 2;\n out[3] = (mat[6] - mat[9]) / S;\n out[0] = 0.25 * S;\n out[1] = (mat[1] + mat[4]) / S;\n out[2] = (mat[8] + mat[2]) / S;\n }\n else if (mat[5] > mat[10]) {\n S = Math.sqrt(1.0 + mat[5] - mat[0] - mat[10]) * 2;\n out[3] = (mat[8] - mat[2]) / S;\n out[0] = (mat[1] + mat[4]) / S;\n out[1] = 0.25 * S;\n out[2] = (mat[6] + mat[9]) / S;\n }\n else {\n S = Math.sqrt(1.0 + mat[10] - mat[0] - mat[5]) * 2;\n out[3] = (mat[1] - mat[4]) / S;\n out[0] = (mat[8] + mat[2]) / S;\n out[1] = (mat[6] + mat[9]) / S;\n out[2] = 0.25 * S;\n }\n return out;\n }\n Mat4.getRotation = getRotation;\n function extractRotation(out, mat) {\n const scaleX = 1 / Math.sqrt(mat[0] * mat[0] + mat[1] * mat[1] + mat[2] * mat[2]);\n const scaleY = 1 / Math.sqrt(mat[4] * mat[4] + mat[5] * mat[5] + mat[6] * mat[6]);\n const scaleZ = 1 / Math.sqrt(mat[8] * mat[8] + mat[9] * mat[9] + mat[10] * mat[10]);\n out[0] = mat[0] * scaleX;\n out[1] = mat[1] * scaleX;\n out[2] = mat[2] * scaleX;\n out[3] = 0;\n out[4] = mat[4] * scaleY;\n out[5] = mat[5] * scaleY;\n out[6] = mat[6] * scaleY;\n out[7] = 0;\n out[8] = mat[8] * scaleZ;\n out[9] = mat[9] * scaleZ;\n out[10] = mat[10] * scaleZ;\n out[11] = 0;\n out[12] = 0;\n out[13] = 0;\n out[14] = 0;\n out[15] = 1;\n return out;\n }\n Mat4.extractRotation = extractRotation;\n function transpose(out, a) {\n // If we are transposing ourselves we can skip a few steps but have to cache some values\n if (out === a) {\n const a01 = a[1], a02 = a[2], a03 = a[3];\n const a12 = a[6], a13 = a[7];\n const a23 = a[11];\n out[1] = a[4];\n out[2] = a[8];\n out[3] = a[12];\n out[4] = a01;\n out[6] = a[9];\n out[7] = a[13];\n out[8] = a02;\n out[9] = a12;\n out[11] = a[14];\n out[12] = a03;\n out[13] = a13;\n out[14] = a23;\n }\n else {\n out[0] = a[0];\n out[1] = a[4];\n out[2] = a[8];\n out[3] = a[12];\n out[4] = a[1];\n out[5] = a[5];\n out[6] = a[9];\n out[7] = a[13];\n out[8] = a[2];\n out[9] = a[6];\n out[10] = a[10];\n out[11] = a[14];\n out[12] = a[3];\n out[13] = a[7];\n out[14] = a[11];\n out[15] = a[15];\n }\n return out;\n }\n Mat4.transpose = transpose;\n function tryInvert(out, a) {\n const a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], b00 = a00 * a11 - a01 * a10, b01 = a00 * a12 - a02 * a10, b02 = a00 * a13 - a03 * a10, b03 = a01 * a12 - a02 * a11, b04 = a01 * a13 - a03 * a11, b05 = a02 * a13 - a03 * a12, b06 = a20 * a31 - a21 * a30, b07 = a20 * a32 - a22 * a30, b08 = a20 * a33 - a23 * a30, b09 = a21 * a32 - a22 * a31, b10 = a21 * a33 - a23 * a31, b11 = a22 * a33 - a23 * a32;\n // Calculate the determinant\n let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;\n if (!det) {\n return false;\n }\n det = 1.0 / det;\n out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;\n out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;\n out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;\n out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;\n out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;\n out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;\n out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;\n out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;\n out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;\n out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;\n out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;\n out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;\n out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;\n out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;\n out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;\n out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;\n return true;\n }\n Mat4.tryInvert = tryInvert;\n function invert(out, a) {\n if (!tryInvert(out, a)) {\n console.warn('non-invertible matrix.', a);\n }\n return out;\n }\n Mat4.invert = invert;\n function mul(out, a, b) {\n const a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];\n // Cache only the current line of the second matrix\n let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];\n out[0] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;\n out[1] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;\n out[2] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;\n out[3] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;\n b0 = b[4];\n b1 = b[5];\n b2 = b[6];\n b3 = b[7];\n out[4] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;\n out[5] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;\n out[6] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;\n out[7] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;\n b0 = b[8];\n b1 = b[9];\n b2 = b[10];\n b3 = b[11];\n out[8] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;\n out[9] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;\n out[10] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;\n out[11] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;\n b0 = b[12];\n b1 = b[13];\n b2 = b[14];\n b3 = b[15];\n out[12] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;\n out[13] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;\n out[14] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;\n out[15] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;\n return out;\n }\n Mat4.mul = mul;\n /**\n * Like `mul` but with offsets into arrays\n */\n function mulOffset(out, a, b, oOut, oA, oB) {\n const a00 = a[0 + oA], a01 = a[1 + oA], a02 = a[2 + oA], a03 = a[3 + oA], a10 = a[4 + oA], a11 = a[5 + oA], a12 = a[6 + oA], a13 = a[7 + oA], a20 = a[8 + oA], a21 = a[9 + oA], a22 = a[10 + oA], a23 = a[11 + oA], a30 = a[12 + oA], a31 = a[13 + oA], a32 = a[14 + oA], a33 = a[15 + oA];\n // Cache only the current line of the second matrix\n let b0 = b[0 + oB], b1 = b[1 + oB], b2 = b[2 + oB], b3 = b[3 + oB];\n out[0 + oOut] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;\n out[1 + oOut] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;\n out[2 + oOut] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;\n out[3 + oOut] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;\n b0 = b[4 + oB];\n b1 = b[5 + oB];\n b2 = b[6 + oB];\n b3 = b[7 + oB];\n out[4 + oOut] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;\n out[5 + oOut] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;\n out[6 + oOut] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;\n out[7 + oOut] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;\n b0 = b[8 + oB];\n b1 = b[9 + oB];\n b2 = b[10 + oB];\n b3 = b[11 + oB];\n out[8 + oOut] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;\n out[9 + oOut] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;\n out[10 + oOut] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;\n out[11 + oOut] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;\n b0 = b[12 + oB];\n b1 = b[13 + oB];\n b2 = b[14 + oB];\n b3 = b[15 + oB];\n out[12 + oOut] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;\n out[13 + oOut] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;\n out[14 + oOut] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;\n out[15 + oOut] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;\n return out;\n }\n Mat4.mulOffset = mulOffset;\n function mul3(out, a, b, c) {\n return mul(out, mul(out, a, b), c);\n }\n Mat4.mul3 = mul3;\n /** Translate a Mat4 by the given Vec3 */\n function translate(out, a, v) {\n const x = v[0], y = v[1], z = v[2];\n let a00, a01, a02, a03, a10, a11, a12, a13, a20, a21, a22, a23;\n if (a === out) {\n out[12] = a[0] * x + a[4] * y + a[8] * z + a[12];\n out[13] = a[1] * x + a[5] * y + a[9] * z + a[13];\n out[14] = a[2] * x + a[6] * y + a[10] * z + a[14];\n out[15] = a[3] * x + a[7] * y + a[11] * z + a[15];\n }\n else {\n a00 = a[0];\n a01 = a[1];\n a02 = a[2];\n a03 = a[3];\n a10 = a[4];\n a11 = a[5];\n a12 = a[6];\n a13 = a[7];\n a20 = a[8];\n a21 = a[9];\n a22 = a[10];\n a23 = a[11];\n out[0] = a00;\n out[1] = a01;\n out[2] = a02;\n out[3] = a03;\n out[4] = a10;\n out[5] = a11;\n out[6] = a12;\n out[7] = a13;\n out[8] = a20;\n out[9] = a21;\n out[10] = a22;\n out[11] = a23;\n out[12] = a00 * x + a10 * y + a20 * z + a[12];\n out[13] = a01 * x + a11 * y + a21 * z + a[13];\n out[14] = a02 * x + a12 * y + a22 * z + a[14];\n out[15] = a03 * x + a13 * y + a23 * z + a[15];\n }\n return out;\n }\n Mat4.translate = translate;\n function fromTranslation(out, v) {\n out[0] = 1;\n out[1] = 0;\n out[2] = 0;\n out[3] = 0;\n out[4] = 0;\n out[5] = 1;\n out[6] = 0;\n out[7] = 0;\n out[8] = 0;\n out[9] = 0;\n out[10] = 1;\n out[11] = 0;\n out[12] = v[0];\n out[13] = v[1];\n out[14] = v[2];\n out[15] = 1;\n return out;\n }\n Mat4.fromTranslation = fromTranslation;\n function setTranslation(out, v) {\n out[12] = v[0];\n out[13] = v[1];\n out[14] = v[2];\n return out;\n }\n Mat4.setTranslation = setTranslation;\n /**\n * Sets the specified quaternion with values corresponding to the given\n * axes. Each axis is a vec3 and is expected to be unit length and\n * perpendicular to all other specified axes.\n */\n function setAxes(out, view, right, up) {\n out[0] = right[0];\n out[4] = right[1];\n out[8] = right[2];\n out[1] = up[0];\n out[5] = up[1];\n out[9] = up[2];\n out[2] = view[0];\n out[6] = view[1];\n out[10] = view[2];\n return out;\n }\n Mat4.setAxes = setAxes;\n function rotate(out, a, rad, axis) {\n let x = axis[0], y = axis[1], z = axis[2];\n let len = Math.sqrt(x * x + y * y + z * z);\n if (Math.abs(len) < EPSILON) {\n return identity();\n }\n len = 1 / len;\n x *= len;\n y *= len;\n z *= len;\n const s = Math.sin(rad);\n const c = Math.cos(rad);\n const t = 1 - c;\n const a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];\n const a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];\n const a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];\n // Construct the elements of the rotation matrix\n const b00 = x * x * t + c, b01 = y * x * t + z * s, b02 = z * x * t - y * s;\n const b10 = x * y * t - z * s, b11 = y * y * t + c, b12 = z * y * t + x * s;\n const b20 = x * z * t + y * s, b21 = y * z * t - x * s, b22 = z * z * t + c;\n // Perform rotation-specific matrix multiplication\n out[0] = a00 * b00 + a10 * b01 + a20 * b02;\n out[1] = a01 * b00 + a11 * b01 + a21 * b02;\n out[2] = a02 * b00 + a12 * b01 + a22 * b02;\n out[3] = a03 * b00 + a13 * b01 + a23 * b02;\n out[4] = a00 * b10 + a10 * b11 + a20 * b12;\n out[5] = a01 * b10 + a11 * b11 + a21 * b12;\n out[6] = a02 * b10 + a12 * b11 + a22 * b12;\n out[7] = a03 * b10 + a13 * b11 + a23 * b12;\n out[8] = a00 * b20 + a10 * b21 + a20 * b22;\n out[9] = a01 * b20 + a11 * b21 + a21 * b22;\n out[10] = a02 * b20 + a12 * b21 + a22 * b22;\n out[11] = a03 * b20 + a13 * b21 + a23 * b22;\n if (a !== out) { // If the source and destination differ, copy the unchanged last row\n out[12] = a[12];\n out[13] = a[13];\n out[14] = a[14];\n out[15] = a[15];\n }\n return out;\n }\n Mat4.rotate = rotate;\n function fromRotation(out, rad, axis) {\n let x = axis[0], y = axis[1], z = axis[2];\n let len = Math.sqrt(x * x + y * y + z * z);\n if (Math.abs(len) < EPSILON) {\n return setIdentity(out);\n }\n len = 1 / len;\n x *= len;\n y *= len;\n z *= len;\n const s = Math.sin(rad);\n const c = Math.cos(rad);\n const t = 1 - c;\n // Perform rotation-specific matrix multiplication\n out[0] = x * x * t + c;\n out[1] = y * x * t + z * s;\n out[2] = z * x * t - y * s;\n out[3] = 0;\n out[4] = x * y * t - z * s;\n out[5] = y * y * t + c;\n out[6] = z * y * t + x * s;\n out[7] = 0;\n out[8] = x * z * t + y * s;\n out[9] = y * z * t - x * s;\n out[10] = z * z * t + c;\n out[11] = 0;\n out[12] = 0;\n out[13] = 0;\n out[14] = 0;\n out[15] = 1;\n return out;\n }\n Mat4.fromRotation = fromRotation;\n function scale(out, a, v) {\n const x = v[0], y = v[1], z = v[2];\n out[0] = a[0] * x;\n out[1] = a[1] * x;\n out[2] = a[2] * x;\n out[3] = a[3] * x;\n out[4] = a[4] * y;\n out[5] = a[5] * y;\n out[6] = a[6] * y;\n out[7] = a[7] * y;\n out[8] = a[8] * z;\n out[9] = a[9] * z;\n out[10] = a[10] * z;\n out[11] = a[11] * z;\n out[12] = a[12];\n out[13] = a[13];\n out[14] = a[14];\n out[15] = a[15];\n return out;\n }\n Mat4.scale = scale;\n function scaleUniformly(out, a, scale) {\n out[0] = a[0] * scale;\n out[1] = a[1] * scale;\n out[2] = a[2] * scale;\n out[3] = a[3] * scale;\n out[4] = a[4] * scale;\n out[5] = a[5] * scale;\n out[6] = a[6] * scale;\n out[7] = a[7] * scale;\n out[8] = a[8] * scale;\n out[9] = a[9] * scale;\n out[10] = a[10] * scale;\n out[11] = a[11] * scale;\n out[12] = a[12];\n out[13] = a[13];\n out[14] = a[14];\n out[15] = a[15];\n return out;\n }\n Mat4.scaleUniformly = scaleUniformly;\n function fromScaling(out, v) {\n out[0] = v[0];\n out[1] = 0;\n out[2] = 0;\n out[3] = 0;\n out[4] = 0;\n out[5] = v[1];\n out[6] = 0;\n out[7] = 0;\n out[8] = 0;\n out[9] = 0;\n out[10] = v[2];\n out[11] = 0;\n out[12] = 0;\n out[13] = 0;\n out[14] = 0;\n out[15] = 1;\n return out;\n }\n Mat4.fromScaling = fromScaling;\n function fromUniformScaling(out, scale) {\n out[0] = scale;\n out[1] = 0;\n out[2] = 0;\n out[3] = 0;\n out[4] = 0;\n out[5] = scale;\n out[6] = 0;\n out[7] = 0;\n out[8] = 0;\n out[9] = 0;\n out[10] = scale;\n out[11] = 0;\n out[12] = 0;\n out[13] = 0;\n out[14] = 0;\n out[15] = 1;\n return out;\n }\n Mat4.fromUniformScaling = fromUniformScaling;\n /**\n * Copies the mat3 into upper-left 3x3 values.\n */\n function fromMat3(out, a) {\n out[0] = a[0];\n out[1] = a[1];\n out[2] = a[2];\n out[4] = a[3];\n out[5] = a[4];\n out[6] = a[5];\n out[8] = a[6];\n out[9] = a[7];\n out[10] = a[8];\n return out;\n }\n Mat4.fromMat3 = fromMat3;\n function compose(out, position, quaternion, scale) {\n const [x, y, z, w] = quaternion;\n const x2 = x + x, y2 = y + y, z2 = z + z;\n const xx = x * x2, xy = x * y2, xz = x * z2;\n const yy = y * y2, yz = y * z2, zz = z * z2;\n const wx = w * x2, wy = w * y2, wz = w * z2;\n const [sx, sy, sz] = scale;\n out[0] = (1 - (yy + zz)) * sx;\n out[1] = (xy + wz) * sx;\n out[2] = (xz - wy) * sx;\n out[3] = 0;\n out[4] = (xy - wz) * sy;\n out[5] = (1 - (xx + zz)) * sy;\n out[6] = (yz + wx) * sy;\n out[7] = 0;\n out[8] = (xz + wy) * sz;\n out[9] = (yz - wx) * sz;\n out[10] = (1 - (xx + yy)) * sz;\n out[11] = 0;\n out[12] = position[0];\n out[13] = position[1];\n out[14] = position[2];\n out[15] = 1;\n return out;\n }\n Mat4.compose = compose;\n const _v3 = [0, 0, 0];\n const _m4 = zero();\n function decompose(m, position, quaternion, scale) {\n let sx = Vec3.magnitude(Vec3.set(_v3, m[0], m[1], m[2]));\n const sy = Vec3.magnitude(Vec3.set(_v3, m[4], m[5], m[6]));\n const sz = Vec3.magnitude(Vec3.set(_v3, m[8], m[9], m[10]));\n // if determine is negative, we need to invert one scale\n const det = determinant(m);\n if (det < 0)\n sx = -sx;\n position[0] = m[12];\n position[1] = m[13];\n position[2] = m[14];\n // scale the rotation part\n copy(_m4, m);\n const invSX = 1 / sx;\n const invSY = 1 / sy;\n const invSZ = 1 / sz;\n _m4[0] *= invSX;\n _m4[1] *= invSX;\n _m4[2] *= invSX;\n _m4[4] *= invSY;\n _m4[5] *= invSY;\n _m4[6] *= invSY;\n _m4[8] *= invSZ;\n _m4[9] *= invSZ;\n _m4[10] *= invSZ;\n getRotation(quaternion, _m4);\n scale[0] = sx;\n scale[1] = sy;\n scale[2] = sz;\n return m;\n }\n Mat4.decompose = decompose;\n function makeTable(m) {\n let ret = '';\n for (let i = 0; i < 4; i++) {\n for (let j = 0; j < 4; j++) {\n ret += m[4 * j + i].toString();\n if (j < 3)\n ret += ' ';\n }\n if (i < 3)\n ret += '\\n';\n }\n return ret;\n }\n Mat4.makeTable = makeTable;\n function determinant(a) {\n const a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], b00 = a00 * a11 - a01 * a10, b01 = a00 * a12 - a02 * a10, b02 = a00 * a13 - a03 * a10, b03 = a01 * a12 - a02 * a11, b04 = a01 * a13 - a03 * a11, b05 = a02 * a13 - a03 * a12, b06 = a20 * a31 - a21 * a30, b07 = a20 * a32 - a22 * a30, b08 = a20 * a33 - a23 * a30, b09 = a21 * a32 - a22 * a31, b10 = a21 * a33 - a23 * a31, b11 = a22 * a33 - a23 * a32;\n // Calculate the determinant\n return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;\n }\n Mat4.determinant = determinant;\n /**\n * Check if the matrix has the form\n * [ Rotation Translation ]\n * [ 0 1 ]\n *\n * Allows for improper rotations\n */\n function isRotationAndTranslation(a, eps) {\n return _isRotationAndTranslation(a, typeof eps !== 'undefined' ? eps : EPSILON);\n }\n Mat4.isRotationAndTranslation = isRotationAndTranslation;\n function _isRotationAndTranslation(a, eps) {\n const a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], a33 = a[15];\n if (!equalEps(a33, 1, eps) || !equalEps(a03, 0, eps) || !equalEps(a13, 0, eps) || !equalEps(a23, 0, eps)) {\n return false;\n }\n // use `abs` to allow for improper rotations\n const det3x3 = Math.abs(a00 * (a11 * a22 - a12 * a21) - a01 * (a10 * a22 - a12 * a20) + a02 * (a10 * a21 - a11 * a20));\n if (!equalEps(det3x3, 1, eps)) {\n return false;\n }\n return true;\n }\n /**\n * Check if the matrix has only translation and uniform scaling\n * [ S 0 0 X ]\n * [ 0 S 0 Y ]\n * [ 0 0 S Z ]\n * [ 0 0 0 1 ]\n */\n function isTranslationAndUniformScaling(a, eps) {\n return _isTranslationAndUniformScaling(a, typeof eps !== 'undefined' ? eps : EPSILON);\n }\n Mat4.isTranslationAndUniformScaling = isTranslationAndUniformScaling;\n function _isTranslationAndUniformScaling(a, eps) {\n const a00 = a[0];\n return (\n // 0 base scaling\n equalEps(a[1], 0, eps) &&\n equalEps(a[2], 0, eps) &&\n equalEps(a[3], 0, eps) &&\n equalEps(a[4], 0, eps) &&\n equalEps(a[5], a00, eps) &&\n equalEps(a[6], 0, eps) &&\n equalEps(a[7], 0, eps) &&\n equalEps(a[8], 0, eps) &&\n equalEps(a[9], 0, eps) &&\n equalEps(a[10], a00, eps) &&\n equalEps(a[11], 0, eps) &&\n // 12, 13, 14 translation can be anything\n equalEps(a[15], 1, eps));\n }\n function fromQuat(out, q) {\n const x = q[0], y = q[1], z = q[2], w = q[3];\n const x2 = x + x;\n const y2 = y + y;\n const z2 = z + z;\n const xx = x * x2;\n const yx = y * x2;\n const yy = y * y2;\n const zx = z * x2;\n const zy = z * y2;\n const zz = z * z2;\n const wx = w * x2;\n const wy = w * y2;\n const wz = w * z2;\n out[0] = 1 - yy - zz;\n out[1] = yx + wz;\n out[2] = zx - wy;\n out[3] = 0;\n out[4] = yx - wz;\n out[5] = 1 - xx - zz;\n out[6] = zy + wx;\n out[7] = 0;\n out[8] = zx + wy;\n out[9] = zy - wx;\n out[10] = 1 - xx - yy;\n out[11] = 0;\n out[12] = 0;\n out[13] = 0;\n out[14] = 0;\n out[15] = 1;\n return out;\n }\n Mat4.fromQuat = fromQuat;\n function fromEuler(out, euler, order) {\n const x = euler[0], y = euler[1], z = euler[2];\n const a = Math.cos(x), b = Math.sin(x);\n const c = Math.cos(y), d = Math.sin(y);\n const e = Math.cos(z), f = Math.sin(z);\n if (order === 'XYZ') {\n const ae = a * e, af = a * f, be = b * e, bf = b * f;\n out[0] = c * e;\n out[4] = -c * f;\n out[8] = d;\n out[1] = af + be * d;\n out[5] = ae - bf * d;\n out[9] = -b * c;\n out[2] = bf - ae * d;\n out[6] = be + af * d;\n out[10] = a * c;\n }\n else if (order === 'YXZ') {\n const ce = c * e, cf = c * f, de = d * e, df = d * f;\n out[0] = ce + df * b;\n out[4] = de * b - cf;\n out[8] = a * d;\n out[1] = a * f;\n out[5] = a * e;\n out[9] = -b;\n out[2] = cf * b - de;\n out[6] = df + ce * b;\n out[10] = a * c;\n }\n else if (order === 'ZXY') {\n const ce = c * e, cf = c * f, de = d * e, df = d * f;\n out[0] = ce - df * b;\n out[4] = -a * f;\n out[8] = de + cf * b;\n out[1] = cf + de * b;\n out[5] = a * e;\n out[9] = df - ce * b;\n out[2] = -a * d;\n out[6] = b;\n out[10] = a * c;\n }\n else if (order === 'ZYX') {\n const ae = a * e, af = a * f, be = b * e, bf = b * f;\n out[0] = c * e;\n out[4] = be * d - af;\n out[8] = ae * d + bf;\n out[1] = c * f;\n out[5] = bf * d + ae;\n out[9] = af * d - be;\n out[2] = -d;\n out[6] = b * c;\n out[10] = a * c;\n }\n else if (order === 'YZX') {\n const ac = a * c, ad = a * d, bc = b * c, bd = b * d;\n out[0] = c * e;\n out[4] = bd - ac * f;\n out[8] = bc * f + ad;\n out[1] = f;\n out[5] = a * e;\n out[9] = -b * e;\n out[2] = -d * e;\n out[6] = ad * f + bc;\n out[10] = ac - bd * f;\n }\n else if (order === 'XZY') {\n const ac = a * c, ad = a * d, bc = b * c, bd = b * d;\n out[0] = c * e;\n out[4] = -f;\n out[8] = d * e;\n out[1] = ac * f + bd;\n out[5] = a * e;\n out[9] = ad * f - bc;\n out[2] = bc * f - ad;\n out[6] = b * e;\n out[10] = bd * f + ac;\n }\n // bottom row\n out[3] = 0;\n out[7] = 0;\n out[11] = 0;\n // last column\n out[12] = 0;\n out[13] = 0;\n out[14] = 0;\n out[15] = 1;\n return out;\n }\n Mat4.fromEuler = fromEuler;\n /**\n * Generates a perspective projection (frustum) matrix with the given bounds\n */\n function perspective(out, left, right, top, bottom, near, far) {\n const x = 2 * near / (right - left);\n const y = 2 * near / (top - bottom);\n const a = (right + left) / (right - left);\n const b = (top + bottom) / (top - bottom);\n const c = -(far + near) / (far - near);\n const d = -2 * far * near / (far - near);\n out[0] = x;\n out[1] = 0;\n out[2] = 0;\n out[3] = 0;\n out[4] = 0;\n out[5] = y;\n out[6] = 0;\n out[7] = 0;\n out[8] = a;\n out[9] = b;\n out[10] = c;\n out[11] = -1;\n out[12] = 0;\n out[13] = 0;\n out[14] = d;\n out[15] = 0;\n return out;\n }\n Mat4.perspective = perspective;\n /**\n * Generates a orthogonal projection matrix with the given bounds\n */\n function ortho(out, left, right, top, bottom, near, far) {\n const w = 1.0 / (right - left);\n const h = 1.0 / (top - bottom);\n const p = 1.0 / (far - near);\n const x = (right + left) * w;\n const y = (top + bottom) * h;\n const z = (far + near) * p;\n out[0] = 2 * w;\n out[1] = 0;\n out[2] = 0;\n out[3] = 0;\n out[4] = 0;\n out[5] = 2 * h;\n out[6] = 0;\n out[7] = 0;\n out[8] = 0;\n out[9] = 0;\n out[10] = -2 * p;\n out[11] = 0;\n out[12] = -x;\n out[13] = -y;\n out[14] = -z;\n out[15] = 1;\n return out;\n }\n Mat4.ortho = ortho;\n /**\n * Generates a look-at matrix with the given eye position, focal point, and up axis\n */\n function lookAt(out, eye, center, up) {\n let x0, x1, x2, y0, y1, y2, z0, z1, z2, len;\n const eyex = eye[0];\n const eyey = eye[1];\n const eyez = eye[2];\n const upx = up[0];\n const upy = up[1];\n const upz = up[2];\n const centerx = center[0];\n const centery = center[1];\n const centerz = center[2];\n if (Math.abs(eyex - centerx) < EPSILON &&\n Math.abs(eyey - centery) < EPSILON &&\n Math.abs(eyez - centerz) < EPSILON) {\n return setIdentity(out);\n }\n z0 = eyex - centerx;\n z1 = eyey - centery;\n z2 = eyez - centerz;\n len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2);\n z0 *= len;\n z1 *= len;\n z2 *= len;\n x0 = upy * z2 - upz * z1;\n x1 = upz * z0 - upx * z2;\n x2 = upx * z1 - upy * z0;\n len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2);\n if (!len) {\n x0 = 0;\n x1 = 0;\n x2 = 0;\n }\n else {\n len = 1 / len;\n x0 *= len;\n x1 *= len;\n x2 *= len;\n }\n y0 = z1 * x2 - z2 * x1;\n y1 = z2 * x0 - z0 * x2;\n y2 = z0 * x1 - z1 * x0;\n len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2);\n if (!len) {\n y0 = 0;\n y1 = 0;\n y2 = 0;\n }\n else {\n len = 1 / len;\n y0 *= len;\n y1 *= len;\n y2 *= len;\n }\n out[0] = x0;\n out[1] = y0;\n out[2] = z0;\n out[3] = 0;\n out[4] = x1;\n out[5] = y1;\n out[6] = z1;\n out[7] = 0;\n out[8] = x2;\n out[9] = y2;\n out[10] = z2;\n out[11] = 0;\n out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez);\n out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez);\n out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez);\n out[15] = 1;\n return out;\n }\n Mat4.lookAt = lookAt;\n /**\n * Generates a matrix that makes something look at something else.\n */\n function targetTo(out, eye, target, up) {\n const eyex = eye[0], eyey = eye[1], eyez = eye[2], upx = up[0], upy = up[1], upz = up[2];\n let z0 = eyex - target[0], z1 = eyey - target[1], z2 = eyez - target[2];\n let len = z0 * z0 + z1 * z1 + z2 * z2;\n if (len > 0) {\n len = 1 / Math.sqrt(len);\n z0 *= len;\n z1 *= len;\n z2 *= len;\n }\n let x0 = upy * z2 - upz * z1, x1 = upz * z0 - upx * z2, x2 = upx * z1 - upy * z0;\n len = x0 * x0 + x1 * x1 + x2 * x2;\n if (len > 0) {\n len = 1 / Math.sqrt(len);\n x0 *= len;\n x1 *= len;\n x2 *= len;\n }\n out[0] = x0;\n out[1] = x1;\n out[2] = x2;\n out[3] = 0;\n out[4] = z1 * x2 - z2 * x1;\n out[5] = z2 * x0 - z0 * x2;\n out[6] = z0 * x1 - z1 * x0;\n out[7] = 0;\n out[8] = z0;\n out[9] = z1;\n out[10] = z2;\n out[11] = 0;\n out[12] = eyex;\n out[13] = eyey;\n out[14] = eyez;\n out[15] = 1;\n return out;\n }\n Mat4.targetTo = targetTo;\n /**\n * Perm is 0-indexed permutation\n */\n function fromPermutation(out, perm) {\n setZero(out);\n for (let i = 0; i < 4; i++) {\n const p = perm[i];\n setValue(out, i, p, 1);\n }\n return out;\n }\n Mat4.fromPermutation = fromPermutation;\n function getMaxScaleOnAxis(m) {\n const scaleXSq = m[0] * m[0] + m[1] * m[1] + m[2] * m[2];\n const scaleYSq = m[4] * m[4] + m[5] * m[5] + m[6] * m[6];\n const scaleZSq = m[8] * m[8] + m[9] * m[9] + m[10] * m[10];\n return Math.sqrt(Math.max(scaleXSq, scaleYSq, scaleZSq));\n }\n Mat4.getMaxScaleOnAxis = getMaxScaleOnAxis;\n const xAxis = [1, 0, 0];\n const yAxis = [0, 1, 0];\n const zAxis = [0, 0, 1];\n /** Rotation matrix for 90deg around x-axis */\n Mat4.rotX90 = fromRotation(zero(), degToRad(90), xAxis);\n /** Rotation matrix for 180deg around x-axis */\n Mat4.rotX180 = fromRotation(zero(), degToRad(180), xAxis);\n /** Rotation matrix for 90deg around y-axis */\n Mat4.rotY90 = fromRotation(zero(), degToRad(90), yAxis);\n /** Rotation matrix for 180deg around y-axis */\n Mat4.rotY180 = fromRotation(zero(), degToRad(180), yAxis);\n /** Rotation matrix for 270deg around y-axis */\n Mat4.rotY270 = fromRotation(zero(), degToRad(270), yAxis);\n /** Rotation matrix for 90deg around z-axis */\n Mat4.rotZ90 = fromRotation(zero(), degToRad(90), zAxis);\n /** Rotation matrix for 180deg around z-axis */\n Mat4.rotZ180 = fromRotation(zero(), degToRad(180), zAxis);\n /** Rotation matrix for 90deg around first x-axis and then y-axis */\n Mat4.rotXY90 = mul(zero(), Mat4.rotX90, Mat4.rotY90);\n /** Rotation matrix for 90deg around first z-axis and then y-axis */\n Mat4.rotZY90 = mul(zero(), Mat4.rotZ90, Mat4.rotY90);\n /** Rotation matrix for 90deg around first z-axis and then y-axis and then z-axis */\n Mat4.rotZYZ90 = mul(zero(), Mat4.rotZY90, Mat4.rotZ90);\n /** Rotation matrix for 90deg around first z-axis and then 180deg around x-axis */\n Mat4.rotZ90X180 = mul(zero(), Mat4.rotZ90, Mat4.rotX180);\n /** Rotation matrix for 90deg around first y-axis and then 180deg around z-axis */\n Mat4.rotY90Z180 = mul(zero(), Mat4.rotY90, Mat4.rotZ180);\n /** Identity matrix */\n Mat4.id = identity();\n})(Mat4 || (Mat4 = {}));\nexport { Mat4 };\n","/**\n * Copyright (c) 2017-2024 mol* contributors, licensed under MIT, See LICENSE file for more info.\n *\n * @author David Sehnal \n * @author Alexander Rose \n */\nimport { EPSILON } from './common';\nimport { Mat4 } from './mat4';\nimport { Vec3 } from './vec3';\nfunction Mat3() {\n return Mat3.zero();\n}\n(function (Mat3) {\n function zero() {\n // force double backing array by 0.1.\n const ret = [0.1, 0, 0, 0, 0, 0, 0, 0, 0];\n ret[0] = 0.0;\n return ret;\n }\n Mat3.zero = zero;\n function identity() {\n const out = zero();\n out[0] = 1;\n out[1] = 0;\n out[2] = 0;\n out[3] = 0;\n out[4] = 1;\n out[5] = 0;\n out[6] = 0;\n out[7] = 0;\n out[8] = 1;\n return out;\n }\n Mat3.identity = identity;\n function setIdentity(mat) {\n mat[0] = 1;\n mat[1] = 0;\n mat[2] = 0;\n mat[3] = 0;\n mat[4] = 1;\n mat[5] = 0;\n mat[6] = 0;\n mat[7] = 0;\n mat[8] = 1;\n return mat;\n }\n Mat3.setIdentity = setIdentity;\n function toArray(a, out, offset) {\n out[offset + 0] = a[0];\n out[offset + 1] = a[1];\n out[offset + 2] = a[2];\n out[offset + 3] = a[3];\n out[offset + 4] = a[4];\n out[offset + 5] = a[5];\n out[offset + 6] = a[6];\n out[offset + 7] = a[7];\n out[offset + 8] = a[8];\n return out;\n }\n Mat3.toArray = toArray;\n function fromArray(a, array, offset) {\n a[0] = array[offset + 0];\n a[1] = array[offset + 1];\n a[2] = array[offset + 2];\n a[3] = array[offset + 3];\n a[4] = array[offset + 4];\n a[5] = array[offset + 5];\n a[6] = array[offset + 6];\n a[7] = array[offset + 7];\n a[8] = array[offset + 8];\n return a;\n }\n Mat3.fromArray = fromArray;\n function fromColumns(out, left, middle, right) {\n out[0] = left[0];\n out[1] = left[1];\n out[2] = left[2];\n out[3] = middle[0];\n out[4] = middle[1];\n out[5] = middle[2];\n out[6] = right[0];\n out[7] = right[1];\n out[8] = right[2];\n return out;\n }\n Mat3.fromColumns = fromColumns;\n /**\n * Copies the upper-left 3x3 values into the given mat3.\n */\n function fromMat4(out, a) {\n out[0] = a[0];\n out[1] = a[1];\n out[2] = a[2];\n out[3] = a[4];\n out[4] = a[5];\n out[5] = a[6];\n out[6] = a[8];\n out[7] = a[9];\n out[8] = a[10];\n return out;\n }\n Mat3.fromMat4 = fromMat4;\n const _m4 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];\n function fromEuler(out, euler, order) {\n Mat4.fromEuler(_m4, euler, order);\n return fromMat4(out, _m4);\n }\n Mat3.fromEuler = fromEuler;\n function create(a00, a01, a02, a10, a11, a12, a20, a21, a22) {\n const out = zero();\n out[0] = a00;\n out[1] = a01;\n out[2] = a02;\n out[3] = a10;\n out[4] = a11;\n out[5] = a12;\n out[6] = a20;\n out[7] = a21;\n out[8] = a22;\n return out;\n }\n Mat3.create = create;\n const _id = identity();\n function isIdentity(m, eps) {\n return areEqual(m, _id, typeof eps === 'undefined' ? EPSILON : eps);\n }\n Mat3.isIdentity = isIdentity;\n function hasNaN(m) {\n for (let i = 0; i < 9; i++)\n if (isNaN(m[i]))\n return true;\n return false;\n }\n Mat3.hasNaN = hasNaN;\n /**\n * Creates a new Mat3 initialized with values from an existing matrix\n */\n function clone(a) {\n return copy(zero(), a);\n }\n Mat3.clone = clone;\n function areEqual(a, b, eps) {\n for (let i = 0; i < 9; i++) {\n if (Math.abs(a[i] - b[i]) > eps)\n return false;\n }\n return true;\n }\n Mat3.areEqual = areEqual;\n function setValue(a, i, j, value) {\n a[3 * j + i] = value;\n }\n Mat3.setValue = setValue;\n function getValue(a, i, j) {\n return a[3 * j + i];\n }\n Mat3.getValue = getValue;\n /**\n * Copy the values from one Mat3 to another\n */\n function copy(out, a) {\n out[0] = a[0];\n out[1] = a[1];\n out[2] = a[2];\n out[3] = a[3];\n out[4] = a[4];\n out[5] = a[5];\n out[6] = a[6];\n out[7] = a[7];\n out[8] = a[8];\n return out;\n }\n Mat3.copy = copy;\n /**\n * Transpose the values of a Mat3\n */\n function transpose(out, a) {\n // If we are transposing ourselves we can skip a few steps but have to cache some values\n if (out === a) {\n const a01 = a[1], a02 = a[2], a12 = a[5];\n out[1] = a[3];\n out[2] = a[6];\n out[3] = a01;\n out[5] = a[7];\n out[6] = a02;\n out[7] = a12;\n }\n else {\n out[0] = a[0];\n out[1] = a[3];\n out[2] = a[6];\n out[3] = a[1];\n out[4] = a[4];\n out[5] = a[7];\n out[6] = a[2];\n out[7] = a[5];\n out[8] = a[8];\n }\n return out;\n }\n Mat3.transpose = transpose;\n /**\n * Inverts a Mat3\n */\n function invert(out, a) {\n const a00 = a[0], a01 = a[1], a02 = a[2];\n const a10 = a[3], a11 = a[4], a12 = a[5];\n const a20 = a[6], a21 = a[7], a22 = a[8];\n const b01 = a22 * a11 - a12 * a21;\n const b11 = -a22 * a10 + a12 * a20;\n const b21 = a21 * a10 - a11 * a20;\n // Calculate the determinant\n let det = a00 * b01 + a01 * b11 + a02 * b21;\n if (!det) {\n console.warn('non-invertible matrix.', a);\n return out;\n }\n det = 1.0 / det;\n out[0] = b01 * det;\n out[1] = (-a22 * a01 + a02 * a21) * det;\n out[2] = (a12 * a01 - a02 * a11) * det;\n out[3] = b11 * det;\n out[4] = (a22 * a00 - a02 * a20) * det;\n out[5] = (-a12 * a00 + a02 * a10) * det;\n out[6] = b21 * det;\n out[7] = (-a21 * a00 + a01 * a20) * det;\n out[8] = (a11 * a00 - a01 * a10) * det;\n return out;\n }\n Mat3.invert = invert;\n function symmtricFromUpper(out, a) {\n if (out === a) {\n out[3] = a[1];\n out[6] = a[2];\n out[7] = a[5];\n }\n else {\n out[0] = a[0];\n out[1] = a[1];\n out[2] = a[2];\n out[3] = a[1];\n out[4] = a[4];\n out[5] = a[5];\n out[6] = a[2];\n out[7] = a[5];\n out[8] = a[8];\n }\n return out;\n }\n Mat3.symmtricFromUpper = symmtricFromUpper;\n function symmtricFromLower(out, a) {\n if (out === a) {\n out[1] = a[3];\n out[2] = a[6];\n out[5] = a[7];\n }\n else {\n out[0] = a[0];\n out[1] = a[3];\n out[2] = a[6];\n out[3] = a[3];\n out[4] = a[4];\n out[5] = a[7];\n out[6] = a[6];\n out[7] = a[7];\n out[8] = a[8];\n }\n return out;\n }\n Mat3.symmtricFromLower = symmtricFromLower;\n function determinant(a) {\n const a00 = a[0], a01 = a[1], a02 = a[2];\n const a10 = a[3], a11 = a[4], a12 = a[5];\n const a20 = a[6], a21 = a[7], a22 = a[8];\n const b01 = a22 * a11 - a12 * a21;\n const b11 = -a22 * a10 + a12 * a20;\n const b21 = a21 * a10 - a11 * a20;\n // Calculate the determinant\n return a00 * b01 + a01 * b11 + a02 * b21;\n }\n Mat3.determinant = determinant;\n function trace(a) {\n return a[0] + a[4] + a[8];\n }\n Mat3.trace = trace;\n function sub(out, a, b) {\n out[0] = a[0] - b[0];\n out[1] = a[1] - b[1];\n out[2] = a[2] - b[2];\n out[3] = a[3] - b[3];\n out[4] = a[4] - b[4];\n out[5] = a[5] - b[5];\n out[6] = a[6] - b[6];\n out[7] = a[7] - b[7];\n out[8] = a[8] - b[8];\n return out;\n }\n Mat3.sub = sub;\n function add(out, a, b) {\n out[0] = a[0] + b[0];\n out[1] = a[1] + b[1];\n out[2] = a[2] + b[2];\n out[3] = a[3] + b[3];\n out[4] = a[4] + b[4];\n out[5] = a[5] + b[5];\n out[6] = a[6] + b[6];\n out[7] = a[7] + b[7];\n out[8] = a[8] + b[8];\n return out;\n }\n Mat3.add = add;\n function mul(out, a, b) {\n const a00 = a[0], a01 = a[1], a02 = a[2], a10 = a[3], a11 = a[4], a12 = a[5], a20 = a[6], a21 = a[7], a22 = a[8];\n const b00 = b[0], b01 = b[1], b02 = b[2], b10 = b[3], b11 = b[4], b12 = b[5], b20 = b[6], b21 = b[7], b22 = b[8];\n out[0] = b00 * a00 + b01 * a10 + b02 * a20;\n out[1] = b00 * a01 + b01 * a11 + b02 * a21;\n out[2] = b00 * a02 + b01 * a12 + b02 * a22;\n out[3] = b10 * a00 + b11 * a10 + b12 * a20;\n out[4] = b10 * a01 + b11 * a11 + b12 * a21;\n out[5] = b10 * a02 + b11 * a12 + b12 * a22;\n out[6] = b20 * a00 + b21 * a10 + b22 * a20;\n out[7] = b20 * a01 + b21 * a11 + b22 * a21;\n out[8] = b20 * a02 + b21 * a12 + b22 * a22;\n return out;\n }\n Mat3.mul = mul;\n function subScalar(out, a, s) {\n out[0] = a[0] - s;\n out[1] = a[1] - s;\n out[2] = a[2] - s;\n out[3] = a[3] - s;\n out[4] = a[4] - s;\n out[5] = a[5] - s;\n out[6] = a[6] - s;\n out[7] = a[7] - s;\n out[8] = a[8] - s;\n return out;\n }\n Mat3.subScalar = subScalar;\n function addScalar(out, a, s) {\n out[0] = a[0] + s;\n out[1] = a[1] + s;\n out[2] = a[2] + s;\n out[3] = a[3] + s;\n out[4] = a[4] + s;\n out[5] = a[5] + s;\n out[6] = a[6] + s;\n out[7] = a[7] + s;\n out[8] = a[8] + s;\n return out;\n }\n Mat3.addScalar = addScalar;\n function mulScalar(out, a, s) {\n out[0] = a[0] * s;\n out[1] = a[1] * s;\n out[2] = a[2] * s;\n out[3] = a[3] * s;\n out[4] = a[4] * s;\n out[5] = a[5] * s;\n out[6] = a[6] * s;\n out[7] = a[7] * s;\n out[8] = a[8] * s;\n return out;\n }\n Mat3.mulScalar = mulScalar;\n const piThird = Math.PI / 3;\n const tmpB = zero();\n /**\n * Given a real symmetric 3x3 matrix A, compute the eigenvalues\n *\n * From https://en.wikipedia.org/wiki/Eigenvalue_algorithm#3.C3.973_matrices\n */\n function symmetricEigenvalues(out, a) {\n const p1 = a[1] * a[1] + a[2] * a[2] + a[5] * a[5];\n if (p1 === 0) {\n out[0] = a[0];\n out[1] = a[4];\n out[2] = a[8];\n }\n else {\n const q = trace(a) / 3;\n const a1 = a[0] - q;\n const a2 = a[4] - q;\n const a3 = a[8] - q;\n const p2 = a1 * a1 + a2 * a2 + a3 * a3 + 2 * p1;\n const p = Math.sqrt(p2 / 6);\n mulScalar(tmpB, Mat3.Identity, q);\n sub(tmpB, a, tmpB);\n mulScalar(tmpB, tmpB, (1 / p));\n const r = determinant(tmpB) / 2;\n // In exact arithmetic for a symmetric matrix -1 <= r <= 1\n // but computation error can leave it slightly outside this range.\n const phi = r <= -1 ? piThird : r >= 1 ?\n 0 : Math.acos(r) / 3;\n // the eigenvalues satisfy eig3 <= eig2 <= eig1\n out[0] = q + 2 * p * Math.cos(phi);\n out[2] = q + 2 * p * Math.cos(phi + (2 * piThird));\n out[1] = 3 * q - out[0] - out[2]; // since trace(A) = eig1 + eig2 + eig3\n }\n return out;\n }\n Mat3.symmetricEigenvalues = symmetricEigenvalues;\n const tmpR0 = [0.1, 0.0, 0.0];\n const tmpR1 = [0.1, 0.0, 0.0];\n const tmpR2 = [0.1, 0.0, 0.0];\n const tmpR0xR1 = [0.1, 0.0, 0.0];\n const tmpR0xR2 = [0.1, 0.0, 0.0];\n const tmpR1xR2 = [0.1, 0.0, 0.0];\n /**\n * Calculates the eigenvector for the given eigenvalue `e` of matrix `a`\n */\n function eigenvector(out, a, e) {\n Vec3.set(tmpR0, a[0] - e, a[1], a[2]);\n Vec3.set(tmpR1, a[1], a[4] - e, a[5]);\n Vec3.set(tmpR2, a[2], a[5], a[8] - e);\n Vec3.cross(tmpR0xR1, tmpR0, tmpR1);\n Vec3.cross(tmpR0xR2, tmpR0, tmpR2);\n Vec3.cross(tmpR1xR2, tmpR1, tmpR2);\n const d0 = Vec3.dot(tmpR0xR1, tmpR0xR1);\n const d1 = Vec3.dot(tmpR0xR2, tmpR0xR2);\n const d2 = Vec3.dot(tmpR1xR2, tmpR1xR2);\n let dmax = d0;\n let imax = 0;\n if (d1 > dmax) {\n dmax = d1;\n imax = 1;\n }\n if (d2 > dmax)\n imax = 2;\n if (imax === 0) {\n Vec3.scale(out, tmpR0xR1, 1 / Math.sqrt(d0));\n }\n else if (imax === 1) {\n Vec3.scale(out, tmpR0xR2, 1 / Math.sqrt(d1));\n }\n else {\n Vec3.scale(out, tmpR1xR2, 1 / Math.sqrt(d2));\n }\n return out;\n }\n Mat3.eigenvector = eigenvector;\n /**\n * Get matrix to transform directions, e.g. normals\n */\n function directionTransform(out, t) {\n fromMat4(out, t);\n invert(out, out);\n transpose(out, out);\n return out;\n }\n Mat3.directionTransform = directionTransform;\n Mat3.Identity = identity();\n /** Return the Frobenius inner product of two matrices (= dot product of the flattened matrices).\n * Can be used as a measure of similarity between two rotation matrices. */\n function innerProduct(a, b) {\n return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]\n + a[3] * b[3] + a[4] * b[4] + a[5] * b[5]\n + a[6] * b[6] + a[7] * b[7] + a[8] * b[8];\n }\n Mat3.innerProduct = innerProduct;\n})(Mat3 || (Mat3 = {}));\nexport { Mat3 };\n","/**\n * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.\n *\n * @author Alexander Rose \n */\nfunction Vec2() {\n return Vec2.zero();\n}\n(function (Vec2) {\n function zero() {\n // force double backing array by 0.1.\n const ret = [0.1, 0];\n ret[0] = 0.0;\n return ret;\n }\n Vec2.zero = zero;\n function clone(a) {\n const out = zero();\n out[0] = a[0];\n out[1] = a[1];\n return out;\n }\n Vec2.clone = clone;\n function create(x, y) {\n const out = zero();\n out[0] = x;\n out[1] = y;\n return out;\n }\n Vec2.create = create;\n function hasNaN(a) {\n return isNaN(a[0]) || isNaN(a[1]);\n }\n Vec2.hasNaN = hasNaN;\n function toArray(a, out, offset) {\n out[offset + 0] = a[0];\n out[offset + 1] = a[1];\n return out;\n }\n Vec2.toArray = toArray;\n function fromArray(a, array, offset) {\n a[0] = array[offset + 0];\n a[1] = array[offset + 1];\n return a;\n }\n Vec2.fromArray = fromArray;\n function copy(out, a) {\n out[0] = a[0];\n out[1] = a[1];\n return out;\n }\n Vec2.copy = copy;\n function set(out, x, y) {\n out[0] = x;\n out[1] = y;\n return out;\n }\n Vec2.set = set;\n function add(out, a, b) {\n out[0] = a[0] + b[0];\n out[1] = a[1] + b[1];\n return out;\n }\n Vec2.add = add;\n function sub(out, a, b) {\n out[0] = a[0] - b[0];\n out[1] = a[1] - b[1];\n return out;\n }\n Vec2.sub = sub;\n function mul(out, a, b) {\n out[0] = a[0] * b[0];\n out[1] = a[1] * b[1];\n return out;\n }\n Vec2.mul = mul;\n function div(out, a, b) {\n out[0] = a[0] / b[0];\n out[1] = a[1] / b[1];\n return out;\n }\n Vec2.div = div;\n function scale(out, a, b) {\n out[0] = a[0] * b;\n out[1] = a[1] * b;\n return out;\n }\n Vec2.scale = scale;\n /**\n * Math.round the components of a Vec2\n */\n function round(out, a) {\n out[0] = Math.round(a[0]);\n out[1] = Math.round(a[1]);\n return out;\n }\n Vec2.round = round;\n /**\n * Math.ceil the components of a Vec2\n */\n function ceil(out, a) {\n out[0] = Math.ceil(a[0]);\n out[1] = Math.ceil(a[1]);\n return out;\n }\n Vec2.ceil = ceil;\n /**\n * Math.floor the components of a Vec2\n */\n function floor(out, a) {\n out[0] = Math.floor(a[0]);\n out[1] = Math.floor(a[1]);\n return out;\n }\n Vec2.floor = floor;\n function distance(a, b) {\n const x = b[0] - a[0], y = b[1] - a[1];\n return Math.sqrt(x * x + y * y);\n }\n Vec2.distance = distance;\n function squaredDistance(a, b) {\n const x = b[0] - a[0], y = b[1] - a[1];\n return x * x + y * y;\n }\n Vec2.squaredDistance = squaredDistance;\n function magnitude(a) {\n const x = a[0], y = a[1];\n return Math.sqrt(x * x + y * y);\n }\n Vec2.magnitude = magnitude;\n function squaredMagnitude(a) {\n const x = a[0], y = a[1];\n return x * x + y * y;\n }\n Vec2.squaredMagnitude = squaredMagnitude;\n /**\n * Returns the inverse of the components of a Vec2\n */\n function inverse(out, a) {\n out[0] = 1.0 / a[0];\n out[1] = 1.0 / a[1];\n return out;\n }\n Vec2.inverse = inverse;\n function areEqual(a, b) {\n return a[0] === b[0] && a[1] === b[1];\n }\n Vec2.areEqual = areEqual;\n function toString(a, precision) {\n return `[${a[0].toPrecision(precision)} ${a[1].toPrecision(precision)}}]`;\n }\n Vec2.toString = toString;\n})(Vec2 || (Vec2 = {}));\nexport { Vec2 };\n","/**\n * Copyright (c) 2017-2018 mol* contributors, licensed under MIT, See LICENSE file for more info.\n *\n * @author David Sehnal \n * @author Alexander Rose \n */\nimport { EPSILON } from './common';\nfunction Vec4() {\n return Vec4.zero();\n}\n(function (Vec4) {\n function zero() {\n // force double backing array by 0.1.\n const ret = [0.1, 0, 0, 0];\n ret[0] = 0.0;\n return ret;\n }\n Vec4.zero = zero;\n function clone(a) {\n const out = zero();\n out[0] = a[0];\n out[1] = a[1];\n out[2] = a[2];\n out[3] = a[3];\n return out;\n }\n Vec4.clone = clone;\n function create(x, y, z, w) {\n const out = zero();\n out[0] = x;\n out[1] = y;\n out[2] = z;\n out[3] = w;\n return out;\n }\n Vec4.create = create;\n function fromSphere(out, sphere) {\n out[0] = sphere.center[0];\n out[1] = sphere.center[1];\n out[2] = sphere.center[2];\n out[3] = sphere.radius;\n return out;\n }\n Vec4.fromSphere = fromSphere;\n function ofSphere(sphere) {\n return fromSphere(zero(), sphere);\n }\n Vec4.ofSphere = ofSphere;\n function hasNaN(a) {\n return isNaN(a[0]) || isNaN(a[1]) || isNaN(a[2]) || isNaN(a[3]);\n }\n Vec4.hasNaN = hasNaN;\n function toArray(a, out, offset) {\n out[offset + 0] = a[0];\n out[offset + 1] = a[1];\n out[offset + 2] = a[2];\n out[offset + 3] = a[3];\n return out;\n }\n Vec4.toArray = toArray;\n function fromArray(a, array, offset) {\n a[0] = array[offset + 0];\n a[1] = array[offset + 1];\n a[2] = array[offset + 2];\n a[3] = array[offset + 3];\n return a;\n }\n Vec4.fromArray = fromArray;\n function toVec3Array(a, out, offset) {\n out[offset + 0] = a[0];\n out[offset + 1] = a[1];\n out[offset + 2] = a[2];\n }\n Vec4.toVec3Array = toVec3Array;\n function fromVec3Array(a, array, offset) {\n a[0] = array[offset + 0];\n a[1] = array[offset + 1];\n a[2] = array[offset + 2];\n a[3] = 0;\n return a;\n }\n Vec4.fromVec3Array = fromVec3Array;\n function copy(out, a) {\n out[0] = a[0];\n out[1] = a[1];\n out[2] = a[2];\n out[3] = a[3];\n return out;\n }\n Vec4.copy = copy;\n function set(out, x, y, z, w) {\n out[0] = x;\n out[1] = y;\n out[2] = z;\n out[3] = w;\n return out;\n }\n Vec4.set = set;\n function add(out, a, b) {\n out[0] = a[0] + b[0];\n out[1] = a[1] + b[1];\n out[2] = a[2] + b[2];\n out[3] = a[3] + b[3];\n return out;\n }\n Vec4.add = add;\n function distance(a, b) {\n const x = b[0] - a[0], y = b[1] - a[1], z = b[2] - a[2], w = b[3] - a[3];\n return Math.sqrt(x * x + y * y + z * z + w * w);\n }\n Vec4.distance = distance;\n function scale(out, a, b) {\n out[0] = a[0] * b;\n out[1] = a[1] * b;\n out[2] = a[2] * b;\n out[4] = a[4] * b;\n return out;\n }\n Vec4.scale = scale;\n /**\n * Math.round the components of a Vec4\n */\n function round(out, a) {\n out[0] = Math.round(a[0]);\n out[1] = Math.round(a[1]);\n out[2] = Math.round(a[2]);\n out[3] = Math.round(a[3]);\n return out;\n }\n Vec4.round = round;\n /**\n * Math.ceil the components of a Vec4\n */\n function ceil(out, a) {\n out[0] = Math.ceil(a[0]);\n out[1] = Math.ceil(a[1]);\n out[2] = Math.ceil(a[2]);\n out[3] = Math.ceil(a[3]);\n return out;\n }\n Vec4.ceil = ceil;\n /**\n * Math.floor the components of a Vec3\n */\n function floor(out, a) {\n out[0] = Math.floor(a[0]);\n out[1] = Math.floor(a[1]);\n out[2] = Math.floor(a[2]);\n out[3] = Math.floor(a[3]);\n return out;\n }\n Vec4.floor = floor;\n function squaredDistance(a, b) {\n const x = b[0] - a[0], y = b[1] - a[1], z = b[2] - a[2], w = b[3] - a[3];\n return x * x + y * y + z * z + w * w;\n }\n Vec4.squaredDistance = squaredDistance;\n function norm(a) {\n const x = a[0], y = a[1], z = a[2], w = a[3];\n return Math.sqrt(x * x + y * y + z * z + w * w);\n }\n Vec4.norm = norm;\n function squaredNorm(a) {\n const x = a[0], y = a[1], z = a[2], w = a[3];\n return x * x + y * y + z * z + w * w;\n }\n Vec4.squaredNorm = squaredNorm;\n function transformMat4(out, a, m) {\n const x = a[0], y = a[1], z = a[2], w = a[3];\n out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w;\n out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w;\n out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w;\n out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w;\n return out;\n }\n Vec4.transformMat4 = transformMat4;\n function dot(a, b) {\n return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];\n }\n Vec4.dot = dot;\n /**\n * Returns the inverse of the components of a Vec4\n */\n function inverse(out, a) {\n out[0] = 1.0 / a[0];\n out[1] = 1.0 / a[1];\n out[2] = 1.0 / a[2];\n out[3] = 1.0 / a[3];\n return out;\n }\n Vec4.inverse = inverse;\n /**\n * Returns whether or not the vectors have exactly the same elements in the same position (when compared with ===)\n */\n function exactEquals(a, b) {\n return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3];\n }\n Vec4.exactEquals = exactEquals;\n /**\n * Returns whether or not the vectors have approximately the same elements in the same position.\n */\n function equals(a, b) {\n const a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];\n const b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];\n return (Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&\n Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&\n Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) &&\n Math.abs(a3 - b3) <= EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3)));\n }\n Vec4.equals = equals;\n function toString(a, precision) {\n return `[${a[0].toPrecision(precision)} ${a[1].toPrecision(precision)} ${a[2].toPrecision(precision)} ${a[3].toPrecision(precision)}]`;\n }\n Vec4.toString = toString;\n})(Vec4 || (Vec4 = {}));\nexport { Vec4 };\n","/**\n * Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.\n *\n * @author Alexander Rose \n * @author David Sehnal \n */\nexport function ObjectKeys(o) {\n return Object.keys(o);\n}\n;\nexport function assertUnreachable(x) {\n throw new Error('unreachable');\n}\nexport function isPromiseLike(x) {\n return typeof (x === null || x === void 0 ? void 0 : x.then) === 'function';\n}\n","/**\n * Copyright (c) 2017-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.\n *\n * @author David Sehnal \n * @author Alexander Rose \n */\nimport { Vec3 } from './vec3';\nimport { EPSILON } from './common';\nimport { assertUnreachable } from '../../../mol-util/type-helpers';\nfunction Quat() {\n return Quat.zero();\n}\n(function (Quat) {\n function zero() {\n // force double backing array by 0.1.\n const ret = [0.1, 0, 0, 0];\n ret[0] = 0.0;\n return ret;\n }\n Quat.zero = zero;\n function identity() {\n const out = zero();\n out[3] = 1;\n return out;\n }\n Quat.identity = identity;\n function setIdentity(out) {\n out[0] = 0;\n out[1] = 0;\n out[2] = 0;\n out[3] = 1;\n }\n Quat.setIdentity = setIdentity;\n function hasNaN(q) {\n return isNaN(q[0]) || isNaN(q[1]) || isNaN(q[2]) || isNaN(q[3]);\n }\n Quat.hasNaN = hasNaN;\n function create(x, y, z, w) {\n const out = identity();\n out[0] = x;\n out[1] = y;\n out[2] = z;\n out[3] = w;\n return out;\n }\n Quat.create = create;\n function setAxisAngle(out, axis, rad) {\n rad = rad * 0.5;\n const s = Math.sin(rad);\n out[0] = s * axis[0];\n out[1] = s * axis[1];\n out[2] = s * axis[2];\n out[3] = Math.cos(rad);\n return out;\n }\n Quat.setAxisAngle = setAxisAngle;\n /**\n * Gets the rotation axis and angle for a given\n * quaternion. If a quaternion is created with\n * setAxisAngle, this method will return the same\n * values as providied in the original parameter list\n * OR functionally equivalent values.\n * Example: The quaternion formed by axis [0, 0, 1] and\n * angle -90 is the same as the quaternion formed by\n * [0, 0, 1] and 270. This method favors the latter.\n */\n function getAxisAngle(out_axis, q) {\n const rad = Math.acos(q[3]) * 2.0;\n const s = Math.sin(rad / 2.0);\n if (s !== 0.0) {\n out_axis[0] = q[0] / s;\n out_axis[1] = q[1] / s;\n out_axis[2] = q[2] / s;\n }\n else {\n // If s is zero, return any axis (no rotation - axis does not matter)\n out_axis[0] = 1;\n out_axis[1] = 0;\n out_axis[2] = 0;\n }\n return rad;\n }\n Quat.getAxisAngle = getAxisAngle;\n function multiply(out, a, b) {\n const ax = a[0], ay = a[1], az = a[2], aw = a[3];\n const bx = b[0], by = b[1], bz = b[2], bw = b[3];\n out[0] = ax * bw + aw * bx + ay * bz - az * by;\n out[1] = ay * bw + aw * by + az * bx - ax * bz;\n out[2] = az * bw + aw * bz + ax * by - ay * bx;\n out[3] = aw * bw - ax * bx - ay * by - az * bz;\n return out;\n }\n Quat.multiply = multiply;\n function rotateX(out, a, rad) {\n rad *= 0.5;\n const ax = a[0], ay = a[1], az = a[2], aw = a[3];\n const bx = Math.sin(rad), bw = Math.cos(rad);\n out[0] = ax * bw + aw * bx;\n out[1] = ay * bw + az * bx;\n out[2] = az * bw - ay * bx;\n out[3] = aw * bw - ax * bx;\n return out;\n }\n Quat.rotateX = rotateX;\n function rotateY(out, a, rad) {\n rad *= 0.5;\n const ax = a[0], ay = a[1], az = a[2], aw = a[3];\n const by = Math.sin(rad), bw = Math.cos(rad);\n out[0] = ax * bw - az * by;\n out[1] = ay * bw + aw * by;\n out[2] = az * bw + ax * by;\n out[3] = aw * bw - ay * by;\n return out;\n }\n Quat.rotateY = rotateY;\n function rotateZ(out, a, rad) {\n rad *= 0.5;\n const ax = a[0], ay = a[1], az = a[2], aw = a[3];\n const bz = Math.sin(rad), bw = Math.cos(rad);\n out[0] = ax * bw + ay * bz;\n out[1] = ay * bw - ax * bz;\n out[2] = az * bw + aw * bz;\n out[3] = aw * bw - az * bz;\n return out;\n }\n Quat.rotateZ = rotateZ;\n /**\n * Calculates the W component of a quat from the X, Y, and Z components.\n * Assumes that quaternion is 1 unit in length.\n * Any existing W component will be ignored.\n */\n function calculateW(out, a) {\n const x = a[0], y = a[1], z = a[2];\n out[0] = x;\n out[1] = y;\n out[2] = z;\n out[3] = Math.sqrt(Math.abs(1.0 - x * x - y * y - z * z));\n return out;\n }\n Quat.calculateW = calculateW;\n /**\n * Performs a spherical linear interpolation between two quat\n */\n function slerp(out, a, b, t) {\n // benchmarks:\n // http://jsperf.com/quaternion-slerp-implementations\n const ax = a[0], ay = a[1], az = a[2], aw = a[3];\n let bx = b[0], by = b[1], bz = b[2], bw = b[3];\n let omega, cosom, sinom, scale0, scale1;\n // calc cosine\n cosom = ax * bx + ay * by + az * bz + aw * bw;\n // adjust signs (if necessary)\n if (cosom < 0.0) {\n cosom = -cosom;\n bx = -bx;\n by = -by;\n bz = -bz;\n bw = -bw;\n }\n // calculate coefficients\n if ((1.0 - cosom) > 0.000001) {\n // standard case (slerp)\n omega = Math.acos(cosom);\n sinom = Math.sin(omega);\n scale0 = Math.sin((1.0 - t) * omega) / sinom;\n scale1 = Math.sin(t * omega) / sinom;\n }\n else {\n // \"from\" and \"to\" quaternions are very close\n // ... so we can do a linear interpolation\n scale0 = 1.0 - t;\n scale1 = t;\n }\n // calculate final values\n out[0] = scale0 * ax + scale1 * bx;\n out[1] = scale0 * ay + scale1 * by;\n out[2] = scale0 * az + scale1 * bz;\n out[3] = scale0 * aw + scale1 * bw;\n return out;\n }\n Quat.slerp = slerp;\n function invert(out, a) {\n const a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];\n const dot = a0 * a0 + a1 * a1 + a2 * a2 + a3 * a3;\n const invDot = dot ? 1.0 / dot : 0;\n // TODO: Would be faster to return [0,0,0,0] immediately if dot == 0\n out[0] = -a0 * invDot;\n out[1] = -a1 * invDot;\n out[2] = -a2 * invDot;\n out[3] = a3 * invDot;\n return out;\n }\n Quat.invert = invert;\n /**\n * Calculates the conjugate of a quat\n * If the quaternion is normalized, this function is faster than quat.inverse and produces the same result.\n */\n function conjugate(out, a) {\n out[0] = -a[0];\n out[1] = -a[1];\n out[2] = -a[2];\n out[3] = a[3];\n return out;\n }\n Quat.conjugate = conjugate;\n function dot(a, b) {\n return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];\n }\n Quat.dot = dot;\n /**\n * Creates a quaternion from the given 3x3 rotation matrix.\n *\n * NOTE: The resultant quaternion is not normalized, so you should be sure\n * to renormalize the quaternion yourself where necessary.\n */\n function fromMat3(out, m) {\n // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes\n // article \"Quaternion Calculus and Fast Animation\".\n const fTrace = m[0] + m[4] + m[8];\n let fRoot;\n if (fTrace > 0.0) {\n // |w| > 1/2, may as well choose w > 1/2\n fRoot = Math.sqrt(fTrace + 1.0); // 2w\n out[3] = 0.5 * fRoot;\n fRoot = 0.5 / fRoot; // 1/(4w)\n out[0] = (m[5] - m[7]) * fRoot;\n out[1] = (m[6] - m[2]) * fRoot;\n out[2] = (m[1] - m[3]) * fRoot;\n }\n else {\n // |w| <= 1/2\n let i = 0;\n if (m[4] > m[0])\n i = 1;\n if (m[8] > m[i * 3 + i])\n i = 2;\n const j = (i + 1) % 3;\n const k = (i + 2) % 3;\n fRoot = Math.sqrt(m[i * 3 + i] - m[j * 3 + j] - m[k * 3 + k] + 1.0);\n out[i] = 0.5 * fRoot;\n fRoot = 0.5 / fRoot;\n out[3] = (m[j * 3 + k] - m[k * 3 + j]) * fRoot;\n out[j] = (m[j * 3 + i] + m[i * 3 + j]) * fRoot;\n out[k] = (m[k * 3 + i] + m[i * 3 + k]) * fRoot;\n }\n return out;\n }\n Quat.fromMat3 = fromMat3;\n function fromEuler(out, euler, order) {\n const [x, y, z] = euler;\n // http://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/content/SpinCalc.m\n const c1 = Math.cos(x / 2);\n const c2 = Math.cos(y / 2);\n const c3 = Math.cos(z / 2);\n const s1 = Math.sin(x / 2);\n const s2 = Math.sin(y / 2);\n const s3 = Math.sin(z / 2);\n switch (order) {\n case 'XYZ':\n out[0] = s1 * c2 * c3 + c1 * s2 * s3;\n out[1] = c1 * s2 * c3 - s1 * c2 * s3;\n out[2] = c1 * c2 * s3 + s1 * s2 * c3;\n out[3] = c1 * c2 * c3 - s1 * s2 * s3;\n break;\n case 'YXZ':\n out[0] = s1 * c2 * c3 + c1 * s2 * s3;\n out[1] = c1 * s2 * c3 - s1 * c2 * s3;\n out[2] = c1 * c2 * s3 - s1 * s2 * c3;\n out[3] = c1 * c2 * c3 + s1 * s2 * s3;\n break;\n case 'ZXY':\n out[0] = s1 * c2 * c3 - c1 * s2 * s3;\n out[1] = c1 * s2 * c3 + s1 * c2 * s3;\n out[2] = c1 * c2 * s3 + s1 * s2 * c3;\n out[3] = c1 * c2 * c3 - s1 * s2 * s3;\n break;\n case 'ZYX':\n out[0] = s1 * c2 * c3 - c1 * s2 * s3;\n out[1] = c1 * s2 * c3 + s1 * c2 * s3;\n out[2] = c1 * c2 * s3 - s1 * s2 * c3;\n out[3] = c1 * c2 * c3 + s1 * s2 * s3;\n break;\n case 'YZX':\n out[0] = s1 * c2 * c3 + c1 * s2 * s3;\n out[1] = c1 * s2 * c3 + s1 * c2 * s3;\n out[2] = c1 * c2 * s3 - s1 * s2 * c3;\n out[3] = c1 * c2 * c3 - s1 * s2 * s3;\n break;\n case 'XZY':\n out[0] = s1 * c2 * c3 - c1 * s2 * s3;\n out[1] = c1 * s2 * c3 - s1 * c2 * s3;\n out[2] = c1 * c2 * s3 + s1 * s2 * c3;\n out[3] = c1 * c2 * c3 + s1 * s2 * s3;\n break;\n default:\n assertUnreachable(order);\n }\n return out;\n }\n Quat.fromEuler = fromEuler;\n const fromUnitVec3Temp = [0, 0, 0];\n /** Quaternion from two normalized unit vectors. */\n function fromUnitVec3(out, a, b) {\n // assumes a and b are normalized\n let r = Vec3.dot(a, b) + 1;\n if (r < EPSILON) {\n // If u and v are exactly opposite, rotate 180 degrees\n // around an arbitrary orthogonal axis. Axis normalisation\n // can happen later, when we normalise the quaternion.\n r = 0;\n if (Math.abs(a[0]) > Math.abs(a[2])) {\n Vec3.set(fromUnitVec3Temp, -a[1], a[0], 0);\n }\n else {\n Vec3.set(fromUnitVec3Temp, 0, -a[2], a[1]);\n }\n }\n else {\n // Otherwise, build quaternion the standard way.\n Vec3.cross(fromUnitVec3Temp, a, b);\n }\n out[0] = fromUnitVec3Temp[0];\n out[1] = fromUnitVec3Temp[1];\n out[2] = fromUnitVec3Temp[2];\n out[3] = r;\n normalize(out, out);\n return out;\n }\n Quat.fromUnitVec3 = fromUnitVec3;\n function clone(a) {\n const out = zero();\n out[0] = a[0];\n out[1] = a[1];\n out[2] = a[2];\n out[3] = a[3];\n return out;\n }\n Quat.clone = clone;\n function toArray(a, out, offset) {\n out[offset + 0] = a[0];\n out[offset + 1] = a[1];\n out[offset + 2] = a[2];\n out[offset + 3] = a[3];\n return out;\n }\n Quat.toArray = toArray;\n function fromArray(a, array, offset) {\n a[0] = array[offset + 0];\n a[1] = array[offset + 1];\n a[2] = array[offset + 2];\n a[3] = array[offset + 3];\n return a;\n }\n Quat.fromArray = fromArray;\n function copy(out, a) {\n out[0] = a[0];\n out[1] = a[1];\n out[2] = a[2];\n out[3] = a[3];\n return out;\n }\n Quat.copy = copy;\n function set(out, x, y, z, w) {\n out[0] = x;\n out[1] = y;\n out[2] = z;\n out[3] = w;\n return out;\n }\n Quat.set = set;\n /**\n * Returns whether or not the quaternions have exactly the same elements in the same position (when compared with ===)\n */\n function exactEquals(a, b) {\n return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3];\n }\n Quat.exactEquals = exactEquals;\n /**\n * Returns whether or not the quaternions have approximately the same elements in the same position.\n */\n function equals(a, b) {\n const a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];\n const b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];\n return (Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&\n Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&\n Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) &&\n Math.abs(a3 - b3) <= EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3)));\n }\n Quat.equals = equals;\n function add(out, a, b) {\n out[0] = a[0] + b[0];\n out[1] = a[1] + b[1];\n out[2] = a[2] + b[2];\n out[3] = a[3] + b[3];\n return out;\n }\n Quat.add = add;\n function normalize(out, a) {\n const x = a[0];\n const y = a[1];\n const z = a[2];\n const w = a[3];\n let len = x * x + y * y + z * z + w * w;\n if (len > 0) {\n len = 1 / Math.sqrt(len);\n out[0] = x * len;\n out[1] = y * len;\n out[2] = z * len;\n out[3] = w * len;\n }\n return out;\n }\n Quat.normalize = normalize;\n /**\n * Sets a quaternion to represent the shortest rotation from one\n * vector to another.\n *\n * Both vectors are assumed to be unit length.\n */\n const rotTmpVec3 = [0, 0, 0];\n const rotTmpVec3UnitX = [1, 0, 0];\n const rotTmpVec3UnitY = [0, 1, 0];\n function rotationTo(out, a, b) {\n const dot = Vec3.dot(a, b);\n if (dot < -0.999999) {\n Vec3.cross(rotTmpVec3, rotTmpVec3UnitX, a);\n if (Vec3.magnitude(rotTmpVec3) < 0.000001)\n Vec3.cross(rotTmpVec3, rotTmpVec3UnitY, a);\n Vec3.normalize(rotTmpVec3, rotTmpVec3);\n setAxisAngle(out, rotTmpVec3, Math.PI);\n return out;\n }\n else if (dot > 0.999999) {\n out[0] = 0;\n out[1] = 0;\n out[2] = 0;\n out[3] = 1;\n return out;\n }\n else {\n Vec3.cross(rotTmpVec3, a, b);\n out[0] = rotTmpVec3[0];\n out[1] = rotTmpVec3[1];\n out[2] = rotTmpVec3[2];\n out[3] = 1 + dot;\n return normalize(out, out);\n }\n }\n Quat.rotationTo = rotationTo;\n /**\n * Performs a spherical linear interpolation with two control points\n */\n const sqlerpTemp1 = zero();\n const sqlerpTemp2 = zero();\n function sqlerp(out, a, b, c, d, t) {\n slerp(sqlerpTemp1, a, d, t);\n slerp(sqlerpTemp2, b, c, t);\n slerp(out, sqlerpTemp1, sqlerpTemp2, 2 * t * (1 - t));\n return out;\n }\n Quat.sqlerp = sqlerp;\n /**\n * Sets the specified quaternion with values corresponding to the given\n * axes. Each axis is a vec3 and is expected to be unit length and\n * perpendicular to all other specified axes.\n */\n const axesTmpMat = [0, 0, 0, 0, 0, 0, 0, 0, 0];\n function setAxes(out, view, right, up) {\n axesTmpMat[0] = right[0];\n axesTmpMat[3] = right[1];\n axesTmpMat[6] = right[2];\n axesTmpMat[1] = up[0];\n axesTmpMat[4] = up[1];\n axesTmpMat[7] = up[2];\n axesTmpMat[2] = -view[0];\n axesTmpMat[5] = -view[1];\n axesTmpMat[8] = -view[2];\n return normalize(out, fromMat3(out, axesTmpMat));\n }\n Quat.setAxes = setAxes;\n function toString(a, precision) {\n return `[${a[0].toPrecision(precision)} ${a[1].toPrecision(precision)} ${a[2].toPrecision(precision)} ${a[3].toPrecision(precision)}]`;\n }\n Quat.toString = toString;\n Quat.Identity = identity();\n})(Quat || (Quat = {}));\nexport { Quat };\n","import { Mat3 } from './3d/mat3';\n/**\n * Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.\n *\n * @author David Sehnal \n * @author Alexander Rose \n */\nimport { Mat4 } from './3d/mat4';\nexport var Tensor;\n(function (Tensor) {\n function Layout(dimensions, axisOrderSlowToFast, ctor) {\n // need to reverse the axis order for better access.\n const axisOrderFastToSlow = [];\n for (let i = 0; i < axisOrderSlowToFast.length; i++)\n axisOrderFastToSlow[i] = axisOrderSlowToFast[axisOrderSlowToFast.length - i - 1];\n const accessDimensions = [1];\n for (let i = 1; i < dimensions.length; i++)\n accessDimensions[i] = dimensions[axisOrderFastToSlow[i - 1]];\n return { dimensions, axisOrderFastToSlow, axisOrderSlowToFast, accessDimensions, defaultCtor: ctor || Float64Array };\n }\n function create(space, data) { return { space, data }; }\n Tensor.create = create;\n function Space(dimensions, axisOrderSlowToFast, ctor) {\n const layout = Layout(dimensions, axisOrderSlowToFast, ctor);\n const { get, set, add, dataOffset, getCoords } = accessors(layout);\n return { rank: dimensions.length, dimensions, axisOrderSlowToFast, create: creator(layout), get, set, add, dataOffset, getCoords };\n }\n Tensor.Space = Space;\n function Data1(values) { return values; }\n Tensor.Data1 = Data1;\n function Vector(d, ctor) { return Space([d], [0], ctor); }\n Tensor.Vector = Vector;\n function ColumnMajorMatrix(rows, cols, ctor) { return Space([rows, cols], [1, 0], ctor); }\n Tensor.ColumnMajorMatrix = ColumnMajorMatrix;\n function RowMajorMatrix(rows, cols, ctor) { return Space([rows, cols], [0, 1], ctor); }\n Tensor.RowMajorMatrix = RowMajorMatrix;\n function toMat4(out, space, data) {\n if (space.rank !== 2)\n throw new Error('Invalid tensor rank');\n const d0 = Math.min(4, space.dimensions[0]), d1 = Math.min(4, space.dimensions[1]);\n for (let i = 0; i < d0; i++) {\n for (let j = 0; j < d1; j++)\n Mat4.setValue(out, i, j, space.get(data, i, j));\n }\n return out;\n }\n Tensor.toMat4 = toMat4;\n function toMat3(out, space, data) {\n if (space.rank !== 2)\n throw new Error('Invalid tensor rank');\n const d0 = Math.min(3, space.dimensions[0]), d1 = Math.min(3, space.dimensions[1]);\n for (let i = 0; i < d0; i++) {\n for (let j = 0; j < d1; j++)\n Mat3.setValue(out, i, j, space.get(data, i, j));\n }\n return out;\n }\n Tensor.toMat3 = toMat3;\n function toVec3(out, space, data) {\n if (space.rank !== 1)\n throw new Error('Invalid tensor rank');\n const d0 = Math.min(3, space.dimensions[0]);\n for (let i = 0; i < d0; i++)\n out[i] = data[i];\n return out;\n }\n Tensor.toVec3 = toVec3;\n function toVec4(out, space, data) {\n if (space.rank !== 1)\n throw new Error('Invalid tensor rank');\n const d0 = Math.min(4, space.dimensions[0]);\n for (let i = 0; i < d0; i++)\n out[i] = data[i];\n return out;\n }\n Tensor.toVec4 = toVec4;\n function areEqualExact(a, b) {\n const len = a.length;\n if (len !== b.length)\n return false;\n for (let i = 0; i < len; i++)\n if (a[i] !== b[i])\n return false;\n return true;\n }\n Tensor.areEqualExact = areEqualExact;\n function accessors(layout) {\n const { dimensions, axisOrderFastToSlow: ao } = layout;\n switch (dimensions.length) {\n case 1: return {\n get: (t, d) => t[d],\n set: (t, d, x) => t[d] = x,\n add: (t, d, x) => t[d] += x,\n dataOffset: (d) => d,\n getCoords: (o, c) => {\n c[0] = o;\n return c;\n }\n };\n case 2: {\n // column major\n if (ao[0] === 0 && ao[1] === 1) {\n const rows = dimensions[0];\n return {\n get: (t, i, j) => t[j * rows + i],\n set: (t, i, j, x) => t[j * rows + i] = x,\n add: (t, i, j, x) => t[j * rows + i] += x,\n dataOffset: (i, j) => j * rows + i,\n getCoords: (o, c) => {\n c[0] = o % rows;\n c[1] = Math.floor(o / rows);\n return c;\n }\n };\n }\n if (ao[0] === 1 && ao[1] === 0) {\n const cols = dimensions[1];\n return {\n get: (t, i, j) => t[i * cols + j],\n set: (t, i, j, x) => t[i * cols + j] = x,\n add: (t, i, j, x) => t[i * cols + j] += x,\n dataOffset: (i, j) => i * cols + j,\n getCoords: (o, c) => {\n c[0] = Math.floor(o / cols);\n c[1] = o % cols;\n return c;\n }\n };\n }\n throw new Error('bad axis order');\n }\n case 3: {\n if (ao[0] === 0 && ao[1] === 1 && ao[2] === 2) { // 012 ijk\n const u = dimensions[0], v = dimensions[1], uv = u * v;\n return {\n get: (t, i, j, k) => t[i + j * u + k * uv],\n set: (t, i, j, k, x) => t[i + j * u + k * uv] = x,\n add: (t, i, j, k, x) => t[i + j * u + k * uv] += x,\n dataOffset: (i, j, k) => i + j * u + k * uv,\n getCoords: (o, c) => {\n const p = Math.floor(o / u);\n c[0] = o % u;\n c[1] = p % v;\n c[2] = Math.floor(p / v);\n return c;\n }\n };\n }\n if (ao[0] === 0 && ao[1] === 2 && ao[2] === 1) { // 021 ikj\n const u = dimensions[0], v = dimensions[2], uv = u * v;\n return {\n get: (t, i, j, k) => t[i + k * u + j * uv],\n set: (t, i, j, k, x) => t[i + k * u + j * uv] = x,\n add: (t, i, j, k, x) => t[i + k * u + j * uv] += x,\n dataOffset: (i, j, k) => i + k * u + j * uv,\n getCoords: (o, c) => {\n const p = Math.floor(o / u);\n c[0] = o % u;\n c[1] = Math.floor(p / v);\n c[2] = p % v;\n return c;\n }\n };\n }\n if (ao[0] === 1 && ao[1] === 0 && ao[2] === 2) { // 102 jik\n const u = dimensions[1], v = dimensions[0], uv = u * v;\n return {\n get: (t, i, j, k) => t[j + i * u + k * uv],\n set: (t, i, j, k, x) => t[j + i * u + k * uv] = x,\n add: (t, i, j, k, x) => t[j + i * u + k * uv] += x,\n dataOffset: (i, j, k) => j + i * u + k * uv,\n getCoords: (o, c) => {\n const p = Math.floor(o / u);\n c[0] = p % v;\n c[1] = o % u;\n c[2] = Math.floor(p / v);\n return c;\n }\n };\n }\n if (ao[0] === 1 && ao[1] === 2 && ao[2] === 0) { // 120 jki\n const u = dimensions[1], v = dimensions[2], uv = u * v;\n return {\n get: (t, i, j, k) => t[j + k * u + i * uv],\n set: (t, i, j, k, x) => t[j + k * u + i * uv] = x,\n add: (t, i, j, k, x) => t[j + k * u + i * uv] += x,\n dataOffset: (i, j, k) => j + k * u + i * uv,\n getCoords: (o, c) => {\n const p = Math.floor(o / u);\n c[0] = Math.floor(p / v);\n c[1] = o % u;\n c[2] = p % v;\n return c;\n }\n };\n }\n if (ao[0] === 2 && ao[1] === 0 && ao[2] === 1) { // 201 kij\n const u = dimensions[2], v = dimensions[0], uv = u * v;\n return {\n get: (t, i, j, k) => t[k + i * u + j * uv],\n set: (t, i, j, k, x) => t[k + i * u + j * uv] = x,\n add: (t, i, j, k, x) => t[k + i * u + j * uv] += x,\n dataOffset: (i, j, k) => k + i * u + j * uv,\n getCoords: (o, c) => {\n const p = Math.floor(o / u);\n c[0] = p % v;\n c[1] = Math.floor(p / v);\n c[2] = o % u;\n return c;\n }\n };\n }\n if (ao[0] === 2 && ao[1] === 1 && ao[2] === 0) { // 210 kji\n const u = dimensions[2], v = dimensions[1], uv = u * v;\n return {\n get: (t, i, j, k) => t[k + j * u + i * uv],\n set: (t, i, j, k, x) => t[k + j * u + i * uv] = x,\n add: (t, i, j, k, x) => t[k + j * u + i * uv] += x,\n dataOffset: (i, j, k) => k + j * u + i * uv,\n getCoords: (o, c) => {\n const p = Math.floor(o / u);\n c[0] = Math.floor(p / v);\n c[1] = p % v;\n c[2] = o % u;\n return c;\n }\n };\n }\n throw new Error('bad axis order');\n }\n default: return {\n get: (t, ...c) => t[dataOffset(layout, c)],\n set: (t, ...c) => t[dataOffset(layout, c)] = c[c.length - 1],\n add: (t, ...c) => t[dataOffset(layout, c)] += c[c.length - 1],\n dataOffset: (...c) => dataOffset(layout, c),\n getCoords: (o, c) => getCoords(layout, o, c),\n };\n }\n }\n function creator(layout) {\n const { dimensions: ds } = layout;\n let size = 1;\n for (let i = 0, _i = ds.length; i < _i; i++)\n size *= ds[i];\n return ctor => new (ctor || layout.defaultCtor)(size);\n }\n function dataOffset(layout, coord) {\n const { accessDimensions: acc, axisOrderFastToSlow: ao } = layout;\n const d = acc.length - 1;\n let o = acc[d] * coord[ao[d]];\n for (let i = d - 1; i >= 0; i--) {\n o = (o + coord[ao[i]]) * acc[i];\n }\n return o;\n }\n function getCoords(layout, o, coords) {\n const { dimensions: dim, axisOrderFastToSlow: ao } = layout;\n const d = dim.length;\n let c = o;\n for (let i = 0; i < d; i++) {\n const d = dim[ao[i]];\n coords[ao[i]] = c % d;\n c = Math.floor(c / d);\n }\n coords[ao[d + 1]] = c;\n return coords;\n }\n // Convers \"slow to fast\" axis order to \"fast to slow\" and vice versa.\n function invertAxisOrder(v) {\n const ret = [];\n for (let i = 0; i < v.length; i++) {\n ret[i] = v[v.length - i - 1];\n }\n return ret;\n }\n Tensor.invertAxisOrder = invertAxisOrder;\n function reorder(xs, indices) {\n const ret = [];\n for (let i = 0; i < xs.length; i++)\n ret[i] = xs[indices[i]];\n return ret;\n }\n function convertToCanonicalAxisIndicesFastToSlow(order) {\n const indices = new Int32Array(order.length);\n for (let i = 0; i < order.length; i++)\n indices[order[i]] = i;\n return (xs) => reorder(xs, indices);\n }\n Tensor.convertToCanonicalAxisIndicesFastToSlow = convertToCanonicalAxisIndicesFastToSlow;\n function convertToCanonicalAxisIndicesSlowToFast(order) {\n const indices = new Int32Array(order.length);\n for (let i = 0; i < order.length; i++)\n indices[order[order.length - i - 1]] = i;\n return (xs) => reorder(xs, indices);\n }\n Tensor.convertToCanonicalAxisIndicesSlowToFast = convertToCanonicalAxisIndicesSlowToFast;\n})(Tensor || (Tensor = {}));\n","/**\n * Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.\n *\n * @author David Sehnal \n * @author Alexander Rose \n */\nimport * as ColumnHelpers from './column-helpers';\nimport { Tensor as Tensors } from '../../mol-math/linear-algebra';\nimport { parseInt as fastParseInt, parseFloat as fastParseFloat } from '../../mol-io/reader/common/text/number-parser';\nvar Column;\n(function (Column) {\n let Schema;\n (function (Schema) {\n // T also serves as a default value for undefined columns\n Schema.str = { '@type': 'str', T: '', valueType: 'str' };\n Schema.ustr = { '@type': 'str', T: '', valueType: 'str', transform: 'uppercase' };\n Schema.lstr = { '@type': 'str', T: '', valueType: 'str', transform: 'lowercase' };\n Schema.int = { '@type': 'int', T: 0, valueType: 'int' };\n Schema.coord = { '@type': 'coord', T: 0, valueType: 'float' };\n Schema.float = { '@type': 'float', T: 0, valueType: 'float' };\n function Str(options) { var _a; return { '@type': 'str', T: (_a = options === null || options === void 0 ? void 0 : options.defaultValue) !== null && _a !== void 0 ? _a : '', transform: options === null || options === void 0 ? void 0 : options.transform, valueType: 'str' }; }\n Schema.Str = Str;\n ;\n function Int(defaultValue = 0) { return { '@type': 'int', T: defaultValue, valueType: 'int' }; }\n Schema.Int = Int;\n ;\n function Float(defaultValue = 0) { return { '@type': 'float', T: defaultValue, valueType: 'float' }; }\n Schema.Float = Float;\n ;\n function Tensor(space, baseType = Schema.float) { return { '@type': 'tensor', T: space.create(), space, valueType: 'tensor', baseType }; }\n Schema.Tensor = Tensor;\n function Vector(dim, baseType = Schema.float) { return Tensor(Tensors.Vector(dim, baseType['@type'] === 'int' ? Int32Array : Float64Array), baseType); }\n Schema.Vector = Vector;\n function Matrix(rows, cols, baseType = Schema.float) { return Tensor(Tensors.ColumnMajorMatrix(rows, cols, baseType['@type'] === 'int' ? Int32Array : Float64Array), baseType); }\n Schema.Matrix = Matrix;\n function Aliased(t) {\n return t;\n }\n Schema.Aliased = Aliased;\n function List(separator, itemParse, defaultValue = []) {\n return { '@type': 'list', T: defaultValue, separator, itemParse, valueType: 'list' };\n }\n Schema.List = List;\n })(Schema = Column.Schema || (Column.Schema = {}));\n function is(v) {\n return !!v && !!v.schema && !!v.value;\n }\n Column.is = is;\n Column.ValueKind = {\n /** Defined value (= 0) */\n Present: 0 /* ValueKinds.Present */,\n /** Expressed in CIF as `.` (= 1) */\n NotPresent: 1 /* ValueKinds.NotPresent */,\n /** Expressed in CIF as `?` (= 2) */\n Unknown: 2 /* ValueKinds.Unknown */\n };\n function Undefined(rowCount, schema) {\n return constColumn(schema['T'], rowCount, schema, 1 /* ValueKinds.NotPresent */);\n }\n Column.Undefined = Undefined;\n function ofConst(v, rowCount, type) {\n return constColumn(v, rowCount, type, 0 /* ValueKinds.Present */);\n }\n Column.ofConst = ofConst;\n function ofLambda(spec) {\n return lambdaColumn(spec);\n }\n Column.ofLambda = ofLambda;\n /** values [min, max] (i.e. include both values) */\n function range(min, max) {\n return ofLambda({\n value: i => i + min,\n rowCount: Math.max(max - min + 1, 0),\n schema: Schema.int\n });\n }\n Column.range = range;\n function ofArray(spec) {\n return arrayColumn(spec);\n }\n Column.ofArray = ofArray;\n function ofIntArray(array) {\n return arrayColumn({ array, schema: Schema.int });\n }\n Column.ofIntArray = ofIntArray;\n function ofFloatArray(array) {\n return arrayColumn({ array, schema: Schema.float });\n }\n Column.ofFloatArray = ofFloatArray;\n function ofStringArray(array) {\n return arrayColumn({ array, schema: Schema.str });\n }\n Column.ofStringArray = ofStringArray;\n function ofStringAliasArray(array) {\n return arrayColumn({ array, schema: Schema.Aliased(Schema.str) });\n }\n Column.ofStringAliasArray = ofStringAliasArray;\n function ofStringListArray(array, separator = ',') {\n return arrayColumn({ array, schema: Schema.List(separator, x => x) });\n }\n Column.ofStringListArray = ofStringListArray;\n function ofIntTokens(tokens) {\n const { count, data, indices } = tokens;\n return lambdaColumn({\n value: (row) => fastParseInt(data, indices[2 * row], indices[2 * row + 1]) || 0,\n rowCount: count,\n schema: Schema.int,\n });\n }\n Column.ofIntTokens = ofIntTokens;\n function ofFloatTokens(tokens) {\n const { count, data, indices } = tokens;\n return lambdaColumn({\n value: (row) => fastParseFloat(data, indices[2 * row], indices[2 * row + 1]) || 0,\n rowCount: count,\n schema: Schema.float,\n });\n }\n Column.ofFloatTokens = ofFloatTokens;\n function ofStringTokens(tokens) {\n const { count, data, indices } = tokens;\n return lambdaColumn({\n value: (row) => {\n const ret = data.substring(indices[2 * row], indices[2 * row + 1]);\n if (ret === '.' || ret === '?')\n return '';\n return ret;\n },\n rowCount: count,\n schema: Schema.str,\n });\n }\n Column.ofStringTokens = ofStringTokens;\n function window(column, start, end) {\n return windowColumn(column, start, end);\n }\n Column.window = window;\n function view(column, indices, checkIndentity = true) {\n return columnView(column, indices, checkIndentity);\n }\n Column.view = view;\n /** A map of the 1st occurence of each value. */\n function createFirstIndexMap(column) {\n return createFirstIndexMapOfColumn(column);\n }\n Column.createFirstIndexMap = createFirstIndexMap;\n function createIndexer(column) {\n return createIndexerOfColumn(column);\n }\n Column.createIndexer = createIndexer;\n function mapToArray(column, f, ctor) {\n return mapToArrayImpl(column, f, ctor || Array);\n }\n Column.mapToArray = mapToArray;\n function areEqual(a, b) {\n return areColumnsEqual(a, b);\n }\n Column.areEqual = areEqual;\n function indicesOf(c, test) {\n return columnIndicesOf(c, test);\n }\n Column.indicesOf = indicesOf;\n /** Makes the column backed by an array. Useful for columns that are accessed often. */\n function asArrayColumn(c, array) {\n if (c.__array)\n return c;\n if (!c.isDefined)\n return Undefined(c.rowCount, c.schema);\n return arrayColumn({ array: c.toArray({ array }), schema: c.schema, valueKind: c.valueKind });\n }\n Column.asArrayColumn = asArrayColumn;\n function copyToArray(c, array, offset = 0) {\n if (!c.isDefined)\n return;\n const cArray = c.__array;\n if (cArray) {\n for (let i = 0, _i = cArray.length; i < _i; i++)\n array[offset + i] = cArray[i];\n }\n else {\n for (let i = 0, _i = c.rowCount; i < _i; i++)\n array[offset + i] = c.value(i);\n }\n }\n Column.copyToArray = copyToArray;\n function isIdentity(c) {\n for (let i = 0, _i = c.rowCount; i < _i; i++) {\n if (i !== c.value(i))\n return false;\n }\n return true;\n }\n Column.isIdentity = isIdentity;\n})(Column || (Column = {}));\nexport { Column };\nfunction createFirstIndexMapOfColumn(c) {\n const map = new Map();\n for (let i = 0, _i = c.rowCount; i < _i; i++) {\n const v = c.value(i);\n if (!map.has(v))\n map.set(c.value(i), i);\n }\n return map;\n}\nfunction createIndexerOfColumn(c) {\n const map = new Map();\n for (let i = 0, _i = c.rowCount; i < _i; i++) {\n const v = c.value(i);\n if (!map.has(v))\n map.set(c.value(i), i);\n }\n return v => map.has(v) ? map.get(v) : -1;\n}\nfunction constColumn(v, rowCount, schema, valueKind) {\n const value = row => v;\n return {\n schema: schema,\n __array: void 0,\n isDefined: valueKind === 0 /* Column.ValueKinds.Present */,\n rowCount,\n value,\n valueKind: row => valueKind,\n toArray: params => {\n const { array } = ColumnHelpers.createArray(rowCount, params);\n for (let i = 0, _i = array.length; i < _i; i++)\n array[i] = v;\n return array;\n },\n areValuesEqual: (rowA, rowB) => true\n };\n}\nfunction lambdaColumn({ value, valueKind, areValuesEqual, rowCount, schema }) {\n return {\n schema: schema,\n __array: void 0,\n isDefined: true,\n rowCount,\n value,\n valueKind: valueKind ? valueKind : row => 0 /* Column.ValueKinds.Present */,\n toArray: params => {\n const { array, start } = ColumnHelpers.createArray(rowCount, params);\n for (let i = 0, _i = array.length; i < _i; i++)\n array[i] = value(i + start);\n return array;\n },\n areValuesEqual: areValuesEqual ? areValuesEqual : (rowA, rowB) => value(rowA) === value(rowB)\n };\n}\nfunction arrayColumn({ array, schema, valueKind }) {\n const rowCount = array.length;\n const defaultValue = schema.T;\n const value = schema.valueType === 'str'\n ? schema.transform === 'lowercase'\n ? row => { const v = array[row]; return typeof v === 'string' ? v.toLowerCase() : `${v !== null && v !== void 0 ? v : defaultValue}`.toLowerCase(); }\n : schema.transform === 'uppercase'\n ? row => { const v = array[row]; return typeof v === 'string' ? v.toUpperCase() : `${v !== null && v !== void 0 ? v : defaultValue}`.toUpperCase(); }\n : row => { const v = array[row]; return typeof v === 'string' ? v : `${v !== null && v !== void 0 ? v : defaultValue}`; }\n : row => array[row];\n const isTyped = ColumnHelpers.isTypedArray(array);\n return {\n schema: schema,\n __array: array,\n isDefined: true,\n rowCount,\n value,\n valueKind: valueKind ? valueKind : row => 0 /* Column.ValueKinds.Present */,\n toArray: schema.valueType === 'str'\n ? schema.transform === 'lowercase'\n ? params => {\n const { start, end } = ColumnHelpers.getArrayBounds(rowCount, params);\n const ret = new (params && typeof params.array !== 'undefined' ? params.array : array.constructor)(end - start);\n for (let i = 0, _i = end - start; i < _i; i++) {\n const v = array[start + i];\n ret[i] = typeof v === 'string' ? v.toLowerCase() : `${v !== null && v !== void 0 ? v : defaultValue}`.toLowerCase();\n }\n return ret;\n }\n : schema.transform === 'uppercase'\n ? params => {\n const { start, end } = ColumnHelpers.getArrayBounds(rowCount, params);\n const ret = new (params && typeof params.array !== 'undefined' ? params.array : array.constructor)(end - start);\n for (let i = 0, _i = end - start; i < _i; i++) {\n const v = array[start + i];\n ret[i] = typeof v === 'string' ? v.toUpperCase() : `${v !== null && v !== void 0 ? v : defaultValue}`.toUpperCase();\n }\n return ret;\n }\n : params => {\n const { start, end } = ColumnHelpers.getArrayBounds(rowCount, params);\n const ret = new (params && typeof params.array !== 'undefined' ? params.array : array.constructor)(end - start);\n for (let i = 0, _i = end - start; i < _i; i++) {\n const v = array[start + i];\n ret[i] = typeof v === 'string' ? v : `${v !== null && v !== void 0 ? v : defaultValue}`;\n }\n return ret;\n }\n : isTyped\n ? params => ColumnHelpers.typedArrayWindow(array, params)\n : params => {\n const { start, end } = ColumnHelpers.getArrayBounds(rowCount, params);\n if (start === 0 && end === array.length)\n return array;\n const ret = new (params && typeof params.array !== 'undefined' ? params.array : array.constructor)(end - start);\n for (let i = 0, _i = end - start; i < _i; i++)\n ret[i] = array[start + i];\n return ret;\n },\n areValuesEqual: (rowA, rowB) => array[rowA] === array[rowB]\n };\n}\nfunction windowColumn(column, start, end) {\n if (!column.isDefined)\n return Column.Undefined(end - start, column.schema);\n if (start === 0 && end === column.rowCount)\n return column;\n if (!!column.__array && ColumnHelpers.isTypedArray(column.__array))\n return windowTyped(column, start, end);\n return windowFull(column, start, end);\n}\nfunction windowTyped(c, start, end) {\n const array = ColumnHelpers.typedArrayWindow(c.__array, { start, end });\n const vk = c.valueKind;\n return arrayColumn({ array, schema: c.schema, valueKind: row => vk(start + row) });\n}\nfunction windowFull(c, start, end) {\n const v = c.value, vk = c.valueKind, ave = c.areValuesEqual;\n const value = start === 0 ? v : row => v(row + start);\n const rowCount = end - start;\n return {\n schema: c.schema,\n __array: void 0,\n isDefined: c.isDefined,\n rowCount,\n value,\n valueKind: start === 0 ? vk : row => vk(row + start),\n toArray: params => {\n const { array } = ColumnHelpers.createArray(rowCount, params);\n for (let i = 0, _i = array.length; i < _i; i++)\n array[i] = v(i + start);\n return array;\n },\n areValuesEqual: start === 0 ? ave : (rowA, rowB) => ave(rowA + start, rowB + start)\n };\n}\nfunction isIdentity(map, rowCount) {\n if (map.length !== rowCount)\n return false;\n for (let i = 0, _i = map.length; i < _i; i++) {\n if (map[i] !== i)\n return false;\n }\n return true;\n}\nfunction columnView(c, map, checkIdentity) {\n if (c.rowCount === 0)\n return c;\n if (checkIdentity && isIdentity(map, c.rowCount))\n return c;\n if (!!c.__array && typeof c.value(0) === typeof c.__array[0])\n return arrayView(c, map);\n return viewFull(c, map);\n}\nfunction arrayView(c, map) {\n const array = c.__array;\n const ret = new array.constructor(map.length);\n for (let i = 0, _i = map.length; i < _i; i++)\n ret[i] = array[map[i]];\n const vk = c.valueKind;\n return arrayColumn({ array: ret, schema: c.schema, valueKind: row => vk(map[row]) });\n}\nfunction viewFull(c, map) {\n const v = c.value, vk = c.valueKind, ave = c.areValuesEqual;\n const value = row => v(map[row]);\n const rowCount = map.length;\n return {\n schema: c.schema,\n __array: void 0,\n isDefined: c.isDefined,\n rowCount,\n value,\n valueKind: row => vk(map[row]),\n toArray: params => {\n const { array } = ColumnHelpers.createArray(rowCount, params);\n for (let i = 0, _i = array.length; i < _i; i++)\n array[i] = v(map[i]);\n return array;\n },\n areValuesEqual: (rowA, rowB) => ave(map[rowA], map[rowB])\n };\n}\nfunction mapToArrayImpl(c, f, ctor) {\n const ret = new ctor(c.rowCount);\n for (let i = 0, _i = c.rowCount; i < _i; i++)\n ret[i] = f(c.value(i));\n return ret;\n}\nfunction areColumnsEqual(a, b) {\n if (a === b)\n return true;\n if (a.rowCount !== b.rowCount || a.isDefined !== b.isDefined || a.schema.valueType !== b.schema.valueType)\n return false;\n if (!!a.__array && !!b.__array)\n return areArraysEqual(a, b);\n return areValuesEqual(a, b);\n}\nfunction areArraysEqual(a, b) {\n const xs = a.__array, ys = b.__array;\n for (let i = 0, _i = a.rowCount; i < _i; i++) {\n if (xs[i] !== ys[i])\n return false;\n }\n return true;\n}\nfunction areValuesEqual(a, b) {\n const va = a.value, vb = b.value;\n for (let i = 0, _i = a.rowCount; i < _i; i++) {\n if (va(i) !== vb(i))\n return false;\n }\n return true;\n}\nfunction columnIndicesOf(c, test) {\n const ret = [], v = c.value;\n for (let i = 0, _i = c.rowCount; i < _i; i++) {\n if (test(v(i)))\n ret[ret.length] = i;\n }\n return ret;\n}\n","/**\n * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.\n *\n * @author David Sehnal \n */\nvar BitFlags;\n(function (BitFlags) {\n function create(flags) { return flags; }\n BitFlags.create = create;\n function has(flags, flag) { return (flags & flag) !== 0; }\n BitFlags.has = has;\n /** toCheck must be non-zero */\n function hasAll(flags, toCheck) { return !!toCheck && (flags & toCheck) === toCheck; }\n BitFlags.hasAll = hasAll;\n})(BitFlags || (BitFlags = {}));\nexport { BitFlags };\n","/**\n * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.\n *\n * Adapted from CIFTools.js (https://github.com/dsehnal/CIFTools.js)\n *\n * @author David Sehnal \n */\nvar StringBuilder;\n(function (StringBuilder) {\n function create(chunkCapacity = 512) {\n return {\n current: [],\n offset: 0,\n capacity: chunkCapacity,\n chunks: []\n };\n }\n StringBuilder.create = create;\n function getString(builder) {\n if (!builder.chunks.length) {\n if (builder.current.length === builder.offset)\n return builder.current.join('');\n return builder.current.splice(0, builder.offset).join('');\n }\n if (builder.offset > 0) {\n builder.chunks[builder.chunks.length] = builder.current.length === builder.offset\n ? builder.current.join('')\n : builder.current.slice(0, builder.offset).join('');\n }\n return builder.chunks.join('');\n }\n StringBuilder.getString = getString;\n function getSize(builder) {\n let size = 0;\n for (const c of builder.chunks)\n size += c.length;\n for (let i = 0; i < builder.offset; i++)\n size += builder.current[i].length;\n return size;\n }\n StringBuilder.getSize = getSize;\n function getChunks(builder) {\n if (builder.offset > 0) {\n if (builder.current.length === builder.offset)\n builder.chunks[builder.chunks.length] = builder.current.join('');\n else\n builder.chunks[builder.chunks.length] = builder.current.slice(0, builder.offset).join('');\n builder.offset = 0;\n }\n return builder.chunks;\n }\n StringBuilder.getChunks = getChunks;\n const __paddingSpaces = [];\n (function () {\n let s = '';\n for (let i = 0; i < 512 /* PaddingSpaces.Count */; i++) {\n __paddingSpaces[i] = s;\n s = s + ' ';\n }\n })();\n function newline(builder) {\n writeSafe(builder, '\\n');\n }\n StringBuilder.newline = newline;\n function whitespace(builder, len) {\n if (len > 0)\n writeSafe(builder, __paddingSpaces[len]);\n }\n StringBuilder.whitespace = whitespace;\n function whitespace1(builder) {\n writeSafe(builder, ' ');\n }\n StringBuilder.whitespace1 = whitespace1;\n function write(builder, val) {\n if (!val)\n return;\n if (builder.offset === builder.capacity) {\n builder.chunks[builder.chunks.length] = builder.current.join('');\n builder.offset = 0;\n }\n builder.current[builder.offset++] = val;\n }\n StringBuilder.write = write;\n /** Write without check. */\n function writeSafe(builder, val) {\n if (builder.offset === builder.capacity) {\n builder.chunks[builder.chunks.length] = builder.current.join('');\n builder.offset = 0;\n }\n builder.current[builder.offset++] = val;\n }\n StringBuilder.writeSafe = writeSafe;\n function writePadLeft(builder, val, totalWidth) {\n if (!val) {\n whitespace(builder, totalWidth);\n return;\n }\n const padding = totalWidth - val.length;\n whitespace(builder, padding);\n writeSafe(builder, val);\n }\n StringBuilder.writePadLeft = writePadLeft;\n function writePadRight(builder, val, totalWidth) {\n if (!val) {\n whitespace(builder, totalWidth);\n return;\n }\n const padding = totalWidth - val.length;\n writeSafe(builder, val);\n whitespace(builder, padding);\n }\n StringBuilder.writePadRight = writePadRight;\n function writeInteger(builder, val) {\n writeSafe(builder, '' + val);\n }\n StringBuilder.writeInteger = writeInteger;\n function writeIntegerAndSpace(builder, val) {\n writeSafe(builder, '' + val + ' ');\n }\n StringBuilder.writeIntegerAndSpace = writeIntegerAndSpace;\n function writeIntegerPadLeft(builder, val, totalWidth) {\n const s = '' + val;\n const padding = totalWidth - s.length;\n whitespace(builder, padding);\n writeSafe(builder, s);\n }\n StringBuilder.writeIntegerPadLeft = writeIntegerPadLeft;\n function writeIntegerPadRight(builder, val, totalWidth) {\n const s = '' + val;\n const padding = totalWidth - s.length;\n writeSafe(builder, s);\n whitespace(builder, padding);\n }\n StringBuilder.writeIntegerPadRight = writeIntegerPadRight;\n /**\n * @example writeFloat(123.2123, 100) -- 2 decim\n */\n function writeFloat(builder, val, precisionMultiplier) {\n writeSafe(builder, '' + Math.round(precisionMultiplier * val) / precisionMultiplier);\n }\n StringBuilder.writeFloat = writeFloat;\n function writeFloatPadLeft(builder, val, precisionMultiplier, totalWidth) {\n const s = '' + Math.round(precisionMultiplier * val) / precisionMultiplier;\n const padding = totalWidth - s.length;\n whitespace(builder, padding);\n writeSafe(builder, s);\n }\n StringBuilder.writeFloatPadLeft = writeFloatPadLeft;\n function writeFloatPadRight(builder, val, precisionMultiplier, totalWidth) {\n const s = '' + Math.round(precisionMultiplier * val) / precisionMultiplier;\n const padding = totalWidth - s.length;\n writeSafe(builder, s);\n whitespace(builder, padding);\n }\n StringBuilder.writeFloatPadRight = writeFloatPadRight;\n})(StringBuilder || (StringBuilder = {}));\nexport { StringBuilder };\n","/**\n * Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.\n *\n * @author David Sehnal \n * @author Alexander Rose \n *\n * based in part on https://github.com/dsehnal/CIFTools.js\n */\n/**\n * Efficient integer and float parsers.\n *\n * For the purposes of parsing numbers from the mmCIF data representations,\n * up to 4 times faster than JS parseInt/parseFloat.\n */\nexport function parseIntSkipLeadingWhitespace(str, start, end) {\n while (start < end && str.charCodeAt(start) === 32)\n start++;\n return parseInt(str, start, end);\n}\nexport function parseInt(str, start, end) {\n let _start = start, ret = 0, neg = 1;\n if (str.charCodeAt(_start) === 45 /* - */) {\n neg = -1;\n ++_start;\n }\n else if (str.charCodeAt(_start) === 43 /* + */) {\n ++_start;\n }\n for (; _start < end; _start++) {\n const c = str.charCodeAt(_start) - 48;\n if (c > 9 || c < 0)\n return (neg * ret) | 0;\n else\n ret = (10 * ret + c) | 0;\n }\n return neg * ret;\n}\nfunction parseScientific(main, str, start, end) {\n // handle + in '1e+1' separately.\n if (str.charCodeAt(start) === 43 /* + */)\n start++;\n return main * Math.pow(10.0, parseInt(str, start, end));\n}\nexport function parseFloatSkipLeadingWhitespace(str, start, end) {\n while (start < end && str.charCodeAt(start) === 32)\n start++;\n return parseFloat(str, start, end);\n}\nexport function parseFloat(str, start, end) {\n let _start = start, neg = 1.0, ret = 0.0, point = 0.0, div = 1.0;\n if (str.charCodeAt(_start) === 45 /* - */) {\n neg = -1;\n ++_start;\n }\n else if (str.charCodeAt(_start) === 43 /* + */) {\n ++_start;\n }\n while (_start < end) {\n let c = str.charCodeAt(_start) - 48;\n if (c >= 0 && c < 10) {\n ret = ret * 10 + c;\n ++_start;\n }\n else if (c === -2) { // .\n ++_start;\n while (_start < end) {\n c = str.charCodeAt(_start) - 48;\n if (c >= 0 && c < 10) {\n point = 10.0 * point + c;\n div = 10.0 * div;\n ++_start;\n }\n else if (c === 53 || c === 21) { // 'e'/'E'\n return parseScientific(neg * (ret + point / div), str, _start + 1, end);\n }\n else {\n return neg * (ret + point / div);\n }\n }\n return neg * (ret + point / div);\n }\n else if (c === 53 || c === 21) { // 'e'/'E'\n return parseScientific(neg * ret, str, _start + 1, end);\n }\n else {\n break;\n }\n }\n return neg * ret;\n}\nexport const NumberType = {\n Int: 0 /* NumberTypes.Int */,\n Float: 1 /* NumberTypes.Float */,\n Scientific: 2 /* NumberTypes.Scientific */,\n NaN: 3 /* NumberTypes.NaN */\n};\nfunction isInt(str, start, end) {\n if (str.charCodeAt(start) === 45 /* - */) {\n start++;\n }\n for (; start < end; start++) {\n const c = str.charCodeAt(start) - 48;\n if (c > 9 || c < 0)\n return false;\n }\n return true;\n}\n// TODO: check for \"scientific integers?\"\nfunction getNumberTypeScientific(str, start, end) {\n // handle + in '1e+1' separately.\n if (str.charCodeAt(start) === 43 /* + */)\n start++;\n return isInt(str, start, end) ? 2 /* NumberTypes.Scientific */ : 3 /* NumberTypes.NaN */;\n}\n/** The whole range must match, otherwise returns NaN */\nexport function getNumberType(str) {\n let start = 0;\n const end = str.length;\n if (str.charCodeAt(start) === 45) { // -\n ++start;\n }\n // string is . or -.\n if (str.charCodeAt(start) === 46 && end - start === 1) {\n return 3 /* NumberTypes.NaN */;\n }\n while (start < end) {\n let c = str.charCodeAt(start) - 48;\n if (c >= 0 && c < 10) {\n ++start;\n }\n else if (c === -2) { // .\n ++start;\n let hasDigit = false;\n while (start < end) {\n c = str.charCodeAt(start) - 48;\n if (c >= 0 && c < 10) {\n hasDigit = true;\n ++start;\n }\n else if (c === 53 || c === 21) { // 'e'/'E'\n return getNumberTypeScientific(str, start + 1, end);\n }\n else {\n return 3 /* NumberTypes.NaN */;\n }\n }\n return hasDigit ? 1 /* NumberTypes.Float */ : 0 /* NumberTypes.Int */;\n }\n else if (c === 53 || c === 21) { // 'e'/'E'\n if (start === 0 || start === 1 && str.charCodeAt(0) === 45) {\n return 3 /* NumberTypes.NaN */; // string starts with e/E or -e/-E\n }\n return getNumberTypeScientific(str, start + 1, end);\n }\n else {\n break;\n }\n }\n return start === end ? 0 /* NumberTypes.Int */ : 3 /* NumberTypes.NaN */;\n}\n","/**\n * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.\n *\n * @author David Sehnal \n */\nexport function arrayLess(arr, i, j) {\n return arr[i] - arr[j];\n}\nexport function arraySwap(arr, i, j) {\n const temp = arr[i];\n arr[i] = arr[j];\n arr[j] = temp;\n}\nfunction medianPivotIndex(data, cmp, l, r) {\n const m = (l + r) >> 1;\n if (cmp(data, l, r) > 0)\n return cmp(data, l, m) > 0 ? cmp(data, m, r) > 0 ? m : r : l;\n else\n return cmp(data, r, m) > 0 ? cmp(data, m, l) > 0 ? m : l : r;\n}\nfunction partition(ctx, l, r) {\n const { cmp, swap, data, parts } = ctx;\n let equals = l + 1, tail = r;\n // move the median to the 1st spot\n swap(data, l, medianPivotIndex(data, cmp, l, r));\n while (cmp(data, tail, l) > 0) {\n --tail;\n }\n for (let i = l + 1; i <= tail; i++) {\n const c = cmp(data, i, l);\n if (c > 0) {\n swap(data, i, tail);\n --tail;\n while (cmp(data, tail, l) > 0) {\n --tail;\n }\n i--;\n }\n else if (c === 0) {\n swap(data, i, equals);\n equals++;\n }\n }\n // move the medians to the correct spots\n for (let i = l; i < equals; i++) {\n swap(data, i, l + tail - i);\n }\n parts[0] = tail - equals + l + 1;\n parts[1] = tail;\n}\nfunction insertionSort({ data, cmp, swap }, start, end) {\n for (let i = start + 1; i <= end; i++) {\n let j = i - 1;\n while (j >= start && cmp(data, j, j + 1) > 0) {\n swap(data, j, j + 1);\n j = j - 1;\n }\n }\n}\nfunction quickSort(ctx, low, high) {\n const { parts } = ctx;\n while (low < high) {\n if (high - low < 16) {\n insertionSort(ctx, low, high);\n return;\n }\n partition(ctx, low, high);\n const li = parts[0], ri = parts[1];\n if (li - low < high - ri) {\n quickSort(ctx, low, li - 1);\n low = ri + 1;\n }\n else {\n quickSort(ctx, ri + 1, high);\n high = li - 1;\n }\n }\n}\nfunction partitionArrayAsc(data, parts, l, r) {\n let equals = l + 1, tail = r;\n // move the median to the 1st spot\n arraySwap(data, l, medianPivotIndex(data, arrayLess, l, r));\n const pivot = data[l];\n while (data[tail] > pivot) {\n --tail;\n }\n for (let i = l + 1; i <= tail; i++) {\n const v = data[i];\n if (v > pivot) {\n arraySwap(data, i, tail);\n --tail;\n while (data[tail] > pivot) {\n --tail;\n }\n i--;\n }\n else if (v === pivot) {\n arraySwap(data, i, equals);\n ++equals;\n }\n }\n // move all medians to the correct spots\n for (let i = l; i < equals; i++) {\n arraySwap(data, i, l + tail - i);\n }\n parts[0] = tail - equals + l + 1;\n parts[1] = tail;\n}\nfunction insertionSortArrayAsc(data, start, end) {\n for (let i = start + 1; i <= end; i++) {\n const key = data[i];\n let j = i - 1;\n while (j >= start && data[j] > key) {\n data[j + 1] = data[j];\n j = j - 1;\n }\n data[j + 1] = key;\n }\n}\nfunction quickSortArrayAsc(data, parts, low, high) {\n while (low < high) {\n if (high - low < 16) {\n insertionSortArrayAsc(data, low, high);\n return;\n }\n partitionArrayAsc(data, parts, low, high);\n const li = parts[0], ri = parts[1];\n if (li - low < high - ri) {\n quickSortArrayAsc(data, parts, low, li - 1);\n low = ri + 1;\n }\n else {\n quickSortArrayAsc(data, parts, ri + 1, high);\n high = li - 1;\n }\n }\n}\nexport function sortArray(data, cmp = arrayLess) {\n return sortArrayRange(data, 0, data.length, cmp);\n}\nexport function sortArrayRange(data, start, end, cmp = arrayLess) {\n if (cmp === arrayLess)\n quickSortArrayAsc(data, [0, 0], start, end - 1);\n else\n quickSort({ data, cmp, swap: arraySwap, parts: [0, 0] }, start, end - 1);\n return data;\n}\nexport function sort(data, start, end, cmp, swap) {\n const ctx = { data, cmp, swap, parts: [0, 0] };\n quickSort(ctx, start, end - 1);\n return data;\n}\n","/**\n * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.\n *\n * @author David Sehnal \n */\nconst now = (function () {\n if (typeof window !== 'undefined' && window.performance) {\n const perf = window.performance;\n return () => perf.now();\n }\n else if (typeof process !== 'undefined' && process.hrtime !== 'undefined' && typeof process.hrtime === 'function') {\n return () => {\n const t = process.hrtime();\n return t[0] * 1000 + t[1] / 1000000;\n };\n }\n else if (Date.now) {\n return () => Date.now();\n }\n else {\n return () => +new Date();\n }\n}());\nfunction formatTimespan(t, includeMsZeroes = true) {\n if (isNaN(t))\n return 'n/a';\n const h = Math.floor(t / (60 * 60 * 1000)), m = Math.floor(t / (60 * 1000) % 60), s = Math.floor(t / 1000 % 60);\n let ms = Math.floor(t % 1000).toString();\n while (ms.length < 3)\n ms = '0' + ms;\n while (!includeMsZeroes && ms.length > 1 && ms[ms.length - 1] === '0')\n ms = ms.substr(0, ms.length - 1);\n if (h > 0)\n return `${h}h${m}m${s}.${ms}s`;\n if (m > 0)\n return `${m}m${s}.${ms}s`;\n if (s > 0)\n return `${s}.${ms}s`;\n return `${t.toFixed(0)}ms`;\n}\nexport { now, formatTimespan };\n","/**\n * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.\n *\n * @author David Sehnal \n */\nimport { now } from '../mol-util/now';\nvar UUID;\n(function (UUID) {\n const _btoa = typeof btoa !== 'undefined' ? btoa : (s) => Buffer.from(s).toString('base64');\n const chars = [];\n /** Creates a 22 characters 'base64' encoded UUID */\n function create22() {\n let d = (+new Date()) + now();\n for (let i = 0; i < 16; i++) {\n chars[i] = String.fromCharCode((d + Math.random() * 0xff) % 0xff | 0);\n d = Math.floor(d / 0xff);\n }\n return _btoa(chars.join('')).replace(/\\+/g, '-').replace(/\\//g, '_').substr(0, 22);\n }\n UUID.create22 = create22;\n function createv4() {\n let d = (+new Date()) + now();\n const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {\n const r = (d + Math.random() * 16) % 16 | 0;\n d = Math.floor(d / 16);\n return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);\n });\n return uuid;\n }\n UUID.createv4 = createv4;\n function is(x) {\n return typeof x === 'string';\n }\n UUID.is = is;\n})(UUID || (UUID = {}));\nexport { UUID };\n","/**\n * Copyright (c) 2017 Mol* contributors, licensed under MIT, See LICENSE file for more info.\n *\n * @author David Sehnal \n */\n// TODO check if the removal of FastSet and the removal of the context object for forEach\n// have any performance implications\nfunction _ascSort(a, b) {\n return a - b;\n}\nexport function sortAsc(array) {\n Array.prototype.sort.call(array, _ascSort);\n return array;\n}\nvar Mask;\n(function (Mask) {\n class EmptyMask {\n has(i) { return false; }\n forEach(f, ctx) { return ctx; }\n constructor() {\n this.size = 0;\n }\n }\n class SingletonMask {\n has(i) { return i === this.idx; }\n forEach(f, ctx) { f(this.idx, ctx); return ctx; }\n constructor(idx) {\n this.idx = idx;\n this.size = 1;\n }\n }\n class BitMask {\n has(i) { return i < this.length && !!this.mask[i]; }\n _forEach(f, ctx) {\n for (let i = 0; i < this.length; i++) {\n if (this.mask[i])\n f(i, ctx);\n }\n }\n forEach(f, ctx) {\n this._forEach(f, ctx);\n return ctx;\n }\n constructor(mask, size) {\n this.mask = mask;\n this.size = size;\n this.length = mask.length;\n }\n }\n class AllMask {\n has(i) { return true; }\n _forEach(f, ctx) {\n for (let i = 0; i < this.size; i++) {\n f(i, ctx);\n }\n }\n forEach(f, ctx) {\n this._forEach(f, ctx);\n return ctx;\n }\n constructor(size) {\n this.size = size;\n }\n }\n class SetMask {\n has(i) { return this.set.has(i); }\n _forEach(f, ctx) {\n for (const idx of this.flatten()) {\n f(idx, ctx);\n }\n }\n flatten() {\n if (this._flat)\n return this._flat;\n const indices = new Int32Array(this.size);\n let offset = 0;\n this.set.forEach(i => indices[offset++] = i);\n sortAsc(indices);\n this._flat = indices;\n return this._flat;\n }\n forEach(f, ctx) {\n this._forEach(f, ctx);\n return ctx;\n }\n constructor(set) {\n this.set = set;\n this._flat = void 0;\n this.size = set.size;\n }\n }\n function always(size) { return new AllMask(size); }\n Mask.always = always;\n Mask.never = new EmptyMask();\n function ofSet(set) {\n return new SetMask(set);\n }\n Mask.ofSet = ofSet;\n function singleton(i) {\n return new SingletonMask(i);\n }\n Mask.singleton = singleton;\n function ofUniqueIndices(indices) {\n const len = indices.length;\n if (len === 0)\n return new EmptyMask();\n if (len === 1)\n return new SingletonMask(indices[0]);\n let max = 0;\n for (const i of indices) {\n if (i > max)\n max = i;\n }\n if (len === max)\n return new AllMask(len);\n const f = len / max;\n if (f < 1 / 12) {\n const set = new Set();\n for (const i of indices)\n set.add(i);\n return new SetMask(set);\n }\n const mask = new Int8Array(max + 1);\n for (const i of indices) {\n mask[i] = 1;\n }\n return new BitMask(mask, indices.length);\n }\n Mask.ofUniqueIndices = ofUniqueIndices;\n function ofMask(mask, size) {\n return new BitMask(mask, size);\n }\n Mask.ofMask = ofMask;\n function hasAny(mask, xs) {\n for (const x of xs) {\n if (mask.has(x))\n return true;\n }\n return false;\n }\n Mask.hasAny = hasAny;\n function complement(mask, against) {\n let count = 0;\n let max = 0;\n against.forEach(i => {\n if (!mask.has(i)) {\n count++;\n if (i > max)\n max = i;\n }\n });\n if (count / max < 1 / 12) {\n // set based\n const set = new Set();\n against.forEach(i => {\n if (!mask.has(i)) {\n set.add(i);\n }\n });\n return ofSet(set);\n }\n else {\n // mask based\n const target = new Uint8Array(max + 1);\n against.forEach(i => {\n if (!mask.has(i)) {\n target[i] = 1;\n }\n });\n return ofMask(target, count);\n }\n }\n Mask.complement = complement;\n})(Mask || (Mask = {}));\nexport { Mask };\n","/**\n * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.\n *\n * @author David Sehnal \n * @author Alexander Rose \n */\nimport { idFactory } from './id-factory';\nvar ValueRef;\n(function (ValueRef) {\n function create(ref) { return { ref }; }\n ValueRef.create = create;\n function set(ref, value) { ref.ref = value; return ref; }\n ValueRef.set = set;\n})(ValueRef || (ValueRef = {}));\nconst getNextId = idFactory(0, 0x7FFFFFFF);\nvar ValueBox;\n(function (ValueBox) {\n function create(value, metadata) {\n return { id: getNextId(), version: 0, value, metadata: metadata };\n }\n ValueBox.create = create;\n /** The box.metadata is carried over from the old box */\n function withValue(box, value) {\n return { id: box.id, version: box.version + 1, value, metadata: box.metadata };\n }\n ValueBox.withValue = withValue;\n})(ValueBox || (ValueBox = {}));\nvar ValueCell;\n(function (ValueCell) {\n function create(value, metadata) {\n return ValueRef.create(ValueBox.create(value, metadata));\n }\n ValueCell.create = create;\n /** The box.metadata is carried over from the old box */\n function update(cell, value) {\n return ValueRef.set(cell, ValueBox.withValue(cell.ref, value));\n }\n ValueCell.update = update;\n function set(cell, box) {\n return ValueRef.set(cell, box);\n }\n ValueCell.set = set;\n /** Updates the cell if the value is has changed, comparing by reference */\n function updateIfChanged(cell, value) {\n return cell.ref.value !== value ? update(cell, value) : cell;\n }\n ValueCell.updateIfChanged = updateIfChanged;\n})(ValueCell || (ValueCell = {}));\nexport { ValueRef, ValueBox, ValueCell };\n","/**\n * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.\n *\n * @author Alexander Rose \n */\n/** Builds id function returning ids within [firstId, maxId) */\nexport function idFactory(firstId = 0, maxId = Number.MAX_SAFE_INTEGER) {\n let _nextId = firstId;\n return () => {\n const ret = _nextId;\n _nextId = (_nextId + 1) % maxId;\n return ret;\n };\n}\n","/**\n * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.\n *\n * @author David Sehnal \n */\nimport { Column } from './column';\nimport { sortArray } from '../util/sort';\nimport { StringBuilder } from '../../mol-util';\n/** An immutable table */\nvar Table;\n(function (Table) {\n function is(t) {\n return t && typeof t._rowCount === 'number' && !!t._columns && !!t._schema;\n }\n Table.is = is;\n function pickColumns(schema, table, guard = {}) {\n const ret = Object.create(null);\n const keys = Object.keys(schema);\n ret._rowCount = table._rowCount;\n ret._columns = keys;\n ret._schema = schema;\n for (const k of keys) {\n if (!!table[k])\n ret[k] = table[k];\n else if (!!guard[k])\n ret[k] = guard[k];\n else\n throw Error(`Cannot find column '${k}'.`);\n }\n return ret;\n }\n Table.pickColumns = pickColumns;\n function ofColumns(schema, columns) {\n const _columns = Object.keys(columns);\n const _rowCount = columns[_columns[0]].rowCount;\n return { _rowCount, _columns, _schema: schema, ...columns };\n }\n Table.ofColumns = ofColumns;\n function ofPartialColumns(schema, partialColumns, rowCount) {\n const ret = Object.create(null);\n const columns = Object.keys(schema);\n ret._rowCount = rowCount;\n ret._columns = columns;\n ret._schema = schema;\n for (const k of columns) {\n if (k in partialColumns)\n ret[k] = partialColumns[k];\n else\n ret[k] = Column.Undefined(rowCount, schema[k]);\n }\n return ret;\n }\n Table.ofPartialColumns = ofPartialColumns;\n function ofUndefinedColumns(schema, rowCount) {\n const ret = Object.create(null);\n const columns = Object.keys(schema);\n ret._rowCount = rowCount;\n ret._columns = columns;\n ret._schema = schema;\n for (const k of columns) {\n ret[k] = Column.Undefined(rowCount, schema[k]);\n }\n return ret;\n }\n Table.ofUndefinedColumns = ofUndefinedColumns;\n function ofRows(schema, rows) {\n const ret = Object.create(null);\n const rowCount = rows.length;\n const columns = Object.keys(schema);\n ret._rowCount = rowCount;\n ret._columns = columns;\n ret._schema = schema;\n for (const k of columns) {\n ret[k] = Column.ofLambda({\n rowCount,\n schema: schema[k],\n value: r => rows[r][k],\n valueKind: r => typeof rows[r][k] === 'undefined' ? 1 /* Column.ValueKinds.NotPresent */ : 0 /* Column.ValueKinds.Present */\n });\n }\n return ret;\n }\n Table.ofRows = ofRows;\n function ofArrays(schema, arrays) {\n var _a;\n const ret = Object.create(null);\n const columns = Object.keys(schema);\n ret._rowCount = 0;\n ret._columns = columns;\n ret._schema = schema;\n for (const k of columns) {\n if (typeof arrays[k] !== 'undefined') {\n ret[k] = Column.ofArray({ array: arrays[k], schema: schema[k] });\n ret._rowCount = (_a = arrays[k]) === null || _a === void 0 ? void 0 : _a.length;\n }\n else {\n ret[k] = Column.Undefined(ret._rowCount, schema[k]);\n }\n }\n return ret;\n }\n Table.ofArrays = ofArrays;\n function view(table, schema, view) {\n const ret = Object.create(null);\n const columns = Object.keys(schema);\n ret._rowCount = view.length;\n ret._columns = columns;\n ret._schema = schema;\n for (const k of columns) {\n ret[k] = Column.view(table[k], view);\n }\n return ret;\n }\n Table.view = view;\n function pick(table, schema, test) {\n const _view = [];\n for (let i = 0, il = table._rowCount; i < il; ++i) {\n if (test(i))\n _view.push(i);\n }\n return view(table, schema, _view);\n }\n Table.pick = pick;\n function window(table, schema, start, end) {\n if (start === 0 && end === table._rowCount)\n return table;\n const ret = Object.create(null);\n const columns = Object.keys(schema);\n ret._rowCount = end - start;\n ret._columns = columns;\n ret._schema = schema;\n for (const k of columns) {\n ret[k] = Column.window(table[k], start, end);\n }\n return ret;\n }\n Table.window = window;\n function concat(tables, schema) {\n const ret = Object.create(null);\n const columns = Object.keys(schema);\n ret._rowCount = 0;\n for (const table of tables) {\n ret._rowCount += table._rowCount;\n }\n const arrays = {};\n for (const column of columns) {\n arrays[column] = new Array(ret._rowCount);\n }\n ret._columns = columns;\n ret._schema = schema;\n let offset = 0;\n for (const table of tables) {\n for (const k of columns) {\n Column.copyToArray(table[k], arrays[k], offset);\n }\n offset += table._rowCount;\n }\n for (const k of columns) {\n ret[k] = Column.ofArray({ array: arrays[k], schema: schema[k] });\n }\n return ret;\n }\n Table.concat = concat;\n function columnToArray(table, name, array) {\n table[name] = Column.asArrayColumn(table[name], array);\n }\n Table.columnToArray = columnToArray;\n /** Sort and return a new table */\n function sort(table, cmp) {\n const indices = new Int32Array(table._rowCount);\n for (let i = 0, _i = indices.length; i < _i; i++)\n indices[i] = i;\n sortArray(indices, (_, i, j) => cmp(i, j));\n let isIdentity = true;\n for (let i = 0, _i = indices.length; i < _i; i++) {\n if (indices[i] !== i) {\n isIdentity = false;\n break;\n }\n }\n if (isIdentity)\n return table;\n const ret = Object.create(null);\n ret._rowCount = table._rowCount;\n ret._columns = table._columns;\n ret._schema = table._schema;\n for (const c of table._columns) {\n ret[c] = Column.view(table[c], indices, false);\n }\n return ret;\n }\n Table.sort = sort;\n function areEqual(a, b) {\n if (a._rowCount !== b._rowCount)\n return false;\n if (a._columns.length !== b._columns.length)\n return false;\n for (const c of a._columns) {\n if (!b[c])\n return false;\n }\n for (const c of a._columns) {\n if (!Column.areEqual(a[c], b[c]))\n return false;\n }\n return true;\n }\n Table.areEqual = areEqual;\n /** Allocate a new object with the given row values. */\n function getRow(table, index) {\n const row = Object.create(null);\n const { _columns: cols } = table;\n for (let i = 0; i < cols.length; i++) {\n const c = cols[i];\n row[c] = table[c].value(index);\n }\n return row;\n }\n Table.getRow = getRow;\n /** Pick the first row for which `test` evaluates to true */\n function pickRow(table, test) {\n for (let i = 0, il = table._rowCount; i < il; ++i) {\n if (test(i))\n return getRow(table, i);\n }\n }\n Table.pickRow = pickRow;\n function getRows(table) {\n const ret = [];\n const { _rowCount: c } = table;\n for (let i = 0; i < c; i++) {\n ret[i] = getRow(table, i);\n }\n return ret;\n }\n Table.getRows = getRows;\n function toArrays(table) {\n const arrays = {};\n const { _columns } = table;\n for (let i = 0; i < _columns.length; i++) {\n const c = _columns[i];\n arrays[c] = table[c].toArray();\n }\n return arrays;\n }\n Table.toArrays = toArrays;\n function formatToString(table) {\n const sb = StringBuilder.create();\n const { _columns: cols, _rowCount } = table;\n let headerLength = 1;\n StringBuilder.write(sb, '|');\n for (let i = 0; i < cols.length; i++) {\n StringBuilder.write(sb, cols[i]);\n StringBuilder.write(sb, '|');\n headerLength += cols[i].length + 1;\n }\n StringBuilder.newline(sb);\n StringBuilder.write(sb, new Array(headerLength + 1).join('-'));\n StringBuilder.newline(sb);\n for (let r = 0; r < _rowCount; r++) {\n StringBuilder.write(sb, '|');\n for (let i = 0; i < cols.length; i++) {\n const c = table[cols[i]];\n if (c.valueKind(r) === 0 /* Column.ValueKinds.Present */) {\n StringBuilder.write(sb, c.value(r));\n StringBuilder.write(sb, '|');\n }\n else {\n StringBuilder.write(sb, '.|');\n }\n }\n StringBuilder.newline(sb);\n }\n return StringBuilder.getString(sb);\n }\n Table.formatToString = formatToString;\n})(Table || (Table = {}));\nexport { Table };\n","/**\n * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.\n *\n * @author David Sehnal \n */\nimport { Table } from './table';\nvar Database;\n(function (Database) {\n function ofTables(name, schema, tables) {\n const keys = Object.keys(tables);\n const ret = Object.create(null);\n const tableNames = [];\n ret._name = name;\n ret._tableNames = tableNames;\n ret._schema = schema;\n for (const k of keys) {\n if (!Table.is(tables[k]))\n continue;\n ret[k] = tables[k];\n tableNames[tableNames.length] = k;\n }\n return ret;\n }\n Database.ofTables = ofTables;\n function getTablesAsRows(database) {\n const ret = {};\n for (const k of database._tableNames) {\n ret[k] = Table.getRows(database[k]);\n }\n return ret;\n }\n Database.getTablesAsRows = getTablesAsRows;\n})(Database || (Database = {}));\nexport { Database };\n","/**\n * Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.\n *\n * @author David Sehnal \n * @author Alexander Rose \n */\nimport { Column, ColumnHelpers } from '../../../mol-data/db';\nimport { getNumberType, parseInt as fastParseInt, parseFloat as fastParseFloat } from '../common/text/number-parser';\nimport { areValuesEqualProvider } from '../common/text/column/token';\nexport function CifFile(blocks, name) {\n return { name, blocks: blocks };\n}\nexport function CifBlock(categoryNames, categories, header, saveFrames = []) {\n return {\n categoryNames, header, categories, saveFrames,\n getField(name) {\n const [category, field] = name.split('.');\n return categories[category].getField(field || '');\n }\n };\n}\nexport function CifSaveFrame(categoryNames, categories, header) {\n return { categoryNames, header, categories };\n}\nexport function CifCategory(name, rowCount, fieldNames, fields) {\n return { rowCount, name, fieldNames: [...fieldNames], getField(name) { return fields[name]; } };\n}\n(function (CifCategory) {\n function empty(name) {\n return { rowCount: 0, name, fieldNames: [], getField(name) { return void 0; } };\n }\n CifCategory.empty = empty;\n ;\n function ofFields(name, fields) {\n const fieldNames = Object.keys(fields);\n return {\n rowCount: fieldNames.length > 0 ? fields[fieldNames[0]].rowCount : 0,\n name,\n fieldNames,\n getField(name) { return fields[name]; }\n };\n }\n CifCategory.ofFields = ofFields;\n function ofTable(name, table) {\n const fields = {};\n for (const name of table._columns) {\n fields[name] = CifField.ofColumn(table[name]);\n }\n return ofFields(name, fields);\n }\n CifCategory.ofTable = ofTable;\n})(CifCategory || (CifCategory = {}));\nexport var CifField;\n(function (CifField) {\n function ofString(value) {\n return ofStrings([value]);\n }\n CifField.ofString = ofString;\n function ofStrings(values) {\n const rowCount = values.length;\n const str = row => { const ret = values[row]; if (!ret || ret === '.' || ret === '?')\n return ''; return ret; };\n const int = row => { const v = values[row]; return fastParseInt(v, 0, v.length) || 0; };\n const float = row => { const v = values[row]; return fastParseFloat(v, 0, v.length) || 0; };\n const valueKind = row => {\n const v = values[row], l = v.length;\n if (l > 1)\n return 0 /* Column.ValueKinds.Present */;\n if (l === 0)\n return 1 /* Column.ValueKinds.NotPresent */;\n const c = v.charCodeAt(0);\n if (c === 46 /* . */)\n return 1 /* Column.ValueKinds.NotPresent */;\n if (c === 63 /* ? */)\n return 2 /* Column.ValueKinds.Unknown */;\n return 0 /* Column.ValueKinds.Present */;\n };\n return {\n __array: void 0,\n binaryEncoding: void 0,\n isDefined: true,\n rowCount,\n str,\n int,\n float,\n valueKind,\n areValuesEqual: (rowA, rowB) => values[rowA] === values[rowB],\n toStringArray: params => params ? ColumnHelpers.createAndFillArray(rowCount, str, params) : values,\n toIntArray: params => ColumnHelpers.createAndFillArray(rowCount, int, params),\n toFloatArray: params => ColumnHelpers.createAndFillArray(rowCount, float, params)\n };\n }\n CifField.ofStrings = ofStrings;\n function ofNumbers(values) {\n const rowCount = values.length;\n const str = row => { return '' + values[row]; };\n const float = row => values[row];\n const valueKind = row => 0 /* Column.ValueKinds.Present */;\n const toFloatArray = (params) => {\n if (!params || params.array && values instanceof params.array) {\n return values;\n }\n else {\n return ColumnHelpers.createAndFillArray(rowCount, float, params);\n }\n };\n return {\n __array: void 0,\n binaryEncoding: void 0,\n isDefined: true,\n rowCount,\n str,\n int: float,\n float,\n valueKind,\n areValuesEqual: (rowA, rowB) => values[rowA] === values[rowB],\n toStringArray: params => ColumnHelpers.createAndFillArray(rowCount, str, params),\n toIntArray: toFloatArray,\n toFloatArray\n };\n }\n CifField.ofNumbers = ofNumbers;\n function ofTokens(tokens) {\n const { data, indices, count: rowCount } = tokens;\n const str = row => {\n const ret = data.substring(indices[2 * row], indices[2 * row + 1]);\n if (ret === '.' || ret === '?')\n return '';\n return ret;\n };\n const int = row => {\n return fastParseInt(data, indices[2 * row], indices[2 * row + 1]) || 0;\n };\n const float = row => {\n return fastParseFloat(data, indices[2 * row], indices[2 * row + 1]) || 0;\n };\n const valueKind = row => {\n const s = indices[2 * row], l = indices[2 * row + 1] - s;\n if (l > 1)\n return 0 /* Column.ValueKinds.Present */;\n if (l === 0)\n return 1 /* Column.ValueKinds.NotPresent */;\n const v = data.charCodeAt(s);\n if (v === 46 /* . */)\n return 1 /* Column.ValueKinds.NotPresent */;\n if (v === 63 /* ? */)\n return 2 /* Column.ValueKinds.Unknown */;\n return 0 /* Column.ValueKinds.Present */;\n };\n return {\n __array: void 0,\n binaryEncoding: void 0,\n isDefined: true,\n rowCount,\n str,\n int,\n float,\n valueKind,\n areValuesEqual: areValuesEqualProvider(tokens),\n toStringArray: params => ColumnHelpers.createAndFillArray(rowCount, str, params),\n toIntArray: params => ColumnHelpers.createAndFillArray(rowCount, int, params),\n toFloatArray: params => ColumnHelpers.createAndFillArray(rowCount, float, params)\n };\n }\n CifField.ofTokens = ofTokens;\n function ofColumn(column) {\n const { rowCount, valueKind, areValuesEqual, isDefined } = column;\n let str;\n let int;\n let float;\n switch (column.schema.valueType) {\n case 'float':\n case 'int':\n str = row => { return '' + column.value(row); };\n int = column.value;\n float = column.value;\n break;\n case 'str':\n str = column.value;\n int = row => { const v = column.value(row); return fastParseInt(v, 0, v.length) || 0; };\n float = row => { const v = column.value(row); return fastParseFloat(v, 0, v.length) || 0; };\n break;\n case 'list':\n const { separator } = column.schema;\n str = row => column.value(row).join(separator);\n int = row => NaN;\n float = row => NaN;\n break;\n default:\n throw new Error(`unsupported valueType '${column.schema.valueType}'`);\n }\n return {\n __array: void 0,\n binaryEncoding: void 0,\n isDefined,\n rowCount,\n str,\n int,\n float,\n valueKind,\n areValuesEqual,\n toStringArray: params => ColumnHelpers.createAndFillArray(rowCount, str, params),\n toIntArray: params => ColumnHelpers.createAndFillArray(rowCount, int, params),\n toFloatArray: params => ColumnHelpers.createAndFillArray(rowCount, float, params)\n };\n }\n CifField.ofColumn = ofColumn;\n function ofUndefined(rowCount, schema) {\n return ofColumn(Column.Undefined(rowCount, schema));\n }\n CifField.ofUndefined = ofUndefined;\n})(CifField || (CifField = {}));\nexport function tensorFieldNameGetter(field, rank, zeroIndexed, namingVariant) {\n const offset = zeroIndexed ? 0 : 1;\n switch (rank) {\n case 1:\n return namingVariant === 'brackets'\n ? (i) => `${field}[${i + offset}]`\n : (i) => `${field}_${i + offset}`;\n case 2:\n return namingVariant === 'brackets'\n ? (i, j) => `${field}[${i + offset}][${j + offset}]`\n : (i, j) => `${field}_${i + offset}${j + offset}`;\n case 3:\n return namingVariant === 'brackets'\n ? (i, j, k) => `${field}[${i + offset}][${j + offset}][${k + offset}]`\n : (i, j, k) => `${field}_${i + offset}${j + offset}${k + offset}`;\n default:\n throw new Error('Tensors with rank > 3 or rank 0 are currently not supported.');\n }\n}\nexport function getTensor(category, space, row, getName) {\n const ret = space.create();\n if (space.rank === 1) {\n const rows = space.dimensions[0];\n for (let i = 0; i < rows; i++) {\n const f = category.getField(getName(i));\n space.set(ret, i, !!f ? f.float(row) : 0.0);\n }\n }\n else if (space.rank === 2) {\n const rows = space.dimensions[0], cols = space.dimensions[1];\n for (let i = 0; i < rows; i++) {\n for (let j = 0; j < cols; j++) {\n const f = category.getField(getName(i, j));\n space.set(ret, i, j, !!f ? f.float(row) : 0.0);\n }\n }\n }\n else if (space.rank === 3) {\n const d0 = space.dimensions[0], d1 = space.dimensions[1], d2 = space.dimensions[2];\n for (let i = 0; i < d0; i++) {\n for (let j = 0; j < d1; j++) {\n for (let k = 0; k < d2; k++) {\n const f = category.getField(getName(i, j, k));\n space.set(ret, i, j, k, !!f ? f.float(row) : 0.0);\n }\n }\n }\n }\n else {\n throw new Error('Tensors with rank > 3 or rank 0 are currently not supported.');\n }\n return ret;\n}\nexport function getCifFieldType(field) {\n let floatCount = 0, hasStringOrScientific = false, undefinedCount = 0;\n for (let i = 0, _i = field.rowCount; i < _i; i++) {\n const k = field.valueKind(i);\n if (k !== 0 /* Column.ValueKinds.Present */) {\n undefinedCount++;\n continue;\n }\n const type = getNumberType(field.str(i));\n if (type === 0 /* NumberTypes.Int */)\n continue;\n else if (type === 1 /* NumberTypes.Float */)\n floatCount++;\n else {\n hasStringOrScientific = true;\n break;\n }\n }\n // numbers in scientific notation and plain text are not distinguishable\n if (hasStringOrScientific || undefinedCount === field.rowCount)\n return Column.Schema.str;\n if (floatCount > 0)\n return Column.Schema.float;\n return Column.Schema.int;\n}\n","/**\n * Copyright (c) 2017-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.\n *\n * @author David Sehnal \n * @author Alexander Rose \n */\nimport { ColumnHelpers } from '../../../../../mol-data/db';\nimport { parseInt as fastParseInt, parseFloat as fastParseFloat } from '../number-parser';\nexport function TokenColumnProvider(tokens) {\n return function (type) {\n return TokenColumn(tokens, type);\n };\n}\nexport function TokenColumn(tokens, schema) {\n const { data, indices, count: rowCount } = tokens;\n const { valueType: type } = schema;\n const value = type === 'str'\n ? row => data.substring(indices[2 * row], indices[2 * row + 1])\n : type === 'int'\n ? row => fastParseInt(data, indices[2 * row], indices[2 * row + 1]) || 0\n : row => fastParseFloat(data, indices[2 * row], indices[2 * row + 1]) || 0;\n return {\n schema: schema,\n __array: void 0,\n isDefined: true,\n rowCount,\n value,\n valueKind: row => 0 /* Column.ValueKinds.Present */,\n toArray: params => ColumnHelpers.createAndFillArray(rowCount, value, params),\n areValuesEqual: areValuesEqualProvider(tokens)\n };\n}\nexport function areValuesEqualProvider(tokens) {\n const { data, indices } = tokens;\n return function (rowA, rowB) {\n const aS = indices[2 * rowA], bS = indices[2 * rowB];\n const len = indices[2 * rowA + 1] - aS;\n if (len !== indices[2 * rowB + 1] - bS)\n return false;\n for (let i = 0; i < len; i++) {\n if (data.charCodeAt(i + aS) !== data.charCodeAt(i + bS)) {\n return false;\n }\n }\n return true;\n };\n}\nexport function areTokensEmpty(tokens) {\n const { count, indices } = tokens;\n for (let i = 0; i < count; ++i) {\n if (indices[2 * i] !== indices[2 * i + 1])\n return false;\n }\n return true;\n}\n","/**\n * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.\n *\n * @author David Sehnal \n */\nfunction createImmediateActions() {\n const thisGlobal = (function () {\n const _window = typeof window !== 'undefined' && window;\n const _self = typeof self !== 'undefined' && typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope && self;\n const _global = typeof global !== 'undefined' && global;\n return _window || _global || _self;\n })();\n const tasksByHandle = {};\n const doc = typeof document !== 'undefined' ? document : void 0;\n let nextHandle = 1; // Spec says greater than zero\n let registerImmediate;\n function setImmediate(callback, ...args) {\n // Callback can either be a function or a string\n if (typeof callback !== 'function') {\n callback = new Function('' + callback);\n }\n // Store and register the task\n const task = { callback: callback, args: args };\n tasksByHandle[nextHandle] = task;\n registerImmediate(nextHandle);\n return nextHandle++;\n }\n function clearImmediate(handle) {\n delete tasksByHandle[handle];\n }\n function run(task) {\n const callback = task.callback;\n const args = task.args;\n switch (args.length) {\n case 0:\n callback();\n break;\n case 1:\n callback(args[0]);\n break;\n case 2:\n callback(args[0], args[1]);\n break;\n case 3:\n callback(args[0], args[1], args[2]);\n break;\n default:\n callback.apply(undefined, args);\n break;\n }\n }\n function runIfPresent(handle) {\n const task = tasksByHandle[handle];\n clearImmediate(handle);\n run(task);\n }\n function installNextTickImplementation() {\n registerImmediate = function (handle) {\n process.nextTick(function () { runIfPresent(handle); });\n };\n }\n function canUsePostMessage() {\n if (thisGlobal && thisGlobal.postMessage && !thisGlobal.importScripts) {\n let postMessageIsAsynchronous = true;\n const oldOnMessage = thisGlobal.onmessage;\n thisGlobal.onmessage = function () {\n postMessageIsAsynchronous = false;\n };\n thisGlobal.postMessage('', '*');\n thisGlobal.onmessage = oldOnMessage;\n return postMessageIsAsynchronous;\n }\n }\n function installPostMessageImplementation() {\n // Installs an event handler on `global` for the `message` event: see\n // * https://developer.mozilla.org/en/DOM/window.postMessage\n // * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages\n const messagePrefix = 'setImmediate$' + Math.random() + '$';\n const onGlobalMessage = function (event) {\n if (event.source === thisGlobal &&\n typeof event.data === 'string' &&\n event.data.indexOf(messagePrefix) === 0) {\n runIfPresent(+event.data.slice(messagePrefix.length));\n }\n };\n if (window.addEventListener) {\n window.addEventListener('message', onGlobalMessage, false);\n }\n else {\n window.attachEvent('onmessage', onGlobalMessage);\n }\n registerImmediate = function (handle) {\n window.postMessage(messagePrefix + handle, '*');\n };\n }\n function installMessageChannelImplementation() {\n const channel = new MessageChannel();\n channel.port1.onmessage = function (event) {\n const handle = event.data;\n runIfPresent(handle);\n };\n registerImmediate = function (handle) {\n channel.port2.postMessage(handle);\n };\n }\n function installReadyStateChangeImplementation() {\n const html = doc.documentElement;\n registerImmediate = function (handle) {\n // Create a