diff --git a/resources/openbim-components.js b/resources/openbim-components.js index 2fedc512b..ca1afbac8 100644 --- a/resources/openbim-components.js +++ b/resources/openbim-components.js @@ -1,5 +1,5 @@ import * as THREE$1 from 'https://unpkg.com/three@0.152.2/build/three.module.js'; -import { Vector3 as Vector3$1, Matrix4, Object3D, Vector2 as Vector2$1, BufferAttribute as BufferAttribute$1, Plane, Line3, Triangle, Sphere, BackSide, DoubleSide, Box3, FrontSide, Mesh, Ray, Raycaster, Quaternion as Quaternion$1, Euler, MeshBasicMaterial, LineBasicMaterial, CylinderGeometry, BoxGeometry, BufferGeometry, Float32BufferAttribute, OctahedronGeometry, Line as Line$2, SphereGeometry, TorusGeometry, PlaneGeometry, Color, PropertyBinding, InterpolateLinear, Source, NoColorSpace, MathUtils, RGBAFormat, InterpolateDiscrete, Scene, NearestFilter, NearestMipmapNearestFilter, NearestMipmapLinearFilter, LinearFilter, LinearMipmapNearestFilter, LinearMipmapLinearFilter, ClampToEdgeWrapping, RepeatWrapping, MirroredRepeatWrapping, SRGBColorSpace, InstancedMesh, EventDispatcher as EventDispatcher$1, MOUSE, TOUCH, Spherical, OrthographicCamera, ShaderMaterial, UniformsUtils, WebGLRenderTarget, Clock, REVISION, HalfFloatType, DepthTexture, UnsignedInt248Type, UnsignedIntType, DepthStencilFormat, DepthFormat, DataTexture, WebGLMultipleRenderTargets, RedFormat, FloatType, UniformsLib, ShaderLib, InstancedBufferGeometry, InstancedInterleavedBuffer, InterleavedBufferAttribute, WireframeGeometry, Vector4 } from 'https://unpkg.com/three@0.152.2/build/three.module.js'; +import { Vector3 as Vector3$1, Matrix4, Object3D, Vector2 as Vector2$1, BufferAttribute as BufferAttribute$1, Plane, Line3, Triangle, Sphere, BackSide, DoubleSide, Box3, FrontSide, Mesh, Ray, Raycaster, Quaternion as Quaternion$1, Euler, MeshBasicMaterial, LineBasicMaterial, CylinderGeometry, BoxGeometry, BufferGeometry, Float32BufferAttribute, OctahedronGeometry, Line as Line$2, SphereGeometry, TorusGeometry, PlaneGeometry, Color, PropertyBinding, InterpolateLinear, Source, NoColorSpace, MathUtils, RGBAFormat, InterpolateDiscrete, Scene, NearestFilter, NearestMipmapNearestFilter, NearestMipmapLinearFilter, LinearFilter, LinearMipmapNearestFilter, LinearMipmapLinearFilter, ClampToEdgeWrapping, RepeatWrapping, MirroredRepeatWrapping, SRGBColorSpace, InstancedMesh, OrthographicCamera, ShaderMaterial, UniformsUtils, WebGLRenderTarget, Clock, REVISION, HalfFloatType, DepthTexture, UnsignedInt248Type, UnsignedIntType, DepthStencilFormat, DepthFormat, DataTexture, WebGLMultipleRenderTargets, RedFormat, FloatType, EventDispatcher as EventDispatcher$1, MOUSE, TOUCH, Spherical, UniformsLib, ShaderLib, InstancedBufferGeometry, InstancedInterleavedBuffer, InterleavedBufferAttribute, WireframeGeometry, Vector4 } from 'https://unpkg.com/three@0.152.2/build/three.module.js'; /** * Components are the building blocks of this library. Everything is a @@ -22175,10530 +22175,11257 @@ class FragmentManager extends Component { FragmentManager.uuid = "fef46874-46a3-461b-8c44-2922ab77c806"; ToolComponent.libraryUUIDs.add(FragmentManager.uuid); -// TODO: Work at the instance level instead of the mesh level? /** - * A tool to handle big scenes efficiently by automatically hiding the objects - * that are not visible to the camera. + * A simple implementation of bounding box that works for fragments. The resulting bbox is not 100% precise, but + * it's fast, and should suffice for general use cases such as camera zooming or general boundary determination. */ -class ScreenCuller extends Component { - constructor(components, updateInterval = 1000, rtWidth = 512, rtHeight = 512, autoUpdate = true) { +class FragmentBoundingBox extends Component { + constructor(components) { super(components); - this.updateInterval = updateInterval; - this.rtWidth = rtWidth; - this.rtHeight = rtHeight; - this.autoUpdate = autoUpdate; - /** {@link Disposable.onDisposed} */ - this.onDisposed = new Event(); - /** Fires after hiding the objects that were not visible to the camera. */ - this.onViewUpdated = new Event(); /** {@link Component.enabled} */ this.enabled = true; - /** - * Needs to check whether there are objects that need to be hidden or shown. - * You can bind this to the camera movement, to a certain interval, etc. - */ - this.needsUpdate = false; - /** - * Render the internal scene used to determine the object visibility. Used - * for debugging purposes. - */ - this.renderDebugFrame = false; - this._meshColorMap = new Map(); - this._visibleMeshes = []; - this._colorMeshes = new Map(); - this._meshes = new Map(); - this._currentVisibleMeshes = new Set(); - this._recentlyHiddenMeshes = new Set(); - this._transparentMat = new THREE$1.MeshBasicMaterial({ - transparent: true, - opacity: 0, - }); - this._colors = { r: 0, g: 0, b: 0, i: 0 }; - // Alternative scene and meshes to make the visibility check - this._scene = new THREE$1.Scene(); - /** - * The function that the culler uses to reprocess the scene. Generally it's - * better to call needsUpdate, but you can also call this to force it. - * @param force if true, it will refresh the scene even if needsUpdate is - * not true. - */ - this.updateVisibility = async (force) => { - if (!this.enabled) - return; - if (!this.needsUpdate && !force) - return; - const camera = this.components.camera.get(); - camera.updateMatrix(); - this.renderer.setSize(this.rtWidth, this.rtHeight); - this.renderer.setRenderTarget(this.renderTarget); - this.renderer.render(this._scene, camera); - const context = this.renderer.getContext(); - await readPixelsAsync(context, 0, 0, this.rtWidth, this.rtHeight, context.RGBA, context.UNSIGNED_BYTE, this._buffer); - this.renderer.setRenderTarget(null); - if (this.renderDebugFrame) { - this.renderer.render(this._scene, camera); - } - this.worker.postMessage({ - buffer: this._buffer, - }); - this.needsUpdate = false; - }; - this.handleWorkerMessage = async (event) => { - const colors = event.data.colors; - this._recentlyHiddenMeshes = new Set(this._currentVisibleMeshes); - this._currentVisibleMeshes.clear(); - this._visibleMeshes = []; - // Make found meshes visible - for (const code of colors.values()) { - const mesh = this._meshColorMap.get(code); - if (mesh) { - this._visibleMeshes.push(mesh); - mesh.visible = true; - this._currentVisibleMeshes.add(mesh.uuid); - this._recentlyHiddenMeshes.delete(mesh.uuid); - } - } - // Hide meshes that were visible before but not anymore - for (const uuid of this._recentlyHiddenMeshes) { - const mesh = this._meshes.get(uuid); - if (mesh === undefined) - continue; - mesh.visible = false; - } - await this.onViewUpdated.trigger(); - }; - components.tools.add(ScreenCuller.uuid, this); - this.renderer = new THREE$1.WebGLRenderer(); - const planes = this.components.renderer.clippingPlanes; - this.renderer.clippingPlanes = planes; - this.renderTarget = new THREE$1.WebGLRenderTarget(rtWidth, rtHeight); - this.bufferSize = rtWidth * rtHeight * 4; - this._buffer = new Uint8Array(this.bufferSize); - this.materialCache = new Map(); - const code = ` - addEventListener("message", (event) => { - const { buffer } = event.data; - const colors = new Set(); - for (let i = 0; i < buffer.length; i += 4) { - const r = buffer[i]; - const g = buffer[i + 1]; - const b = buffer[i + 2]; - const code = "" + r + "-" + g + "-" + b; - colors.add(code); - } - postMessage({ colors }); - }); - `; - const blob = new Blob([code], { type: "application/javascript" }); - this.worker = new Worker(URL.createObjectURL(blob)); - this.worker.addEventListener("message", this.handleWorkerMessage); - if (autoUpdate) - window.setInterval(this.updateVisibility, updateInterval); + /** {@link Disposable.onDisposed} */ + this.onDisposed = new Event(); + this._meshes = []; + this.components.tools.add(FragmentBoundingBox.uuid, this); + this._absoluteMin = FragmentBoundingBox.newBound(true); + this._absoluteMax = FragmentBoundingBox.newBound(false); } - /** - * {@link Component.get}. - * @returns the map of internal meshes used to determine visibility. - */ - get() { - return this._colorMeshes; + static getDimensions(bbox) { + const { min, max } = bbox; + const width = Math.abs(max.x - min.x); + const height = Math.abs(max.y - min.y); + const depth = Math.abs(max.z - min.z); + const center = new THREE$1.Vector3(); + center.subVectors(max, min).divideScalar(2).add(min); + return { width, height, depth, center }; + } + static newBound(positive) { + const factor = positive ? 1 : -1; + return new THREE$1.Vector3(factor * Number.MAX_VALUE, factor * Number.MAX_VALUE, factor * Number.MAX_VALUE); + } + static getBounds(points, min, max) { + const maxPoint = max || this.newBound(false); + const minPoint = min || this.newBound(true); + for (const point of points) { + if (point.x < minPoint.x) + minPoint.x = point.x; + if (point.y < minPoint.y) + minPoint.y = point.y; + if (point.z < minPoint.z) + minPoint.z = point.z; + if (point.x > maxPoint.x) + maxPoint.x = point.x; + if (point.y > maxPoint.y) + maxPoint.y = point.y; + if (point.z > maxPoint.z) + maxPoint.z = point.z; + } + return new THREE$1.Box3(min, max); } /** {@link Disposable.dispose} */ async dispose() { - this.enabled = false; - this._currentVisibleMeshes.clear(); - this._recentlyHiddenMeshes.clear(); - this._scene.children.length = 0; - this.onViewUpdated.reset(); - this.worker.terminate(); - this.renderer.dispose(); - this.renderTarget.dispose(); - this._buffer = null; - this._transparentMat.dispose(); - this._meshColorMap.clear(); - this._visibleMeshes = []; - for (const id in this.materialCache) { - const material = this.materialCache.get(id); - if (material) { - material.dispose(); - } - } const disposer = this.components.tools.get(Disposer); - for (const id in this._colorMeshes) { - const mesh = this._colorMeshes.get(id); - if (mesh) { - disposer.destroy(mesh); - } + for (const mesh of this._meshes) { + disposer.destroy(mesh); } - this._colorMeshes.clear(); - this._meshes.clear(); - await this.onDisposed.trigger(ScreenCuller.uuid); + this._meshes = []; + await this.onDisposed.trigger(FragmentBoundingBox.uuid); this.onDisposed.reset(); } - /** - * Adds a new mesh to be processed and managed by the culler. - * @mesh the mesh or instanced mesh to add. - */ - add(mesh) { - if (!this.enabled) - return; - const isInstanced = mesh instanceof THREE$1.InstancedMesh; - const { geometry, material } = mesh; - const { r, g, b, code } = this.getNextColor(); - const colorMaterial = this.getMaterial(r, g, b); - let newMaterial; - if (Array.isArray(material)) { - let transparentOnly = true; - const matArray = []; - for (const mat of material) { - if (this.isTransparent(mat)) { - matArray.push(this._transparentMat); - } - else { - transparentOnly = false; - matArray.push(colorMaterial); - } - } - // If we find that all the materials are transparent then we must remove this from analysis - if (transparentOnly) { - colorMaterial.dispose(); - return; - } - newMaterial = matArray; + get() { + const min = this._absoluteMin.clone(); + const max = this._absoluteMax.clone(); + return new THREE$1.Box3(min, max); + } + getSphere() { + const min = this._absoluteMin.clone(); + const max = this._absoluteMax.clone(); + const dx = Math.abs((max.x - min.x) / 2); + const dy = Math.abs((max.y - min.y) / 2); + const dz = Math.abs((max.z - min.z) / 2); + const center = new THREE$1.Vector3(min.x + dx, min.y + dy, min.z + dz); + const radius = center.distanceTo(min); + return new THREE$1.Sphere(center, radius); + } + getMesh() { + const bbox = new THREE$1.Box3(this._absoluteMin, this._absoluteMax); + const dimensions = FragmentBoundingBox.getDimensions(bbox); + const { width, height, depth, center } = dimensions; + const box = new THREE$1.BoxGeometry(width, height, depth); + const mesh = new THREE$1.Mesh(box); + this._meshes.push(mesh); + mesh.position.copy(center); + return mesh; + } + reset() { + this._absoluteMin = FragmentBoundingBox.newBound(true); + this._absoluteMax = FragmentBoundingBox.newBound(false); + } + add(group) { + for (const frag of group.items) { + this.addMesh(frag.mesh); } - else if (this.isTransparent(material)) { - // This material is transparent, so we must remove it from analysis - colorMaterial.dispose(); + } + addMesh(mesh) { + if (!mesh.geometry.index) { return; } - else { - newMaterial = colorMaterial; - } - this._meshColorMap.set(code, mesh); - const count = isInstanced ? mesh.count : 1; - const colorMesh = new THREE$1.InstancedMesh(geometry, newMaterial, count); - if (isInstanced) { - colorMesh.instanceMatrix = mesh.instanceMatrix; - } - else { - colorMesh.setMatrixAt(0, new THREE$1.Matrix4()); - } - mesh.visible = false; - colorMesh.applyMatrix4(mesh.matrix); - colorMesh.updateMatrix(); - const parent = mesh.parent; - if (parent instanceof FragmentsGroup) { - const manager = this.components.tools.get(FragmentManager); - const coordinationModel = manager.groups.find((model) => model.uuid === manager.baseCoordinationModel); - if (coordinationModel) { - colorMesh.applyMatrix4(parent.coordinationMatrix.clone().invert()); - colorMesh.applyMatrix4(coordinationModel.coordinationMatrix); - } - } - this._scene.add(colorMesh); - this._colorMeshes.set(mesh.uuid, colorMesh); - this._meshes.set(mesh.uuid, mesh); - } - getMaterial(r, g, b) { - const colorEnabled = THREE$1.ColorManagement.enabled; - THREE$1.ColorManagement.enabled = false; - const code = `rgb(${r}, ${g}, ${b})`; - const color = new THREE$1.Color(code); - let material = this.materialCache.get(code); - const clippingPlanes = this.components.renderer.clippingPlanes; - if (!material) { - material = new THREE$1.MeshBasicMaterial({ - color, - clippingPlanes, - side: THREE$1.DoubleSide, - }); - this.materialCache.set(code, material); + const bbox = FragmentBoundingBox.getFragmentBounds(mesh); + mesh.updateMatrix(); + const meshTransform = mesh.matrix; + const instanceTransform = new THREE$1.Matrix4(); + for (let i = 0; i < mesh.count; i++) { + mesh.getMatrixAt(i, instanceTransform); + const min = bbox.min.clone(); + const max = bbox.max.clone(); + min.applyMatrix4(instanceTransform); + min.applyMatrix4(meshTransform); + max.applyMatrix4(instanceTransform); + max.applyMatrix4(meshTransform); + if (min.x < this._absoluteMin.x) + this._absoluteMin.x = min.x; + if (min.y < this._absoluteMin.y) + this._absoluteMin.y = min.y; + if (min.z < this._absoluteMin.z) + this._absoluteMin.z = min.z; + if (min.x > this._absoluteMax.x) + this._absoluteMax.x = min.x; + if (min.y > this._absoluteMax.y) + this._absoluteMax.y = min.y; + if (min.z > this._absoluteMax.z) + this._absoluteMax.z = min.z; + if (max.x > this._absoluteMax.x) + this._absoluteMax.x = max.x; + if (max.y > this._absoluteMax.y) + this._absoluteMax.y = max.y; + if (max.z > this._absoluteMax.z) + this._absoluteMax.z = max.z; + if (max.x < this._absoluteMin.x) + this._absoluteMin.x = max.x; + if (max.y < this._absoluteMin.y) + this._absoluteMin.y = max.y; + if (max.z < this._absoluteMin.z) + this._absoluteMin.z = max.z; } - THREE$1.ColorManagement.enabled = colorEnabled; - return material; - } - isTransparent(material) { - return material.transparent && material.opacity < 1; } - getNextColor() { - if (this._colors.i === 0) { - this._colors.b++; - if (this._colors.b === 256) { - this._colors.b = 0; - this._colors.i = 1; - } - } - if (this._colors.i === 1) { - this._colors.g++; - this._colors.i = 0; - if (this._colors.g === 256) { - this._colors.g = 0; - this._colors.i = 2; - } + static getFragmentBounds(mesh) { + const position = mesh.geometry.attributes.position; + const maxNum = Number.MAX_VALUE; + const minNum = -maxNum; + const min = new THREE$1.Vector3(maxNum, maxNum, maxNum); + const max = new THREE$1.Vector3(minNum, minNum, minNum); + if (!mesh.geometry.index) { + throw new Error("Geometry must be indexed!"); } - if (this._colors.i === 2) { - this._colors.r++; - this._colors.i = 1; - if (this._colors.r === 256) { - this._colors.r = 0; - this._colors.i = 0; - } + const indices = Array.from(mesh.geometry.index.array); + for (const index of indices) { + const x = position.getX(index); + const y = position.getY(index); + const z = position.getZ(index); + if (x < min.x) + min.x = x; + if (y < min.y) + min.y = y; + if (z < min.z) + min.z = z; + if (x > max.x) + max.x = x; + if (y > max.y) + max.y = y; + if (z > max.z) + max.z = z; } - return { - r: this._colors.r, - g: this._colors.g, - b: this._colors.b, - code: `${this._colors.r}-${this._colors.g}-${this._colors.b}`, - }; + return new THREE$1.Box3(min, max); } } -ScreenCuller.uuid = "69f2a50d-c266-44fc-b1bd-fa4d34be89e6"; -ToolComponent.libraryUUIDs.add(ScreenCuller.uuid); +FragmentBoundingBox.uuid = "d1444724-dba6-4cdd-a0c7-68ee1450d166"; +ToolComponent.libraryUUIDs.add(FragmentBoundingBox.uuid); -/* - * Dexie.js - a minimalistic wrapper for IndexedDB - * =============================================== - * - * By David Fahlander, david.fahlander@gmail.com - * - * Version 3.2.4, Tue May 30 2023 - * - * https://dexie.org - * - * Apache License Version 2.0, January 2004, http://www.apache.org/licenses/ +/** + * Full-screen textured quad shader */ - -const _global = typeof globalThis !== 'undefined' ? globalThis : - typeof self !== 'undefined' ? self : - typeof window !== 'undefined' ? window : - global; -const keys = Object.keys; -const isArray = Array.isArray; -if (typeof Promise !== 'undefined' && !_global.Promise) { - _global.Promise = Promise; -} -function extend(obj, extension) { - if (typeof extension !== 'object') - return obj; - keys(extension).forEach(function (key) { - obj[key] = extension[key]; - }); - return obj; -} -const getProto = Object.getPrototypeOf; -const _hasOwn = {}.hasOwnProperty; -function hasOwn(obj, prop) { - return _hasOwn.call(obj, prop); -} -function props(proto, extension) { - if (typeof extension === 'function') - extension = extension(getProto(proto)); - (typeof Reflect === "undefined" ? keys : Reflect.ownKeys)(extension).forEach(key => { - setProp(proto, key, extension[key]); - }); -} -const defineProperty = Object.defineProperty; -function setProp(obj, prop, functionOrGetSet, options) { - defineProperty(obj, prop, extend(functionOrGetSet && hasOwn(functionOrGetSet, "get") && typeof functionOrGetSet.get === 'function' ? - { get: functionOrGetSet.get, set: functionOrGetSet.set, configurable: true } : - { value: functionOrGetSet, configurable: true, writable: true }, options)); -} -function derive(Child) { - return { - from: function (Parent) { - Child.prototype = Object.create(Parent.prototype); - setProp(Child.prototype, "constructor", Child); - return { - extend: props.bind(null, Child.prototype) - }; - } - }; +const CopyShader = { + + uniforms: { + + 'tDiffuse': { value: null }, + 'opacity': { value: 1.0 } + + }, + + vertexShader: /* glsl */` + + varying vec2 vUv; + + void main() { + + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + + }`, + + fragmentShader: /* glsl */` + + uniform float opacity; + + uniform sampler2D tDiffuse; + + varying vec2 vUv; + + void main() { + + gl_FragColor = texture2D( tDiffuse, vUv ); + gl_FragColor.a *= opacity; + + + }` + +}; + +class Pass { + + constructor() { + + this.isPass = true; + + // if set to true, the pass is processed by the composer + this.enabled = true; + + // if set to true, the pass indicates to swap read and write buffer after rendering + this.needsSwap = true; + + // if set to true, the pass clears its buffer before rendering + this.clear = false; + + // if set to true, the result of the pass is rendered to screen. This is set automatically by EffectComposer. + this.renderToScreen = false; + + } + + setSize( /* width, height */ ) {} + + render( /* renderer, writeBuffer, readBuffer, deltaTime, maskActive */ ) { + + console.error( 'THREE.Pass: .render() must be implemented in derived pass.' ); + + } + + dispose() {} + } -const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; -function getPropertyDescriptor(obj, prop) { - const pd = getOwnPropertyDescriptor(obj, prop); - let proto; - return pd || (proto = getProto(obj)) && getPropertyDescriptor(proto, prop); -} -const _slice = [].slice; -function slice(args, start, end) { - return _slice.call(args, start, end); -} -function override(origFunc, overridedFactory) { - return overridedFactory(origFunc); -} -function assert(b) { - if (!b) - throw new Error("Assertion Failed"); -} -function asap$1(fn) { - if (_global.setImmediate) - setImmediate(fn); - else - setTimeout(fn, 0); -} -function arrayToObject(array, extractor) { - return array.reduce((result, item, i) => { - var nameAndValue = extractor(item, i); - if (nameAndValue) - result[nameAndValue[0]] = nameAndValue[1]; - return result; - }, {}); -} -function tryCatch(fn, onerror, args) { - try { - fn.apply(null, args); - } - catch (ex) { - onerror && onerror(ex); - } -} -function getByKeyPath(obj, keyPath) { - if (hasOwn(obj, keyPath)) - return obj[keyPath]; - if (!keyPath) - return obj; - if (typeof keyPath !== 'string') { - var rv = []; - for (var i = 0, l = keyPath.length; i < l; ++i) { - var val = getByKeyPath(obj, keyPath[i]); - rv.push(val); - } - return rv; - } - var period = keyPath.indexOf('.'); - if (period !== -1) { - var innerObj = obj[keyPath.substr(0, period)]; - return innerObj === undefined ? undefined : getByKeyPath(innerObj, keyPath.substr(period + 1)); - } - return undefined; -} -function setByKeyPath(obj, keyPath, value) { - if (!obj || keyPath === undefined) - return; - if ('isFrozen' in Object && Object.isFrozen(obj)) - return; - if (typeof keyPath !== 'string' && 'length' in keyPath) { - assert(typeof value !== 'string' && 'length' in value); - for (var i = 0, l = keyPath.length; i < l; ++i) { - setByKeyPath(obj, keyPath[i], value[i]); - } - } - else { - var period = keyPath.indexOf('.'); - if (period !== -1) { - var currentKeyPath = keyPath.substr(0, period); - var remainingKeyPath = keyPath.substr(period + 1); - if (remainingKeyPath === "") - if (value === undefined) { - if (isArray(obj) && !isNaN(parseInt(currentKeyPath))) - obj.splice(currentKeyPath, 1); - else - delete obj[currentKeyPath]; - } - else - obj[currentKeyPath] = value; - else { - var innerObj = obj[currentKeyPath]; - if (!innerObj || !hasOwn(obj, currentKeyPath)) - innerObj = (obj[currentKeyPath] = {}); - setByKeyPath(innerObj, remainingKeyPath, value); - } - } - else { - if (value === undefined) { - if (isArray(obj) && !isNaN(parseInt(keyPath))) - obj.splice(keyPath, 1); - else - delete obj[keyPath]; - } - else - obj[keyPath] = value; - } - } -} -function delByKeyPath(obj, keyPath) { - if (typeof keyPath === 'string') - setByKeyPath(obj, keyPath, undefined); - else if ('length' in keyPath) - [].map.call(keyPath, function (kp) { - setByKeyPath(obj, kp, undefined); - }); -} -function shallowClone(obj) { - var rv = {}; - for (var m in obj) { - if (hasOwn(obj, m)) - rv[m] = obj[m]; - } - return rv; -} -const concat = [].concat; -function flatten(a) { - return concat.apply([], a); -} -const intrinsicTypeNames = "Boolean,String,Date,RegExp,Blob,File,FileList,FileSystemFileHandle,ArrayBuffer,DataView,Uint8ClampedArray,ImageBitmap,ImageData,Map,Set,CryptoKey" - .split(',').concat(flatten([8, 16, 32, 64].map(num => ["Int", "Uint", "Float"].map(t => t + num + "Array")))).filter(t => _global[t]); -const intrinsicTypes = intrinsicTypeNames.map(t => _global[t]); -arrayToObject(intrinsicTypeNames, x => [x, true]); -let circularRefs = null; -function deepClone(any) { - circularRefs = typeof WeakMap !== 'undefined' && new WeakMap(); - const rv = innerDeepClone(any); - circularRefs = null; - return rv; -} -function innerDeepClone(any) { - if (!any || typeof any !== 'object') - return any; - let rv = circularRefs && circularRefs.get(any); - if (rv) - return rv; - if (isArray(any)) { - rv = []; - circularRefs && circularRefs.set(any, rv); - for (var i = 0, l = any.length; i < l; ++i) { - rv.push(innerDeepClone(any[i])); - } - } - else if (intrinsicTypes.indexOf(any.constructor) >= 0) { - rv = any; - } - else { - const proto = getProto(any); - rv = proto === Object.prototype ? {} : Object.create(proto); - circularRefs && circularRefs.set(any, rv); - for (var prop in any) { - if (hasOwn(any, prop)) { - rv[prop] = innerDeepClone(any[prop]); - } - } - } - return rv; -} -const { toString } = {}; -function toStringTag(o) { - return toString.call(o).slice(8, -1); -} -const iteratorSymbol = typeof Symbol !== 'undefined' ? - Symbol.iterator : - '@@iterator'; -const getIteratorOf = typeof iteratorSymbol === "symbol" ? function (x) { - var i; - return x != null && (i = x[iteratorSymbol]) && i.apply(x); -} : function () { return null; }; -const NO_CHAR_ARRAY = {}; -function getArrayOf(arrayLike) { - var i, a, x, it; - if (arguments.length === 1) { - if (isArray(arrayLike)) - return arrayLike.slice(); - if (this === NO_CHAR_ARRAY && typeof arrayLike === 'string') - return [arrayLike]; - if ((it = getIteratorOf(arrayLike))) { - a = []; - while ((x = it.next()), !x.done) - a.push(x.value); - return a; - } - if (arrayLike == null) - return [arrayLike]; - i = arrayLike.length; - if (typeof i === 'number') { - a = new Array(i); - while (i--) - a[i] = arrayLike[i]; - return a; - } - return [arrayLike]; - } - i = arguments.length; - a = new Array(i); - while (i--) - a[i] = arguments[i]; - return a; -} -const isAsyncFunction = typeof Symbol !== 'undefined' - ? (fn) => fn[Symbol.toStringTag] === 'AsyncFunction' - : () => false; -var debug = typeof location !== 'undefined' && - /^(http|https):\/\/(localhost|127\.0\.0\.1)/.test(location.href); -function setDebug(value, filter) { - debug = value; - libraryFilter = filter; -} -var libraryFilter = () => true; -const NEEDS_THROW_FOR_STACK = !new Error("").stack; -function getErrorWithStack() { - if (NEEDS_THROW_FOR_STACK) - try { - getErrorWithStack.arguments; - throw new Error(); - } - catch (e) { - return e; - } - return new Error(); -} -function prettyStack(exception, numIgnoredFrames) { - var stack = exception.stack; - if (!stack) - return ""; - numIgnoredFrames = (numIgnoredFrames || 0); - if (stack.indexOf(exception.name) === 0) - numIgnoredFrames += (exception.name + exception.message).split('\n').length; - return stack.split('\n') - .slice(numIgnoredFrames) - .filter(libraryFilter) - .map(frame => "\n" + frame) - .join(''); +// Helper for passes that need to fill the viewport with a single quad. + +const _camera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); + +// https://github.com/mrdoob/three.js/pull/21358 + +const _geometry = new BufferGeometry(); +_geometry.setAttribute( 'position', new Float32BufferAttribute( [ - 1, 3, 0, - 1, - 1, 0, 3, - 1, 0 ], 3 ) ); +_geometry.setAttribute( 'uv', new Float32BufferAttribute( [ 0, 2, 0, 0, 2, 0 ], 2 ) ); + +class FullScreenQuad { + + constructor( material ) { + + this._mesh = new Mesh( _geometry, material ); + + } + + dispose() { + + this._mesh.geometry.dispose(); + + } + + render( renderer ) { + + renderer.render( this._mesh, _camera ); + + } + + get material() { + + return this._mesh.material; + + } + + set material( value ) { + + this._mesh.material = value; + + } + } -var dexieErrorNames = [ - 'Modify', - 'Bulk', - 'OpenFailed', - 'VersionChange', - 'Schema', - 'Upgrade', - 'InvalidTable', - 'MissingAPI', - 'NoSuchDatabase', - 'InvalidArgument', - 'SubTransaction', - 'Unsupported', - 'Internal', - 'DatabaseClosed', - 'PrematureCommit', - 'ForeignAwait' -]; -var idbDomErrorNames = [ - 'Unknown', - 'Constraint', - 'Data', - 'TransactionInactive', - 'ReadOnly', - 'Version', - 'NotFound', - 'InvalidState', - 'InvalidAccess', - 'Abort', - 'Timeout', - 'QuotaExceeded', - 'Syntax', - 'DataClone' -]; -var errorList = dexieErrorNames.concat(idbDomErrorNames); -var defaultTexts = { - VersionChanged: "Database version changed by other database connection", - DatabaseClosed: "Database has been closed", - Abort: "Transaction aborted", - TransactionInactive: "Transaction has already completed or failed", - MissingAPI: "IndexedDB API missing. Please visit https://tinyurl.com/y2uuvskb" -}; -function DexieError(name, msg) { - this._e = getErrorWithStack(); - this.name = name; - this.message = msg; +class ShaderPass extends Pass { + + constructor( shader, textureID ) { + + super(); + + this.textureID = ( textureID !== undefined ) ? textureID : 'tDiffuse'; + + if ( shader instanceof ShaderMaterial ) { + + this.uniforms = shader.uniforms; + + this.material = shader; + + } else if ( shader ) { + + this.uniforms = UniformsUtils.clone( shader.uniforms ); + + this.material = new ShaderMaterial( { + + defines: Object.assign( {}, shader.defines ), + uniforms: this.uniforms, + vertexShader: shader.vertexShader, + fragmentShader: shader.fragmentShader + + } ); + + } + + this.fsQuad = new FullScreenQuad( this.material ); + + } + + render( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) { + + if ( this.uniforms[ this.textureID ] ) { + + this.uniforms[ this.textureID ].value = readBuffer.texture; + + } + + this.fsQuad.material = this.material; + + if ( this.renderToScreen ) { + + renderer.setRenderTarget( null ); + this.fsQuad.render( renderer ); + + } else { + + renderer.setRenderTarget( writeBuffer ); + // TODO: Avoid using autoClear properties, see https://github.com/mrdoob/three.js/pull/15571#issuecomment-465669600 + if ( this.clear ) renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); + this.fsQuad.render( renderer ); + + } + + } + + dispose() { + + this.material.dispose(); + + this.fsQuad.dispose(); + + } + } -derive(DexieError).from(Error).extend({ - stack: { - get: function () { - return this._stack || - (this._stack = this.name + ": " + this.message + prettyStack(this._e, 2)); - } - }, - toString: function () { return this.name + ": " + this.message; } -}); -function getMultiErrorMessage(msg, failures) { - return msg + ". Errors: " + Object.keys(failures) - .map(key => failures[key].toString()) - .filter((v, i, s) => s.indexOf(v) === i) - .join('\n'); + +class MaskPass extends Pass { + + constructor( scene, camera ) { + + super(); + + this.scene = scene; + this.camera = camera; + + this.clear = true; + this.needsSwap = false; + + this.inverse = false; + + } + + render( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) { + + const context = renderer.getContext(); + const state = renderer.state; + + // don't update color or depth + + state.buffers.color.setMask( false ); + state.buffers.depth.setMask( false ); + + // lock buffers + + state.buffers.color.setLocked( true ); + state.buffers.depth.setLocked( true ); + + // set up stencil + + let writeValue, clearValue; + + if ( this.inverse ) { + + writeValue = 0; + clearValue = 1; + + } else { + + writeValue = 1; + clearValue = 0; + + } + + state.buffers.stencil.setTest( true ); + state.buffers.stencil.setOp( context.REPLACE, context.REPLACE, context.REPLACE ); + state.buffers.stencil.setFunc( context.ALWAYS, writeValue, 0xffffffff ); + state.buffers.stencil.setClear( clearValue ); + state.buffers.stencil.setLocked( true ); + + // draw into the stencil buffer + + renderer.setRenderTarget( readBuffer ); + if ( this.clear ) renderer.clear(); + renderer.render( this.scene, this.camera ); + + renderer.setRenderTarget( writeBuffer ); + if ( this.clear ) renderer.clear(); + renderer.render( this.scene, this.camera ); + + // unlock color and depth buffer for subsequent rendering + + state.buffers.color.setLocked( false ); + state.buffers.depth.setLocked( false ); + + // only render where stencil is set to 1 + + state.buffers.stencil.setLocked( false ); + state.buffers.stencil.setFunc( context.EQUAL, 1, 0xffffffff ); // draw if == 1 + state.buffers.stencil.setOp( context.KEEP, context.KEEP, context.KEEP ); + state.buffers.stencil.setLocked( true ); + + } + } -function ModifyError(msg, failures, successCount, failedKeys) { - this._e = getErrorWithStack(); - this.failures = failures; - this.failedKeys = failedKeys; - this.successCount = successCount; - this.message = getMultiErrorMessage(msg, failures); + +class ClearMaskPass extends Pass { + + constructor() { + + super(); + + this.needsSwap = false; + + } + + render( renderer /*, writeBuffer, readBuffer, deltaTime, maskActive */ ) { + + renderer.state.buffers.stencil.setLocked( false ); + renderer.state.buffers.stencil.setTest( false ); + + } + } -derive(ModifyError).from(DexieError); -function BulkError(msg, failures) { - this._e = getErrorWithStack(); - this.name = "BulkError"; - this.failures = Object.keys(failures).map(pos => failures[pos]); - this.failuresByPos = failures; - this.message = getMultiErrorMessage(msg, failures); + +class EffectComposer { + + constructor( renderer, renderTarget ) { + + this.renderer = renderer; + + this._pixelRatio = renderer.getPixelRatio(); + + if ( renderTarget === undefined ) { + + const size = renderer.getSize( new Vector2$1() ); + this._width = size.width; + this._height = size.height; + + renderTarget = new WebGLRenderTarget( this._width * this._pixelRatio, this._height * this._pixelRatio ); + renderTarget.texture.name = 'EffectComposer.rt1'; + + } else { + + this._width = renderTarget.width; + this._height = renderTarget.height; + + } + + this.renderTarget1 = renderTarget; + this.renderTarget2 = renderTarget.clone(); + this.renderTarget2.texture.name = 'EffectComposer.rt2'; + + this.writeBuffer = this.renderTarget1; + this.readBuffer = this.renderTarget2; + + this.renderToScreen = true; + + this.passes = []; + + this.copyPass = new ShaderPass( CopyShader ); + + this.clock = new Clock(); + + } + + swapBuffers() { + + const tmp = this.readBuffer; + this.readBuffer = this.writeBuffer; + this.writeBuffer = tmp; + + } + + addPass( pass ) { + + this.passes.push( pass ); + pass.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio ); + + } + + insertPass( pass, index ) { + + this.passes.splice( index, 0, pass ); + pass.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio ); + + } + + removePass( pass ) { + + const index = this.passes.indexOf( pass ); + + if ( index !== - 1 ) { + + this.passes.splice( index, 1 ); + + } + + } + + isLastEnabledPass( passIndex ) { + + for ( let i = passIndex + 1; i < this.passes.length; i ++ ) { + + if ( this.passes[ i ].enabled ) { + + return false; + + } + + } + + return true; + + } + + render( deltaTime ) { + + // deltaTime value is in seconds + + if ( deltaTime === undefined ) { + + deltaTime = this.clock.getDelta(); + + } + + const currentRenderTarget = this.renderer.getRenderTarget(); + + let maskActive = false; + + for ( let i = 0, il = this.passes.length; i < il; i ++ ) { + + const pass = this.passes[ i ]; + + if ( pass.enabled === false ) continue; + + pass.renderToScreen = ( this.renderToScreen && this.isLastEnabledPass( i ) ); + pass.render( this.renderer, this.writeBuffer, this.readBuffer, deltaTime, maskActive ); + + if ( pass.needsSwap ) { + + if ( maskActive ) { + + const context = this.renderer.getContext(); + const stencil = this.renderer.state.buffers.stencil; + + //context.stencilFunc( context.NOTEQUAL, 1, 0xffffffff ); + stencil.setFunc( context.NOTEQUAL, 1, 0xffffffff ); + + this.copyPass.render( this.renderer, this.writeBuffer, this.readBuffer, deltaTime ); + + //context.stencilFunc( context.EQUAL, 1, 0xffffffff ); + stencil.setFunc( context.EQUAL, 1, 0xffffffff ); + + } + + this.swapBuffers(); + + } + + if ( MaskPass !== undefined ) { + + if ( pass instanceof MaskPass ) { + + maskActive = true; + + } else if ( pass instanceof ClearMaskPass ) { + + maskActive = false; + + } + + } + + } + + this.renderer.setRenderTarget( currentRenderTarget ); + + } + + reset( renderTarget ) { + + if ( renderTarget === undefined ) { + + const size = this.renderer.getSize( new Vector2$1() ); + this._pixelRatio = this.renderer.getPixelRatio(); + this._width = size.width; + this._height = size.height; + + renderTarget = this.renderTarget1.clone(); + renderTarget.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio ); + + } + + this.renderTarget1.dispose(); + this.renderTarget2.dispose(); + this.renderTarget1 = renderTarget; + this.renderTarget2 = renderTarget.clone(); + + this.writeBuffer = this.renderTarget1; + this.readBuffer = this.renderTarget2; + + } + + setSize( width, height ) { + + this._width = width; + this._height = height; + + const effectiveWidth = this._width * this._pixelRatio; + const effectiveHeight = this._height * this._pixelRatio; + + this.renderTarget1.setSize( effectiveWidth, effectiveHeight ); + this.renderTarget2.setSize( effectiveWidth, effectiveHeight ); + + for ( let i = 0; i < this.passes.length; i ++ ) { + + this.passes[ i ].setSize( effectiveWidth, effectiveHeight ); + + } + + } + + setPixelRatio( pixelRatio ) { + + this._pixelRatio = pixelRatio; + + this.setSize( this._width, this._height ); + + } + + dispose() { + + this.renderTarget1.dispose(); + this.renderTarget2.dispose(); + + this.copyPass.dispose(); + + } + } -derive(BulkError).from(DexieError); -var errnames = errorList.reduce((obj, name) => (obj[name] = name + "Error", obj), {}); -const BaseException = DexieError; -var exceptions = errorList.reduce((obj, name) => { - var fullName = name + "Error"; - function DexieError(msgOrInner, inner) { - this._e = getErrorWithStack(); - this.name = fullName; - if (!msgOrInner) { - this.message = defaultTexts[name] || fullName; - this.inner = null; - } - else if (typeof msgOrInner === 'string') { - this.message = `${msgOrInner}${!inner ? '' : '\n ' + inner}`; - this.inner = inner || null; - } - else if (typeof msgOrInner === 'object') { - this.message = `${msgOrInner.name} ${msgOrInner.message}`; - this.inner = msgOrInner; - } + +class RenderPass extends Pass { + + constructor( scene, camera, overrideMaterial, clearColor, clearAlpha ) { + + super(); + + this.scene = scene; + this.camera = camera; + + this.overrideMaterial = overrideMaterial; + + this.clearColor = clearColor; + this.clearAlpha = ( clearAlpha !== undefined ) ? clearAlpha : 0; + + this.clear = true; + this.clearDepth = false; + this.needsSwap = false; + this._oldClearColor = new Color(); + + } + + render( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) { + + const oldAutoClear = renderer.autoClear; + renderer.autoClear = false; + + let oldClearAlpha, oldOverrideMaterial; + + if ( this.overrideMaterial !== undefined ) { + + oldOverrideMaterial = this.scene.overrideMaterial; + + this.scene.overrideMaterial = this.overrideMaterial; + + } + + if ( this.clearColor ) { + + renderer.getClearColor( this._oldClearColor ); + oldClearAlpha = renderer.getClearAlpha(); + + renderer.setClearColor( this.clearColor, this.clearAlpha ); + + } + + if ( this.clearDepth ) { + + renderer.clearDepth(); + + } + + renderer.setRenderTarget( this.renderToScreen ? null : readBuffer ); + + // TODO: Avoid using autoClear properties, see https://github.com/mrdoob/three.js/pull/15571#issuecomment-465669600 + if ( this.clear ) renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); + renderer.render( this.scene, this.camera ); + + if ( this.clearColor ) { + + renderer.setClearColor( this._oldClearColor, oldClearAlpha ); + + } + + if ( this.overrideMaterial !== undefined ) { + + this.scene.overrideMaterial = oldOverrideMaterial; + + } + + renderer.autoClear = oldAutoClear; + + } + +} + +/** + * postprocessing v6.33.3 build Mon Oct 30 2023 + * https://github.com/pmndrs/postprocessing + * Copyright 2015-2023 Raoul van RĂ¼schen + * @license Zlib + */ + + +// src/utils/BackCompat.js +Number(REVISION.replace(/\D+/g, "")); + +const $e4ca8dcb0218f846$var$_geometry = new BufferGeometry(); +$e4ca8dcb0218f846$var$_geometry.setAttribute("position", new BufferAttribute$1(new Float32Array([ + -1, + -1, + 3, + -1, + -1, + 3 +]), 2)); +$e4ca8dcb0218f846$var$_geometry.setAttribute("uv", new BufferAttribute$1(new Float32Array([ + 0, + 0, + 2, + 0, + 0, + 2 +]), 2)); +// Recent three.js versions break setDrawRange or itemSize <3 position +$e4ca8dcb0218f846$var$_geometry.boundingSphere = new Sphere(); +$e4ca8dcb0218f846$var$_geometry.computeBoundingSphere = function() {}; +const $e4ca8dcb0218f846$var$_camera = new OrthographicCamera(); +class $e4ca8dcb0218f846$export$dcd670d73db751f5 { + constructor(material){ + this._mesh = new Mesh($e4ca8dcb0218f846$var$_geometry, material); + this._mesh.frustumCulled = false; } - derive(DexieError).from(BaseException); - obj[name] = DexieError; - return obj; -}, {}); -exceptions.Syntax = SyntaxError; -exceptions.Type = TypeError; -exceptions.Range = RangeError; -var exceptionMap = idbDomErrorNames.reduce((obj, name) => { - obj[name + "Error"] = exceptions[name]; - return obj; -}, {}); -function mapError(domError, message) { - if (!domError || domError instanceof DexieError || domError instanceof TypeError || domError instanceof SyntaxError || !domError.name || !exceptionMap[domError.name]) - return domError; - var rv = new exceptionMap[domError.name](message || domError.message, domError); - if ("stack" in domError) { - setProp(rv, "stack", { get: function () { - return this.inner.stack; - } }); + render(renderer) { + renderer.render(this._mesh, $e4ca8dcb0218f846$var$_camera); + } + get material() { + return this._mesh.material; + } + set material(value) { + this._mesh.material = value; + } + dispose() { + this._mesh.material.dispose(); + this._mesh.geometry.dispose(); } - return rv; } -var fullNameExceptions = errorList.reduce((obj, name) => { - if (["Syntax", "Type", "Range"].indexOf(name) === -1) - obj[name + "Error"] = exceptions[name]; - return obj; -}, {}); -fullNameExceptions.ModifyError = ModifyError; -fullNameExceptions.DexieError = DexieError; -fullNameExceptions.BulkError = BulkError; -function nop() { } -function mirror(val) { return val; } -function pureFunctionChain(f1, f2) { - if (f1 == null || f1 === mirror) - return f2; - return function (val) { - return f2(f1(val)); - }; -} -function callBoth(on1, on2) { - return function () { - on1.apply(this, arguments); - on2.apply(this, arguments); - }; -} -function hookCreatingChain(f1, f2) { - if (f1 === nop) - return f2; - return function () { - var res = f1.apply(this, arguments); - if (res !== undefined) - arguments[0] = res; - var onsuccess = this.onsuccess, - onerror = this.onerror; - this.onsuccess = null; - this.onerror = null; - var res2 = f2.apply(this, arguments); - if (onsuccess) - this.onsuccess = this.onsuccess ? callBoth(onsuccess, this.onsuccess) : onsuccess; - if (onerror) - this.onerror = this.onerror ? callBoth(onerror, this.onerror) : onerror; - return res2 !== undefined ? res2 : res; - }; -} -function hookDeletingChain(f1, f2) { - if (f1 === nop) - return f2; - return function () { - f1.apply(this, arguments); - var onsuccess = this.onsuccess, - onerror = this.onerror; - this.onsuccess = this.onerror = null; - f2.apply(this, arguments); - if (onsuccess) - this.onsuccess = this.onsuccess ? callBoth(onsuccess, this.onsuccess) : onsuccess; - if (onerror) - this.onerror = this.onerror ? callBoth(onerror, this.onerror) : onerror; - }; -} -function hookUpdatingChain(f1, f2) { - if (f1 === nop) - return f2; - return function (modifications) { - var res = f1.apply(this, arguments); - extend(modifications, res); - var onsuccess = this.onsuccess, - onerror = this.onerror; - this.onsuccess = null; - this.onerror = null; - var res2 = f2.apply(this, arguments); - if (onsuccess) - this.onsuccess = this.onsuccess ? callBoth(onsuccess, this.onsuccess) : onsuccess; - if (onerror) - this.onerror = this.onerror ? callBoth(onerror, this.onerror) : onerror; - return res === undefined ? - (res2 === undefined ? undefined : res2) : - (extend(res, res2)); - }; -} -function reverseStoppableEventChain(f1, f2) { - if (f1 === nop) - return f2; - return function () { - if (f2.apply(this, arguments) === false) - return false; - return f1.apply(this, arguments); - }; -} -function promisableChain(f1, f2) { - if (f1 === nop) - return f2; - return function () { - var res = f1.apply(this, arguments); - if (res && typeof res.then === 'function') { - var thiz = this, i = arguments.length, args = new Array(i); - while (i--) - args[i] = arguments[i]; - return res.then(function () { - return f2.apply(thiz, args); - }); - } - return f2.apply(this, arguments); - }; -} -var INTERNAL = {}; -const LONG_STACKS_CLIP_LIMIT = 100, -MAX_LONG_STACKS = 20, ZONE_ECHO_LIMIT = 100, [resolvedNativePromise, nativePromiseProto, resolvedGlobalPromise] = typeof Promise === 'undefined' ? - [] : - (() => { - let globalP = Promise.resolve(); - if (typeof crypto === 'undefined' || !crypto.subtle) - return [globalP, getProto(globalP), globalP]; - const nativeP = crypto.subtle.digest("SHA-512", new Uint8Array([0])); - return [ - nativeP, - getProto(nativeP), - globalP - ]; - })(), nativePromiseThen = nativePromiseProto && nativePromiseProto.then; -const NativePromise = resolvedNativePromise && resolvedNativePromise.constructor; -const patchGlobalPromise = !!resolvedGlobalPromise; -var stack_being_generated = false; -var schedulePhysicalTick = resolvedGlobalPromise ? - () => { resolvedGlobalPromise.then(physicalTick); } - : - _global.setImmediate ? - setImmediate.bind(null, physicalTick) : - _global.MutationObserver ? - () => { - var hiddenDiv = document.createElement("div"); - (new MutationObserver(() => { - physicalTick(); - hiddenDiv = null; - })).observe(hiddenDiv, { attributes: true }); - hiddenDiv.setAttribute('i', '1'); - } : - () => { setTimeout(physicalTick, 0); }; -var asap = function (callback, args) { - microtickQueue.push([callback, args]); - if (needsNewPhysicalTick) { - schedulePhysicalTick(); - needsNewPhysicalTick = false; - } -}; -var isOutsideMicroTick = true, -needsNewPhysicalTick = true, -unhandledErrors = [], -rejectingErrors = [], -currentFulfiller = null, rejectionMapper = mirror; -var globalPSD = { - id: 'global', - global: true, - ref: 0, - unhandleds: [], - onunhandled: globalError, - pgp: false, - env: {}, - finalize: function () { - this.unhandleds.forEach(uh => { - try { - globalError(uh[0], uh[1]); - } - catch (e) { } - }); - } -}; -var PSD = globalPSD; -var microtickQueue = []; -var numScheduledCalls = 0; -var tickFinalizers = []; -function DexiePromise(fn) { - if (typeof this !== 'object') - throw new TypeError('Promises must be constructed via new'); - this._listeners = []; - this.onuncatched = nop; - this._lib = false; - var psd = (this._PSD = PSD); - if (debug) { - this._stackHolder = getErrorWithStack(); - this._prev = null; - this._numPrev = 0; - } - if (typeof fn !== 'function') { - if (fn !== INTERNAL) - throw new TypeError('Not a function'); - this._state = arguments[1]; - this._value = arguments[2]; - if (this._state === false) - handleRejection(this, this._value); - return; - } - this._state = null; - this._value = null; - ++psd.ref; - executePromiseTask(this, fn); -} -const thenProp = { - get: function () { - var psd = PSD, microTaskId = totalEchoes; - function then(onFulfilled, onRejected) { - var possibleAwait = !psd.global && (psd !== PSD || microTaskId !== totalEchoes); - const cleanup = possibleAwait && !decrementExpectedAwaits(); - var rv = new DexiePromise((resolve, reject) => { - propagateToListener(this, new Listener(nativeAwaitCompatibleWrap(onFulfilled, psd, possibleAwait, cleanup), nativeAwaitCompatibleWrap(onRejected, psd, possibleAwait, cleanup), resolve, reject, psd)); - }); - debug && linkToPreviousPromise(rv, this); - return rv; - } - then.prototype = INTERNAL; - return then; - }, - set: function (value) { - setProp(this, 'then', value && value.prototype === INTERNAL ? - thenProp : - { - get: function () { - return value; - }, - set: thenProp.set - }); - } -}; -props(DexiePromise.prototype, { - then: thenProp, - _then: function (onFulfilled, onRejected) { - propagateToListener(this, new Listener(null, null, onFulfilled, onRejected, PSD)); - }, - catch: function (onRejected) { - if (arguments.length === 1) - return this.then(null, onRejected); - var type = arguments[0], handler = arguments[1]; - return typeof type === 'function' ? this.then(null, err => - err instanceof type ? handler(err) : PromiseReject(err)) - : this.then(null, err => - err && err.name === type ? handler(err) : PromiseReject(err)); - }, - finally: function (onFinally) { - return this.then(value => { - onFinally(); - return value; - }, err => { - onFinally(); - return PromiseReject(err); - }); - }, - stack: { - get: function () { - if (this._stack) - return this._stack; - try { - stack_being_generated = true; - var stacks = getStack(this, [], MAX_LONG_STACKS); - var stack = stacks.join("\nFrom previous: "); - if (this._state !== null) - this._stack = stack; - return stack; - } - finally { - stack_being_generated = false; - } + +const $1ed45968c1160c3c$export$c9b263b9a17dffd7 = { + uniforms: { + "sceneDiffuse": { + value: null + }, + "sceneDepth": { + value: null + }, + "sceneNormal": { + value: null + }, + "projMat": { + value: new Matrix4() + }, + "viewMat": { + value: new Matrix4() + }, + "projViewMat": { + value: new Matrix4() + }, + "projectionMatrixInv": { + value: new Matrix4() + }, + "viewMatrixInv": { + value: new Matrix4() + }, + "cameraPos": { + value: new Vector3$1() + }, + "resolution": { + value: new Vector2$1() + }, + "time": { + value: 0.0 + }, + "samples": { + value: [] + }, + "bluenoise": { + value: null + }, + "distanceFalloff": { + value: 1.0 + }, + "radius": { + value: 5.0 + }, + "near": { + value: 0.1 + }, + "far": { + value: 1000.0 + }, + "logDepth": { + value: false + }, + "ortho": { + value: false + }, + "screenSpaceRadius": { + value: false } }, - timeout: function (ms, msg) { - return ms < Infinity ? - new DexiePromise((resolve, reject) => { - var handle = setTimeout(() => reject(new exceptions.Timeout(msg)), ms); - this.then(resolve, reject).finally(clearTimeout.bind(null, handle)); - }) : this; - } -}); -if (typeof Symbol !== 'undefined' && Symbol.toStringTag) - setProp(DexiePromise.prototype, Symbol.toStringTag, 'Dexie.Promise'); -globalPSD.env = snapShot(); -function Listener(onFulfilled, onRejected, resolve, reject, zone) { - this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; - this.onRejected = typeof onRejected === 'function' ? onRejected : null; - this.resolve = resolve; - this.reject = reject; - this.psd = zone; -} -props(DexiePromise, { - all: function () { - var values = getArrayOf.apply(null, arguments) - .map(onPossibleParallellAsync); - return new DexiePromise(function (resolve, reject) { - if (values.length === 0) - resolve([]); - var remaining = values.length; - values.forEach((a, i) => DexiePromise.resolve(a).then(x => { - values[i] = x; - if (!--remaining) - resolve(values); - }, reject)); - }); - }, - resolve: value => { - if (value instanceof DexiePromise) - return value; - if (value && typeof value.then === 'function') - return new DexiePromise((resolve, reject) => { - value.then(resolve, reject); - }); - var rv = new DexiePromise(INTERNAL, true, value); - linkToPreviousPromise(rv, currentFulfiller); - return rv; - }, - reject: PromiseReject, - race: function () { - var values = getArrayOf.apply(null, arguments).map(onPossibleParallellAsync); - return new DexiePromise((resolve, reject) => { - values.map(value => DexiePromise.resolve(value).then(resolve, reject)); - }); - }, - PSD: { - get: () => PSD, - set: value => PSD = value - }, - totalEchoes: { get: () => totalEchoes }, - newPSD: newScope, - usePSD: usePSD, - scheduler: { - get: () => asap, - set: value => { asap = value; } - }, - rejectionMapper: { - get: () => rejectionMapper, - set: value => { rejectionMapper = value; } - }, - follow: (fn, zoneProps) => { - return new DexiePromise((resolve, reject) => { - return newScope((resolve, reject) => { - var psd = PSD; - psd.unhandleds = []; - psd.onunhandled = reject; - psd.finalize = callBoth(function () { - run_at_end_of_this_or_next_physical_tick(() => { - this.unhandleds.length === 0 ? resolve() : reject(this.unhandleds[0]); - }); - }, psd.finalize); - fn(); - }, zoneProps, resolve, reject); - }); - } -}); -if (NativePromise) { - if (NativePromise.allSettled) - setProp(DexiePromise, "allSettled", function () { - const possiblePromises = getArrayOf.apply(null, arguments).map(onPossibleParallellAsync); - return new DexiePromise(resolve => { - if (possiblePromises.length === 0) - resolve([]); - let remaining = possiblePromises.length; - const results = new Array(remaining); - possiblePromises.forEach((p, i) => DexiePromise.resolve(p).then(value => results[i] = { status: "fulfilled", value }, reason => results[i] = { status: "rejected", reason }) - .then(() => --remaining || resolve(results))); - }); - }); - if (NativePromise.any && typeof AggregateError !== 'undefined') - setProp(DexiePromise, "any", function () { - const possiblePromises = getArrayOf.apply(null, arguments).map(onPossibleParallellAsync); - return new DexiePromise((resolve, reject) => { - if (possiblePromises.length === 0) - reject(new AggregateError([])); - let remaining = possiblePromises.length; - const failures = new Array(remaining); - possiblePromises.forEach((p, i) => DexiePromise.resolve(p).then(value => resolve(value), failure => { - failures[i] = failure; - if (!--remaining) - reject(new AggregateError(failures)); - })); - }); - }); -} -function executePromiseTask(promise, fn) { - try { - fn(value => { - if (promise._state !== null) - return; - if (value === promise) - throw new TypeError('A promise cannot be resolved with itself.'); - var shouldExecuteTick = promise._lib && beginMicroTickScope(); - if (value && typeof value.then === 'function') { - executePromiseTask(promise, (resolve, reject) => { - value instanceof DexiePromise ? - value._then(resolve, reject) : - value.then(resolve, reject); - }); - } - else { - promise._state = true; - promise._value = value; - propagateAllListeners(promise); - } - if (shouldExecuteTick) - endMicroTickScope(); - }, handleRejection.bind(null, promise)); + depthWrite: false, + depthTest: false, + vertexShader: /* glsl */ ` +varying vec2 vUv; +void main() { + vUv = uv; + gl_Position = vec4(position, 1); +}`, + fragmentShader: /* glsl */ ` + #define SAMPLES 16 + #define FSAMPLES 16.0 +uniform sampler2D sceneDiffuse; +uniform highp sampler2D sceneNormal; +uniform highp sampler2D sceneDepth; +uniform mat4 projectionMatrixInv; +uniform mat4 viewMatrixInv; +uniform mat4 projMat; +uniform mat4 viewMat; +uniform mat4 projViewMat; +uniform vec3 cameraPos; +uniform vec2 resolution; +uniform float time; +uniform vec3[SAMPLES] samples; +uniform float radius; +uniform float distanceFalloff; +uniform float near; +uniform float far; +uniform bool logDepth; +uniform bool ortho; +uniform bool screenSpaceRadius; +uniform sampler2D bluenoise; + varying vec2 vUv; + highp float linearize_depth(highp float d, highp float zNear,highp float zFar) + { + return (zFar * zNear) / (zFar - d * (zFar - zNear)); } - catch (ex) { - handleRejection(promise, ex); + highp float linearize_depth_ortho(highp float d, highp float nearZ, highp float farZ) { + return nearZ + (farZ - nearZ) * d; } -} -function handleRejection(promise, reason) { - rejectingErrors.push(reason); - if (promise._state !== null) - return; - var shouldExecuteTick = promise._lib && beginMicroTickScope(); - reason = rejectionMapper(reason); - promise._state = false; - promise._value = reason; - debug && reason !== null && typeof reason === 'object' && !reason._promise && tryCatch(() => { - var origProp = getPropertyDescriptor(reason, "stack"); - reason._promise = promise; - setProp(reason, "stack", { - get: () => stack_being_generated ? - origProp && (origProp.get ? - origProp.get.apply(reason) : - origProp.value) : - promise.stack - }); - }); - addPossiblyUnhandledError(promise); - propagateAllListeners(promise); - if (shouldExecuteTick) - endMicroTickScope(); -} -function propagateAllListeners(promise) { - var listeners = promise._listeners; - promise._listeners = []; - for (var i = 0, len = listeners.length; i < len; ++i) { - propagateToListener(promise, listeners[i]); + highp float linearize_depth_log(highp float d, highp float nearZ,highp float farZ) { + float depth = pow(2.0, d * log2(farZ + 1.0)) - 1.0; + float a = farZ / (farZ - nearZ); + float b = farZ * nearZ / (nearZ - farZ); + float linDepth = a + b / depth; + return ortho ? linearize_depth_ortho( + linDepth, + nearZ, + farZ + ) :linearize_depth(linDepth, nearZ, farZ); } - var psd = promise._PSD; - --psd.ref || psd.finalize(); - if (numScheduledCalls === 0) { - ++numScheduledCalls; - asap(() => { - if (--numScheduledCalls === 0) - finalizePhysicalTick(); - }, []); + + vec3 getWorldPosLog(vec3 posS) { + vec2 uv = posS.xy; + float z = posS.z; + float nearZ =near; + float farZ = far; + float depth = pow(2.0, z * log2(farZ + 1.0)) - 1.0; + float a = farZ / (farZ - nearZ); + float b = farZ * nearZ / (nearZ - farZ); + float linDepth = a + b / depth; + vec4 clipVec = vec4(uv, linDepth, 1.0) * 2.0 - 1.0; + vec4 wpos = projectionMatrixInv * clipVec; + return wpos.xyz / wpos.w; } + vec3 getWorldPos(float depth, vec2 coord) { + #ifdef LOGDEPTH + return getWorldPosLog(vec3(coord, depth)); + #endif + float z = depth * 2.0 - 1.0; + vec4 clipSpacePosition = vec4(coord * 2.0 - 1.0, z, 1.0); + vec4 viewSpacePosition = projectionMatrixInv * clipSpacePosition; + // Perspective division + vec4 worldSpacePosition = viewSpacePosition; + worldSpacePosition.xyz /= worldSpacePosition.w; + return worldSpacePosition.xyz; + } + + vec3 computeNormal(vec3 worldPos, vec2 vUv) { + ivec2 p = ivec2(vUv * resolution); + float c0 = texelFetch(sceneDepth, p, 0).x; + float l2 = texelFetch(sceneDepth, p - ivec2(2, 0), 0).x; + float l1 = texelFetch(sceneDepth, p - ivec2(1, 0), 0).x; + float r1 = texelFetch(sceneDepth, p + ivec2(1, 0), 0).x; + float r2 = texelFetch(sceneDepth, p + ivec2(2, 0), 0).x; + float b2 = texelFetch(sceneDepth, p - ivec2(0, 2), 0).x; + float b1 = texelFetch(sceneDepth, p - ivec2(0, 1), 0).x; + float t1 = texelFetch(sceneDepth, p + ivec2(0, 1), 0).x; + float t2 = texelFetch(sceneDepth, p + ivec2(0, 2), 0).x; + + float dl = abs((2.0 * l1 - l2) - c0); + float dr = abs((2.0 * r1 - r2) - c0); + float db = abs((2.0 * b1 - b2) - c0); + float dt = abs((2.0 * t1 - t2) - c0); + + vec3 ce = getWorldPos(c0, vUv).xyz; + + vec3 dpdx = (dl < dr) ? ce - getWorldPos(l1, (vUv - vec2(1.0 / resolution.x, 0.0))).xyz + : -ce + getWorldPos(r1, (vUv + vec2(1.0 / resolution.x, 0.0))).xyz; + vec3 dpdy = (db < dt) ? ce - getWorldPos(b1, (vUv - vec2(0.0, 1.0 / resolution.y))).xyz + : -ce + getWorldPos(t1, (vUv + vec2(0.0, 1.0 / resolution.y))).xyz; + + return normalize(cross(dpdx, dpdy)); } -function propagateToListener(promise, listener) { - if (promise._state === null) { - promise._listeners.push(listener); + +mat3 makeRotationZ(float theta) { + float c = cos(theta); + float s = sin(theta); + return mat3(c, - s, 0, + s, c, 0, + 0, 0, 1); + } + +void main() { + vec4 diffuse = texture2D(sceneDiffuse, vUv); + float depth = texture2D(sceneDepth, vUv).x; + if (depth == 1.0) { + gl_FragColor = vec4(vec3(1.0), 1.0); return; - } - var cb = promise._state ? listener.onFulfilled : listener.onRejected; - if (cb === null) { - return (promise._state ? listener.resolve : listener.reject)(promise._value); - } - ++listener.psd.ref; - ++numScheduledCalls; - asap(callListener, [cb, promise, listener]); -} -function callListener(cb, promise, listener) { - try { - currentFulfiller = promise; - var ret, value = promise._value; - if (promise._state) { - ret = cb(value); + } + vec3 worldPos = getWorldPos(depth, vUv); + #ifdef HALFRES + vec3 normal = texture2D(sceneNormal, vUv).rgb; + #else + vec3 normal = computeNormal(worldPos, vUv); + #endif + vec4 noise = texture2D(bluenoise, gl_FragCoord.xy / 128.0); + vec3 helperVec = vec3(0.0, 1.0, 0.0); + if (dot(helperVec, normal) > 0.99) { + helperVec = vec3(1.0, 0.0, 0.0); } - else { - if (rejectingErrors.length) - rejectingErrors = []; - ret = cb(value); - if (rejectingErrors.indexOf(value) === -1) - markErrorAsHandled(promise); + vec3 tangent = normalize(cross(helperVec, normal)); + vec3 bitangent = cross(normal, tangent); + mat3 tbn = mat3(tangent, bitangent, normal) * makeRotationZ(noise.r * 2.0 * 3.1415962) ; + + float occluded = 0.0; + float totalWeight = 0.0; + float radiusToUse = screenSpaceRadius ? distance( + worldPos, + getWorldPos(depth, vUv + + vec2(radius, 0.0) / resolution) + ) : radius; + float distanceFalloffToUse =screenSpaceRadius ? + radiusToUse * distanceFalloff + : radiusToUse * distanceFalloff * 0.2; + float bias = (min( + 0.1, + distanceFalloffToUse * 0.1 + ) / near) * fwidth(distance(worldPos, cameraPos)) / radiusToUse; + float phi = 1.61803398875; + float offsetMove = 0.0; + float offsetMoveInv = 1.0 / FSAMPLES; + for(float i = 0.0; i < FSAMPLES; i++) { + vec3 sampleDirection = tbn * samples[int(i)]; + + float moveAmt = fract(noise.g + offsetMove); + offsetMove += offsetMoveInv; + + vec3 samplePos = worldPos + radiusToUse * moveAmt * sampleDirection; + vec4 offset = projMat * vec4(samplePos, 1.0); + offset.xyz /= offset.w; + offset.xyz = offset.xyz * 0.5 + 0.5; + + vec2 diff = gl_FragCoord.xy - floor(offset.xy * resolution); + // From Rabbid76's hbao + vec2 clipRangeCheck = step(vec2(0.0),offset.xy) * step(offset.xy, vec2(1.0)); + float sampleDepth = textureLod(sceneDepth, offset.xy, 0.0).x; + + #ifdef LOGDEPTH + + float distSample = linearize_depth_log(sampleDepth, near, far); + + #else + + float distSample = ortho ? linearize_depth_ortho(sampleDepth, near, far) : linearize_depth(sampleDepth, near, far); + + #endif + + float distWorld = ortho ? linearize_depth_ortho(offset.z, near, far) : linearize_depth(offset.z, near, far); + + float rangeCheck = smoothstep(0.0, 1.0, distanceFalloffToUse / (abs(distSample - distWorld))); + + float sampleValid = (clipRangeCheck.x * clipRangeCheck.y); + occluded += rangeCheck * float(sampleDepth != depth) * float(distSample + bias < distWorld) * step( + 1.0, + dot(diff, diff) + ) * sampleValid; + + totalWeight += sampleValid; + } + float occ = clamp(1.0 - occluded / (totalWeight == 0.0 ? 1.0 : totalWeight), 0.0, 1.0); + gl_FragColor = vec4(0.5 + 0.5 * normal, occ); +}` +}; + + + +const $12b21d24d1192a04$export$a815acccbd2c9a49 = { + uniforms: { + "sceneDiffuse": { + value: null + }, + "sceneDepth": { + value: null + }, + "tDiffuse": { + value: null + }, + "transparencyDWFalse": { + value: null + }, + "transparencyDWTrue": { + value: null + }, + "transparencyDWTrueDepth": { + value: null + }, + "transparencyAware": { + value: false + }, + "projMat": { + value: new Matrix4() + }, + "viewMat": { + value: new Matrix4() + }, + "projectionMatrixInv": { + value: new Matrix4() + }, + "viewMatrixInv": { + value: new Matrix4() + }, + "cameraPos": { + value: new Vector3$1() + }, + "resolution": { + value: new Vector2$1() + }, + "color": { + value: new Vector3$1(0, 0, 0) + }, + "blueNoise": { + value: null + }, + "downsampledDepth": { + value: null + }, + "time": { + value: 0.0 + }, + "intensity": { + value: 10.0 + }, + "renderMode": { + value: 0.0 + }, + "gammaCorrection": { + value: false + }, + "logDepth": { + value: false + }, + "ortho": { + value: false + }, + "near": { + value: 0.1 + }, + "far": { + value: 1000.0 + }, + "screenSpaceRadius": { + value: false + }, + "radius": { + value: 0.0 + }, + "distanceFalloff": { + value: 1.0 + }, + "fog": { + value: false + }, + "fogExp": { + value: false + }, + "fogDensity": { + value: 0.0 + }, + "fogNear": { + value: Infinity + }, + "fogFar": { + value: Infinity + }, + "colorMultiply": { + value: true } - listener.resolve(ret); - } - catch (e) { - listener.reject(e); - } - finally { - currentFulfiller = null; - if (--numScheduledCalls === 0) - finalizePhysicalTick(); - --listener.psd.ref || listener.psd.finalize(); + }, + depthWrite: false, + depthTest: false, + vertexShader: /* glsl */ ` + varying vec2 vUv; + void main() { + vUv = uv; + gl_Position = vec4(position, 1); + }`, + fragmentShader: /* glsl */ ` + uniform sampler2D sceneDiffuse; + uniform highp sampler2D sceneDepth; + uniform highp sampler2D downsampledDepth; + uniform highp sampler2D transparencyDWFalse; + uniform highp sampler2D transparencyDWTrue; + uniform highp sampler2D transparencyDWTrueDepth; + uniform sampler2D tDiffuse; + uniform sampler2D blueNoise; + uniform vec2 resolution; + uniform vec3 color; + uniform mat4 projectionMatrixInv; + uniform mat4 viewMatrixInv; + uniform float intensity; + uniform float renderMode; + uniform float near; + uniform float far; + uniform bool gammaCorrection; + uniform bool logDepth; + uniform bool ortho; + uniform bool screenSpaceRadius; + uniform bool fog; + uniform bool fogExp; + uniform bool colorMultiply; + uniform bool transparencyAware; + uniform float fogDensity; + uniform float fogNear; + uniform float fogFar; + uniform float radius; + uniform float distanceFalloff; + uniform vec3 cameraPos; + varying vec2 vUv; + highp float linearize_depth(highp float d, highp float zNear,highp float zFar) + { + return (zFar * zNear) / (zFar - d * (zFar - zNear)); } -} -function getStack(promise, stacks, limit) { - if (stacks.length === limit) - return stacks; - var stack = ""; - if (promise._state === false) { - var failure = promise._value, errorName, message; - if (failure != null) { - errorName = failure.name || "Error"; - message = failure.message || failure; - stack = prettyStack(failure, 0); - } - else { - errorName = failure; - message = ""; - } - stacks.push(errorName + (message ? ": " + message : "") + stack); + highp float linearize_depth_ortho(highp float d, highp float nearZ, highp float farZ) { + return nearZ + (farZ - nearZ) * d; } - if (debug) { - stack = prettyStack(promise._stackHolder, 2); - if (stack && stacks.indexOf(stack) === -1) - stacks.push(stack); - if (promise._prev) - getStack(promise._prev, stacks, limit); + highp float linearize_depth_log(highp float d, highp float nearZ,highp float farZ) { + float depth = pow(2.0, d * log2(farZ + 1.0)) - 1.0; + float a = farZ / (farZ - nearZ); + float b = farZ * nearZ / (nearZ - farZ); + float linDepth = a + b / depth; + return ortho ? linearize_depth_ortho( + linDepth, + nearZ, + farZ + ) :linearize_depth(linDepth, nearZ, farZ); } - return stacks; -} -function linkToPreviousPromise(promise, prev) { - var numPrev = prev ? prev._numPrev + 1 : 0; - if (numPrev < LONG_STACKS_CLIP_LIMIT) { - promise._prev = prev; - promise._numPrev = numPrev; + vec3 getWorldPosLog(vec3 posS) { + vec2 uv = posS.xy; + float z = posS.z; + float nearZ =near; + float farZ = far; + float depth = pow(2.0, z * log2(farZ + 1.0)) - 1.0; + float a = farZ / (farZ - nearZ); + float b = farZ * nearZ / (nearZ - farZ); + float linDepth = a + b / depth; + vec4 clipVec = vec4(uv, linDepth, 1.0) * 2.0 - 1.0; + vec4 wpos = projectionMatrixInv * clipVec; + return wpos.xyz / wpos.w; + } + vec3 getWorldPos(float depth, vec2 coord) { + // if (logDepth) { + #ifdef LOGDEPTH + return getWorldPosLog(vec3(coord, depth)); + #endif + // } + float z = depth * 2.0 - 1.0; + vec4 clipSpacePosition = vec4(coord * 2.0 - 1.0, z, 1.0); + vec4 viewSpacePosition = projectionMatrixInv * clipSpacePosition; + // Perspective division + vec4 worldSpacePosition = viewSpacePosition; + worldSpacePosition.xyz /= worldSpacePosition.w; + return worldSpacePosition.xyz; } -} -function physicalTick() { - beginMicroTickScope() && endMicroTickScope(); -} -function beginMicroTickScope() { - var wasRootExec = isOutsideMicroTick; - isOutsideMicroTick = false; - needsNewPhysicalTick = false; - return wasRootExec; -} -function endMicroTickScope() { - var callbacks, i, l; - do { - while (microtickQueue.length > 0) { - callbacks = microtickQueue; - microtickQueue = []; - l = callbacks.length; - for (i = 0; i < l; ++i) { - var item = callbacks[i]; - item[0].apply(null, item[1]); + + vec3 computeNormal(vec3 worldPos, vec2 vUv) { + ivec2 p = ivec2(vUv * resolution); + float c0 = texelFetch(sceneDepth, p, 0).x; + float l2 = texelFetch(sceneDepth, p - ivec2(2, 0), 0).x; + float l1 = texelFetch(sceneDepth, p - ivec2(1, 0), 0).x; + float r1 = texelFetch(sceneDepth, p + ivec2(1, 0), 0).x; + float r2 = texelFetch(sceneDepth, p + ivec2(2, 0), 0).x; + float b2 = texelFetch(sceneDepth, p - ivec2(0, 2), 0).x; + float b1 = texelFetch(sceneDepth, p - ivec2(0, 1), 0).x; + float t1 = texelFetch(sceneDepth, p + ivec2(0, 1), 0).x; + float t2 = texelFetch(sceneDepth, p + ivec2(0, 2), 0).x; + + float dl = abs((2.0 * l1 - l2) - c0); + float dr = abs((2.0 * r1 - r2) - c0); + float db = abs((2.0 * b1 - b2) - c0); + float dt = abs((2.0 * t1 - t2) - c0); + + vec3 ce = getWorldPos(c0, vUv).xyz; + + vec3 dpdx = (dl < dr) ? ce - getWorldPos(l1, (vUv - vec2(1.0 / resolution.x, 0.0))).xyz + : -ce + getWorldPos(r1, (vUv + vec2(1.0 / resolution.x, 0.0))).xyz; + vec3 dpdy = (db < dt) ? ce - getWorldPos(b1, (vUv - vec2(0.0, 1.0 / resolution.y))).xyz + : -ce + getWorldPos(t1, (vUv + vec2(0.0, 1.0 / resolution.y))).xyz; + + return normalize(cross(dpdx, dpdy)); + } + + #include + #include + void main() { + //vec4 texel = texture2D(tDiffuse, vUv);//vec3(0.0); + vec4 sceneTexel = texture2D(sceneDiffuse, vUv); + float depth = texture2D( + sceneDepth, + vUv + ).x; + #ifdef HALFRES + vec4 texel; + if (depth == 1.0) { + texel = vec4(0.0, 0.0, 0.0, 1.0); + } else { + vec3 worldPos = getWorldPos(depth, vUv); + vec3 normal = computeNormal(getWorldPos(depth, vUv), vUv); + // vec4 texel = texture2D(tDiffuse, vUv); + // Find closest depth; + float totalWeight = 0.0; + float radiusToUse = screenSpaceRadius ? distance( + worldPos, + getWorldPos(depth, vUv + + vec2(radius, 0.0) / resolution) + ) : radius; + float distanceFalloffToUse =screenSpaceRadius ? + radiusToUse * distanceFalloff + : distanceFalloff; + for(float x = -1.0; x <= 1.0; x++) { + for(float y = -1.0; y <= 1.0; y++) { + vec2 offset = vec2(x, y); + ivec2 p = ivec2( + (vUv * resolution * 0.5) + offset + ); + vec2 pUv = vec2(p) / (resolution * 0.5); + float sampleDepth = texelFetch(downsampledDepth,p, 0).x; + vec4 sampleInfo = texelFetch(tDiffuse, p, 0); + vec3 normalSample = sampleInfo.xyz * 2.0 - 1.0; + vec3 worldPosSample = getWorldPos(sampleDepth, pUv); + float tangentPlaneDist = abs(dot(worldPosSample - worldPos, normal)); + float rangeCheck = exp(-1.0 * tangentPlaneDist * (1.0 / distanceFalloffToUse)) * max(dot(normal, normalSample), 0.0); + float weight = rangeCheck; + totalWeight += weight; + texel += sampleInfo * weight; } } - } while (microtickQueue.length > 0); - isOutsideMicroTick = true; - needsNewPhysicalTick = true; -} -function finalizePhysicalTick() { - var unhandledErrs = unhandledErrors; - unhandledErrors = []; - unhandledErrs.forEach(p => { - p._PSD.onunhandled.call(null, p._value, p); - }); - var finalizers = tickFinalizers.slice(0); - var i = finalizers.length; - while (i) - finalizers[--i](); -} -function run_at_end_of_this_or_next_physical_tick(fn) { - function finalizer() { - fn(); - tickFinalizers.splice(tickFinalizers.indexOf(finalizer), 1); + if (totalWeight == 0.0) { + texel = texture2D(tDiffuse, vUv); + } else { + texel /= totalWeight; + } } - tickFinalizers.push(finalizer); - ++numScheduledCalls; - asap(() => { - if (--numScheduledCalls === 0) - finalizePhysicalTick(); - }, []); -} -function addPossiblyUnhandledError(promise) { - if (!unhandledErrors.some(p => p._value === promise._value)) - unhandledErrors.push(promise); -} -function markErrorAsHandled(promise) { - var i = unhandledErrors.length; - while (i) - if (unhandledErrors[--i]._value === promise._value) { - unhandledErrors.splice(i, 1); - return; + #else + vec4 texel = texture2D(tDiffuse, vUv); + #endif + + #ifdef LOGDEPTH + texel.a = clamp(texel.a, 0.0, 1.0); + if (texel.a == 0.0) { + texel.a = 1.0; } -} -function PromiseReject(reason) { - return new DexiePromise(INTERNAL, false, reason); -} -function wrap(fn, errorCatcher) { - var psd = PSD; - return function () { - var wasRootExec = beginMicroTickScope(), outerScope = PSD; - try { - switchToZone(psd, true); - return fn.apply(this, arguments); + #endif + + float finalAo = pow(texel.a, intensity); + float fogFactor; + float fogDepth = distance( + cameraPos, + getWorldPos(depth, vUv) + ); + if (fog) { + if (fogExp) { + fogFactor = 1.0 - exp( - fogDensity * fogDensity * fogDepth * fogDepth ); + } else { + fogFactor = smoothstep( fogNear, fogFar, fogDepth ); + } } - catch (e) { - errorCatcher && errorCatcher(e); + if (transparencyAware) { + float transparencyDWOff = texture2D(transparencyDWFalse, vUv).a; + float transparencyDWOn = texture2D(transparencyDWTrue, vUv).a; + float adjustmentFactorOff = transparencyDWOff; + float adjustmentFactorOn = (1.0 - transparencyDWOn) * ( + texture2D(transparencyDWTrueDepth, vUv).r == texture2D(sceneDepth, vUv).r ? 1.0 : 0.0 + ); + float adjustmentFactor = max(adjustmentFactorOff, adjustmentFactorOn); + finalAo = mix(finalAo, 1.0, adjustmentFactor); } - finally { - switchToZone(outerScope, false); - if (wasRootExec) - endMicroTickScope(); + finalAo = mix(finalAo, 1.0, fogFactor); + vec3 aoApplied = color * mix(vec3(1.0), sceneTexel.rgb, float(colorMultiply)); + if (renderMode == 0.0) { + gl_FragColor = vec4( mix(sceneTexel.rgb, aoApplied, 1.0 - finalAo), sceneTexel.a); + } else if (renderMode == 1.0) { + gl_FragColor = vec4( mix(vec3(1.0), aoApplied, 1.0 - finalAo), sceneTexel.a); + } else if (renderMode == 2.0) { + gl_FragColor = vec4( sceneTexel.rgb, sceneTexel.a); + } else if (renderMode == 3.0) { + if (vUv.x < 0.5) { + gl_FragColor = vec4( sceneTexel.rgb, sceneTexel.a); + } else if (abs(vUv.x - 0.5) < 1.0 / resolution.x) { + gl_FragColor = vec4(1.0); + } else { + gl_FragColor = vec4( mix(sceneTexel.rgb, aoApplied, 1.0 - finalAo), sceneTexel.a); + } + } else if (renderMode == 4.0) { + if (vUv.x < 0.5) { + gl_FragColor = vec4( sceneTexel.rgb, sceneTexel.a); + } else if (abs(vUv.x - 0.5) < 1.0 / resolution.x) { + gl_FragColor = vec4(1.0); + } else { + gl_FragColor = vec4( mix(vec3(1.0), aoApplied, 1.0 - finalAo), sceneTexel.a); + } } - }; -} -const task = { awaits: 0, echoes: 0, id: 0 }; -var taskCounter = 0; -var zoneStack = []; -var zoneEchoes = 0; -var totalEchoes = 0; -var zone_id_counter = 0; -function newScope(fn, props, a1, a2) { - var parent = PSD, psd = Object.create(parent); - psd.parent = parent; - psd.ref = 0; - psd.global = false; - psd.id = ++zone_id_counter; - var globalEnv = globalPSD.env; - psd.env = patchGlobalPromise ? { - Promise: DexiePromise, - PromiseProp: { value: DexiePromise, configurable: true, writable: true }, - all: DexiePromise.all, - race: DexiePromise.race, - allSettled: DexiePromise.allSettled, - any: DexiePromise.any, - resolve: DexiePromise.resolve, - reject: DexiePromise.reject, - nthen: getPatchedPromiseThen(globalEnv.nthen, psd), - gthen: getPatchedPromiseThen(globalEnv.gthen, psd) - } : {}; - if (props) - extend(psd, props); - ++parent.ref; - psd.finalize = function () { - --this.parent.ref || this.parent.finalize(); - }; - var rv = usePSD(psd, fn, a1, a2); - if (psd.ref === 0) - psd.finalize(); - return rv; -} -function incrementExpectedAwaits() { - if (!task.id) - task.id = ++taskCounter; - ++task.awaits; - task.echoes += ZONE_ECHO_LIMIT; - return task.id; -} -function decrementExpectedAwaits() { - if (!task.awaits) - return false; - if (--task.awaits === 0) - task.id = 0; - task.echoes = task.awaits * ZONE_ECHO_LIMIT; - return true; -} -if (('' + nativePromiseThen).indexOf('[native code]') === -1) { - incrementExpectedAwaits = decrementExpectedAwaits = nop; -} -function onPossibleParallellAsync(possiblePromise) { - if (task.echoes && possiblePromise && possiblePromise.constructor === NativePromise) { - incrementExpectedAwaits(); - return possiblePromise.then(x => { - decrementExpectedAwaits(); - return x; - }, e => { - decrementExpectedAwaits(); - return rejection(e); - }); - } - return possiblePromise; -} -function zoneEnterEcho(targetZone) { - ++totalEchoes; - if (!task.echoes || --task.echoes === 0) { - task.echoes = task.id = 0; - } - zoneStack.push(PSD); - switchToZone(targetZone, true); -} -function zoneLeaveEcho() { - var zone = zoneStack[zoneStack.length - 1]; - zoneStack.pop(); - switchToZone(zone, false); -} -function switchToZone(targetZone, bEnteringZone) { - var currentZone = PSD; - if (bEnteringZone ? task.echoes && (!zoneEchoes++ || targetZone !== PSD) : zoneEchoes && (!--zoneEchoes || targetZone !== PSD)) { - enqueueNativeMicroTask(bEnteringZone ? zoneEnterEcho.bind(null, targetZone) : zoneLeaveEcho); - } - if (targetZone === PSD) - return; - PSD = targetZone; - if (currentZone === globalPSD) - globalPSD.env = snapShot(); - if (patchGlobalPromise) { - var GlobalPromise = globalPSD.env.Promise; - var targetEnv = targetZone.env; - nativePromiseProto.then = targetEnv.nthen; - GlobalPromise.prototype.then = targetEnv.gthen; - if (currentZone.global || targetZone.global) { - Object.defineProperty(_global, 'Promise', targetEnv.PromiseProp); - GlobalPromise.all = targetEnv.all; - GlobalPromise.race = targetEnv.race; - GlobalPromise.resolve = targetEnv.resolve; - GlobalPromise.reject = targetEnv.reject; - if (targetEnv.allSettled) - GlobalPromise.allSettled = targetEnv.allSettled; - if (targetEnv.any) - GlobalPromise.any = targetEnv.any; - } - } -} -function snapShot() { - var GlobalPromise = _global.Promise; - return patchGlobalPromise ? { - Promise: GlobalPromise, - PromiseProp: Object.getOwnPropertyDescriptor(_global, "Promise"), - all: GlobalPromise.all, - race: GlobalPromise.race, - allSettled: GlobalPromise.allSettled, - any: GlobalPromise.any, - resolve: GlobalPromise.resolve, - reject: GlobalPromise.reject, - nthen: nativePromiseProto.then, - gthen: GlobalPromise.prototype.then - } : {}; -} -function usePSD(psd, fn, a1, a2, a3) { - var outerScope = PSD; - try { - switchToZone(psd, true); - return fn(a1, a2, a3); - } - finally { - switchToZone(outerScope, false); - } -} -function enqueueNativeMicroTask(job) { - nativePromiseThen.call(resolvedNativePromise, job); -} -function nativeAwaitCompatibleWrap(fn, zone, possibleAwait, cleanup) { - return typeof fn !== 'function' ? fn : function () { - var outerZone = PSD; - if (possibleAwait) - incrementExpectedAwaits(); - switchToZone(zone, true); - try { - return fn.apply(this, arguments); - } - finally { - switchToZone(outerZone, false); - if (cleanup) - enqueueNativeMicroTask(decrementExpectedAwaits); + #include + if (gammaCorrection) { + gl_FragColor = LinearTosRGB(gl_FragColor); } - }; -} -function getPatchedPromiseThen(origThen, zone) { - return function (onResolved, onRejected) { - return origThen.call(this, nativeAwaitCompatibleWrap(onResolved, zone), nativeAwaitCompatibleWrap(onRejected, zone)); - }; -} -const UNHANDLEDREJECTION = "unhandledrejection"; -function globalError(err, promise) { - var rv; - try { - rv = promise.onuncatched(err); } - catch (e) { } - if (rv !== false) - try { - var event, eventData = { promise: promise, reason: err }; - if (_global.document && document.createEvent) { - event = document.createEvent('Event'); - event.initEvent(UNHANDLEDREJECTION, true, true); - extend(event, eventData); - } - else if (_global.CustomEvent) { - event = new CustomEvent(UNHANDLEDREJECTION, { detail: eventData }); - extend(event, eventData); - } - if (event && _global.dispatchEvent) { - dispatchEvent(event); - if (!_global.PromiseRejectionEvent && _global.onunhandledrejection) - try { - _global.onunhandledrejection(event); - } - catch (_) { } - } - if (debug && event && !event.defaultPrevented) { - console.warn(`Unhandled rejection: ${err.stack || err}`); - } - } - catch (e) { } -} -var rejection = DexiePromise.reject; + ` +}; -function tempTransaction(db, mode, storeNames, fn) { - if (!db.idbdb || (!db._state.openComplete && (!PSD.letThrough && !db._vip))) { - if (db._state.openComplete) { - return rejection(new exceptions.DatabaseClosed(db._state.dbOpenError)); - } - if (!db._state.isBeingOpened) { - if (!db._options.autoOpen) - return rejection(new exceptions.DatabaseClosed()); - db.open().catch(nop); + + +const $e52378cd0f5a973d$export$57856b59f317262e = { + uniforms: { + "sceneDiffuse": { + value: null + }, + "sceneDepth": { + value: null + }, + "tDiffuse": { + value: null + }, + "projMat": { + value: new Matrix4() + }, + "viewMat": { + value: new Matrix4() + }, + "projectionMatrixInv": { + value: new Matrix4() + }, + "viewMatrixInv": { + value: new Matrix4() + }, + "cameraPos": { + value: new Vector3$1() + }, + "resolution": { + value: new Vector2$1() + }, + "time": { + value: 0.0 + }, + "r": { + value: 5.0 + }, + "blueNoise": { + value: null + }, + "radius": { + value: 12.0 + }, + "worldRadius": { + value: 5.0 + }, + "index": { + value: 0.0 + }, + "poissonDisk": { + value: [] + }, + "distanceFalloff": { + value: 1.0 + }, + "near": { + value: 0.1 + }, + "far": { + value: 1000.0 + }, + "logDepth": { + value: false + }, + "screenSpaceRadius": { + value: false } - return db._state.dbReadyPromise.then(() => tempTransaction(db, mode, storeNames, fn)); + }, + depthWrite: false, + depthTest: false, + vertexShader: /* glsl */ ` + varying vec2 vUv; + void main() { + vUv = uv; + gl_Position = vec4(position, 1.0); + }`, + fragmentShader: /* glsl */ ` + uniform sampler2D sceneDiffuse; + uniform highp sampler2D sceneDepth; + uniform sampler2D tDiffuse; + uniform sampler2D blueNoise; + uniform mat4 projectionMatrixInv; + uniform mat4 viewMatrixInv; + uniform vec2 resolution; + uniform float r; + uniform float radius; + uniform float worldRadius; + uniform float index; + uniform float near; + uniform float far; + uniform float distanceFalloff; + uniform bool logDepth; + uniform bool screenSpaceRadius; + varying vec2 vUv; + + highp float linearize_depth(highp float d, highp float zNear,highp float zFar) + { + highp float z_n = 2.0 * d - 1.0; + return 2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear)); } - else { - var trans = db._createTransaction(mode, storeNames, db._dbSchema); - try { - trans.create(); - db._state.PR1398_maxLoop = 3; + highp float linearize_depth_log(highp float d, highp float nearZ,highp float farZ) { + float depth = pow(2.0, d * log2(farZ + 1.0)) - 1.0; + float a = farZ / (farZ - nearZ); + float b = farZ * nearZ / (nearZ - farZ); + float linDepth = a + b / depth; + return linearize_depth(linDepth, nearZ, farZ); + } + highp float linearize_depth_ortho(highp float d, highp float nearZ, highp float farZ) { + return nearZ + (farZ - nearZ) * d; + } + vec3 getWorldPosLog(vec3 posS) { + vec2 uv = posS.xy; + float z = posS.z; + float nearZ =near; + float farZ = far; + float depth = pow(2.0, z * log2(farZ + 1.0)) - 1.0; + float a = farZ / (farZ - nearZ); + float b = farZ * nearZ / (nearZ - farZ); + float linDepth = a + b / depth; + vec4 clipVec = vec4(uv, linDepth, 1.0) * 2.0 - 1.0; + vec4 wpos = projectionMatrixInv * clipVec; + return wpos.xyz / wpos.w; + } + vec3 getWorldPos(float depth, vec2 coord) { + #ifdef LOGDEPTH + return getWorldPosLog(vec3(coord, depth)); + #endif + + float z = depth * 2.0 - 1.0; + vec4 clipSpacePosition = vec4(coord * 2.0 - 1.0, z, 1.0); + vec4 viewSpacePosition = projectionMatrixInv * clipSpacePosition; + // Perspective division + vec4 worldSpacePosition = viewSpacePosition; + worldSpacePosition.xyz /= worldSpacePosition.w; + return worldSpacePosition.xyz; + } + #include + #define NUM_SAMPLES 16 + uniform vec2 poissonDisk[NUM_SAMPLES]; + void main() { + const float pi = 3.14159; + vec2 texelSize = vec2(1.0 / resolution.x, 1.0 / resolution.y); + vec2 uv = vUv; + vec4 data = texture2D(tDiffuse, vUv); + float occlusion = data.a; + float baseOcc = data.a; + vec3 normal = data.rgb * 2.0 - 1.0; + float count = 1.0; + float d = texture2D(sceneDepth, vUv).x; + if (d == 1.0) { + gl_FragColor = data; + return; } - catch (ex) { - if (ex.name === errnames.InvalidState && db.isOpen() && --db._state.PR1398_maxLoop > 0) { - console.warn('Dexie: Need to reopen db'); - db._close(); - return db.open().then(() => tempTransaction(db, mode, storeNames, fn)); - } - return rejection(ex); + vec3 worldPos = getWorldPos(d, vUv); + float size = radius; + float angle; + if (index == 0.0) { + angle = texture2D(blueNoise, gl_FragCoord.xy / 128.0).x * PI2; + } else if (index == 1.0) { + angle = texture2D(blueNoise, gl_FragCoord.xy / 128.0).y * PI2; + } else if (index == 2.0) { + angle = texture2D(blueNoise, gl_FragCoord.xy / 128.0).z * PI2; + } else { + angle = texture2D(blueNoise, gl_FragCoord.xy / 128.0).w * PI2; } - return trans._promise(mode, (resolve, reject) => { - return newScope(() => { - PSD.trans = trans; - return fn(resolve, reject, trans); - }); - }).then(result => { - return trans._completion.then(() => result); - }); - } -} -const DEXIE_VERSION = '3.2.4'; -const maxString = String.fromCharCode(65535); -const minKey = -Infinity; -const INVALID_KEY_ARGUMENT = "Invalid key provided. Keys must be of type string, number, Date or Array."; -const STRING_EXPECTED = "String expected."; -const connections = []; -const isIEOrEdge = typeof navigator !== 'undefined' && /(MSIE|Trident|Edge)/.test(navigator.userAgent); -const hasIEDeleteObjectStoreBug = isIEOrEdge; -const hangsOnDeleteLargeKeyRange = isIEOrEdge; -const dexieStackFrameFilter = frame => !/(dexie\.js|dexie\.min\.js)/.test(frame); -const DBNAMES_DB = '__dbnames'; -const READONLY = 'readonly'; -const READWRITE = 'readwrite'; + mat2 rotationMatrix = mat2(cos(angle), -sin(angle), sin(angle), cos(angle)); + float radiusToUse = screenSpaceRadius ? distance( + worldPos, + getWorldPos(d, vUv + + vec2(worldRadius, 0.0) / resolution) + ) : worldRadius; + float distanceFalloffToUse =screenSpaceRadius ? + radiusToUse * distanceFalloff + : radiusToUse * distanceFalloff * 0.2; -function combine(filter1, filter2) { - return filter1 ? - filter2 ? - function () { return filter1.apply(this, arguments) && filter2.apply(this, arguments); } : - filter1 : - filter2; -} -const AnyRange = { - type: 3 , - lower: -Infinity, - lowerOpen: false, - upper: [[]], - upperOpen: false + for(int i = 0; i < NUM_SAMPLES; i++) { + vec2 offset = (rotationMatrix * poissonDisk[i]) * texelSize * size; + vec4 dataSample = texture2D(tDiffuse, uv + offset); + float occSample = dataSample.a; + vec3 normalSample = dataSample.rgb * 2.0 - 1.0; + float dSample = texture2D(sceneDepth, uv + offset).x; + vec3 worldPosSample = getWorldPos(dSample, uv + offset); + float tangentPlaneDist = abs(dot(worldPosSample - worldPos, normal)); + float rangeCheck = dSample == 1.0 ? 0.0 :exp(-1.0 * tangentPlaneDist * (1.0 / distanceFalloffToUse)) * max(dot(normal, normalSample), 0.0) * (1.0 - abs(occSample - baseOcc)); + occlusion += occSample * rangeCheck; + count += rangeCheck; + } + if (count > 0.0) { + occlusion /= count; + } + #ifdef LOGDEPTH + occlusion = clamp(occlusion, 0.0, 1.0); + if (occlusion == 0.0) { + occlusion = 1.0; + } + #endif + gl_FragColor = vec4(0.5 + 0.5 * normal, occlusion); + } + ` }; -function workaroundForUndefinedPrimKey(keyPath) { - return typeof keyPath === "string" && !/\./.test(keyPath) - ? (obj) => { - if (obj[keyPath] === undefined && (keyPath in obj)) { - obj = deepClone(obj); - delete obj[keyPath]; - } - return obj; - } - : (obj) => obj; -} -let Table$3 = class Table { - _trans(mode, fn, writeLocked) { - const trans = this._tx || PSD.trans; - const tableName = this.name; - function checkTableInTransaction(resolve, reject, trans) { - if (!trans.schema[tableName]) - throw new exceptions.NotFound("Table " + tableName + " not part of transaction"); - return fn(trans.idbtrans, trans); + +const $26aca173e0984d99$export$1efdf491687cd442 = { + uniforms: { + "sceneDepth": { + value: null + }, + "resolution": { + value: new Vector2$1() + }, + "near": { + value: 0.1 + }, + "far": { + value: 1000.0 + }, + "viewMatrixInv": { + value: new Matrix4() + }, + "projectionMatrixInv": { + value: new Matrix4() + }, + "logDepth": { + value: false } - const wasRootExec = beginMicroTickScope(); - try { - return trans && trans.db === this.db ? - trans === PSD.trans ? - trans._promise(mode, checkTableInTransaction, writeLocked) : - newScope(() => trans._promise(mode, checkTableInTransaction, writeLocked), { trans: trans, transless: PSD.transless || PSD }) : - tempTransaction(this.db, mode, [this.name], checkTableInTransaction); - } - finally { - if (wasRootExec) - endMicroTickScope(); + }, + depthWrite: false, + depthTest: false, + vertexShader: /* glsl */ ` + varying vec2 vUv; + void main() { + vUv = uv; + gl_Position = vec4(position, 1); + }`, + fragmentShader: /* glsl */ ` + uniform highp sampler2D sceneDepth; + uniform vec2 resolution; + uniform float near; + uniform float far; + uniform bool logDepth; + uniform mat4 viewMatrixInv; + uniform mat4 projectionMatrixInv; + varying vec2 vUv; + layout(location = 1) out vec4 gNormal; + vec3 getWorldPosLog(vec3 posS) { + vec2 uv = posS.xy; + float z = posS.z; + float nearZ =near; + float farZ = far; + float depth = pow(2.0, z * log2(farZ + 1.0)) - 1.0; + float a = farZ / (farZ - nearZ); + float b = farZ * nearZ / (nearZ - farZ); + float linDepth = a + b / depth; + vec4 clipVec = vec4(uv, linDepth, 1.0) * 2.0 - 1.0; + vec4 wpos = projectionMatrixInv * clipVec; + return wpos.xyz / wpos.w; + } + vec3 getWorldPos(float depth, vec2 coord) { + if (logDepth) { + return getWorldPosLog(vec3(coord, depth)); } + float z = depth * 2.0 - 1.0; + vec4 clipSpacePosition = vec4(coord * 2.0 - 1.0, z, 1.0); + vec4 viewSpacePosition = projectionMatrixInv * clipSpacePosition; + // Perspective division + vec4 worldSpacePosition = viewSpacePosition; + worldSpacePosition.xyz /= worldSpacePosition.w; + return worldSpacePosition.xyz; } - get(keyOrCrit, cb) { - if (keyOrCrit && keyOrCrit.constructor === Object) - return this.where(keyOrCrit).first(cb); - return this._trans('readonly', (trans) => { - return this.core.get({ trans, key: keyOrCrit }) - .then(res => this.hook.reading.fire(res)); - }).then(cb); + + vec3 computeNormal(vec3 worldPos, vec2 vUv) { + ivec2 p = ivec2(vUv * resolution); + float c0 = texelFetch(sceneDepth, p, 0).x; + float l2 = texelFetch(sceneDepth, p - ivec2(2, 0), 0).x; + float l1 = texelFetch(sceneDepth, p - ivec2(1, 0), 0).x; + float r1 = texelFetch(sceneDepth, p + ivec2(1, 0), 0).x; + float r2 = texelFetch(sceneDepth, p + ivec2(2, 0), 0).x; + float b2 = texelFetch(sceneDepth, p - ivec2(0, 2), 0).x; + float b1 = texelFetch(sceneDepth, p - ivec2(0, 1), 0).x; + float t1 = texelFetch(sceneDepth, p + ivec2(0, 1), 0).x; + float t2 = texelFetch(sceneDepth, p + ivec2(0, 2), 0).x; + + float dl = abs((2.0 * l1 - l2) - c0); + float dr = abs((2.0 * r1 - r2) - c0); + float db = abs((2.0 * b1 - b2) - c0); + float dt = abs((2.0 * t1 - t2) - c0); + + vec3 ce = getWorldPos(c0, vUv).xyz; + + vec3 dpdx = (dl < dr) ? ce - getWorldPos(l1, (vUv - vec2(1.0 / resolution.x, 0.0))).xyz + : -ce + getWorldPos(r1, (vUv + vec2(1.0 / resolution.x, 0.0))).xyz; + vec3 dpdy = (db < dt) ? ce - getWorldPos(b1, (vUv - vec2(0.0, 1.0 / resolution.y))).xyz + : -ce + getWorldPos(t1, (vUv + vec2(0.0, 1.0 / resolution.y))).xyz; + + return normalize(cross(dpdx, dpdy)); + } + void main() { + vec2 uv = vUv - vec2(0.5) / resolution; + vec2 pixelSize = vec2(1.0) / resolution; + vec2[] uvSamples = vec2[4]( + uv, + uv + vec2(pixelSize.x, 0.0), + uv + vec2(0.0, pixelSize.y), + uv + pixelSize + ); + float depth00 = texture2D(sceneDepth, uvSamples[0]).r; + float depth10 = texture2D(sceneDepth, uvSamples[1]).r; + float depth01 = texture2D(sceneDepth, uvSamples[2]).r; + float depth11 = texture2D(sceneDepth, uvSamples[3]).r; + float minDepth = min(min(depth00, depth10), min(depth01, depth11)); + float maxDepth = max(max(depth00, depth10), max(depth01, depth11)); + float targetDepth = minDepth; + // Checkerboard pattern to avoid artifacts + if (mod(gl_FragCoord.x + gl_FragCoord.y, 2.0) > 0.5) { + targetDepth = maxDepth; + } + int chosenIndex = 0; + float[] samples = float[4](depth00, depth10, depth01, depth11); + for(int i = 0; i < 4; ++i) { + if (samples[i] == targetDepth) { + chosenIndex = i; + break; + } + } + gl_FragColor = vec4(samples[chosenIndex], 0.0, 0.0, 1.0); + gNormal = vec4(computeNormal( + getWorldPos(samples[chosenIndex], uvSamples[chosenIndex]), uvSamples[chosenIndex] + ), 0.0); + }` +}; + + + + + + + + + +var $06269ad78f3c5fdf$export$2e2bcd8739ae039 = `5L7pP4UXrOIr/VZ1G3f6p89FIWU7lqc7J3DPxKjJUXODJoHQzf/aNVM+ABlvhXeBGN7iC0WkmTjEaAqOItBfBdaK5KSGV1ET5SOKl3x9JOX5w2sAl6+6KjDhVUHgbqq7DZ5EeYzbdSNxtrQLW/KkPJoOTG4u5CBUZkCKHniY9l7DUgjuz708zG1HIC8qfohi1vPjPH9Lq47ksjRrjwXD4MlVCjdAqYFGodQ8tRmHkOfq4wVRIAHvoavPHvN1lpk3X4Y1yzAPGe8S9KBs3crc4GwlU1dEOXiWol/mgQqxkNqB1xd04+0Bmpwj0GcCc4NUi+c731FUxjvaexCkCJ0qhrJJ++htWqetNC4NewClu8aFRSwrqiJEGe+qtTg4CYCHaF1wJI0sy/ZBQAI0qAMyBvVjWZlv2pdkCaro9eWDLK5I4mbb8E4d7hZr9dDJiTJm6Bmb5S+2F7yal/JPdeLUfwq7jmVLaQfhv4tWMJAt7V4sG9LuAv2oPJgSj1nnlBvPibfHM2TrlWHwGCLGxW/5Jm2TotaDL+pHDM5pn1r0UuTZ24N8S5k68bLHW9tfD+2k4zGev23ExJb4YTRKWrj82N5LjJ26lj1BkGZ0CsXLGGELoPaYQomjTqPxYqhfwOwDliNGVqux9ffuybqOKgsbB51B1GbZfG8vHDBE2JQGib1mnCmWOWAMJcHN0cKeDHYTflbDTVXajtr68mwfRje6WueQ/6yWqmZMLWNH7P27zGFhMFqaqfg11Q88g/9UA/FROe9yfq0yOO0pnNAxvepFy2BpEbcgG+mCyjCC01JWlOZlIPdf1TtlyOt7L94ToYGCukoFt4OqwOrofamjECpSgKLLmrRM+sNRAw12eaqk8KtdFk7pn2IcDQiPXCh16t1a+psi+w9towHTKPyQM0StKr61b2BnN1HU+aezFNBLfHTiXwhGTbdxLLmrsAGIVSiNAeCGE8GlB0iOv2v78kP0CTmAPUEqnHYRSDlP+L6m/rYjEK6Q85GRDJi2W20/7NLPpSOaMR++IFvpkcwRuc59j8hh9tYlc1xjdt2jmp9KJczB7U9P43inuxLOv11P5/HYH5d6gLB0CsbGC8APjh+EcCP0zFWqlaACZweLhVfv3yiyd8R3bdVg8sRKsxPvhDaPpiFp9+MN+0Ua0bsPr+lhxfZhMhlevkLbR4ZvcSRP6ApQLy3+eMh9ehCB3z5DVAaN3P6J8pi5Qa88ZQsOuCTWyH6q8yMfBw8y8nm6jaOxJhPH6Hf0I4jmALUBsWKH4gWBnyijHh7z3/1HhQzFLRDRrIQwUtu11yk7U0gDw/FatOIZOJaBx3UqbUxSZ6dboFPm5pAyyXC2wYdSWlpZx/D2C6hDO2sJM4HT9IKWWmDkZIO2si/6BKHruXIEDpfAtz3xDlIdKnnlqnkfCyy6vNOPyuoWsSWBeiN0mcfIrnOtp2j7bxjOkr25skfS/lwOC692cEp7TKSlymbsyzoWg/0AN66SvQYo6BqpNwPpTaUu25zMWlwVUdfu1EEdc0O06TI0JmHk4f6GZQbfOs//OdgtGPO6uLoadJycR8Z80rkd88QoNmimZd8vcpQKScCFkxH1RMTkPlN3K7CL/NSMOiXEvxrn9VyUPFee63uRflgaPMSsafvqMgzTt3T1RaHNLLFatQbD0Vha4YXZ/6Ake7onM65nC9cyLkteYkDfHoJtef7wCrWXTK0+vH38VUBcFJP0+uUXpkiK0gDXNA39HL/qdVcaOA16kd2gzq8aHpNSaKtgMLJC6fdLLS/I/4lUWV2+djY9Rc3QuJOUrlHFQERtXN4xJaAHZERCUQZ9ND2pEtZg8dsnilcnqmqYn3c1sRyK0ziKpHNytEyi2gmzxEFchvT1uBWxZUikkAlWuyqvvhteSG9kFhTLNM97s3X1iS2UbE6cvApgbmeJ/KqtP0NNT3bZiG9TURInCZtVsNZzYus6On0wcdMlVfqo8XLhT5ojaOk4DtCyeoQkBt1mf5luFNaLFjI/1cnPefyCQwcq5ia/4pN4NB+xE/3SEPsliJypS964SI6o5fDVa0IERR8DoeQ+1iyRLU1qGYexB61ph4pkG1rf3c2YD6By1pFCmww9B0r2VjFeaubkIdgWx4RKLQRPLENdGo8ezI5mkNtdCws19aP1uHhenD+HKa8GDeLulb2fiMRhU2xJzzz9e4yOMPvEnGEfbCiQ17nUDpcFDWthr68mhZ4WiHUkRpaVWJNExuULcGkuyVLsQj59pf6OHFR7tofhy9FMrWPCEvX1d5sCVJt8yBFiB6NoOuwMy4wlso9I2G4E5/5B2c6vIZUUY9fFujT3hpkdTuVhbhBwLCtnlIjBpN4cq+waZ0wXSrmebcl+dcrb7sPh9jKxFINkScDTBgjSUfLkC3huJJs/M4M8AOFxbbSIVpBUarYFmLpGsv+V6TJnWNTwI41tubwo7QSI1VOdRKT/Pp8U3oK2ciDbeuWnAGAANvQjGfcewdAdo6H83XzqlK/4yudtFHJSv9Y+qJskwnVToH1I0+tJ3vsLBXtlvMzLIxUj/8LcqZnrNHfVRgabFNXW0qpUvDgxnP3f54KooR3NI+2Q/VHAYFigMkQE5dLH6C6fGs/TKeE6E2jOhZQcP9/rrJjJKcLYdn5cw6XLCUe9F7quk5Yhac+nYL5HOXvp6Q/5qbiQHkuebanX77YSNx34YaWYpcEHuY1u/lEVTCQ7taPaw3oNcn/qJhMzGPZUs3XAq48wj/hCIO2d5aFdfXnS0yg57/jxzDJBwkdOgeVnyyh19Iz1UqiysT4J1eeKwUuWEYln23ydtP7g3R1BnvnxqFPAnOMgOIop2dkXPfUh/9ZKV3ZQbZNactPD4ql5Qg9CxSBnIwzlj/tseQKWRstwNbf17neGwDFFWdm/8f+nDWt/WlKV3MUiAm3ci6xXMDSL5ubPXBg/gKEE7TsZVGUcrIbdXILcMngvGs7unvlPJh6oadeBDqiAviIZ/iyiUMdQZAuf/YBAY0VP1hcgInuWoKbx31AOjyTN2OOHrlthB3ny9JKHOAc8BMvqopikPldcwIQoFxTccKKIeI815GcwaKDLsMbCsxegrzXl8E0bpic/xffU9y1DCgeKZoF2PIY77RIn6kSRdBiGd8NtNwT74dyeFBMkYraPkudN26x9NPuBt4iCOAnBFaNSKVgKiZQruw22kM1fgBKG7cPYAxdHJ8M4V/jzBn2jEJg+jk/jjV4oMmMNOpKB5oVpVh7tK529Z+5vKZ0NSY2A4YdcT0x4BdkoNEDrpsTmekSTjvx9ZBiTHrm9M/n/hGmgpjz4WEjttRfAEy5DYH5vCK/9GuVPa4hoApFaNlrFD/n2PpKOw24iKujKhVIz41p1E0HwsCd/c17OA0H0RjZi1V/rjJLexUzpmXTMIMuzaOBbU4dxvQMgyvxJvR6DyF3BaHkaqT4P3FRYlm+zh8EEGgmkNqD1WRUubDW62VqLoH8UEelIpL7C8CguWWGGCAIDPma9bnh+7IJSt0Cn6ACER2mYk8dLsrN70RUVLiE0ig+08yPY9IOtuqHf/KYsT84BwhMcVq7t8q1WVjpJGNyXdtIPIjhAzabtrX03Itn29QO3TCixE9WpkHIOdAoGvqCrw1D3x9g9Px8u0yZZuulZuGy0veSY34KDSlhsO1zx2ZMrpDBzCHPB4niwApk6NevIvmBxU3+4yaewDvgEQDJ6Of5iRxjAIpp9UO8EzNY4blj4qh8SCSZTqbe/lShE6tNU9Y5IoWHeJxPcHF9KwYQD7lFcIpcscHrcfkHJfL2lL1zczKywEF7BwkjXEirgBcvNWayatqdTVT5oLbzTmED3EOYBSXFyb2VIYk3t0dOZWJdG1nP+W7Qfyeb8MSIyUGKEA57ptPxrPHKYGZPHsuBqQuVSrn0i8KJX+rlzAqo8AawchsJ26FckxTf5+joTcw+2y8c8bushpRYEbgrdr64ltEYPV2AbVgKXV3XACoD1gbs01CExbJALkuItjfYN3+6I8kbiTYmdzBLaNC+xu9z/eXcRQV1Lo8cJoSsKyWJPuTncu5vcmfMUAWmuwhjymK1rhYR8pQMXNQg9X+5ha5fEnap+LhUL1d5SURZz9rGdOWLhrMcMKSaU3LhOQ/6a6qSCwgzQxCW2gFs53fpvfWxhH+xDHdKRV6w29nQ6rNqd9by+zm1OpzYyJwvFyOkrVXQUwt4HaapnweCa7Tj2Mp/tT4YcY3Q/tk1czgkzlV5mpDrdp1spOYB8ionAwxujjdhj5y9qEHu0uc36PAKAYsKLaEoiwPnob0pdluPWdv4sNSlG8GWViI+x/Z4DkW/kSs2iE3ADFjg4TCvgCbX3v0Hz0KZkerrpzEIukAusidDs2g/w0zgmLnZXvVr5kkpwQTLZ0L6uaTHl0LVikIuNIVPmL3fOQJqIdfzymUN0zucIrDintBn6ICl/inj5zteISv5hEMGMqtHc2ghcFJvmH3ZhIZi34vqqTFCb9pltTYz582Y3dwYaHb9khdfve1YryzEwEKbI8qm62qv+NyllC+WxLLAJjz0ZaEF2aTn35qeFmkbP6LDYcbwqWxA0WKsteB7vy8bRHE4r8LhubWDc0pbe90XckSDDAkRej0TQlmWsWwaz18Tx2phykVvwuIRzf4kt9srT8N7gsMjMs0NLAAldabFf2tiMoaaxHcZSX51WPc1BrwApMxih227qTZkcgtkdK1h314XvZKUKh/XysWYnk1ST4kiBI1B9OlfTjB3WHzTAReFLofsGtikwpIXzQBc/gOjz2Thlj36WN0sxyf4RmAFtrYt64fwm+ThjbhlmUTZzebLl4yAkAqzJSfjPBZS2H/IvkkTUdVh0qdB6EuiHEjEil5lk9BTPzxmoW4Jx543hiyy4ASdYA2DNoprsR9iwGFwFG3F2vIROy4L5CZrl230+k733JwboSNBKngsaFPtqo+q3mFFSjC1k0kIAFmKihaYSwaSF7konmYHZWmchuaq15TpneA2ADSRvA07I7US0lTOOfKrgxhzRl0uJihcEZhhYWxObjvNTJ/5sR4Aa5wOQhGClGLb746cJhQ2E6Jie1hbGgWxUH7YSKETptrTeR/xfcMNk2WM12S0XElC9klR8O7jLYekEOZdscP0ypSdoCVZAoK+2ju2PHE869Q9rxCs9DVQco4BriiPbCjN/8tBjsah4IuboR5QbmbyDpcdXVxGMxvWKIjocBuKbjb+B4HvkunbG0wX0IFCjQKoNMFIKcJSJXtkP3EO+J16uh4img0LQlBAOYwBLupu5r1NALMo0g3xkd9b4f7KoCBWHeyk24FmYUCy/PGLv0xErOTyORp8TJ5nnc2k1dOVBTJok7iHye9dwxwRVP3c7eAS8pMmJYHGpzIHz6ii2WJm8HMTPAZdA4q+ugj3PNCL/N45kyglqvQV4f/+ryDDG5RPy5HVoV9FVuJcq2dxF9Y0heVoipV6q1LyfAeuMzbsUV+rsSBmCSV+1CdKlxy0T0Y6Om0X6701URm2Ml6DIQgJ/3KO6kwcMYRrmKsY7TfxWhSXZll+1PfyRXe9HS0t1IKTQMZL7ZqQ8D/o+en57Y9XAQ9C+kZYykNr0xOMxEwu2+Cppm69mQyTm3H7QX6kHvXF201r+KVAf354qypJC5OHSeBU47bM1bTaVmdVEWQ+9CcvvHdu8Ue5UndHM+EeukmR82voQpetZ7WJjyXs+tPS60nk09gymuORoHNtbm0VuvyigiEvOsyHiRBW7V6FyTCppLPEHvesan91SlEh1/QEunq+qgREFXByDwNKcAH5s8/RFg8hP4wcPmFqX0xXGSKY087bqRLsBZe52jThx0XLkhKQUWPvI18WQQS3g2Ra1pzQ1oNFKdfJJjyaH5tJH6w0/upJobwB8KZ5cIs9LnVGxfBaHXBfvLkNpab7dpU6TdcbBIc+A4bqXE/Xt8/xsGQOdoXra4Us5nDAM6v2BNBQaGMmgMfQQV+ikTteSHvyl8wUxULiYRIEKaiDxpBJnyf9OoqQdZVJ8ahqOvuwqq5mnDUAUzUr/Lvs1wLu2F+r4eZMfJPL4gV5mKLkITmozRnTvA7VABaxZmFRtkhvU5iH9RQ1z26ku7aABokvptx7RKZBVL6dveLKOzg0NC7HAxcg5kE1wuyJiEQLOpO0ma3AtWD2Q2Wmn2oPZeDYAwVyEpxuwDy7ivmdUDSL95ol3h2JByTMovOCgxZ1q4E5nwwa7+4WtDAse6bDdr27XgAi5Px3IWbyZ/vRiECKwOMeJSuIl8A4Ds0emI3SgKVVWVO5uyiEUET+ucEq0casA+DQyhzRc8j+Plo0pxKynB/t0uXod1FVV4fX1sC4kDfwFaUDGQ4p9HYgaMqIWX3OF/S8+vcR0JS0bDapWKJwAIIQiRUzvh5YwtzkjccbbrT9Ky/qt5X7MAGA0lzh43mDF9EB6lCGuO/aFCMhdOqNryvd73KdJNy3mxtT8AqgmG4xq7eE1jKu6rV0g8UGyMatzyIMjiOCf4lIJFzAfwDbIfC72TJ/TK+cGsLR8blpjlEILjD8Mxr7IffhbFhgo12CzXRQ2O8JqBJ70+t12385tSmFC8Or+U8svOaoGoojT1/EmjRMT7x2iTUZ7Ny02VGeMZTtGy029tGN1/9k7x3mFu63lYnaWjfJT1m1zpWO3HSXpGkFqVd/m3kDMv4X9rmLOpwEeu8r6TI6C2zUG+MT6v90OU3y5hKqLhpyFLGtkZhDmUg/W1JGSmA8N1TapR4Kny+P6+DuMadZ9+xBbv06nfOjMwkoTsjG0zFmNbvlxEjw+Pl5QYK+V8Qyb+nknZ0Nb/Ofi9+V0eoNtTrtD1/0wzUGGG5u2D/J1ouO/PjXFJVx6LurVnPOyFVbZx7s3ZSjSq+7YN3wzTbFbUvP8GBh7cKieJt56SIowQ2I577+UEXrxUKMFO+XaLLCALuiJWB2vUdpsT+kQ+adoeTfwOulXhd/KZ7ygjj6PhvGT1xzfT7hTwd6dzSB4xV70CesHC0dsg2VyujlMGBKjg5snbrHHX/LNj3SsoLGSX+bZNTDDCNTXh+dCVPlj4K8+hJ/kVddrbtZw26Hx5qYiv3oNNg5blHRSPtmojhZmBQAz8sLC9nAuWNSz1dIofFtlryEKklbdkhBCcx5dhj7pinXDNlCeatCeTCEjYCpZ3HRf5QzUcRR1Tdb3gwtYtpPdgMxmWfJGoZSu1EsCJbIhS16Ed97+8br4Ar1mB1GcnZVx/HPtJl4CgbHXrrDPwlE4od8deRQYLt9IlsvCqgesMmLAVxB+igH7WGTcY/e3lLHJ4rkBgh2p1QpUBRb/cSQsJCbosFDkalbJigimldVK7TIHKSq2w8mezku9hgw8fXJxGdXoL1ggma52kXzjP78l0d0zMwtTVlt0FqnRyGLPGEjmICzgSp7XPFlUr7AeMclQ4opqwBFInziM5F8oJJ8qeuckGOnAcZZOLl1+ZhGF17pfIuujipwFJL7ChIIB2vlo0IQZGTJPNa2YjNcGUw+a/gWYLkCp+bOGIYhWr08UIE709ZEHlUoEbumzgpJv1D0+hWYNEpj+laoZIK5weO2DFwLL6UBYNrXTm9YvvxeN9U9oKsB3zKBwzFFwDgid5ESMhy68xBnVa55sCZd+l5AnzT8etYjIwF/BGwEx1jjzFv32bk6EeJulESARh8RZ48o7rKw67UZpudPa15SDnL8AL8xMV2SC0D1P53p190zhCFkMmEiir2olwxcJppl/kLm6/0QSUQLNaxi1AC3Pg1CTosX2YQr73PjEIxIlg4mJ62vP7ZyoHE55B0SX9YrrrCPtNsrJEwtn6KOSt7nLT3n3DLJTPbLulcqQ1kETP6Huts29oP+JLEqRGWgnrqMD+mhCl1XCZifjgQ39AeudE8pyu2DqnYU3PyPbJhStq1HbP+VxgseWL+hQ+4w1okADlA9WqoaRuoS7IY77Cm40cJiE6FLomUMltT+xO3Upcv5dzSh9F57hodSBnMHukcH1kd9tqlpprBQ/Ij9E+wMQXrZG5PlzwYJ6jmRdnQtRj64wC/7vsDaaMFteBOUDR4ebRrNZJHhwlNEK9Bz3k7jqOV5KJpL74p2sQnd7vLE374Jz+G7H3RUbX17SobYOe9wKkL/Ja/zeiKExOBmPo0X29bURQMxJkN4ddbrHnOkn6+M1zTZHo0efsB23WSSsByfmye2ZuTEZ12J3Y8ffT6Fcv8XVfA/k+p+xJGreKHJRVUIBqfEIlRt987/QXkssXuvLkECSpVEBs+gE1meB6Xn1RWISG6sV3+KOVjiE9wGdRHS8rmTERRnk0mDNU/+kOQYN/6jdeq0IHeh9c6xlSNICo9OcX1MmAiEuvGay43xCZgxHeZqD7etZMigoJI5V2q7xDcXcPort7AEjLwWlEf4ouzy2iPa3lxpcJWdIcHjhLZf1zg/Kv3/yN1voOmCLrI1Fe0MuFbB0TFSUt+t4Wqe2Mj1o2KS0TFQPGRlFm26IvVP9OXKIQkjfueRtMPoqLfVgDhplKvWWJA673+52FgEEgm+HwEgzOjaTuBz639XtCTwaQL/DrCeRdXun0VU3HDmNmTkc6YrNR6tTVWnbqHwykSBswchFLnvouR0KRhDhZiTYYYNWdvXzY+61Jz5IBcTJavGXr9BcHdk/3tqaLbwCbfpwjxCFSUs1xfFcRzRfMAl+QYuCpsYGz9H01poc1LyzhXwmODmUSg/xFq/RosgYikz4Om/ni9QCcr28ZPISaKrY7O+CspM/s+sHtnA9o9WgFWhcBX2LDN2/AL5uB6UxL/RaBp7EI+JHGz6MeLfvSNJnBgI9THFdUwmg1AXb9pvd7ccLqRdmcHLRT1I2VuEAghBduBm7pHNrZIjb2UVrijpZPlGL68hr+SDlC31mdis0BjP4aZFEOcw+uB17y5u7WOnho60Vcy7gRr7BZ9z5zY1uIwo+tW1YKpuQpdR0Vi7AxKmaIa4jXTjUh7MRlNM0W/Ut/CSD7atFd4soMsX7QbcrUZZaWuN0KOVCL9E09UcJlX+esWK56mre/s6UO9ks0owQ+foaVopkuKG+HZYbE1L1e0VwY2J53aCpwC77HqtpyNtoIlBVzOPtFvzBpDV9TjiP3CcTTGqLKh+m7urHvtHSB/+cGuRk4SsTma9sPCVJ19UPvaAv5WB8u57lNeUewwKpXmmKm5XZV91+FqCCT6nVrrrOgXfYmGFlVjqsSn3/yufkGIdtmdD0yVBcYFR3hDx43e3E4iuiEtP3Me9gcsBqveQdKojKR//qD2nEDY0IktMgFvH+SqVWi9mAorym92NEGbY8MeDjp553MiTXCRSASPt+Ga5q7pB9vwFQCTpaoevx0yEfrq9rMs3eU6wclBMJ9Ve8m6QuLYZ58J41YG3jW/khW92h6M/vbFIUPuopZ6VVtpciesU74Ef7ic8iSymDohGeUn4ubT0vRsXmbsjaJaYhL8f+8I5EiD5l680MJbxX/4GYrOg4iPQqpKp0qddSu/HKtznHeVyxgTwhfEORMCwnaqetVSzvidaWN9P+fXtGXfEP9cTdwx2gKVfDdICq7hecgRhIs0qlCt6+5pGlCc6kWoplHa/KjP+FJdXBU/IDoKMxRjFhSYkggIkhvRKiN/b2ud8URPF+lB87AGAwyMjr/Wju2Uj5IrppXZWjI3d14BdKE2fhALyQPmHqqA+AXd2LwvRHcBq4mhOQ4oNRWH7wpzc6Pggfcbv9kqhLxrJKEaJqA6Rxi+TDNOJstd5DoRVCDjmVspCVyHJsFEWPg9+NA8l1e4X2PDvOd5MPZAGw6LRhWqeZoSQcPf9/dGJYAyzCmttlRnx0BfrKQ/G9i5DVJft9fuJwMi3OD/0Dv1bRoxcXAyZ0wMJ6rwk9RjRTF4ZK8JviCCNuVt/BqQYiphOzWCpnbwOZt6qXuiAabQWrS4mNXQ7cEErXR/yJcbdFp5nWE1bPBjD0fmG3ovMxmOq5blpcOs0DtNQpci1t+9DKERWAO53IVV/S4yhMklvIp0j0FIQgwjdUptqmoMYGVWSI5YkTKLHZdXRDv9zs+HdFZt1QVcdlGOgATro3fg6ticCrDQKUJC7bYX50wdvetilEwVenHhlr85HMLRLTD6nDXWId4ORLwwe5IXiOhpuZTVTv+xdkTxJofqeCRM/jcZqQlU0gFVTlYlfwMi6HKR2YG4fQ8TOtgR+yV+BMZb6L5OwDc/28/xdfD7GXFaVA2ZSObiIxBwT2Zev637EuvpM6rxcogdM4FJFa0ZhF7nrqtNsqWg5M7hZMORpjd4szf/wS+Ahs1shY54Ct5J1dOBO4sdEtSnRc0P9PhgyOCt6aQW98R22DpAcNTDe72AHK40vutKTPfpokghRPuGvz0dulBPKfC3O4KVDCyWrJGO7Ikdu06A0keKlVfi0tGcpO0NhzXEh75NHyMysAMV19fq7//sPC0For1k2uFEvq8lwrMAfmP7afR69U2RqaILHe7glpc8HmVf87Qb2ohsw+Di9U+ePdHLecS66MhB/0OwdcXR5WBcWTZLGq/kiAaT+bzkjR8GIpWdv6pfIgQ+Q0xdiKvo+gNB7/Nf9knNJGxnh7LeZEFtMn517tNc74PPS0M4K3I6HHZqNPA+VZcBc/g5a2ARyqKrJ4Z3krsuA+VOJJz2KJpBMgCCWFln3u7k6/q3DETAubKG/pt3ObaNT0NI0Qug90L2ip5dHnZJUjPTvK5E96aX/4mRU2u8n8kh6MKbY7ANBro3huF06U+JvfyELQP25oIaj+n0ITQ4KT9rXZD4EtBIOj95fYNldDN3io/VMIvWNj9P/b95WEMq8UAVfG2XG0N6fSYdnBEC7sUEbatbDICH9qA8TTuW9kEt9DlFOZFP7bdfYLa/khSY8W5K/AkIIAPXtMvyVKyESjKx9nfragssxC0jFMVY94d8lOAwRocdS/l/P43cBGa3IqDa0ihGPcmwS8O8Vj16Uy55rOrnN0shhRJZdW8I7F0Q0KeHc35GFo4aJOFc25gNafBu1V/VO0qS4Qkb6wjRrnlepUWjtYyaDABZceValuOMtoDdeIITWKOJiwGPpB12lQgwkmXh9M86podb0D117mNQ8ElluFvbaS8RTKQ6lyj88dUwoJU/ofOeubhoXWBF8eNumkVJu+As3ED/AvLlrV91UowIWI2m8HBG+a3k247ZKAGYsOcWe7fTWqL8eqwM5ZFuoXbeugPKuMOAtOsN+4dSwkhrSAlfGNTzFwEmCNWtzpa9CgPbYNcmoHtO8pj8qMvlGET6nrkJoQ2lp5MEUV1E2A4ZH70JUlCLXvqTIpZlzyxdr5p/GZiD1/BuFOGbyfFzhuxaC/l3lC2jjt6GNRBa06AqqPlYtdA7kiidYa5Qi0/XpXiMDyMXNOj3kmJEaXufW0GO8+DF8OoMULX1vvjCePKNis4AmxQKLCF+cjf/wyilCJvuiyLVPSdsuRTPZ0AhpdDF/1uFmDwG7iP3qYwNsKzqd3sYdnMolCOuQOIHWy1eQpWhuV+jmSeAC5zCc0/KsOIXkZPdiw8vtB33jEBpezpGDBP4JLY2wH1J7Fzp8y8RICqVd25mDT2tDb/L1mh4fv9TOfDH5dTeATqu+diOZi+/sIt18hiTovPsVQVaqXLPRx/4R/uH/86tBMcF+WBkThKLfblcVCIECc8DgNRVX97KdrsCeIK+CvJZMfwrftcDZDZyp7G8HeKl7bPYnTKX88dXAwAyz66O2chkPDHy/2K2XcT/61XnlAKgPwtI8yP9Vu45yh55KHhJu93mL4nfo8szp/IyDjmFHtSMqqoWsj8WaVhbjXgzZxcqZcyOe7pUK6aXF/Y32LnBOt0WN28UmHRiOpL525C63I2JQPX8vvOU0fz2ij74OeJ1Apgu3JRObfdo9xGDpp7cv3TdULEfNS6Gu3EJu7drBsBsogUqUc6wAUW3ux0/1hLVI/JEKJrAGm8g72C2aJSsGAsKFW4CBvBXVlNIKa5r7HvT1BeGYBfxTR1vhNlFFNN8WQYwr39yT/13XzRGiF2IsfE8HcN0+lN1zN/OnzekVBKkFY11GgrK5CLxrE/2HCEMwQb9yOuP2rTXiZzTEETp/ismFGcTWmbM9G1Sn2D/x3G74uWYZY4rgKB2Zo2bTKS6QnM5x1Yee66Y1L7K44AyiY5K2MH5wrTwxMFh+S8LzNQ25z6sunWZyiRwFIIvSnioltUXNiOr+XMZ6O9h9HcHxZJkfF0tUm6QkU7iJ2ozXARitiL86aqVsMOpmvdIBROhUoanPtCjgft8up3hAaKpw9Qs9MzYtBA2ijHXotzarkV3zKEK0dFFQUwT74NgCmGGuSCEDmFCezXPC9BhyGhmzNa6rQeQQz+r9CmGUZjIQEPsHwe86oCOQhWaHERsv5ia9rZvJ//7UXO7B329YUkLLAiqpLRsVV5XpcfdawlJqi/BVcCqO6dr9YJTFFRMVGhfUbB9YWNvYPY6RyaydAFYq1YIBQxuNAGfYWLMAHtt2XRHoOKCLz+qf5HCVBDOPOktQ3SdJBfxUkaiD585bmTzMwU3oeXUHZ55EC99Kz9kk4ZXMIENwVVpqW2JmGIcUiutIMj2KkpjE2QD+dIZUCxcX57kH7hiuUPnKCTdaw4KN95XPeFRvMcvo5L8LexWqvaJPECzwXCs/4XPAlSMpWUzBBjK3pEnkbueMkMJQrYcnXf7PjbAoJra1VLX4YuscQLpaeYWbT+h24hCFrfcHjxxx6WTSe4AGY/KHRZCQKqTuFWt0D8RmGWmvXSdg1ptIefYPshuIVZT7CV4Ny67fvjJugy0TNYHqoCO45CB88kxrvIsih19DqjD0UqiJsTFPcGW3P/ULOG3nb8CjpgVTIoa5nO9ZYEX4uEHu8hLXrJPjV1lTQ5xTdZVagg+Wj8V0EE4yPsTc345KM6lVXqLiHtm+G6edC4GVEiPgd98g+twSYm18gCsPnjqlLcFm9e72CLJbYD+ocIZOxuVjrX6IKh9fh7WqdIZ66x9PWkDGOVVGkx7jM76Ywe16DX9ng205kg5eq+R2q2MguTJxYv/wWHliD9mOYpzZKNXYC3Wr4iBGkm54hBwkPzFhiX/VBHdVH/KJ1ZIMOHxIN6arKdxrm6EBsgwDt0mPe0MX1HRUMq8ctcmysU6xX0bzM1J07kAvq33jw1q0Pq2cyMWme8F7aVkfhzZEFdyi8fVBQav0YZqvAjZ83WKH726rBx5Bn7GHFthR6H4lFsltu+jWmsAibJ3kpWMG/QbncU7n9skIBL0MuXXtj9sJg+4Dl0XhKJ1LcrMydaIgyrgZgScP4k8YQvcsBmD26X1iYXKLzMYfZn2IfRjznsrJ1e5cnl/3a5xiNoI6n1x1U36FWckJbyx+hiSZg0QqAqeeSvzFYMlZ2REnO/a6yoQhu7PdHMYEPFIvfyGeyCU8e7rpju4DrlOhszj9rOIpNsvCkuD+TLyf5J7D/wsPkBpscFVI1q7oUSU9bN30vH5AqnO7bsf+9rGhtVjOJQ32H9hHSAzR2ape4L0Cz4WxaySm4jvuGXwkFp5NMMLrgZ8LdA+5uLuyxO5SMOmJNDBcbbLefv7z6LyxBwltnfQLd7qqpG1MmNcoLUcx73BkNF/xpdS0cKd6G646ntChXSeTZJJTFYGw39T7fqXDPKoG2cF7/ZcTvME42gXLVjTqzAER1Rt5m7GYsh0X0+XgOeW9MJqE5j/rpGzY6vUu6ACcCTzDMdZHiWELpDnvgE1hmztLcSYz0MtNyUBLqvylUJJnJu79Sku9NMHCTkgqozTnhMFfduV2NLCSYvAI5HUvQp1h/M02vKFD6eosIkGTg6mujUo1W8hy5Knf/erkBQC9LzNqPAYCgR+hczgevta88NNqSlBZryq9QNeUK7RpbvHjoNhUKAAeNYH55LeTW36KyFaXdAkBvyNP9xmRuBokPi2OhqDby6IZ61mwfzG+GmACkS+G80A4WGON5izgJWeeDK91jzusfOi0RmEsVJXwbVUr8u/J2LCQaMnHhi+wJTEPN9tS2b6W4GRGCNmtjAMgPsP357nOeD3H2tcDAPu5xQBKMHf/j4ZhXlkvvy3YmBJsjsd4pSOlfPZCnw5JvzxEXM5JIc+E2mU4CgB0mdJnH4NEsCHYNeVRDXFNuyZUE4nuvaJf1h+11AWLdAZ72D9XNRcxfb2+XHZN/SN48U7yl+sNZhg5gn/PD8wkBtnRj1zBUPIWnoMP6yGUEEzuT+VaX3x2jEIZAZsr3rs9wCfY1Ss0EdIFFzBbyruUup4EPanbSYew5tf16/ZWVup5iykttuqL4xoC/jdZWsAZeSfDSd3fP9kbyAFYXkf0Q2lmxaTkKRZrCo9XCoiUG4yP1URJ5G7+HSOhhJp0Anz0N07QZtyFUye6rcgiOFbtyoO1lkuV0iQ602MTyFK9xLqNHtNy4cJaTO6hjtiwNynVc34ZA6H7k8ai6S6eF6jIG0xJx+JfP97lzuCZr8vU5SIzImaNpiQhyvDbz23//PJcOk7hD4iIvJzfIgOGIR6ZPEJpWHZQoacbF+omeHw8aWHaNOfaIyGeG4lEryMfhtNmWh4RAIpn8dLs7ZE2eTVDwK++xDoSUgh47WDmKlZ/k6OosEUoQjk7Q+Kp7OxwgMFShAv6z4pTW8loVj2+qXLQ0T3hmIue8qHy1o/HXjm089m71t6mrrUyDftqMYtmfvQXKDlZ+K1HR/FkqPSqcjGlcPPIwbMw3wIFKBdVMJ4pFLt+oOIkWZMw8pkoYZ3byw4LmAF+7BdicGXFcb5PWtDw5XNNVc6eB9dv0rAEpgr5J+bLr010bpfGw+IkRoxDbkDFmQdEQUSElP5bViLo1ur/23KN0jEwl+rGC6AUMKxHcv+T9F1Ktpn8jSSrKxJnVkK8UD/tH5DN6nXB8mjUdFU539e9ywLtLYCwmHYVEVqnFmdubduaSd1ivIo4pTsX+mJcOAkrR1D60RIoocCBIdwJhCBM1rOE2XSlPo0U+khALvw+zfxYzwzd4roWlLJkZheFRR8QB8v4USwmAcDswUZ2P/7v7Xa51Fs7orYebYyww4YW5869Y/c6Kq2eTR9HLSjYuChTkXaDygoo8nz/yJ0KzfX8oowaNAwz8HvQdlLU9V9hjqYMURyYvPzZ60G0itmUdZwB+sY6rUkMAZZtWStbDFmnk/dQorhwr3121XQWffrK3as0g29ASwxbsZ3dZAq/96b7/XWckbjmo8+jwdE680DzoEUUivnBgowMuBQxHXoGyp+w/cSGY88rWtmwoyNNIvChs/QsZRnbdV7y8x7t2RkliJV/j8e6qfctrTsMV22zoqgQuTSNFh7U7p/Q49L0kygXNnEYXCBDgi5BeNWxu7VjULcUHI+lGj+OTCEATzWrDmaynq3wT9IAejtvh3esCu6sEu9JOsXxMDpqxm4Tzl+pt2Wa5Bq3TM5TKH4N7KLir8FGIPA569+uJ1VEL3fW8Jyigz/nEUjAVYrdCWq2MnS4hQVgcvXq9aF7Xke/k++rAtIQqckPNwjKrV2t7HCOrA1ps88Y5Rw1Zp+9itnB71j8tNiQc7mV1kUCQXkoi5fOsq1uC6hUPUL7Z69NAM6lg0c/aeiifHoi35v+pVBh7CDM1XfvYpiK5JIbIQFHafmnhHfRTnMagKcjdE7zzgtxkTPKVrObTySTT51g9bB5ro/dzn/sB24fNM2LGJuRQsmC49PLi1jTRfZaLpo8Txxxczij5Pl2vur+S1wQW3W5qyVcIUySZHtFDQHv+EYDoZG1T1J7D91vEIV8dHzUBzW1UyuxRbP+M/CM/vsas6RzmS5traXnQ0Jzv9hYXxKHcs15TQCP744XsLjzFjILYURXFnhM+nnV0iO6nwls9TR4tlz1J9/NvE8FGg5mgpZA4htS05AK0NnU2gxuqf2vjCyWlm3ypKvaX4vxh8Um1MHGB2NTeAFhbDyGm+5w2zqJAWxVlj6dVePb5yR+aMhuz05YubCQJ0BOtoYQ6PoDoW5fCwCtXj5SHvCgL/3B5z2mcXWaRTf8/GsFAfX/ntdWZWFc2xg8MJeenwZ4dZUToce43If4zVb1ex3BMAWGhgkPwR5EgktZhW3Yi+nsnZTUr9FYI160YhAraB0zMV+ouHz6hYm25/ETDM0MTmcypoGgZISSkfwYAQaHGY45yZ91K4A4Mm4fnbMk8GTc4orypT3NLBqAxYdcY/qCH82PpIkmVOEHi1NoYaUymuImLLcib5pmd2MHTB3JR+4rLdRc3gtQ9zeFdciciRiWviu3HkqaLSxJeI2rgc7OKQslItumACQow89elXmi4P3gTZeCauvMH5nF4VrBcLjjwGD+KlKqe/RWIEgT2wGqAgSuL6b+RTTPnQZzxZ5y5HQJkEEKJp5NfoB8hJBM8qn6xbOFtyzBjVBrwSS1zCJR3lEc9ODQ5Wu/xct9/2Q6qLHnmNx6XwZus/i8rEd6UsVxGtoDrm+Br0L5oUojlwdcqyVV4PIMsR60JhZwJtgX7izQWj+GOeF9DA8Wexdmv6DWjgR8LEBp9YuPAM8tJDu3uCumNqHnF2ATYX/tuVO55OgQuiUhmDmJbF9jJyifBRtxOVI9DCNLUY71IXZYTuiYcnILQ/XHuVJ8aHDStL0N+3eYNvXwHi2vEiTPnBqzsC4TsPnFVnYY042j5i7C11AVdBZ1pGSa52jM9dIL119rry0mgGxFzI8xPs+7bmMfYKh37A4HtA081olG1m9S4Zch2hoNCGVvVhd6UL7C2d5hKIBHoB+Uxarq/4aQXhh7IWjSj+ca7Vhqb4+ZwY3nHXh2S9JH4XZxQojbe/eINxYlozTYtT2rpU/xbj+W2hXjFQ+z+dQ8wh9751MP0UpjutQdxz3/FJYAEG5BF400JXWCBs7KrCRf/l+F+d9EuwVk6thOPDB+HNS9iWlLmDgXvY6K0vgiyoeA3An+jWufdAG1suUMBuJT+/w0FNJZbObUT8c5q5WtQxASQF6E+/u8UwVBs1eo8jTamCrcdhZJlADJbqn3crcDHQlBQNGq7btcGKiJXW6q0cn3F0xzf+k1JJS2testB3rx15ZPTDXm8QV5XE2qxBOdM2n6t5YbxyNOmEdsHx+hMp+y9pWkcgw1NikeXuafJvzcjaNwE1Ad6gG79S68aO7jWpKgBETYLmV4ONHhBk7Be8tjf2WVvWMDQvQdOnk448yeMv1tQKU1xev0L171e/qxkMZbmkfKnd29XRCK2hgNNJhwt1qiYWZGKz7Di6K3fGDT7DO2YQ7WU33svE/WKGbWQEvzUV2w+VNYDocI4yxQ6i3i4zU2TjmjCwu5Pk+Ja9HSwLpEoUswq3tFJ1jimthgMXd7KjSl6Qd0K+vxWT8G4/+xITHsWDGSfQTSdFQth5uVVfa8wrkDZHTGVgpJys2ik+3I0dSf6TNo6A/sVptyY/kx1hdAWKPI6t/xj6s+fPMU3hg1vkEB0RRHq/tCy3KUUhzU/d0JKxTyjvUms5iy1GbOFco0NA4t83SK9sBmtLWm4kOLLflyxqgQYP08iyXwYXzKnlQ6VTipuaspSJ9g5H5Lu3eLMnPKbhcwuEg0VZ80ppJWjUnhS3rL35erzysp+fJhxsUs86m28/UwW+IgrS5Y0zWaxlFJ8xML5wk8sg1ragF+eNajyI0Y4mwStxt1RZH2BjaAhvu+SnNNIK88thEgZEsoHv+ii+OMmXJL7dnAiINVDz3tCnqDgpQX9OguNGgZj3axcjq1UgxDw785yNIpqNiLgv57399jVmJ0/RStNswaFIs6FtnkilFZldxj6m562jL4p5g3Y9XCiXRJX6nq2PGJFifFR7EyPG4jDMnBM4t+O8ZpEp3th7TCxEw+ZG4afHl4sNFaqxyLh6+979tt0Aq9BrqI+CS2U7HJoKiGmyVU1lFa3/0O5mNC1bzRgNMy+GXyifLwJP7FwUSUmxmVRpn+gnXWoIuswPutsiciurvN6lsMG7yqEc2Y5ZI3jrPgPq0xEKPZpF7teJa0TQn8BQL4Th+hjv2ByfwKookyXEmj0d1KMcsmfKaeKK3cZZubiYqmSCrnGpYTwgPk5itKucVtjViuswQsDR6TuyGSIHYvlz7wkLg1Rr0K9kV1o8RgABlhbLrN74cVWJW6TnfXN0q12JFMpUbEa8t1+j440FA+17o8qa8PQ9igkctVROVIfB3jU5vtGm5pYYHYSDvU2TEc15pIz19ka1q6c/7WXfF8+POkApdOw7nn7Kqz6V4tru7NXgnA/u0g6+fPRT3hp/QrDQwMsjwNCZxdWrR6pgCBDJNc7/KAlwC0UZ4yWQs0KsuwbbOgcTxQPK54wiXr7s+221hzZ8RVxfoRUKM3e4lpxHC83JllxlrV760tl06f7/65qhE1jhMfivAUXIXfRMe3uY/G2TpWYzDrw5Cm5cS062Bx9lhHq9gtJp8xZwAtSdSuW/Kd7+orEAiswA76N8ezmVGYgNaYlQ/xk930LAWAtKVBC4U6R08L45IohB1kFia7XJs0TcaT2zBZoLFuOGu4iJaoAnfjL3uS6gnRH7G7A+aT6ETlmkYUfgrBuaSLLDJfhPJe01PfN0oqBTeQURasl3N8BZiQSgdr0aDv3hPTiog4NSyfAUyy98WP7dnTDWQTY+Qwzgk1uxwRqHl5MpC/84Cuw1TXfRlgJrwPop10kCHjmffnFdxCe2J3R3J5j+3H/sZn3IUu3Suy+I+dAOMWvzwExNR3RRPVelZAhtarKlXPWNjPRIVP4JsAFSRXs3o/fSYAPaV/zP8q6DltH47/rYhCLdy/LrpOsbaLf09eACcClJosNefetNElkSFSuCgeY7oTAAl+8Y2zOXJb/bgEDpoDXfQqc6lnlBr/WsmVznkBS1M7ufiqpxvKXjwvR4WxLbh5NbMNy8LsnX4UiuAi8XonbSUcVZKQOWBYUecSOMj6jMG8gHu7WNreBHY90lV7FocDprSrSbexkAtMW9KlXcnrOyLnZdodGYdxz8aw71HztIqLhRdCOB6NyzHPoS2hDy6wLk0I5Jr2t+U0A+A7EsgSn/Ih03A5CspHnVF4MOic+Lck3m61Um+GHDEe4DrHBhmgtDlRQl1XJ/V/VumCHtUDDcZCkgjVMBOmVOGYW0Rcdi1ahdjhBcFlfjA+5cRjBop1aNDvdrf7CxkLVgxiCxhRctW8wczM8+kVmIrGtkaHGlr8y2D098HXE23r7fnJFUU68zyeyM265igNOGPzFG0dIgUDWN6S3ZcfMERJdWVvpGhVEHXNLeWqHiTcF3wOt0FbJY4XHEpmkoG9MQPJJ4ueQ01+MB+SR0rCSGzlE8zod19q75LlLWgzogpnJoD4gPxUYcX+Gpc5Ly4nk+Zm8LDXcNR7SNVxLh6NAcx8ekjb/AC7ADlRnfuHaHJaBodZr7RBX9FLTvocY6kY8bavdAkQicE9bbwGLkZu6whTCJ56lOvM39ijehpTOFqR3V53nQx4hfOvwRPU2y2w7UU8yiRbcyaX6jGJ9CRvl9ybV1tebTp5MMuMnwLcx/lven0w9T0atJuiUE2WtYGiVMaP3EchABl5AsyaCpu/BKAWDFvU2vaCL2/fJBKCKLjxG6xzT4Mh4wHhH3/EqsGSoQAHu2wbHmXHj2LvoW19GXDa2oyeKRwGG1PU+S7mE/S+UmjHiDF1oqJ0R5QsdjAZYN1MzpNX5YDqWYfhfdjAXyFQaVyGKkp1oEGTR8MK6jaGfRDFd41u2Ex8ac8jKPYu3pXsk8gu+m9tr1RVzTTuDsACW4S1h32yFHX7qpXSmA0QVEcR8W9j2Juu0pcYqTmdis88VgT3gq7iYue5Hx/3K6hFQa9rZrNSDcjaSQlNn4LSqs20bypnKqpzvnnxjMdz5StbzvoAJKgVZa4DLCVoJW765/KyTF4s4YztmAT1c0pTmKJHTpa106FegDo8p2zD6uOnwpYi0vJlRMDe9wPT6964UfAf6lq3qWypUOx9q6BbKEYt7K3gWMXDNN6wAm1fNnSOnZ4JkbPq7jLQrl0wL1V7QwO/sXneKGfTgUL28I5iPVG9dA2gS7Ki005JUR7Vmw4gX4TJvy1WS74cIXD08LCF5obqcZwamuoZ+FPMJEck0TLHjyH1baPr55/Cy0ptDfRJ7d89pbP48tLMHG5dO11Z8xSSpPGQSgXDWmpsNsmm+MvxJjMCi7OFDHxxpmTtjgnOCq+c7Fi1DybfhAntviKccz+sj+OPKPYOKeYYPLvq6MpUx/chSvBccg9dfbeqetQNCs3eiCFZTU1mrDido/mib64STMgsa+IKLk9PyxGGbVSQB9GsHto6f5prAFIbRDSItDedz3t5+Nn69FFS0nEfmkF7hKBmNVce5xv65USKGBoHYxJyutSGnRIq7vMDsAMvirOEJOzNi5Kt7fypuSU2c2Npo6UH5jMOkePH0TwgpammO3Fb2FX6f11309z/mqRmQ949HHRj/wMzKNx95M9pwKf+UQkMEwisL3YVotvHhCv4y00Ui0Ql8dR7tGqFcSdYtmoAOuAodkBNs4PZSjAAF7S/szwLddFMdCyB/dWPgFUiUE+WmUUCjYrKfJLQfNNpQ4NKaF57w7Kp/isZVwQPUJyjJavN3fQNKU+F74jVBJYQEcEdw0Niinyea0l9PJ1/AcTm/LI91RZjDvLI81pnat7RKU2P4/TnIAa3hIEfeg4iGQ+wTDlURK6YjNpN5s5VkQW9w7sDYKU4XmjyZsCQLxztqd4SDQvLyuPDhURAJXKfR1c7tq3mRu4usFHPqz7HgS0X7kNxiWWR3fb3uVwbgKpmgLYkwKrXKt09COw4MjhxeZlDXKy7nNLHXAIKPtferWQnZLboonQXK81x+BB3oUidBehK1swSXxVbscj/LsfONu/xYEXYPM3aMqIYd+2hAnFvDHbdrJLhGEd3sG5PyxqhzejhQJo9wauFK3xmPYqxB99J8zYU9/yzrEZNzzbvPoR9vUlE3Ha4zspVDzHHffPZMJ1VLZkKqGCf8ZqupqMt6T+NRPfmPm2xeDgvzMrRJEL4/zzlu7Z35smvzbgeC25VP2CUrZkRxEi15A0769ojdO1d7C9OG+swj1ROMM3NgKdeBADoRMeJkRZcZ1FbQu6C0BS9NNSaoxtFzYT4lX7+PQ7BKa84yrN+ujVVef+SgnEie1G0N+eOtbZF/UU+wkeerWjloYqFiqo0vBnmxh+TwNMo9I/8lfU2XTCT0K4OoWE08ipyNHjxHvfhY6qa3x4HzdQ8+jkiO5+j91YkihS5memfpFREHP/2veN5XcRue2zCVuAub8V6vDlOvyP+PBm+owyRhMmng5wwGGIXsOkQekXrXpE/6dFjkHwwoFoj5bIFiqp+4wHpSWRbv2xGrRpd2c87FzMP6Hfj/3LWIBqFiNOAxBw+AAP1XqUBszdZhzOSQrQS4Ein4fyV7MaGsB0VsMF4bPb4lx/foTGQRJv45LpoxDd84xCawHaX7jpXUrOdkFxx2oUvY2xqpgIvcVufwd+zAnaaVTnEyDXD7S/o/xrrk4mgTjXhcjj5Rzrbr23NmuZQvpdNzny5MCR9bwvIRIqzOZZLsstZSCDYa56JTvzxgBs20dYTtTUbe21uljlWqGfSh2bYAzOpf6UguK30ZxNXgLHs6Y6urtxFA5iLYvlue5mDONW0MOtQjhqr8fRbCkYneiDkvzHkQVT4F9v9vxh2SIGPBH8bZb8ugo/BSgXojeSdNXbBAIDsB6DUNSXnwlu/bFLaCqSbvu4+YLplwO1JbtrMf9ZUfsxerAZjB7E/zl3qwgK27FswemUmSM4i37YAVhQSocuV8AcDI/CSeCDNPavESshDQ8A/lVIrAJAMdP/rHXouiNU8RL/TIvfQiuZEb6dkIKMGGOW5kT8vO8pivWnT4v7qmwuJo52AS1r/RyQ2g/7c9ZJgmMIzf0GvJJRfMNu1utRNuLWHOm9JIMcJK3qiDtVpGCDP45W1oTTMUnMC91kYhP0GHjhCW8V38xhjHgFFBfuWMsmSQ9MvNqKXiqtUhDAkIy0PW7YSKaKUv6zctAiIk+Jt17kG6LpNVOeMvJnlVBaJSkKe0HTJJUMvf8R2zna35/yh2wNlWLzIP3BJR5aRNxkV94ICOlycI1/JYRZtzvWMNoIpQrdNvyBuBydhSwhRwPo079Xk/XQZpbhzN/KK4NbdJQV0JIMP+Y5UBIM3TTYlFGYVjcvA5yVozkimco91Fx/eo+ydgAx1gMezTh+bYxCtXPYkMoPdtaElRusxlmdSV9zgF4Np+iylun3LVxCycAFxGCFsmARf6y4I6zXY0tx81aQyalr3/ih+ZjxGNWdhItgNLdEZ/BOIJpPoAveh2bKbEFxU/M0+4xqDo3Ox8MnNn8Lmv15NJigSvJV+y2W/ZogEXNiv0/nuFzZGr0pKujOShzcdkEVlMw8mNZXZCbtM9V+mfawtLxCTvo+enFWhJcFv8LVTFycDjPGBXRQKNN+z68HJtYdpH++g5WdhQpCO+DE7Qdu6TmZgtetrpU2ZlgpslOx+4hb3aXaqbdc92LCh51er8vm1GQ9uWD9+fAPRV50ixhgc5zi2Jsg1xQVxzlaELRWJ5biyF+eCwNV0oFnTbBHr3Glm9qlGVOpoOsQC8hlNG88fxeAekkCGnHFn6i5WzyO7ShDYbZ2KM4eqndyy01v+6TFhmkxgc0dndt7EzRCcEfBxSaWZwcev6MDZcuvSZQ9CNSd4Tx25TY6UAbrhikuP1vNFfPdZhCG1pe6vx4D6Ez3zIb0zDa42FPpxWvIpEeXb7YTcfZOahSpSYaWLH/vq0F3U1KO7ZxliZpoMBBYJs91IE0bOkrPNQ/USYY0qKCO3CU+AFbOYxzKWBkIglrX34377BZ18MKQCv1KWfIHEeguSpvrNH5RQOD4LeiH2gdx1MOAKphlL41F4RpxaU4dy8xERFgqoyICQq9XmQ8WJSokwqvhQM0fLtsvyCO2PAkJ3BZg5IqoR5q/GdTLgOWPFR53Nqw9Ma5vBzZcQ4+iZgetmKg5ZIn+/7Jbi+VlViXuD9CaAUtdEmnwWTS7wZWuskVvc/SDaaKV+Jz6HrZTHo3UrAu0IZDBkXWmL+mTTjdTb1A+MdhKkY/hvFNwXj1FzUngsN58u/kTdJ3Xi0hy7efR6faAOi4SKGaiOty8lxDFkiD9wq2GW1EZEsoWGw/WzxXhWDzYY8CC7WuLFHc+x19jhH+FiLXwDIARRtnkJPF2BUPZ9+grZ3tjqAWhhN3h74w5pooRQUNATy05A9HDLnILGSCtfESoSilqtqAIQ/TV2t3KhOc+teDf5t+DqZDdB8Ob9YXyklrSO73pR0QAxPvQj57c6FIR5dOciqeHZ2LRABMROo8Jk8V6JFewCL8TCd/A5MSbXLky1cW7mXobqgeEXdFDoEydKo5oCuyn+2JYI/7pIGFAzErlHZ5hOaiT17HC3zp2HpJwsIAb4/oIoZ8x8ak43Yp83Ermq55Dg8HxKGHXbXs47sh0PzQELTGFsf5eO3lYAuJjMneoYWk8W/3tW2WLntEKBZEW4hOFgo8K58Rj0vk5KLyezu1d8SO/JcuxpOJqFUM2sxBmbQ/9qqwb90R0WulpR/Ju84bQ5/fTh7po/pbBb7AQaYNdK3fatD3K4TLHAaa66MQzp/+ZGyCjzo5OXRzJ8UHyg/YpNHvvlOpwQIOjakpLHwGV4WsLDPjEIqG23ily3LL0dlkYQxj3Xx0ApCo35zYGoGOtIclYS83MnI5TwVdQ+Hg453WFQN694DaqhGaL/dm0KncXYqXLi5polgT4DOrzD4oSVhrkh8GW2PaXjOFDCLPcn4RQj8dRGIJuV81LxMPZ0UL6zpkaebhbFBxcRJe38UiTbUPDjFWk2jBqzrBvXcKmgdDcmRyJhIpuq+3DQY464AlY42z2EM0yIK0I6b+VgpanMfpdWo7OxKY8RM5tSJv340/qD8SxrYsybMuUkF8fHj7HcvxEPC5YYrH4LW1YKg6QaeFZLvPbrHZHvi4OXLKkN8cGQO8019OKqcv6QnBlj01e7qS5evoGm53rv+VmDxxCXDiOrDg+IaPeMPrn8TJ1oReXYI3yb+4HQbikxP5TQXHk4YXPUv95+KmkxGsRgTwP71YiMpqNXp0loHZeXRp9i3euKrVtxMM0e6XAoACwNtcc6sOuhZVb1htBLudzahrDFt5GkdlwHjZl5y0LbvSHwII+qYeDwRKTTzyXaInHIM+8rc5TrjUlPRVwB5LKFpQnV8e7vLv7T7V/iJTW9h9TnRtNCSGcofBWYm5P7wZcAq3AFamEW/GMbo27ldz0plt5HI53ddWkn9IuCZY+Iy0MATUh3YenRTbVgdLYtu893SuN6EL4e9V4NhlzUjI8nOS6B99ecyC1Ot8sDahQpWHbmt2YvWGyL3S9tEVLKYs+LnghBmmSl2uPWfqPobPwBHNLW21LUjfZb7jfLMTsMp3icGO1npK/rCsUgdBVKVg0Ys+/WKuTmVJoC8Oe5h3PK1TQhbpZ2ytP9nlutQPtLAEt+CVT90DfVkn7lHLOX8AfS6HLzfHeAhu1alnl19RHKV1LI0G7RPzYgVaSpX7th9f06uo2WpxjL86i/2uzK2qj/ClHbGDyQr3F9/axmq4kJ7zZFVXVVwfiFr5bhUGVZeQJHKFAcsnqPKsb8vHyB9SpFpT9U1U7D4aS9vYgqajxhC+hOkolJV2dKAxysCkWBo3SPiPUrSQYZxOWwWCoQzbV0oeaDEcgUtqI3nq9TSmpQ688/+wb26P2CHLY1H7q5lypXSrnwnnztq/jN1o9lyvLmLyGguV0VJnDCREkiUNrZqGG06MsyA+Phd9CuFoM5M1Pyk7S6TJaHdTw0ni3n5ysAup0kyxr65lFc81NcH8xSmpp+iOEtQZrH/y01k1rGMRJAGFhi+nDecpUlnrh+qBOCMZCcSCovOPJrxjZnZJDMLdpMVu+tBSVS1nKxsYjY9Dtq1/++riVfLUVhzofIcIgQQPOqHioELxU3EpCcZMoL9laa5YlOZAMEp5apx7CphrkL+fyKbBAf8ctwVd93FTo7F5Oc/alNsCgK6lHruPROtN2RybiLqx8P5LTUZXU+Aoyz08zYHasR3U8hPDKj+6arWXR9yWdJoMn45prCSURKKy3+JHgvs2Ot6v6GbEtdCumgCttv2VNoU3KOqUwqNIWHqYm4eMijTM9VWB7umEyp7UPOI8fduHJY0W9xSCZdvc2xMjo3Zdu2o/WZKDMOSh9UmLvo45IBppD2dG++HJu8kbfFdlwuIxk2KHhgHQeNKcHhFkYGRzL2VJVMOAb0Co64wvds5CaYl9ZmBm4zuGDeaO2eI1XM4+rD/HmZyRF62SabgAe8TF43VuMutigJJMfbW2UK0azGLFbOfujnHD+GGBYmSmOQbUCOY99HYvswBQA6r9hrc2jtsUUxLVjxnZ4JnIrTwIVdWCTPtpJpvlA7m01/4tbUMyz9mv1jdN1jkiHQCJXXKg8bJ+aqW6rbwbn5yDSHBTcFXIegrhHGAjJOZI1pyP83Z3vMYTAJoo8V9IwyS+U6OVg78+IhSYHDYjRs8FrF8smHQ9h4qAYxp49rRP2d5uxLAuP72GvZaYvfeLOkMrcg0PkPuq7NsXhMFmiZa6PKBH1l+oKHI5DBLdZCvCwTPdXqmnz8gLzVRb/ixLTSdit2nrzt0x+5rDeZT+ac31NKNskQs6noKlQccyD3UxzfVZFmcbpmrfPsZD0Ve34xpKWk/E9Khn4A5yVPVq+dwnv0EyYecPqXGU7R8suTW0A6NJWweLI3iSGDlQXzMYsSWkSMhFTfyA2vTDt/3wXk+mVU6bRNkZvNnyVHYiA4tmnNwdh/RVsk/EgSerfTIf5VBmuAc2IKSeL5Nbrg3acgFj80mI8SWsc3dNAGCBLLMP89gH5UnLTKq78d9SxQH/g7DVnBh/qnBdw5CDrw/uMzcdXSxWqGIFcnQZt/1aOHxUg88MN2w+FPx/V75gy2wzEVe6G51PQIR2tZsxbv62HhgjwtlzrVREw/yzlaAiuXC26cnpvQzWXp2mOgihyPCWqq38nEadX2T7f1Y5zGxEGBaT//IcL/BsquAJX5EDbX8X1p8nLWR2yyjFRvqC/jssoCJBCDJOsZvoBfXqQSEKhNARH1YfueeKBslAwLi24/wAO1BHptlf1kQFNsOPlDvlYednrEp3a4SAz/G7LIVEsZBu0EKWZu/euB/XKdkGonP6t6lgEcCOw8mceuzvEVzyoPnMyzrqoNQXJb9C8ZCXSiedKiCgNwfNkpVlHbUgE2Rb9WFScOeEad+T+jT8XlSc8rcvkIuhAv/gxRu2eb2GonLTyokjcGF1EBpCJbhy2H3lhL0rdZIw1okA5pBg2oRfQceXTPzhuNKorTEF7t1UIgDqIo7/loxyTgbtKu29o9K9KujvCqUGyPY7upcfiZLNBVKh5uXAAZjQjhlhBp0ukmO4Avxu4xAVhCtnsOIA/tAm94U3HEuSr3wq+ZLo8pyoC9EB/q3pOzQRyCTkozmJwo1Ln/2xEbtNnS2S0NUIS3yz3/mBIdxONHxqP9FW+uoGI1F415lI1nZwK0SoPA0+flaokBGEoXgZnO4GOExU7VOjdPns59ekmDxqNhEHeAF5i5N/3W2NC1XGFjTpqLrnCECiwVkOTrLtp2ehUIaejOG6+1336YQSKMSsL4zhUjw6SQKryVRz5Ldn3R5/r8AOi02RJkQXPdvPsl/FMg96E/cJmIFLmEDzr1Gkh9G3zisG4pqM/MV6XIz+CtDUh6hmJB97VzN8jaPSS90vgDjvnaNlKky2/zIhE9ObugwrftI+Oi2a4VVaB/Mwn3VmaWjsU9NOf2usbcN/GLQMjvfeU/YvyEERPKw1leXZWWk1HXzY3P9MUq6MZq1hkEgFzds51mv8mnp1i4pQprPwY0TId1szXwe5TG+R5mMD76nGPQr7/EhQWksjsgGs7Zy5QYvMcGV5tcXJR+6hlHFIAc/M6XjkKYtwm673Bi+K1tNO9i1YBePTur4I+gMsOK7f7980mcJXhgdWdhNzUN2JvFsvXq3zZRG2V30sJtJYxj0aUv1u4/ppVHi1iHnTY3gDHsrQS8YwMX5XwZ2gcFYYe2wd7ZO9swr0gb8zf/fXx8QWKPXcK1UdJk3760B/TMlpWLCbhkqVoSTsOqzgkmFmFteCCTGhNyvFhw1RrTIWzRxq8Tj5FirvKvtkp2GAVhnZ7vnr71pyI0rKwQbVxKZuqM7GAvn2mRBj5p8djlHUsh/r/eBECptpbbjP5nFyuN4mvQLZCaxeTkDUzd/kNGLIzBFv1CElQO+xmf7Dzt1f7GM1Bh+wLDCJZlhcVDXbtPuGssdEie3lZNiWcXMTjZtWAT5MCmpq6JCRuFSHZYGKcSFZ9kOYJfEqLIcWdzpTA+Hmu+ktgSUwXVSwkaa/aHdZXh7IOyrudCBalCZpgXGRNbhN2XpEY60DXXO1Ci5ayZSoxtG0WRCC50+XtgWz7qgX5MRA5S+jzXCYy7O7Nn0ljVxiBxQNCZKZMTqi6mPfy2LZx76uyRUXHjnpJJEimflHDUxyX7fFg7iJvSrsZMH6Uv2xbfQNx5eCbx3oKycUrBY22KPmgfg/w07CDVsw6tb5VxPg5/X38cQtXI47U7MAGGjO28II12T+PjaXHlstPtkUQNn0DKkCYis+kVAkA1wyAJgYKLGnKD3nlVCarYqCkNIZbiVwO2Ydjl7N6iOtvvbAfuq7VKZLo0jEdw1YdsRaHcuJQulgb51JyELzYBkP1hd03IDcZfPg5XmNvYQSOINsCSn3BuLtkCPZRalK7+S97zxvJHiJCZJM9XP785NZ8B8fqDe/Ot0BS3PH1ptErwxBtpgfOj4d/41nrSjJQf9bV1kfdBHJxYbHILxOsWkZvoP/Z4Sl0Yx3bDjTF96xf96+6uIoQ351Ce6DeTwTnkPr20YwATlnhskWIddUohklNITCq/07zkiEc3B58uiBG6d9YAc4h/7s44FN2RG1UuZWeojrOZIhElvDP4KqHcOYbqqS95o7ilQH5ONJfy+aYiB+sPpn35HfHG3duLpNvBjXc+Klf4IKrFHjeVty02xPTNnbdL4gtkqPqMLhSgR/fDXzxJbSScqewiF1wdVoJ/fGL/nGWZfVlDHOQKD+/i/mqwXqvNqxtZeRHwoe/bodk66B9soOnZp36gdzVMRRQsQiBFf+HXjRcrRf9FsGghw3+qoN0JeeMvDJrkSBPsESDai/uVOzn2Ohge+UVdi050fdWpsjP0D/QuTdYs6QyI9xnhU8WT2+KBKzoZ7Bq8fOdKPeLulUhJjT34/EOnUloqus8+pzqNh/UdUOhgTlrbkuTfsaIYDm87u/GNIl3N53uaU8bgaBjpz0jdu1f59K4KFDtwUUeEUoeYx6DEkWKHdi7dtHhQF44lbysk7PqERrsuAQu2D5tDMl7kFoGdI8r/s8rMytJzYBU40wqeFvTl0ZVLdOB6Ya9E/f8VPbGx5MdpYqYMLMyB0QxVdnoJ+tgAQVWfH+jtOHD3PsjuT8dOTSrupuvHWRHQoGI1Qj1Hc6k+Mg84FAZ/gzl3SEzuGWZKFwuo2D3EiG95D2Z1szTqAuFRmT1nEh20tkC4ysmXx6JtN0taK1iRR62s2uNW5rSAvMEJ8yotr3UhJe22brlQn8Gvcq1I0aODaHJucQKVe6SXyfcDWODMw8xf+2C7Zx5a4Qlh7pJs550DictL4OxcDXKvVmLgVWRwb3moxv4kcxzm89EERJXCl7X/BziBkGQWOHPGF+6K5NFJYOFVv4+NyFq+OPMaSWZKoydplufY+CYyL63T8MCMmwqLTmAE8h0prhi174wnx7DHZWYuRJSYZ63uz97AGOzyI3aebclnud77znbZetbWUripe+AadLQeZPtWsF+FNiaXCy/98km137lWewyc7Gamai1Hd3Ls+KMMVh0R3NKTQ08TIClDfMKwUGKy/7YZlJHU3uW60X0r74Afh02v5MJgVOYkjmors6GAaDU7yKHydfkXYd6nEjYc76xws1LDLWCNNKBtUHNyLseOyNDgmHiJ41lXvq638RzDGis8WIniOb/pbTs+HsQVGPi6mxG+CU+oflMR6/qx3pVP+GPgqa0U0lo8MVmI1cBgSnPGgrh+J+m9TVg8nivua0EQP7xai44ruC5gsAVOp9bLsDXfHQujo6IpBmpfbbU8PDavZpTuJtmflVQuOImnRQ5kKoQz2NBFjdiHH3cF9QLgDP5vz/W5trCy22Uk+TCjXjdbCCHB3rJhKYTwiyQUf8xu6yTKtIwrbw4tzFgXDODmWYEnnpDupk3b4AP3qz4AZ2En5wi6aZV287AgCF4vH8TlWLni1E5Hd93vLxSYLBWSuj3eXGFtWyWpBkIeKu+YsBh19VeakA8OePM0ILu6dYYl9DNIK3kU1ybH+A5xYhFI/EqSX3vtNs6V5eQgxYLvu0hYFjiG+n8JzqLQVROiVa8XNQDYJtDAetPFSuEtGI3B8rnbbrNo9TJn/z3lRYq0ecBIe7a03vLESwhKOm1bGTk2kPMv/Sh9wyCOmIore7JhSFT9HIjonBfi+gcdDLfFt7dpShJmW1gkcXmitWwm1cC480CraHm/or2MHphB9Q1bmt/SBXFqXJdcv5GTt3IS2fRgqThhInCjRkh7Dk1iS2vMBLSGtRPppb4FEu762JehUMQxxLQre365CKoJGvJwVde91XQ+bDp5ZsMu/QHmLgITmwGXSpQFQlQBajqquxlwIOe2cyfezaSHIoRNLcwjW+epnmAtmmWA9KU29v/cA2iuWbj9ZV7HR4anhHkjbxnzKPHnIZ7Mm5wAf2o/3xUhnfH++quS20TdhalHgNhusidPKWyKWV8ZjFLgb1fX2r7ifLyUtxuKHHIfCWXQJ/DKeU61vxmPT34MTi2Q9r7/sK1CYuHVqMBsgtfenn31bUzCoyPN89KiO5wHveqnk3uyHnJSUBVTQQ3NyRPmeRKTQvWEBZ4QWcSgMyZF0RQgvUXRcp6KflF056fwahSioP622TdcTVYi4cAwSZLWDvfjoKFLMowPQpzn6ogXHc93fFA5NZmnwslSuesOyNI1EE3RM8kzat6thkmpOiGmm69Yn8yNuxz1YuuPWekoybkee106T9WTPXo44ea9E5QH2Ig6FZn716DBa2FyXHG1B+YfnmhbEpANlOi61BoGO4+G3WMJDokJXj9GhNsFqdaLjA1pkhLP+/mGCZoYsxNI+A+sMvWyoj+PMWeR8koRz+r9pNVEWT70WhiAkNTrojdr0sBLwxIM7D4zT+cVy96ZE+ABi9CqkM9VK7iOfkJVp7AqCqQ9EZ9emn8rB8zfoQZUBrVd6YS2AqiTFt0nJ8HfPGmnBWf3Xi5CgyWoLAmHJp/AfTdHB0+Ns5DlhL6UJ+O/6xys+CWVKtL9S8fVHkpwZZMJn6jVtiUTtXjywmiVXw9a6f/G7Qd4tZtcoS3aytxXYA9aGGmEeBobjiammhUaMDicH3nlOkDvvz19NqWOvHC2SMv7OQHtDIykYerPuoLz6SQNOBtw6oX2Sj3ZLITBDcWNx9CuZYYVaE+vleXnATrwn+PnuQ34jL52tp85aIOk684SUlQ8uyO2t+eIOHndZ3oxD+BcMAba/JVxRYUAUZoEw3D80WWOz0/ul+fYbhFnffx3PgOy2LLiu82D5FMSpi+Pd4EkIFTgfv7p/0vnX1wp0VpNzyXs/5S/4z0RFS21vIF67k1ERTfFuhLM/8fdbKognohMqTNF/+oqvXXLuJB7IHeDdn1X2eParLBEpz8y9CAN2g5VdE7EimekAOhkw+tTzqeEsgyQL4iVDnWrP/RcBd6CDm16/5t+I1SAxCn9wo8knzmpg8DYP8V/vHw8Stu7cliAt+G/VR4XPNZXWF2rZBeQO75os2jFJrbtkfhN9BzHT4HGgXTjyTy8NGsiQdeOw12GjYKCyxP+34kRHZqYsn0pFvVubB0+/emKRgiGXNRWQwMSvAB1xvTprD0Zyt08BjP/4W9HGNfNBcA0Qb9qF5hdQ4dDqpKAFLoIW2gFEVKOganw3M9/4WP9ckP0/g6kaJDRurtxNgT+PjvWYEWlFa80wKYCkd/0ZChV94njjGyg0t98Pz3AL2AFAhvRRiJwdfRcQqqhWkv/o6X45d5w1YLJOye3v7rgta7Ya0jAl/an42ng5Wz4S5we7n2+1W94JnpoGyV8WW2HYjKLkKmp4hBKlNtb5y4W1MrsG/wfq2N5Xrz2kqhdPQL/YoxgCQd6Y2KNkADVu7TxugQRWVuNL0BUj3JRFyWNeCmB74Wsz54OPnbq0GFFxzSkoiJ3Rtq8yEJMKvOMMalFKH7YFHKjb2nwrKVfuUUuRtTfJDiBuaEHHoX+MUrM2bBaAsSdnY5PjqcMBn/wwojQxzt2MoOCC3OEArr09ghhsj2M0mue5ntQcmcC1R/sK3zfShGJuazS+mJUeKxk5u36CYj8+SJCq8ZEv7bNf1+BywGeDQoTDGq6Yh1xW3Suwo2O/ykazTPK/TdVOICyiwK8MuQpK+FX3mqSPzxfLwFJ/iYDjs0WgW2kqXYgm+gkNToB5+jYH83Xlt0cbtEmkkBaVGlHz61rVuWzrK1yjn5nYHKvKCrBPPRth3AKDQQB83fdrbgIeIfB3iHya5NPpEyxbzmtN5Dnk7GqrQ4uu4h3QSoHU+74zs31cWqIx4SZ2bwWLvIxUtR6gufZhNZoMcmSB5z1O9TKvHMORD+VmuiqzsyJKA1OaApB+b9x6u9FTvUkalgl0r7raV+wRqimc2D7B1z/OiSagdd5UME2igLGUcgPlMSX1VsKQp/9yDiYei87KTBA2NPCUmgaLwVdvQFFFxWp2vGCY/KCUvxt3FOu6xIgwS4Vybvbj6feUCkrQPpO/wPHJPhAobSj/aa5YrUvjHMcQkDZwfc9mvghrk/PIPvcJa5InhVBfjh3Xr9vIvA4ac+m+pywS/EqkSX55xgiyj0TB1EE0NT3W2CPFdVD88P72SpdFzHS/6XsmbGtM8JE/m8eojzd4PM1bNADliZ+XG/9hbcKg6PftVKyKKt/8Bz4lGsHyT0VKj2vDGp/qDGBajSHrqzmpEjW5LXsb5kTV6HgbMcnPW2dzQju9N1sI/gPVlgGmk0bHKOX2Ws1q4aPizhcM/XiJ5EZNUK6bZNUeFaUJVTvGxglRUY7vdnoVOe0Raho3huh1XDeTlHpk/2gBjjhUQXe8FN5A4zcRqkNtKpSVq0xyw9j3yQlQxq/Lnqklpz8lXmzHkz8sX9HJjHwyn8UAjblvN0ZFIk4liejx0lVACoKvpsT9+pQoLY4weMHRzcuVC60DUFkaqLfclS4UJti5WK4FE3dYcc0OilX50uscLJomlR6pXriD6ELNNBWOSMt50CJjPkyt3Zn/xj1dlPVP1t6XExK+b3jMoULLPOrEGvjELfAMM1qcuBb0AijkIuFca8f8xapUlkvLjmmJW7RK94r8HaPzvmHHSqX9MXdivNI4A+JHy0VCe79UZZJvzMGzpnsj+Q6k3EItDBiA12fTMlSbEOMAWCdQq9TtyUiAaAqJozMzryEg0k+yVHqCc/DyJcCE2V4WXIhEnsOc5c8f4ChWfUaONhPPWogpDs/lyVCvp3m0NSfrAJKNiVy5aNC9gZ6c9BqwYgj/cDO3kdam6gCjhR+akALFYmt4ixHkWxKhDTGs5K+CwRiKJnvxP9dbxRPCBHbiVa8gsd2GuiNHZD98MNwXMdMC0MubVodd7dnyk3UQFfCIIL1osPxY0ZJ6DvZXwtZ2I0th6aqlTMULVo+lhSIU/5qO63lTSa3MgPRJEOi0AJ8/UlZuvgqLw9dyEDQoHTKWOsq+6fzoAyvIpv14fLaY+braPd6NkSaq0RClMenK1QLH87NZriUaeuCo6SZ7/CfUt2K6VOt0AjIK2jR0vorf6R8+TVzxZb+QdLimH9pU5tQc73xW93QRPMGy/gCK+R+YzmV4fHK52GWBEBL05EEoTY6OYG1WWji66dWnVTg0uPNw839p/yjLxkCfdTaH+v6hVUCd6HlROj6W8Mil6AYGC7NI2+qkZvJh/dAw/iQspXQNwwWHr6slLIp0hBHYTDh/J7Ba7ZR6cp3iU4bSXdmzhTahYDev4yKiIHyN64EANhI5OHYv1G4KXfIOvQizYWchPhzQg5eVGNMxsqrvWVxjtIbkKuHzE+IcA2NZ83GKz0D8z5zmgRnoJGKigseP9TmMS7BgAqtqyixA/SLc1KEUWrhXOQ6kA5ZQRazp3wwSa404cppBnfsS8EsEpbr/gXyW36cZ9pt1RhzyxGxDUmnZeBz/Uf1AP+gyLIg9x04u1fThm2w/H1ZXGvVqsO1VqutV5gUhFkdkwoCjzz3F3FUr1v0njGYT2mSZYvoF/fSd1W11c5VIhkEO06US5wYRmHVPYXmZnbK5YHQ8pkIDJ0yqssqFK34CuHE8RWb+Dr4omk779QOOcYomAMYQ9ILt2KUk2uNlahW/IjGtenuGLxb/t3aFoVz4oNwMZ7iyp4td8mdzgJAfnCcYtklubGAUB9k6bGC5DSkf5VFarnGEBWz600VGR8QywZ+jIYFZbtKT2QdDOYP6k7D8qVgEZByGmRedZRWaQDTggLyNgDD6pQwEeSs82+hTxWypqwU3zuAWqfwil+mytzVnKztyvMFJyJwPFaPr4Z3mTjyxCR2Jv674JVGGMUSWb0l+GtcYtd+NBGChwr8mB2hlyccget9liJhQEb0XgXfgVRlHlbO+jlZ9CcAew0Nw+tRcWgNnz/GL9Kur7RohRhaYZBBmQA6JhvzkazHRcdZDn0zDkfBmYP1PfQjP3d6qqx6gE7vrb3lBKEfK3Y/nCe4COdpr23oZCoIpssGXmqE8CGpO2bEwkSN6uqeqR4UtWR+xsgOzNeR49PTLJpFEAkXha5YaecJ8t/KR+eG7/HKV23zPZAMvHDC1rdxQ0l+6wlIgZbUybjBe6yusL7isRuuYYwg4+8+4lia2ox8RCdvmXlt00ZshBnAIfLkSwIqUzCcsD/d1ZG6Az728L4FCIqBKpbA6bzkJ87lYQpbaHpwPpqu3S0UqNDCwgg3q9MEn02X16E4xibz/rLx7NMDtHcwMOt9r1dVU6Hws9TvJVH7THrnSFESgN5eBy53Nq2Fdb8mySTxz5CitvVE+ZjHaYS3hq9Bax+uS7TxMIT4qJE7HGdsHM1/9uPNBylhP04Lck39JMe8v2dPOSJzyQoy8m/8Fc6h+X+5/mBVA9jAsG4vmx/KdUW+NXxgRt//SS2Ib7aGILsjOz+ZZQu/NMeuAsP1pFRTN90rqIVULbJ20ZJlrjoZD1VxHEoDFFGVWCVOT3jGK+vFD06gc3yDUSnZ7ZHjGmw4ZiAglY2nm78aUpXxI4BfUHqL6YQKFDCazUIryLi53RczlaTh0ry7WN4WpWK9sPJ0J49fu6RGUMYZd3+NrRvEdOrS5n+EJOTkr4lNzo8vawcYnR/n1Dq0rCHu5o2BGBEHABJbsFLi/mlWFO1MjpvUu6UPJjXlXse6MtBROT/mQfyegWGmFRQ7Q/O+rJp471+tQF10+bvkExfBoTQrewd5UwhAUODpyeW+aK6vx2AroUo2bGBZ/ZjcsJFfMYEMsm47LdQSq7T7peI2Ex+4/9oIAJGfhidbXA9UYPNhxigFTg83CETNYfYVkoambj3vv4MZNtE/wrIfTguBNqkQk9ebLPTmY2U4UCzbYqPKO5vjaZXeVksobDAJzhVjoU7p9TdFmNMyLyCQJryBSOcm0hFk/pcwcV15KZ/+IIqeQGPkTbiY1haWSnuQYBeyW5uSPHGtYw28cQS/v3rToNAUGVBSQ6zpBt4CHvaOfEJhuDJYZCcxvPeOStdCzaoSQn9nDe8wDc1MXrJ0+9N9TAKcS6u8ANLCLY4UfHLGf884/LFIn4OLOlRcNl7FS1IJgu1/vLm4INkgHt5ISp2vC3MFJHz1zJnopnKS1AgJtCmhJRZDaW6wis8CJ0KAJW0Yy0+kWI3lJ9N8yqJht68FMNVgkgaAGi5LuKmkZWm+ztKvf9gT8hJrXZkM/QdHI6wy9BqVeWa7g7ZM1YLbUv37YSnLmGsCrl/UVi/tG+fZbzY4bGye0zH08VQpGmyd/v++fS9EtasmbkQEIYnmLZLxO+tNHp3myIGwYBZVXjlWvrCiQcsP/Fu9l0HWmLBu3gvuJ4phtJsXXllJdM8iZIQR8Z6zEMs+cqVL7+TYhxDd0c0l4sbyIEw6N+V0v3ZbUlidyekdcz/aIomGdZtmdI+1QUrrHw7eDXT+G3zbTZMXxpEgJc4zY5bH5az8eHzwoo8QUleUKpVRrsErGmSF6GPJ2OltKYL6/C4zx4rHdcfsrQTcWBmrBWMMiFiU4NGtpYeACqYafRyu8j8x7ltp3nxVbsPO0MSoaR8tv61/q+YCqHX3h4vy4HzjCYEl+4ZDtj2+mawuj4J0rBpcDw+spzuCQ2khFbks09lPGxK8HYJl0Y/lNLUxGLZ+2h6+EFSaD22bYzF7dk/EhCWh6u/v1HUVKC/r/Wl6JHtd1V68J9zdOTgbvJuQug4r4vUV3JJolQQ5tecHKqcNoYjOIs6BZTlfB+yHGfGdxTKsGxbU/4taKuH8Qpd/M7fIG5zebrpiDHV97T4jiUNt7K64/u1e/+erXV34aOjfddcKNO76EzIf1pfD+KivBsRlzlsjj17aDPq/lnKHQCLsD+3TK021HNzhZyuwpLRKS3KE0XH/0TqUOr3VqLMcsSZM6349QJDznPG+sUqeS6wwMWp28TAoDKdmjzW6f+2au71HsOzLIeWencRa5JapKkVTYpvwMIC8u2L+/hYGJmk0588rq6Nnqe041NMzU6lj1K5KmSj0ZRiVpzu2FSTl4PBYHAuhe5dtwnRQwvvNqIELVxKMFWedxxB7UO4zpYRe2x0zH4X6pI2m4g6YdCs08vR9B7omy/goQUYbUZA+wJamq7/c0FhkNm74Mp05NSCK1Dcy1+9qp82p8XVkUB4+SsVRJ/Tqtn8v2esmemr7zjCfjLicMb05JqNoL6zzz0KaYkXeStBrF9+T7EbZTo2Fa/wS5NhJvRoZc8QUfS46HX8HIZ8A6LK8zKtROnakAnEEFoonVlvYR71xYuBAXbjtxfu/bteN8WkArB3//qp+3btpi2SIMyK6rX03iCLnzOd2OrPnD6xqgVT35e6NUMpN7EJSz0DRRzyze1J+Dx3cfx0M577W84qifD51mZG8VNbBf+5PxmGGrGOmkO+Q41YnCkx51D+X3CXsNAjaz/XfcPJUXJ00vaQyfYDtmFq4kU1ZHdnep48T4IskzPsYT9or3rd/ubiYLqeBqjnGbuNWb9ZdPDxkeBmJwYTjsTU+VugQmtz5+C3QBX0piVh3d7BK+Hk4mO3q8qJVQXeIqs4hKuRvBfIwwUyKg9W1x8dv+EwESuk2Bgs1+Zc3wzx4eGasynWs3V360wH3fKXZFTckeHZdgtzTqcQPC2hCHhSXyFMyljvrneLE+c+b/YQ0XcDBam1oAPzvKmmcgER6AqnyC32Ic4HMP4FQN2rh4Y2ntrawByV+9oq/Z8hdwQEPYRYiELBCnuGGXDQbl3ZLuUo0vfKU/AuMwYfNXmNM2vkn/GRrpc5WDP+MEL80tbJDZfDNBRfpfcvVpf75u0LrkIIjnU4adaolZWzB2yjIVwNrF7zF//n4N5xHeaGc7Vh1EYRdc0h2l23qFvLBNQ5kHbmX8Yta2Vj4DU6eBN3XyJBvJf9iL4x+hw1hx/7Ej5U8EZr/Qhgoni5r9PxBfU3fdvXICGW9DzST7GV141bvyMDXblFG5PizNjJUVAWNSxIAStz6+eDAbkYeAKTj6DIR6ysFvZAloBLCgSdMFd3ol/WXDQh3BbBtLqO9hp08BfumZjLpTJGRAIHzDizXZfhbgqejNSS27BIXQLV0muwzgXGqYt9McSvtLWo1Fos3k6Nu2qGyFftqQyDz0/bmgvtZyiFce/SLYnjt2Q9BnlmUVBWOtbDPvUgOSizvJDhdiSkbLLP96MJ7dKO3eUK2nZnpb4s4b2XGF4T6gC4qo9TDv9z2SY4Rffb/RjPs76P0YiWADpPB/nQjC2tDRlxt4sdNCIjmMsLgU+cr8cpyaMSYI9maP4HHww2jTPkGKvF6H6+DFAF+jAZKT9oi23gpZ2zavE0xXPkF7a2FTNJ3bwxvsJV+o0fXZAkmouYq6B2+6ccHhnUIeL10QtZaPoZPJB7/Xry/2Nv+JJFmQ/p2NSiO5bYGA8ej1vh5QlWhaX3JMs5gMBnyyIfXIMf4im0WEUnCPAJzq9q04Tmxzy7nGKKEf31kAp6IFk95aj0AogL7iljLVJlOXNvV7BwZn4dKfuZweSEZBqy+Mvual0TVDHiwHuIuXbvaw+OkU7aeAfck0Hc6H0jgt9g6Rxb6dAuaiKEN1cUYtD88y0b9Arq1q6ML9B20/FunTnZNF+IHgsg641FfllDFpQ+dqrIPKQ8IkLx/2ppx0ivQSrehNaf5dwtBjnPHroRGzG/RWOdiW0COPzepxIqcsWjhfmBXSUD7YCvPm/qTGcSnhcriFKew6a5s0AgK03I1gEifX6y90cJBY9REbQ7yW/XB+zAXN1XZQVEs7r+0ajtx8KvVBKJksKj5YFGdhEennMbwgCJJIMdt/pJD6FIcNVegt2LiQS70DAJeiNNG86dQVNYNZmYEfo8oa002xKLh1+rHlBX40iY8Wlv7FqswQFktpyLn5oSdo1jBRz8V3aRIOmhSnrs2wxGwGBEVEXvRm8RZVvSQ0xlKMVWs9Y7nnmJ9jEVuDL08D2ES3plzvCNP3FpKQeSknFeVBXv5T1Yk0/X5vdj1J1LYa6Ffxxrv90ObLHARkCI+tz6+0i5cZTinvgIYLMVnV/OL+m4RCsTy/+9VQPsYv6X2qSSlVdQ3KM1SOntMNUBpb4C0MsDh10xHQ0cbJK0gsR6X93ru63BDYbRZmPISt1casVwVVE7+u3l55XJGJ0Ev6S+2zpNqOAH66RuzpVskXE6X8x6wHOfp5PAI/7YG3Zozh1U27IXGEEKIm13Rt/nTE3pKWA7i1NFdVQKQ0CNdqEsBkjiuM41dd5rIbR4DMnoDva07v1esxYBGU4JWJUJQyejYbI9p7pqjrpHZUNlz2exX1lTAks+WxY6CExoPlSlNNv6AIsE0VdPmHOj4m0a8bigDelTpIL1WoePLhblmhRlkPDKiZvkzz6eG8vLeJjCGJL1+VFa4QREBVyuhcpZm1ygJm9kuQ+8v4yEMw0VO+TKee6sMFRVc/kS4IirJupnw48LoR2aRk+GuDBZ25xnKFxdSYqZqvWlEcemsbzl7wvQg5z2xKxEUsquyGziyzd/X+XFl/ct9KRLzyyb6ComIL8Wam9x6LPNZXvhO0QQZmQ8T2MFjmRJ42WyRzfyLGkJKft94uO0Yy6Fflo3AoIEon3XBygpi3Je932ToU5EKoikvqkeLFACpsBN5dseemiMdHxOJKrVJDdTS0qCcTzPCyz506oyENFdelskwdghmUnWyXK2WeJX2CBXudNUBON/i8kMdtJm52REvmGqVmxe5aricuTCGLbgZtYvigT++E7xltEh/ZgUoMP+d8vaPU/HdhZaUjsgQ8OoqZeezvNR2JFm2on+IliVyYQ/58LmZ2stgKoBbs4SllwiTpNRw7ecL2WR8bbg05aTN00C8aGWtReWSsYsirJ0K0I97flI2gJRRN717wESryWahXUAFZAdyD08j9SIZQm+wq5GkoUkK5cQ3wk1x01x4fKLPgPIj6D6lZiylqvWGtl6KxCfoSQXlNZIHeDsrIRqhINxdrCinM0iMMkveNxhqrEzhnBn8F6nXVY5zUDLzOXpp338I2HycFa2pueObEof3HQgFEMnHS3/CDKwJAyYl3HyA4X5vXUE8MMa79gYELseTf0IEUJRsfSa873vl6n29lFq+GCqF1I+mB5PSyLFvgHv6hG5Hd14PAHTKhY+xzCgOwwRZxygPwNET0UiO9ynH0p3j7GAFEs+VSjl4ArhHJbySohRLfm6B7FxxYJLJxJlQr5UdD+5Vs0nM6CehSZZNYw4FzcpYoL6nS+wGGSNKLVLXgbgvzAbT4B1J4GMS16IKMlo5S/dzM/NM4NI+a1Fuk4qwaewoHqGp78vgp+SkuhLyAVhI2Or50Id4LlHwRon9o7JT3D2pibchFvFi2VTEx6cLX/qorW2YGSSmnu9+M8teW9DIRH1TfabuDIuLk16NFz3kNr5QLPGAd0JzN2IYFA140yqfi9LfBcZI3aUK/Gt2bfMMk8eqttN8c92OmUYKUaHbB9C9cpEwaOYs49MztuGtI0VMqDDHN8HiRP55BpRIJtIWbSyi0/LOC94XhzqGVyuzaVaBfg0f++sV8wy7ytxlQYA9w1ejE0XaCkpM9zbOrymf4OrEaIyQX84Z9e6wQ1czIvOihnSaq/fcFdkxJcMzE2kWcARwWT1U80dW6B+v6HdclWMyMWLYr49iKWrhm7o1yumJKxVGiv1Rx3Tw61jrh+vuNjikpFRxa0F9G7ZWs57nuhaIeT8ZRjYzuyq4WZBEXs4CyfvmZxGcS4/G2aWon2O/UkjqrfdbBUF0yavSPdNJacaaZxFQNejGDPK7SCF82XxiahbNpwFs/t07gbCJkDUvvKjqaYv1SNJBa21RKsOuGJNKO/F6HTjc1Q5t8lqLL4e83gWTT4aubYGtE+D4e9zdPPo2R3dvG7bDrCQosp62YhTaV3B/kEQGqtzvu59fbgA6lFyGe7urhYr3TWCBFYBmrEpB78fWnXUEd1z0LSzMcWL6vuh4CJYR0tg1jX4H0wkw9mkbM07MXopLJ2Rt7/aL3Hl3MjO8h/1lqNlK74QTbgkurmgd23XflEcMhjO52Y/Wsz+CqwkBCDN8SUcd0hvJ6srikURdDKw75ZZMyms8NdzvzfsXreeCzpVaPKbkgWo0BlD+qWqaXziVa7YTSezNkCD1UBphMwE3IFwG3+Oja0AILbwR+VMjirrIkRPt+DMtp+OKLpkiE15AVv3jn19brZGZkhhAsuT2sTiWSjLvxJkMICAGdQY6CcJ1bmQsycrXCCxoxrME8B5k7aYQkl31h4kmnvmUA1Uo5bGEJkzebQNuMeVIRwKr7shM3Y3iowzuO8Jm833ALhjeDbR9i+ajGdiv5nuQcBDW0PZ0CB/GHvnmE702e3iEmWKin/StmkbfvsVh9mXnjLzZCRfht3g5Fu6OpDSsq1DSVUie4hNThGTSTWkOhTKbARv54Bxp1m/BqW0CfvfUJMQYci+HzQBrAw7lHJI8klNzq1wbwtxf0zzTFIpYQcsU3ddDWDMuciKmN+BHJ47B6FkgX4uR5QSWzLqgN2wQK1aLp2hgMJGqMII4rLK56VcDk89QQhw6cy8PCM19olNpuDwdrQFvP+77wiyyKx8Z4MVJNxV5vJWOwvF+aDouZMW5HNno5d960qcPPO89qYm6Zh6UO7MyFx272aWYtu/0+UZ6eThOP3s/uMGRarrYNGVN2bkl0VbM7ZArP2AnCQLuPoIbkry4nTS/RsIdFmPg98zeYI4R0RY41FQsBym1OXnJcHtmKPjfEXuujVQGfCPrCZsaT+vFbMFWIvUy7OxquIvdi2DVp3+q3E3NGG06d/cz77wgHGWrfcy5LJIzCMZHkk6m2QnZCXYVXwMsVhJI9nJcgG/CrU5lgDb/DlVEsXG06BHIuqVfnTyLdAQZYmJlEEk43pdgF69V12XC+sB9W5Tfm3jPwiHn/VmGszkYx+Er49CLbyk3hDBSKuzDj+nzCo77ZO40EIP4ZROdSwWlf5S8wfYcAzjNdj/aZ8uknw3tur126RfCzMA+cUo5mPaZL9cVp33X0mRTUIS2vgtwDRgsSSX5xcJUWR8gZbdeqyqQEEAeDu3+BMlrgYP2SH/le2u1yfVFn5JX9VQ04X9mmABR/KOd3rAYqR+OQwLWao9MXVS1y+0OKo0FlXuirKuPaY1BQbY3Vo05Gf/+N+u4rDcFBQqiCrYhgRAEjvVW9eNCaOsukcJWEaDuo/pWCYGJLadm4ssTCPvVVEJNBfVXAcTIxH4EFtWFMJUy5of50QNXNZBl+oRuFIkdbt04DeU6j2A3vzzP+IkMahLD6zBVJv+xRBIc5fODvnJMmJRMI8kcyMFqxpeWZAHxC68tGFNyl6yyGN95SwNYXwDSIQCPlL9bzjZaWNWvs5puiP2lbEBlDw5vCHtVmb/sD8QBgOhRassChwM5o5g4lhlD4u86wmdmVmhmEXnCyLeQJ0rRtqYIWRhg72ieDnqmPvOkDTWtKR38TeJwrK/7IRYfbNspygrU6yV9YtJyw3I3uEkDgbPrpcNUpISYvzv3beFg3ZN+swedqf3IVKkcdiAezu/KpHGHPyvX9oT6qzTS342/DenW9ctM197UfFl4rk21KxSma1KnLIWlGGasMF4+G3dxTnqBscul4CqNda6Qy8ita7HCzKlYa86yljm+HQA2B5ArJoZy4LNxeT9izFuQhEoEhUTNJQj2pCc/O44h8GpQX6XgpaAvAQJLVNq0yXGFbzb3O54XQ6sm557+lT3A+VWPyCJn1MLbsssHIdFhJcMtBFQYi0bS+exQ4Rq74xNE2CIRSzi3nj5TNy2AoO0gdyBC0/2iH67UB581jmM92OHqgD4EzAzyxDauPnlIdZu0nWwB4dtxWN+meq/faIuQpK2hoRP/ULwIJ9r3xyxtXxfFwJ3YquXldSEnxoPiYD85u0OAHvKOG6+3eBraUiOgvdfp1EjiroeSLLFutuPPV9XqhAReYPaRy87OAkV5tzSqvyfufCvOMTtkpxApWsJ9n+cNM2uBWu4lj1oDjGasCfCt6cfgCzh6UbZanbL/qCgf/iHjKYaavIiRLJrU2BuzdsP97XHkXLYbbfsHVTlXSohKOXOJ+3LiR6ix9UFLo9qieejYk+P4e5wC64jGQLSxJzYt3cErx1Rtc2+xlJaEBynLN4hLl/qOrgBM7a+yswC0Mh2OieA4SR6MfM9WK/FOWbVyoUBIUAKOhhIZp2LOgukk0/DInn7sF7dRP6Nw77MaAcYg6k0gdjQN9/1wtGVSBm+6LwkI+xfcK9l+JiWepXul+/EEdV7XXp/9lUsW4RQmIkda9H38FJj3EYJTrG4hEU9YWtNd2lKI1683cXFVzSMkh+2nuu9K0JUBoAnrYkKVZpAKF9G7y5n/KMZrP2xPuUFSOaruqriffSEX9Euj/k5dgewEyQCFTif83LhkIjt5qJ1LyI4ynIznWl1SoAdecEp+I5WmKBB2fr5yw33NX94q6HIP0jW3Np2E0r1f7fUjqdxV+iCRULU+yAwPXFvTL7HqfFLj+wCfIbOg+nsW03rGTf1haLvAZA/nC52pSDnC4f0qOiA6WtK20BldZUaA6GO3m5ZOCGyemGK4a12hM3BXnbladA/yTRV+pH7IiT/9WOijGGNXzV+K4wmdmRjU3It+QwUCRat2mGkEHhOcQY06pWeQqBGjHkWcceX8/drkk+tYysHMXVk8hLhLGjUVgivK1Ra4K+RtUcZO5fkVkWQ4W8fyo2tafhGEDSsflUH7yj8wsATBE9YpskR+r7Ac8xqdxtEAfRioGXSprjbLI2DAZZz9HAYR7rUHzvh/UPpFvrLbd/hFf7sF3RimWNpiGsQRZ11RqfZkck9IJu/FPU2DYr/HWUdskJHuLufXCvDbKn0F9sM31Hn3zIuAMTUc+tQsO9ll6jnNnW9Ulo7d32jEQMqJIrWQL5+Se0a8lKRp+XhYp4IfyUaTRC58vFEjKupeFEpU4EOp1AjeALc7vZV0ovza8QSl3ru6xFpY0/ckElMOChkhLWSDHLCKaFK/qC/SIfT50GJZnkCr5SgXZRddXq8Gc6XNjIzSdCF+9YlUFKMiri/sn1Gp/dEMhARah97GidLqitLNBlF+H8XoQmdrM3GXBSCN6izNn2ON0OzpCxOuM917OZCw2ZC0DSvNuTOFCGGYf1TYgUbgK2KKc4zm/25dz3GhVpFqs6x4yhZBbiy/6FD1vXW/aIcDiSUoIhwrUtxuGGZijb47Jz8JfUTblzx4eNPbXeYpygkQo1xXonjeouTuJvAH/zH+FK50zOLAtbN9AO6xjfX09CsjKitMVlHWmmQybLoBHBPkC5IbAZxvs3cH1VAcy2X90WL6y/0SXNsGeLBdr1OWVuYg+/wUNiR7QnP2ec7jNrZZOosT6Olwn02Dh6zSwKoDnMFLfk7lBO0p9mWjex7gEFXNfxFO19qmaoISUZEgdTuy7sHgrD/36o3XeFdzLFoFnOJa4yaENBXdTSmVZacz+5IGdVkEgjQt/TxuhNGHGtQuzNDfM4iNZ28Ly9S9WkUGMNAfDRLr4ipZkJxUA6HnlOi4Yb04/Ze8rB+HEXpDGC5Jpr4fN62LQh8o6kxknE1P5/rNmz43jehFlRUvCyNi3Y5St7lC7a2ogCt3Za6M7AshQdbVV2+R2DuuiLEJz0MLhnn/1/F2Z2U3h560PrnhR0Gc/5GW5DwO/DGrR/4PvL046BKjUp1lfrtKfE4osRTS9/oB0GrNW3cYgvhU8ld61sHhKOf4P94t4n7h9zdRXDaFv4ORPHokkY+NA9QA49RmsGMfJLu1/RXuluq0J4fsUUBoa9dL9T0yDJXvGtuoln8aYrNzoapa7E8cR73/wX6KwBPpwCUUlxsBtOj0rnca7zu5FqJC5W0U8Yt529SAI0S6nmWnS8zguQLRzf/gRLaqSQ6E9T6Q84u1cs56dzBMv2eBG+zAKw2V0x1NJX1gC8M2MYZpScdXEKPG1442UFWTEUlkM9OjbR4FurtJNV4IqEu1htlgltESO0SeZMHZ1JM7bNtYegevwPSCmW+S8uEGj7FTSSV0HbDg1rOnt4Ws8DxqN2T/HOXNd5NGboZ8VTSD6g6rLWcoWOwsyeG08GPG6KHPiLRunEdTPNmY74ObRGT1VCHP7nmBYmjnH+kqK6rDyrEoNjdqc8uG8yZrHWBXU9weqD5rpQ6S/annq7P/GiYepA2ZDdJA/GbdxpHYatPgkXt5sop564gVHZamW6cq/cdADaLCXWt1WgK7y11WaQR90YOen8BECQ56pmJbLvzzfWBhUUJP+dAEEK4o4wZv2+IBAFEdNkNF3mKntsLE5PDLA/IEiV0rziyORzLJsoxRMCQV/HlpCkXsaizcHT/vxU9iadf2hOkKehGum3973fFs7uRlqxz/oDerFL0617PqG+VYIxjeRb2IRLZJGH8vp8ITzF7U7HUg8Crs3WpVY5r8wxn8tzGvUUwY5csVu15Vmm1xcs0UL/lUCkrOXdLtlaa4pHLeQgpd/vu1ZzjMOcgzfQaIwiZK+fMZjRLAHUf83TSCOkovb3xPkD0jElmb4TBqFrwn8G4KWr+RM58qhCnlVimQ390m8YLz+fNHbBRDs7GJgHSK+v5Z9cwZq4glnR2eTjnqTy8Wo7BEg24CL/RT1AKzOIE7muo8oegzn8R6qab08LzTcbb0ippsScfjQoJhsr4jKG2pMVczpCYqptZcGD5rxTHFbL3+NDnEUptRMyARhF2FMiM7pgaB/IpAna1AHa5EPt7oBdzMGg7kOdSOpxrPXbdP3l/+QCfCLMpCsxFd3VAxA/IPVvK8JaenCYCadhyZ6rJeGxTUh11+OOAjrXIJxb/EbIy8rv6h7hywPp9ZhPCcgt9BN808JhGIaKwtL85jO5nipQyAF690xJ9A2DMuCx55TSG88fN6rqBMYDI+I+DtFmoAqJB27B/xxN9xMLnQwLcLCHOx4GIFCq3/6i7gwJePjoG/HKNb0XjhuEQmYFzTgtt/uIo1bBX4C+y1jrb+R0mRj+RyaDkRus8W4WW73qbcjpjIh2tGUY6KJyhEaKiK+LHG5euQeYZO4zXoKbZOWiJTvJNNVrWugpXkIIIE4zK/g4JKATQjtaC1qbJ6khaJHxOTS2goU5zGyjmaPKvVPrBh27E7E2iZ/6omwpBARV/9EKeU1m4Msz8Q7y3MzEF0C8VIIqAxB+Fk8qG970lhV/ZIX6CsxiHqybemqil3Qv/cWKm96fPoMJWSA1dcF03dSwSyNMdvKKBCYVYLuqr2pISKPaNRJJw2R43RNE6avh/TNA1tGJ/ilW/e4LbOvIh7cS2OsbjyXcD6WS0DYaDa+og0lSxehZQiDSt2fVdtF+DO7/cEUAM3uju47Fl17rUPkRPaheA+6/jpSYK5Nh6rSwO8Pbi1y4/L0L5SStva0NcscpH0pw/3Y9+Eqw1SDVvRn2r2d8vRC6YhQywdhKWraKGBMILqjiU2l5d3jb1tnQIwi95QiTJW7MAjJD4Plr9FGRGlM4NQyAiG8wSAKUbRCpmxE+zk9YhXjiC/Rbt983pV0VzovJW+90dH65IOb2VS+Wk+MpsRgZ86uEuxeGPyB++07HlAwqFjq0sm5Lvom/rcHSaLduJrDdabujYJRWbbY2QZptvGwTHAiaqsAafE9NQa2oq6hV8+E2YRbdEcrirxyx9JVWpti7CsFfA/egMevH0MR40/X1jQzMYbw6mr01MI833RiE3EuU79cpspC8tuN6QxFB7ExHF8yrFQ4vRniEkTgKc8kT2tC2HgNJJ+l/FwYXky6qbHj1cMtBGVOw3SFMHn5l5odYVrLqhL6R4DujKq/CEsEj742QjUogvrSb9DOh1Mm5Z7n6MI+YHii3bWp2abi25FJIiX3GM/137MQVr4wwQ5IQETnYx0CoXX1nLeqLjQ2VlOulhy58iVxN5d0Q2TEV6MPr+wA6lluGEC5890db42elDUvTbbMcjHGrT7WA4eEhNLqVT35NhLruSPkwg1UCAUz94Dj23i6dqS1MPh40Oyi0W+wfoWYXIw+siweU3qKdQM/IWLUwDjgMQuiK+CTyRgR/Cg+XmfazCLiF1JChK7C2x+ROCl4t2WjYngGRxBWRQqqrNqx1EesLx8Z8GOimBJK3Ip3O0TWp1z6fhibUBvCtBpCBH7Wz0MrsYEtW/6gd/rLbB2IcMxOrxgW5u+/ZBOjd+9Zg9SRf7ln5tqXgM7wZE2rj4u7BOezWvuyca2TpJkQOR8U/bR+LRjmN6RAS7MCfYSPtJWSbZYnQL8vGmJb39SyiYiER2Via1nlShjJEe3JgCwTOTiIQJ5h+NQeEs7qWkpIDJiQHb7VwcR7T1gLGhKAqUT5DPO5zvGPny/DOh+Lo+Xhxf5wTkF5p5yY0vM1gw2UZQ2nhCedQ+PBxACaAeuBYTyBs9aNWvYATPBLUtXJ3H/+rMIUQ3Xz5MJKdV6OhLEEK73rb9hfjPlA0gKO4j120U6VHh4AJvL3WqjaY/KCbwpCzUCADZmnJdpD4p4U5ry6/YuhcWXcVV4dFm5J8qADBWw9jPITjUtkf0lhIJkzhXLTcXQBZaaunvCCxyWh6ifYzNTTCGJcUD6DyfGam2zj4qdBy7DwBaL2S2IxicF7F2ubPDvx0+DEQVydAIF4Utn+/niyxDQpGlaaG5eRQcfYEHaZeHBOfZ8x6KnSsZnB8YZbLVBcEF3Mv/87cj4r/BYDYAaUWrrm/rWPImSVpvPlB3xQvVG305B+bCj4kIW4ZWzFnX7/nApDibPZxncAV04laDsD872g54z55DZylkUKHXF7Y5iFwsc0HDovYpJ1P+XIAb4pKZnw/e2BrTZn6jCeAAvAt6Z8EdXqS/KoRwK37xhZL7w17n2PYpqnoCtRAvnU/CocUq+el+PFEwM2GkhLBAJXvVbqxBMfPWlA8XMNY1+dfsV9Uy0C+WgSzcXw/ylN23DlELK9DPZ1nzFCvyDWygh1ABv0LXhuVuDEraYOrX0J/NpbYoxjl/mfncXN1DorfumMjOo/dWEk/OvdZ8w/66CtISpGM2htGRpT929qEz+kRM+2XpAqcSS9GOrLWVVUVIm3Ez/yIqAWm019Td/ytbE6eeYJaY+mJpelcp0h+4Y1hmcF9J6cZQEJi7foY8n1psVTCzE0QYMX+ScYxKxb/bU9eproUaSNTxHeNhomtba4y/CfLAZYXndn5ndeIjFIsRWRpwX3HwrIsKxRgd52tRs/iun5uy44w8u2wZgayiPbOTWGXUn/BDqak5EZebXbdQHyE0yEhUO5HcDnE6xlAuZFDSKLDTTZz9bWcfe1wy8KhSOwh15cBRibt+faUQgl7/5na6Nl5d1o7iUWTjOhjQa4z2Pha1PNGSn0hZFeICMKGtHJ6EGQbB+HF6+M2e8YSQjJ2cnG2SVpdzXlnkzxYqwXv0s0WM8nggSh7Viq5joXNiF3RJ0A9637p1HFJd2I7GrQ4ZTOWRi8jcZaL/25Pox9feMT7VDPV6TT++0Ri3a1aLS8IABZh2dWfxnBmXDWPdvrxmBiF3eePVqd2ZM5bI9YAN23/3qVLElDeD61xvgRdjkXkl2tqif3zsX1gGp9mzEm6suh1kWL75XC2kXlrCreiNi2pfI+iWVFJDXPd3MBNp7VSAZRp1jpt3ug1pQEM470lZXwotpDljklvGxuNeKwTuKNJw0EK74nc0d851QXL9P4pxZdM7pkmbA7IU2S2Xa/AJRP2VOz3Kyp9oW6FgoQi4noNkoHeNnprbQod8n+dQSSbMzNRZIuL/riHaxoOHkaGYwROCZwqcbK1tUnU2Qt1J+3UTvklj6wOD/d8lrZG7ucjZiCyHxK5XVtzq9lDJ4N1FvARCTUfnLeOLc5bmrtGvb8mmsr0lDDyR5607k41wzglZH1fExfmsXrEjiNLSzSKGb7FVusl07/BgeCclDsQkds2G654GVeUpX7UHaqQBEmJsIyvfxvz85+WyRaoYuQfSH9WpJLeUoXpUt7+Crnl1Jqz+eARyCmzL59OUUBwBuoQAl5VddIrfG6xvDA/RZBOV5AfwjOrJ2xRo4N42rCSFCcnOY7xfewl6tVLetiM2tGLqRLc9k/owyHriX1A9BnluzfDc5xdEUKyuwzWPG+tZGNDV0WLl1JyHPflzcBpj92G0AR0lGaMSZuKui5/LUMn69X9wPKc6FVkNEHEjHjQKPQjuFCokjN+N/6DlMscpE48IhHIa0Ghrc36GwGEiPRymXWKD/di92yfjZjDM3fdHBdwSxJRSBVKHSwh6Ey1/zWZRZ4kk+KMS8HuroIw1UPa+PDVpsSIKvmqZnZisbfHFWNW/dl9n5+wM4VIzhmrETz3k9WU3s+z84SHh2f7dGT/G5WvoisBYAgwm+pqFS0A8xyhy4PiKfgS+6TgnQD5hDEerpzgFSaMcw3yvDZ0+xfL0yznf0uY8N6APiqHdoJZOWqTPnTIbeBLc5dvFdh+mvD+sDtl8BAWzYR7QkSgnx30Ru7TH5a/g4byacurCNvG0lTgpkj9w42uqBp1zMsKr2riOCQwfCRKkuSX9CGADOYGqCHh1JUsk6RwvI9OvM9fCJoL7Sap8NUQ7mAvdB2ougA01NdqxVo8NeGta0R9C7QybiN4uAtDxw2zLTG9+0we68JkqZrj9tJilUV/f4wOLc83GfstXOVF2bAJ6zf56YworQQEDj6QnC+lqyMkGAr0QuAikm0jqS7fy9bYSBz5hekPILc94b8aUau3Kt69QI1kFEmcb19aFQA4bSegA9/hFi61RDIVQ7iOBqViYdGaK8d3zH5qWIjed0hR9e6o4zELdXWhOVOcPCmZIYYXvgUsAyGUoCszsCiTdwOaPEL2kRnYh0mNSZGb6/kr8XfbyUdbEZ7mDBYy0yTDxhkrpIoJmVutN6FHk/E4cTEolaGnv7x+QxQIKZus8IEygpdtBDxj+lC5M6HaJ313pLDYbjpCA+oYl11ISRJ/fB2oIdDBHFLefQmF1uHk7vtSmIyI7Q9HG0qxu8QRWecP8ipKR1o4bGrAhR2KcGEDE6k8r2F7N9lNUZCswXi/EXaOlPb9fdsaw1Sspku1xrmyADIImEs//XiPqI3Jl8BlrsHf1mAVCBmlqE7usMbDEpilt45ia5CXzVqlIZ95Fesu48LEATS3dyXVEjwQAqVbFBttbLfXvX4LhaGKv6P3XBsKWvqEFfq1rPYdohHtQH03ehlVMpZ/BRCBFV6dffGCrIa7OngRAbORd6wsIcR/gQSxhfrfHFmb9Ws3Pk/SikwIvAIYljNbXbvIpKTROSiPcmBDp4hxLkrjR+MfBFZLV5I4usLY6WYmjhT2kzW9XAxxLYCELLIf6lg6p/GFgpoRTm+yQ6PYtmKVvdTHyBxv28y3vTiy+reYBZqmC7x0TDasiMCcA+TxdKgDY4s61MpZyI1+RUzeMfx1qh9MBXg1tI/HSKpcUj7+qTrwp35J3ezefo6UZiEWMPBtx0/tJyaej7NUmUHVRBJfB1q0bsw4yHfui2ZOPNh/6R2/I0j09t9QGeRxpuJzB6DNbaPTOmER6WTXYEGXq7DhzkvCP247uSz6r7MfaasDs419fVF4RAt4XoxkFRmk3sjrhpNSeuDoG5RpjE4pI3rH/ESPaF6RIIJBiAbVU/ct/nKrDmBQPBYlNob0WmW07GhOvvz0m/BXTsPB8qA8Iesm6PsDuOLEEm5+jbniDFyXfndwIXHgWBB1GCyGV52MU+5iXguncQS8T+WyxaPDqCCXMjwPJxGObdF8mBkG2+SpqaBQkeN+1IL8Cbb72d3ySQUR/uO+N9v36KAiKVEPx8EERU0vfKi53JWN50+LSYqgHmF0UrnnHCNpcwfX8ezokGL4sK/rgFZlXnIqg6a8EJh7DfMOwMgTwRjjZ+TrXsj7SA6EaMRroFgxXRIOGDPYZgkadllrCosfuVZqNQwAY1cDJzuD4ocR7PgZYXbCA3g9Jd1PRx7PyRTNad56qFMVIv/9AYYd32opL/KQOuEa2LIoyMUHWsHVeJEgDnTAizkdfigKSmZVUDrztoGXA+B+9B+MYT2q5BETXJUKRLiEw3upTpXnlh7hkEk8/0D3rV1lUxxSlnDzLfFArxdnXRhBNu085RxiTwTISjItGPuj0MQknBfLTi9AeLTT9QUKRG7bxHm7P2Kei6fVAeNBP31q/OVsTuBJZfKaxLodsCxObxFdyJNLV2tAt+2SCAO5/VWcDOd7Or0wzbVGwbXJr73+/PYn3VfNQ4CSxdqgXNPWDqh9ZFVRQbSeb+bFmOpdkO7C70y6dTSHVuHlIY33/KV1QHDJ226atG4ltS4fk0ZNDrmPZ2Lps6qyMYO+Wkmsyw/ECuxfXcZ0zM7vmLjkk/LsX/XG0vaL3KZb2C51I5TVf8fBJmMxHHzKvaXDwSTGiya0f8ZZ3olqbqcd2cjXM0jicXlX0cJsaB81POyuItwEiYZwsHn4gymrnlD0mfAro2YoSC7KxDdL1DQVO+0a7fN1fLkv8ElaXx46Z8EGJ/W6akIr6uEuiFIQB9fHujgNzIzAgaDEYVITJJO5XQkyimdgaTBvra1hUbw4jb8imqVpd7G9dSoQVNPatqBlbm7NLsdI/einfpw6HdFlo9bpLb/wBxf2BGK/YWhn6LhzEvBuRuBZJTDv7HV9WfnA2SyT3HV/F6f+23aOYC8rxO7QQ1FI4/0m/OAHdCwYedzx6F6TIlSh668B+Id3ZxNP3V+Z82Tt/AHYSzDsxyYC8mxyk+Za4Q6u8y70AKpUm1NPP2WMeSHfqCc5mUcG67RR+sJWZg7P5iG4FPnFmWKv1nwwk+fM0IIA5p7xmHnj1zbj89sN0hc81tzI6enBjIyPd6P5GXzsmp9IRHKS506SAEK7IxfjQLxkNK1x+M8YAYLrD1qWXqo03kTvXgYllmtbguZX1FQGpXYjbZzgqSLxcXTKqQ/GhYqBJzZtvPaYGODBTozt0Rw6/vP+hTUJGOAYcEWWr5Mqy4792lLWmElkf2k2HiF5268DSkEL2oQl+VXl2NXgbfa8xxQoI7lpuNkURcA/pNz/go3LD+w41q4eQy20ecjCwekr0XfODump0XPUm2vvNfk4P/tAVA2PLhl21zoFOrSKjd6D1AiMtz/f41uWlBWCDDY4tDRMhyGsls4GW7P8b0/dGx6VTgC6oCCWxMyJyOgl5RPaFDE/EzGGGL9XUm5X9L3crn0DvEELm/Vx6HwlGWtnfZK7dA8/zJkr9b7PBgLeFlmXyfUBxZHF8kxgW5tcxvkEz0roS70jNLvk3QNCTUIwCHnqk5NRDEaewDCzjTR5lKzNzx1RHHJNiZZJ0lXrAsSM03iKPyYNdJfMwUAvRlKP49yIx7XS9cvseBWVvGNAc2I0PmR6Xc9KjqauqjgG/Q8i16OIPtQ2Ll3qDkunTNq2O65AEFG5qycHaB2/159N4n67iMEpyNowNdkq/ZlDxsX4dRKNvBUJaYqhID70qa2Rgq8+AzqTaJhuYrqrDDO1n/0rWggrBcFsYwo7ujJZblKGamFf+3B5MTAXNUOKn5PW91Gx56gtqTqz1dYMML1dFR/KZUZom7Wky7v9EfKnYbBseAvDuBFBFFCuXnhvWc/JS4ipUIe59Ls/kL+W5lteo1xt5bkJYfug17vGw6cqrOjTG4nQXZ+RbEDCMTf5JZ4DBcuVv+tGPyucc3B6R9NMF/lc4ubulrqcBPhRUjGBILbQ+4uBJ9eUHMAj2ijfMskRMLcV5FdgqIWhiEvxNVlZSRrzTzySfBUjZHCJQtbgDZ8nRWLwk6rQKWD5aSHuJh0vBgvlNTP+a4P7p59l0FYBPtoNpiFl/dOo05KHesQCueTxj7IB6io9sqTWxTu2PK2C3ACiXWNyxs52441hxg3eco87pSRV1NUvQeac35o3tgUpXtmtl2yHh3QO1mQ55wSqIri3PtVxJ57l0nOuyav/0ixzLEq3QlLZmLb8Y2JVlrdQMjhpcC1j0DS+VHrYIB4JgyXacVu9PCRoC5Y2+p8qfeJA3OFreaabxWxz5omyn/l55+ufQkO5e9iODCdLWl2crwLrUpaMCi8EUcVXGb3Z8oBCUdwuuohn1sivwQp1O+DaRFYXIbHQibdPfq4dU8WeiYJ4WKMlNEuQr/BRIGwOrAIM3Ppjmzvh27Lyx6xK14sUHgNy2ggNG57CBbXznFP/0NVrUQef5mMdso3AJ33SJxInqYebzcZ2pEVYHYczXE/+mcptBHb4ANtGohwQabL1xmFHav/wFH/al8TKjzGnYiFLEifJHL7OJD0x/rtzWuCrDToEWPBNtRKXFZqz/kBH6gsxzy/TUzP6R+C/A456FbGm8soK/uYyafgNmX0re6fgXeehUvtDCXdAUJElJt7AMv+VMdIrrOK7TAaHo6E8Khx1rq48yOqMqtC08so9cQh/AV760CiEtSm6PBL7JKCZBV4m7t8Gbbc4TQRawpuwTFyS/vt1JBnAQUBDPdEddlJlVAfbGy+OKkohOw9BB/JY9rDZQK1o/kpfl82umHijUnj0gVqhJCsrzUxYl+ygkRPDEPZqUIo/+AtsGplmBSxL8bUE1iBc8lCtShF2iqMC1DdHIH1DcucbSNtxOF9LY4IMng4T9eTYzDr+gnOPVxWBYMambJUexTzxyvFOneFg3r4FBEHqG3QZRgnKISYUQKv9B23A8vhFRe8uNZpBtiMtXqOQlVEbO/HzkRbqVaGj4s2XRVlhO+ewkvEaTp4pNLXG1OVF6ncxf3Fq94KmGuG29LLsFI1fuX35J0TsRNGo+TCioyTrXLVEjPztNVQL1/q5tGSrMPhfJEaQxHcrnqhVVqN1gfF+JK9Pgcud/lGa+Ig7eKQpJuUN+PYhBYQ/b6ahi4nLNe5+d8rQlfK/gl3OQ3WDGWuUMOt1YlBKoX+99JWlZr6tTAVgDF0NSHs5fqbU0euO7cXKnvVB3taBFHP6/KKZCBfGqzNo6DgZgiAELh1EYOni64dmOWUuwAQCKu+L8tnTFLlL6uKkaNtO8YGlOBVU9mQFYx4aGPgGEI/HTycxYXBClfKbmSErtcsuhalOh73FnzRz/thPjvRJcRwPtZmCHs1nYjivLMWWGprl4fRUOlrCDiwNU+9TZuaVsuCxj/4DzKfcla139igH7Z+0uskWkEq/c0mrsRLlVpl8ln0G77hwK9rLKc+RLeI6KLKy3Um5C6Of3qiKNoY/7ad3EFvdP4VICsuTMTii/bee9efmKAiym0A+l3hS7SofuEJ46In7BEO+Kf597wnd6s5mL1d5zNRBdOEmfNKyPdUuCW3u/SfFQes7nYlfV/B1DOE9p/pmgK+bx+eZdZUMu44uBGlaPvej5wxU9aumiyt/uCCZ4PyO0OYfFAMMqTaYcI8GxYeHO/3tDJsJisLleLpS/gvPLbEksIm3R4OCJ21S4P//uyzQ4EJZyYmWZjtknKJbz0vFEi0zDWnZHl4kvpMSPlVI8cEAG5r0JoNN59joEsMhUcPZ1YtIDYX9cnR711x6SQEnBGgTz6d3b1iebIdotlgqE03w87xlD0+qEykcVizaOB3Z+ocaMGWybZTIdpR4niV9mDm65EzKK8VQq59iMlABk54A7zAlMdkYNmaRuWJN+bLJ7RqEZf8vrpM0+3cwD0NctuwJJA13JIJVFlPStNIXzAW4pp1OnTx3rMZQfF+o4p92WDkF2tx1MUdC14Er9l1RlYsEYnOubj2IotL4tkgKwnE219ZsjXb8PJFkzakaWhRBJAkgbR6myiYFsJgC/lellsN9g1ML0j4HX4rwIzHbq20FDkBdfqN9SUnIbJf0QQr+QxHx4f0kRekXaqKZYUXYMbRKa6OObLPOaKGft7xFAgT2pHuSw7kdfloER91zsJPWQJbkAzyDFkkgUg80kW7n7n+WBN3CMXA3lU6QR23Ipx/98577h2OGkpcp5YiTX/TikBkcza+iwBGNBi/j+GwW8tGbKxpiSNEQqUDdqfscbVMQ+OSYGoeQKSLwREfUGDjR/emc+ZAJsy3sraTZkpHFZAI69dwO1dvsOw/Q+O/2lgghmEsk6NKzmfI+OYuOG2UoagP9Le/y9UABk4VHk54+6fW891qe1yVDT2KUc5hNeePBaQwVb5BQYPt/+2xEpqsHC4GY37hXyRSGvfwYa7DGUDbMKd8vud28h67mpOl7fe4uFRe/HOKf3TFs+9RX+QpL0+C2b4R/8VfkUQOABt4tcaDV34nU/UFXBUDvPYMYe0F24AZPIWphY9bLwt+tWvmuWwhvAgPN1rxvo3hpXvQNSPsVKgFUKENrmSCjWPYCUoQfJFpepI6oqpsVwJt6IlBFGO4soABNOS2KtnF9P7E9sSLK1WWOdGvYNhxKO5/D5ACMSM3oLy6XvjzPe57hP26DKKsIbhLZqcz8tJOcm1zlVKV87cVqDh5iOgGkNIKp7JU8eBp4VRPvv6peu3DR+ROhro3GOnpo6Cdltkq395hUi+pDXzwcONA2YjC4BKvX3JGZi77wJboSzwwPelRCe5297Gau3hHdjkNfDMaoCdfo4BX1IthlFNEHUm2nTsuiPe/rOux7FSlxIwT09NqnvyBmWQYcleqlPEreuoCZRFvXL07v84AxlxNdJM/atDmCjpmzumIoYOf4uVqV/8ZnSwV78WW0S0R7AwI0EDq4B6IaI6AUBwPrNLY0eeSw24zQ6qVAgBGW5aK79Mg+Skj4XxdPl8axMl4x6nwmnAfEBIju1ssp4yr/gdi9kl+ScGW3r5NVqJ1fXRkW9O0A6JBottvWGypQioSH2C46bepNpt5dXRK28XY0hseEnW9fDBaUMHziavWy8Q7jttulrsjOd5WunqGz20rPiwX/3fdKuQgv0g4CDqGBMamo9htCyKqN0qTOxWP5MmZG0lur+eIMwtcrfYqJujT19J3dps8mrCySt1MRdmlNIykG8cIMszw/nMlRV1DmpxNn2zf3gflXm1sXSH00EqrICj29dnyNSbIteQOqjPLqBf2QDDVVCAgcCz7vER9m5X4XkTIeB4ppqaFa2UHE05QSkAhs7FkyPf40UFGlKG8GnrdKq0ZLUk9m5jleTBwhdDsYP8HCDKRE6LS48qLHD4pvSl3XFvmH8KBEmyeyNwwJzAJQd8MqhmKsdandB6Ec1bHOw8agmVGP/vvY2C60X8AnR2r2HhdkUbclW9+ozjmxmipA1AJIZnqxg4aa1Le0RHfU2vkpf68y/rFMYgCXue7eNqxoS0NkOw9a9/WcDFJOh0Grb8zYjPgaSDENIFMCM0H5OlIqq2r2FKGkaQSMzVm87r9L7fysa4xxVMD0h7CIExLBVbCe1/r/WavK3yPhHVe3XBjyVTDOqI4/90N/Cm5KnqxFrVYOHbwMIXa3GwNwVME+38OpXvNwD6l+jN8BDCRDEjGDFC+WObTdm+5/tfm0QeEfVUYFtA7gTobiCnl8rywroMyBHNClofz+W7OhssrGuos+fRhh8kBA+Ni0fYdhKK+qCZaY0LUDpn17UUKCX6dOZccCYzSsD2iSQP74pFnhlkOzACsapdT20zbjF6ZqLgELUPT8IglaX38zP6zfdyBF+NjNf247XNtmIz4QCO5iRy/GcS8jjaWMfTxI3EbUvzrprtgRQDOz/eMnyVQVbbFiTMZfhfQLeu+j6iY0Qs/QYGFdHefwzAYuVpPhVZK/tXsy6DAioLlmNDzAu1eQ5ihCnobO+MOZtSD0+uTpiOAvPwGWf52xDUHj4zbdFtZULPV4c1TmWflDGMkg/Ia6kPHprHErwFTGoBg+1D6oX8lSPdz5srAF0RbktUTmq44+USAYYowZQOVbM3BWMc603Oy9SQD3buNTgzJ7yaMBbo/pjkzVrpW5xYH0Ra11ykiz32vo4nBg9Zvm92KHWhJm7uQJV5DMPA1JHBWBMcjz/uZupwXqjoTffeHZ17N3waXUaR7cZDs94ewlhsbQrmI7/A4zJDUZj0qKiVQhn3f3AneEhDwl6GUdCBdKY14q9n6ay58twW2PRXXPJ6UE6TUs6oqH/0xgDpP3bx/mfcCUy5oo91agCPtpTfowGZ0tyw5mIOsUqvdURDhjuWLX/WIqaPlYx3zmJ3ahTcxtC5xQgKWrQskF57LaOvwYN0lzIwz/joNYkiZwLyB7Joi0CsWWRC6SapEN5TClIisNQtNPmfwKaKYb+Hguo76RtcQMXdRZWjEJNHq8KZKeg/uWWDOW6aygLP9JDrNNW7JfWDyHPR8GL+29zBAD5FY1WZXsmYfdKU1VTLLzAHERJJGTpwKZH5k0uZrDYM8zG9WX+RVDM8bsmN8cI2wKz0Td8GEq9T4DvY6FuhMsqPGHC1tkLdxuwBYP0Lu2RvjXaxodrZhKfkkIwGcfm+lFS4WMFPCz3FwWwuvNLNqv7c85xnk3aXWl49yCW0YTzTqwyKuKWSIFJum5G8BBjvxx2yDOZMh18M2WhRGX5VA0p3eAilBsGa54P+iEat2c0lLnTrXg7fzDLJrjO/213hRmT/92zHwHShntUiR+9KUWKWRcx9OrMWfefEo/p2FR7dbNWoP/P/se7JJUfBzJixcPvTzMvSTQrccDAmpwoLnh6pnsAF37U9Cakvwb0EZzywhYhfUyAZ4oAu4R1X55yrbJifKRbLIC6NaYqZxbpzV9ec4/SFSjJKEvmVGa9tHfUJayAvrPPbVHNaxlbdJOOn7f43GTTdGGufXu/daAhuYtol2y5rFVUxlDpyKCfYRz3fOyJZEjhxizetlF5kpK8kUuEpKNWnSG9VEdmcn7Tu0/U9Pho+IZiTincXepD9zQXGusmr6j19TKRCe4dmbGmRl1cDDNABYeOKT51fHc6+d1Q9T2n1UMmkd+aiSUgNIrogqtnInezaEs7HmtmpjKttWg7ulLhPvEEnGE5TqPY3iCItPzYojGET4V755b+cNmqdG6OBTlbYjDs4AAp+ho1Iq8R/eWa0/FOyB4K5JLQ/WqwpaNPuaoufHcJMEld4peiw/7uIRZ9U4otV2lACBY2PfSUUu7vJ/iZUtvPoJmd8K/BmbnNo2iumTtQxEeARnjsHdzf1JrE1L6NGFsI7t81c5GCgmWILKM5pWDA5HO53I6aju6916JkUl1YcYyk9Hwwf/waKzGbNaeXD2d1jBd+rriDyPgR5p32kxAb41vjMM5QjUrVztISMmbVDBnx2qArnLJ6ECRGZcfK4U6LCAMxRtE+Y32MobWIYqbeJLCsaF4pCXyZjPABVmN36NRAavX8RXO80JuF2m/Snmg2NL0dSW67EVH9I4fcFSjpL73r6ohLh/V+uK3786Tpz4u9p1byZEEFVjn4eK4wBNeQ7DGhdbFbRTt6/9b55EBMfJGakrqZ4U+Fgnh2uIpidUcG+iBjHE5HMRX2ZKkKLyYQElkw/Kbj2w8OvDaxd8rzWoSUnwkiP9DB4L1FBdrrf9anTqNfPehHTBlyG9cgcQLrR8tQEZN9zuxs8BV1Zf+cIk9kSStcCODphQCbZP7NYhgTuqPh967gyo6DhJVEeM/gq2arEo3NkVtX7D7mzM4zzsjwEazeZbygY6xwP5F5NLqPJ0Hxncni2XMn/GdHQmTbQF1zee4LOhZaDlBzMZLsKXcJ3sJsBmPODcSW/FKYiVgzz7wLdz0C3bFpTwedWpIZzG+H0kpS6hOFF5yNj/xUGHEQK75qxYUFuXq2vFITPVf7aaAWUF+eBV5VbBqFcUccHNaTmGaDdRTdXTurKJ8ATxX0DHWz2qNhGP4nrYJRCKI12hvvahdfR6RlR+zca42mjybVuHEEGrU2KvnHy9+mmlQDH4jYHZKC6knkne5Q28ldgrISAF0p2u8YVTy2bGLZqUkIV6zWDXi0DuZMiQhOJwUgZQNnrjzpboxif7CaCAFdxHukA5fPTubF6aLOTWCnS/EP8ZSOIyNGpkn86BVLEgxNoCo5XDdJHdnSB0Zy+5O4NQSsoKdZzikwg0eSvXAE6j6WW27irlXjNHHxiuOY/LaFsSgXv62JfK2/O09r1DMjpxv32Y457Wd8wFBf9V6i6CdLP2Z9qNFsxcP88S7N6b5FAkZAkO78T3f4mpUVnXed/QQC1AAudBr+gg118i202+jHf4m1tBvD2iwt/8PqoAWQSajReU2kDJ91lZ9cqfgKVbzge5mUlKDSh7aeClFOoVz9UEdTQyNyjj+u7JaX9DWyqtt6955fcvBJF1aKEjjPQjYV4+FQr9Fnd8NqWavBRL91OUcILzXVselzvLQtPmmvtdhkUNi8G+O+b/qcVyHvls9lJjRGbe0YWtuq9zXA02yIjtBjoQd1vY0EmEFvb3u3xiPt9Wix6NZ7ljWQVbw229SAPrh/hsIECHTLmxKxWD3/K6TUieQeqJIfpcIoOQcgmvHDyyRUevzKImeikRzg+ly1+qSicz7hh/DCm/39Fyk6M86XNkhcEgJKANNt1matUHBPuMmqkqR0Irsee0uIofjg8efSzC4Ml6OzAV1PuydANODV+SaVqKrg8qTvT2ROpiQHqoOAq3EdFRo1QW+1ak/AYmGEVA4cF99A82GRm5mLHhLHqOSqBVNF5d+tjFko2morW+bAtWqE3Mhi2uYPJEeL+puWOoJaLV9uHtQIj2GvjqEnPiF3gSNk2kq1rb+v31DDwcalu1nsmfE1n7J39uQgliDyyoBoudkZrUtnIUrDsC6iGs/DA1YU+EpC8VYQ4iw91D0O8kJIRK0Zo3YzUzYnm6vxq+9EDAP5SWf+Eyupwlhcyq7rgfu0UcsS/cyy18bZBvpooyg1q0GNkTJ+MwtXBtDoaChHEqMdF/a7GjUgboSb8jHDJrfqRhQ/bbI62r8nHoOa6UgOaJLxxg1EhXpXmkd3Rch7uNxgpPzxP/mBdrGsygnoth1z7Q/YLYJb7LwpuGREdhP+ef4imi3CBmJrq9pWR8/s43S4uxqNYHUv9ha9RBACBhuz+S4xTQTZaCKSoDHnxC8CxGhiHczvJUTlt4rrWQpu9+AvsrR2wMvwqpTTd2ETTsO/P3JJiLBUvcs0TXCPCRY2h9Nx8ZqMz8XSEqa9ByDLoNM8PxxK/62v/Wkztb9dlxfHsl4u4UjIZo5lD7knNDevOZvFRYHhwFE22lXrX+Sffrt3y9R1DKaG/GlAPLQQX/Hetzpmce0TT69U3cFZSUWj1hcJa25OoCXx3O5jXSizjPu68eF6JRu4ly0GPmihJAcdY54LAu+PeTtHdGWaRfb6RVp9zxwP+2PoTSQm+qFhD5LkhsYuT1IwWLIAUjU9P0z7IOUj2QP4sYABt2vX5hJCVUnjOBPVGQTmwyR8LSRc2WvhlmD4DMitovW8AmruHvsuxxMnY/ybXB0f6jgvY+7tMu0sJN5r4DBEBXa37SH5PepbiAlY5L6+09qF9dbg57qZdXr+Lkj+9ODwIdoY9Ogs9QXAMPBK9sNLNDM1mFaODMVpqeBBx3+/X8BkyPofOmxl+kYJsG1PP50FDBXj0A4uVUwSXOnyDvjHd5pupMiy5DyOMVDjPDi22YVTeKKPxtGz5/wLm/x/DzHO4PBKlriUyR2fdazZ8MZwZO2yzm40RwLqezNhsNT7aqhOqWBMfTbYcyVtVzrROKLQ/cw8h9MBYgLQZ5m7RtajLhjAmwWRubbOysVY9+MbTxulvSqQymjxTj0/yGmowXOk8LorLHbyciHZbi5Wipq5e028xOnXPq0SO1Ei/BmXFCr+iw4toQwld1d5KXZJaq1eDPduqLEuVRpKA9CzB7KJsTTpdrYpMaOsIFM7Wgr9Oh/caoRAohQN6A6HSrmbUuxffYlS4ymc4W40QYfauuqpQ/JTXe2l3gW1vBU3Q0CQWi+YnGMAlM7QCe806vIrrgQmejgYb3z21bFn0KNZj8qMbtk0fubcrDYYwmBhjZezZtAK7N3MQKKCODWwtmN/WYEGctudKJzRB3xrBGIXPbh2oyOsQ4psvw2packPl36ulG2AlW5rvS3xsDrZG0jPgcLNOBZVquBKudvtx5EyYnivmLREWPn30cbkfL4RsfTwuJVSFZZJFh6UkofGq/bkz/WqbPwyDk8xppCVNz7JQstijvxEWrb40THMQJebLnzyY2q2jx2SLecaR7/0b676f5ddR3aDQqQxzS6YlPvFcYbw+8vic5SAk75H9CSsEorQCVlJSk7DU5HBRkzDnV2QtTJe9fsfqy1sQNBXqUXzv+3HDVDSjlHNPKEmNGm5+zlEP/Pa0mLR8hxOG5PeuHfsO4YAaC+btxGwKVWC9Se7tv8fBJBx1n+Kox6GyPB1SVukkNQkjh9dl8s6dR8uwRo6Ep3zrpyoDHwNvpGU0zV5/27gpveUjCyrt2ZF4TOPsS/WygLkfE2dbNXsNDXjU0kggbh+REnbrOGVNbeYAoc4ZX0aRdyTYOFzlRKaGo4MoHLkMH9FMwYlY+jItBYVbIzsByLIUmu7xM7N3q4VtOAzdBtYpwYx/5yTIIJ9yh2VZWg/uPZimDRgASUeaIeF/TU+n3NBLOkQvsf4CKuJi9s4FqpE2p0HLaw6yIcFU8mcl8Jx6XPWv+eL9Uv+Eyr1QVYQfaJcVwJ6kjFn9GSZ3uvbIxaZMwi7x+nNLp60sgdzogotqc5oVT+LDsygUDk+S361me7L2BWYFkcDER/Rx+J0tgDZ6wwKRu7kFtxCpqtt19WgsF6LzpqmDlLORvOsY68JnuZgBdo7ozFmFR6uGXxbySNeCvPKl92vkVsYEYjZ70nSsNQz9WiIy0pcd4Cjnd16gHVj3X+IIr+ZH/gTnYy0JQvVtpoQKA3yqTH8ZK5WAWFLSXjNeHCwtYmaan6uJoOWW3ktmR0n9j0uxSEniCHfobcaa4adhh6U65iKCHer9DsvpoFJxkj5jhGLhPSjJ+hLddzatV/1Ocn1CE5uZoZAMtgkhUYN5zk9+VUjJxOTjDsX8kQFan+fCSw0rK8IhXNp3dynfHXSYCNq076Pn60lpsgbLC41pl75UNjAtdkXJ0OFBP9SOFxYd/qxoACmCf2c4BNjgll3P8P77ikGQPLbKe6Bprf5RR7SLTcoLj+WEriYD+XvlnCQ6gwN09MIkc6PH+xS8JfJD7iyBoSsLx/L/1AzaxG7e0eIP2dxroERhpC6jg8arrg7XQBksDHIJZIPRhy16WjWaucMUOLtxrgBU9rezETjoCtMnBYdaOAagkVHdueRkp+p0+SRoZ4ejQaCwhOiYRYYJC7NsV73oO8dwYLioC3qILoo9B/eMud5uERJdTB+L3gaZcXObntZ43fegezhpmSwHyw4dM10xfsXF1MY5XAR1XmGR9Qz8Yrc2BSBiUUf1wSye1tGQLKtmsheBI0zWEKzJu8/tdWQ84lcWgnXo9INPwDU5XiJi0OyBQbwRH1ahR14L10g9kAYWlDK/0N3VzcgYYursjTtw/2wSHmfTGJsx5NOXmMmVliBLLHGu6G0jFBLZtUkH7EzFzorhlKhKRrLqXXlXpO8crQ3CHEcZLu9XzwCc9SvkPe94gxwonijdizLHtGfLLKLF1cdtXMFa7Mf4P/JQHiBZIRXBzCKoqPaIuvh7X4/SQdEJnxbsIECUF90ZnrLUpBjTXiX4XAc3Mse7eTXKyZp8Q3Sf1S3esZyDQl+BBER4PmbGOeQ+K1112FbEeyqQZg56WiQ0jRCUmP+Kew9A1ZxSjutLVOfkpuBwoSkP4RGNoe7WrmyTXKI6nk1Tnz0oe2Vm3PjBDf8Gwhe+fwAYSAjlPra1TtCj1uu1GcdIAm6ViQn9Srqf1ym9fPIxInLxt48mCIl6DSTi4ZJ+XkJrz2dXWQqhpSF4nNWapdIjJH+p1Opedufkw0xHlr4vORb9BCJ3W8vAPdZSqI7VxbNaaOfqhI/8w7L9horVKv7MLnEr2l2XgUM6+i5Ix58xgRlYVxa+ltEdaupD5yktPEOlldMIatEHTM9j7h7hxVvQPEbtQP6BmDdVaPz2u/o7+Aiy4lsXGE+Km2ss6828uqY4y28croxcwQBaemP2+4hEA88WmmXnQTmIMFje/i5qVzP/dynhApy5GEB55hU7+jPdveexxyrULupZB1hjyqISvKscuKXOXZUnp8dPLlTkOIlOhMu9t4Vx5PLPIDK0SdUiZ95AlS0+/1macnq6hXYYejgXigt9NePxN2PY9CC0HftH0q8httvBeLZ48ootbmSIZgK7/Wm1zqq/lUDZBL6CYC5KDyLg/WfRKIQMNyN2X432uLr/f/9AoV132hvDNWvIbdgJKmzFwnqjd8+MjwrCINW480Y/0ve7EpvtXHg4WzJv5MuILg89gjdMk86QRO9Q/YKdmb+HV6eMqRTq/oudO/E6zvH3NzGgHNz/zI4Clc1kXUMDTrnDpBI2KbWe//7iI6d1A8nhX4F+4tGki7hfsA4VOK83fdLmcdAGqQRjtItVXa3J7vhE+x0h3K+fVJpM2FZDdY7gVF9ME1rtQmyQOE+F7b6vQAUregqMnIegpxtIKRhyTvfx+DFWZLf+VUZHUO+CicH8sE+9LpldACFUpG+WMfE56X+8xIB5l+Eu4ij2kBUNYythq4o1kyIEuD1kt9XQ97gS9+waaIHokWae6jm/Y8Govgmk31Z2M0SBZAIeudbA/y6RkBys3zsWVHoPxD73jIs92cougppJ3Uxf/pQcoOw/qt20epdVJgHhT5/Rg5mNf+bvQ4LJnwSxs7VE9Qc/myZF4IFBUAom49bMTIghVW6RJ2gfXkP6ovc0THTEpxZWx4zTkARVTfH75vftaIkZptS+h3ERciwL+zFBfxojqrdRqqdkYWAVmXpf+ueckOfXPrN5b9eEwl8OJWgoXwyPM73RDn5ix09+qYTUbhIRquBAIHnO03H3q5TFdSXzP+sPDF+FV61ALiJwLttts7/NF2qhFJI57p4sixeZfoEtm0Dg5wGwPCH6tc6aqO8oe5R+IkDR8TuyFEN2w2kBdTxxvejaSoap3bQlCW4svakUIjVrpe7zCbbcGL0xSe/T3hysCfb20Xj0oFitmmY1Q+1QAbHJj3MfeeZfxuvYYoF7mLnb9sF2SPQEFrRwt08qapY0ODw4ReEM3TamVg4j3BvgKWWLIeWrMXPSM+I3hBzjUn6TbqMNWIPDWj5FBYrWBwXYB71BOpmX+5iYomjHoQ7LUcQ867QRS3qZXYnBbLy/FO2tEGfzE/rGyNxED2nvMySIIs4Fx3fZIsIZn/tCkocG9krZ5TWha4eDI3zmyCQeBMYsXlRDNsMfjEEBFh6/Qhq12c9IUp606kEY5bwbG/QnU+IAyJhlftn2f8iRL5A7v4R9oAJGU2GYjNHqZUGg2z6az4YMtQyXcV9X9WBRlaYnfVIRsmuVGDhDBIoG6C8AkCK6LdXd0NgeShgVCNpx7iacd6L5r4rVi1Gco6rCBwBfwyIJs4Fhnq8IZrURn9zhkJ2FenUPijnbIom4cDNJT3zqMfvySGt4ko2KqwoGDH25QLfuWMbcuRhuQwYKgCX9VgClxETR6DM5DNjTv7F3ysG0kI8NKZ5AZDzjJnJD4VVPwVR/fNKHpzgM8QQGSapVEbQCuiSw0xjHphp0eDxZeames1Mp9WwQ2puhmhj5ql1Lv0eYJEpN8RFa01yfNY0KZkTpYzcO/Ckhbb36k9esVXSMPl1G/K7/sR9Mcqvz7tEmdFwGaO02c6azfLxlRg6byx5y5aqHXBgH+N8X+0pGSjHsaENs0tEcJU4XtLrRLBJGIFVEe3TvIYkvc3siaU1d3xi9t7TPq1L/+hMRqojqmp8jBLyo7KEuYZeOKHFM3mUkV+XkyhiFhmwxtLgSsGMbh8fE6hCR2rTOIinlmsF74yj7IpViQkLbyCbrvDt5/yX6I7Y1abrFs7QBI3D9QnlxlwbgZHvFTKeaFKcI3NvUQFQURMimQ5M+eF6vwSlYff+7/cWpYmvPrIh9BVONzVYOe2tQdAWWT5fJSYL5Upt0L6Dl/pZObBEdo+FPC4b2+iU09eJ6vb/kc2/uq9CvCUV9KB+C/CPAJdOu7vq8wf/Yxy8081PEnm7VGsIzzoFYnDvfYTUyPhdXV2yICWljxWqkyEe4e1n+SZCRACDyiLTdzj5Dq5ThMdA+CNJhV09iM2iW1Pgf2XiLDkIpNo8ugDtNdVTMEBsO+uHzrqEI+EwMOFr2gevD8TkmyjvrYH9Bw6rkARUFwc7DRpOCIaACn2Edjv7bmiS3MFeVgdj1y0Rv+v1DYqY6EwHst3CNlpq6XBW7Q/fu+F1R20aHUR5Z1LIZ7wvY0E/w99bKzAyUjG7671ZUYF6F5+Ynv4Cm0twLZ+GTrBp8VL/LMeq8XYgzYldrklMglyWJS7iWBhdA5GraO3m3rO2AorN4N62bHcpIhG8kbvIkybnRVTEWt5a5f7iIYJN61OO1gLp+lMKa9CuaUR/y9eoF3/jHgqh6iPSadglFYQ/GTsLkzIXMTFtBelXwJHtvmQtoXItuOsLGvL2IK/M295YD8SaNfSND8zTfgUXGYQRyrzsPYC1cxWOto+YkW9R3EinZBFUy/5HWXF6WeqLcPADGeJH3U642mjV9hMqA/GY+7DcN2bpls25VizlGv+FyH0qhDmmd0gUS8y90rDX+Xk6y6McJ6S7gM/DYcoTHv/2NeKg4rjMw8TqrlL9LBcLKWQxtuJxVX7ObKDCs6fNlfUj6iRrGPFdJD+ziFknCJKgixZ5RJQEQZi2MefRmUYi5crYu3Oh50a5Jf+upvNzFAo7KhxO8WRvoqnLO0wvvdcPsaVUOIcvfZoUierdTyFyoxwnJI91KCBroEodybtBGshuLseewOL8RJP+H2Oqsca/SYdeeRtivXY+FFQeTQ33eeX3DdtS0+wgHXVCCQk/CkG/az4aY+ExO9eyJRmpeKAXose57USPZEoRKo6m3uIY0rsGhjw0xAS7X1DuBTFVuo29v3dChgu70cPjpl5/xQmrPdA36PXNZRWOszr9FtTYYxG7dHUooremnYo1QnUGWsN/xygLq9TDGLLhVH/pc4pD+15uGiALFzU4PINmfD25G8LAsJea1dQlpC1s7rkYJUQqIwFNDY4Eh0dawLn8fCol/rhUCEbEHM1dJlCBpXxKfm7zt/ZpsbXgy68nEkEoLjs9rk0E9GFFZoYLZv/4qZR7nl7qBbeALu0FWvdWoNb4hCvlkME+i5nbMafn9uVxxXlpXBlOxHA7IKvKJLMXQanWkuK9A+2VI1JSDoY06+R0/g5TPJIHfO3roljfhM9ncx6Qrk66xY1H0+2UgF+oQgm28A27u9+T4rGo0sT6suA8Jdwthg1T9gojZro33dFb5pubkZ5ZHchLzsKkibaR3DHxf769V4iImNuKKrpgMMK8vcvF4YgFx9Asca63MVyNPtp5+zXPASns3bwdmsxnn1S54GTdkB4DwX4L7JXMnQGqIaS+mPgWxbIZbFcDNIrMilEIEGFczfvcACtmReTyzqnpITyfsh5QK4RKX9ZWtvUy4bWXjsLYbNV7MrrZsT82c9cmf4f8I0sSYqVIlcUYgI782imxBuEKs3OWcogWDmwlr9TGLtVSSTlyzHUW4PU9f7Wv06gLioBSoAf5esTj3FD9kKtTKQZfTKEIOcCYWcfIk4IkcfoFGKSLqsHhBpBOTfEJ6dxkBJXCSlknDrb8XJYO4/96XFd4ThAg4/Heg3u5p1kP3QG2yMuUrty2cFQaT3cWMABIB2diEu/1KfFFSKbfjTp8aUhb99C/ZA5m7h8JWsGwT5Ml9Uhw6CmNHyRA15TyVwIsOH0I1tFeVqQaoqT7wGjyqrJ9bI+WtpjMv5CAGQfj+k2aPOJZ/zLvxAtkd/Bzh9BZPEwVE0I0DI82uWK72P5+mHKig5zbXYrQE5bSNA9/gHvSND2qLV3hLPnoJp5q/NeZX7mhb2aWf7qkF8iM4HEHQ6YiYA+E+kPmfMGabHq62QBi8sSJ3yb68iTcA4YT6f+gJb6G3adGkY9eeu7XQZiQEi2fXRSKUOj/zLkyh4R3hOAX6xhT1yCvCHT2Jb9tAzSMxe0RFbM3g6b/VHgP8nyZkt45j1ZYBTwOpQIaFU7nU5focNbiclNOds9b6I+FOnBXwyAf1ViJPMKBBofmR8wg+77g5o3CiYUzQ+KdNxUo14XQc58/GKrIq3XSIefM9azql5sX7KlTsU8DGT1HlHIYnd10cJYsAEHoN0mLKcHTySHsjTFesKWsmK+siZFXhlavE6F44mweXOrX6FBoELRrvIrsst4OH+O47VaML4CK/cNrjlTodfRr3u2XZsHCcw9kXLGX/15sm10DYmP3G3387x7LDyVoplrs0pzIvfcy41eb2Ob/wM6tQNLxQKnfSbL0eyYL+RWR09qeHT/lWpCFvcISYlmdF/jMaIWDyxE/LA1tguYOSiQtSqHfgqHr1n/k5nFhnUBnU1J1eys/8qySmWwIplgfD3uNcFHlg6trf2B11Om/f7E9onO53sWHhas4nNuhBJsUn2OjOnOAFZi2dcAvexHytVxIdybjHcEdXUcp0jkab19hwZ0RddTUGjtyulBmpbfGD+4d+oynTEjmMlYS/pfoCyhEk9XbgbBf7wtFs5qleFrCmB0NrUYZLxmw+2wFqYEUy2hYP3ZxY8uhRZeFXZfhOD58zGBx7lo4yMjiBc0zvOGqVQm8d4tk1CRpyGJOGJWVU4EpHPxqgMP6hV7f0IxJugziIEJHavrZauRXe0/THYEOKpl/a4jm/fah+oAzHRBqwetjJBSjNp5LaZ3ZUNQElZJBDOF1e4muumSHF6da394Cvppq45QN1B2wYBfbx4Y9fnq5b+heTNTCmP9XhMQGniDhmdhGzfPUY5YPvTUhEcaaA2ucNDUO/xvaUVhXDIodrM/05R31bnFkjUjn34N7Aiuagl9VB9SjYsu83Ws9eoevaZVwZMC4uiZko2GtNzZCyMHRq6GKhvEGBiM1gLyvMZk3eR2dGcn19YX72JnDBY6RWncG7lGAg0YZR9lyoCyQ13gtnyBi05gPlO9yOeIYGqQrhgRpR+pAvx4czdaBMpVI7SgZMAhMSsdPUEQ9stTtwSabBmrln0uHsOMhDvi0bNRUWUmqnu3eiLgzk2XKGyTaHCe59vZZcmDkk8aOO6pTw5H+DWALBPMcCOmfIz4cF9E5zesXbQkQNDFk7vlnAcetbpid+Ce9MnTb3Clhv0lL7lyusJYCpLpalVXmQ67YNR+IIDh9vW7XeWnU3FFfdnO0yqCON1josSLVMTTaH/T3Q7Y+gOUofDwwXaGyGRB+4GRC2kk7zANlgd7PmE5kXda4IpmTbP2OqUJ/O9EXW4aslQR5PtYy3tNMamtk4Lwzb6WIFll7MVBneG5vPfEGslblvK4unzLLIvceI6WxhiZNc/nr10k9nn8ikKPz5jmA9oC+lWIE8QR4XYTcO6WZ7VMORykmWLBbTE1NQc8/TBpYSaYjlsyOK50EEwZC6/hyMiltFDU/OcVfSs/4s0Rk68qJkU5mIFxzQcySQSzLKmqQzkbb2ZlC8MLMP8Tt/ui2UK3r3IoyOWjDNfAV+2/iYAbaU/gcEuC9PqZbBCpHpobrsMSJpIpAbdk+lZArMaQfdQP2kY9Krk6TsjNb/ad7Ghc/HTlJyxRISEoijGyuLhUJB5Ch35PrR1oibmRE3vvhC5cWj/AFFMlliT5ELHoj9ieMLEG0BOkVRUXKuv2bfaF8AdXORnzTtMfXYqB8UVY5TvybX4Mkg9YXaiDDrp7KV8wVHpmx3MIlmRkznG4Q7DbYNTZBEi2yxQfQW37NrAOyCP8AXP/EHi/BLLFg/ip1tleZLojlnpdzKgSmJyi4IRDWNifCtFxTRjzh2z9DNa3KUZLZnixrksQWHwp2gRkmuu7HYPHYIQrdjih0WnNb7CL7hFDLjbfGaVLQh5Fu7SHtZTqDYzgY4QnM/x2PC8v6+qmCAMbOvWxZOIxjgpUF1ud2/e41K1bJAXPTZ0ctJLsigJDqNH6fNsXGGXNx7cwJPgP6INK3Qxc3ylfv0L1e9m37k+CqkJJTN6MvvQuae8WjO1l0JvBh6yHIrZgf/Bt/DNS1QULgHfUCLdwH6GVXxn8JChzrTEJL4dTZGD6nCwPWD+eeU/jxNc/wph/HYngIZcSTOnA7ZoHemc7pUYXx0Nr45Sbce9CyAvFnCzoIYbXxoDXYVwt/7sf509VEfvoLzjbFrRKr4vntb5dgeDiwRX6neO0yQZsOSoVjVvOOSAuP4PT+ezKgOTL5CMeBFh5fTyCTneXHNexLrs1pBpLHH3kmt/Gi6938ByjJyGR1wM7/rvRQQoS1drQjQ0vefqIJKlavxUAyi0PuILAyGGfaeCzz00DKjY1cowpRuwwf7rYPEZOByjttnqj6EUZ84F5gZp+4HJmTpMjNq0q/lyKFhwHKG0wkVp5h+gESx82VKGR+mbao8YOh23JnEy+eNJ45yos7d1gFc6GC67dt+OzE5TpAYicEpe2YtuuIHNt0hQpdLBdS8eqx9D9RSrya3h16jYIp9Ogfv58USTrQa6bOJgC6Fuw3VSohoUOQpQ/XY+PVKw2eV8Q1N6yxzymT6QIiLizm3kcA+jtFVJVj/IlTTGr7Tj6P8fQmh0ag3AJfRbLs8nmEQ1QHGUtaUv9djTgKNG5hVLyiujHLL77tNlHcYLwqquU6Z2V+WMoDwfBiMDqK39/tNhs7dXQhQTHYkold5VgNmV+WJr8ETyoKTHTS8g1RZL+KCbZw1LZoGTgR6eNleq+XGRggG9pbw1+WcW0jzJpvQle+pDWTA3yPaJogeuohg7EijR/48Se6kjwNpGStelAHWNOtzrfgmNxtH9r1eSRWLz79nRNF5th43Vy+rZ9FcwK7PlfJojQmk6yDIgDVpS2IJtFflHkl2pdrA/ZK4Grks9dfURGUNk54HimplKaYEZX5dE2M9W/60vxTLBE6XeIZ01h4YiHBHGMX+eAHZAHpSk2dFZUbQL/ylbq8VdzyOCnwzB532xAsz2XqmJFNJCZ6YuvEpyZtLa07GuhPki8MeZUI63KN4jC30SSX7/bWpsMyfpqrzmMI+cCYlmRUB0Mu4kG/untuIlFzWG2JnuSThOvNB87WuxDF4K9MPLtApA2nPV+2yMqZtQu/5eBgMzg8/6FBhddJz3kV0onK4Jbo71w6dhI4czF3ksh7/wVe0vAH8B/pVGb1v7xscPIhg6KL+hvTtq6g1+kCPpBURUhkj6yrfPgZ3/Xtc22MaQJp0ouI8smF0IW7P8ZfkCNRlxyoz5rOlXJ2YoBYf+hZJACLpIW6Ecg7s2fptIWtvuAgGvGV7dSNLkYv17ghjkJQx6tLucnApd6V56PAKNj/7Yyi6MOC9uwvXC4HnQSolMT49c6/5ZRIfWauOyw+arQBxET3gqjgZPldHDuhPDdYxffuJ1ityuwa75OUwVzCfQ3DhhKAfuieBFYqqN1i5usxjNFwKad4V39gjt2wLjcS1yX59qz0LCyVW9KbSYU9A28hy5DC7hdtdQxRU9PX4vfg8R4KZzpT7OhJe4Rwnuob88KsYJT3Xdb5uQj/iI2b9k+IAL2RazReg2nxwi3ia771jH8mWcStAs1NJu+cMgx6oarFqLe8b1HSRxQ7za0WtQhVKdhOSo+l5MyUbO7l4rtMf8vOidRDYSBoESyiDirZR/lirb7mNwOHR9B00U3KDHjR+/6/p0FjHCVpWNOzJcWfIRQkZ6XmbdXoGNbYi+/6K31kVQSpEiFHlf0XTAzQKDh03BJv6aoldSXInQfAEINY34mN7TGvaILI1iq1F8qQD9LdUyM1y1GkmIcoViAyaqPmTF6srtanuyTM4L1D0wyuj0tEVAfuycGdwEON4fnsCqlt5T6S1obgnUutprS4s5WpzQgzd4U9TRXJErli2+o2bS7A/uISBZhgh/679K/zLda6gWtuZwAvTGNdCbAN9uwZti3Hk9kKWrIq/zDHz00+fSYLcc5sgjgY5sWd/F9nGirgGojICMTxUzGmVVyjsC+0iZ7i++UKuLA2KCekIgylXj+DAZVKUFgBgXYW5+1bwyASMUltB5MhCcaMuivyyhZw3MJ7OjjmJyH+sH7zwWOwFaztw+KQpl6ETunGZ4wgXDkkep9RDpXHKdERy5R1KfOfi61l4kXklOVi+UvIPbGuKxTqSuKxjgg5aUU0X3V/EKdOugbYyeYKlYTyfe6Py6u2Z+A0k4k2giHiUVqkoC8MKxTXxmChSs68WryAMhUxyo84ORdwTONcLdmrVJbnyH+ugmyyx9iKEPADsMijuo2U3uJDa7Wnfr9gcycQq006VxIwrhk0FV/BDjqzquNOsEJXdrimGw0G+JVU4/5BNk+lE5kSCYz9cOOfNBtbtPUoVHnu1jfPwwGlaTc7GUxPcDFnEgwaHh5znVnSwPAAdXz5o6vI34Epz0NKfx11wmUjfW8nTAn60/CwPV4XjHM2yzXbq/EA9hUimpPyH+gMWQc8fiEpaTtk7l1iADxvDO8EMdlaQ0nXdXnhCuCrsoC+Uvlb9IaXpTbhDyzTzYYUPRsJ1khYU6+UMPk1YHn7mE5V3/F28Yia/wrwDdF+R6TmVzsqudzix7NyUGk46wXs0WaHIURcZDicGiV7SEhoVNTU0zgBoaSd49LNnCcmSgWRMUa0JKdpcVnfovdDcIyEcqOXD4VeP1baW1O5XKi8DuZzNuEL/drafxlkHz2RIla0Jp8ILNn7S3fdeg9UhAx9q0+SKtkZq2KsJrdjjyAjr3GfTjVIDAz98414NxYOtS7EWs2ZaFK7+4WBYoC5Hkeq4b/TVXen2W5sxGUXGVbea0PfIOieEzqtacY9iZH8JBwrLvaO9mQx8S8Xs1qoQA5mRuhLUFIcDGMj1wJK/K+vclB5Bl071Plrpq5+L4WJ77f/haemR3QBDVN+DYo/NMMFkqokI7b1nRwuzDmI5dEx4XMlGANd6UtZZVQ12+CHjwiLfAM9yPWaei6wRjGbxBRZUWxyt/lA3BanlqVbrdSdMBG5p3j4Pa9sSfYjUr77zB9h2qpnC6V8u1+XFmGBTP3y97KCCHykGfB6mbCNng2OYcDfFxSp12MaqtqOwry+xB9gUkHlnfW9DENAGqcYOxFOWwZHAJEeIuPuyLr3pc8euQGkJA6K1rmHJDoeAl370hmHY+Wk02WBNr6bOj8owlbEPXZobBQ/xU4JVN9l2GH0nnIedokXyCvBiq+jOf90wECFhhyXgaKiOos+J5t5i72+cySCooSeyr88ULT2mwUuMCLDw9Pty72PByiEtatpiqNeZF8Kladg4jD+8iY+w8ru/PveAVmrABMft/YevFyzmyB1LNidUz8yrnolKmitwK2bPJrQzSfyMg7RCZtnj801QmxB2Hh1RdODJ04NYCR84mkyeVmLrySQsPfWBiZawIPusj3W803YTrCIFZh55a7RhYSAh5uolGsv0TMC+pfZ8CJFMfhrjIkPX4iPlpoVij0m+1EDPaObMhssohxiQLjAb8un88eH/6Z8SnJxoDDY9JjIkM28xe9G9BMqE8CdRizNqXF+yzFoq+i0JXmGCunk6mGwVz7dw0Aht2yZLXL1jgrrUpP84ikBVljLiJmABWcOUt5aq4e2FLPP4IYwNw6/6kBGhUw92jqGvzzSz2IXFoSGkFThCZ6Hdi95k3hbTR+UyOtNXxKf3qOHtoG1+tO5u2H6XvCe4OZ0IsSdV2C22f4X0XRjnoLI9dkAJcmaPzyLbgrWgj/dizWHsrNz5PzGCCZ7zywhZMyk6RrEJ5ucZ5k4Fosm8+U94ZyJFHYaHthMhJSLgoHd9plpggxNFeaBMx2BdSg8d0qM1P9s3xHTr7n+uvFsfU5qJafAkyfAi/gC+OLxCw0uMl/XJ+id3bpdG4VxQwyKvZaxCWrPaRHIy9KcdR43jv9jfykGUTzB9KjyF1G0SkyMHMeY5wgAmcEp9B8ffD92GR4FQExXAD/Rm70xyf9mrg0HowJ+Y5o1trz3gJx6Em+pGPt0PvCVSXsmyA7BLMqIiL8iKyvmFzR0O7FJPoUD5dZJ1eKn4tDUJJ4Umb72XTHqR1qs8KsHPpu1Bas2jM6FoTMyoX5aScTz2RVJH0xso6SkxxuMBg3uUblz4fj83SnK1GADX8ZJtrY6l5lrbF1/ZuSi1BShVAdFnfBB3Sh1SW4KQz2mL+Y4svWwspzeGp4W6pTFKdMDjOxHzkJHkAfLjLjqf+T1Axa9og+Cl7gRTi70bSWjsQM9F19HqH1IdJOoerLMQTLpuVpFU//G6/hsxG6sFsnzMJ7n73SbIizBrcriqJQot6sKe+uP1gONUVuBIPlDJA49atkvafSdkS4NR+zciAFrwoHjdIsVSJKqDxAVrM15uFJb4cUI1Z5j3Wgo4gLqLZDMdNtYKJ1P7oBTGSBKZGTqguAYXj9FtcQ4sSbuwAvEKj0iSHfGzNYpAzMhIVEl+O5tVLe4s/3uEd9Gsrl6bogS5HKQwX3XK8Vnj7lf+5qIQiTSzRnfkEpdxxgU0LAZG7OSxjiHkVD2gFaZ1GjKhIedce7dFUwac8qA8Ut250wwH7O4rKHFECWEhhPfyyNNFFWeFrcIjCB9QkpXuz0U80DXFirexggv6bCvxlzrpYL2A02HykHogeIIum14ATyzZnKSfKNZqYUHkFr6qN2/mPO1WK01C9CpwXcl3fLEficn+qMiFNH5a/JFJBAF2ZZWJ5EP8mGzPCF9CDlr0z0YHruP+6bAUG47CNw5yDdR0WDTjq/DqDE8W+/fc6iTB4r9945YbHjR76ZqoOFAkp3KnRniRLdWK5iKvLCCH/Jf9vzHnX4LfdHlAiEucOADd6aaTJnMDTB0DnLoW9pvA/TvJPoH2GYOwUyBgDkGv7VLqRPzjz9nIWylnnWqIlm7L9YRAuucHIleKaTQCeUrXP0Wnyp2nmBxzeDiVOPsap6l6MYLHO4xg8HBAK3J1dgvBpIjcYDKZexJV5mf8c0hpw5ODKTwdkKCeeTezcPXh/9nI/FlRcIYy8sH3nKCQ0EEucVi+uinLNXGTmZXSuB5jYC2k1R6X8FYDLSs7G3qg+Wa30/SZZVsN+vbIWPDRqs9HMz/V2eXRrxClGwzMRZTnpwuqrD1GTjLUluOf9uPygJGxe+/EB6Ak5UCCsCWe2GLD5iZX8ywqGyaP9CGKOOsQ504tSVjAMPPpKo7Ex8LT3xYdh4QReijfasLvMKd8/bu689y+WY+S8IO9LXV7KYzmOOycnb7imsjeiBPCZgNd2Hd2fLIQOaLorPkKjFZcGRaNO6lp+pBPTMvw9QIbYuQZBlhu48VmV3i/3Y0m71BChUWR3cdNSS4D96YC5J0Y7ZFqMHBW6G9p9pf1EMvsoq2dzX2wSvNYXqdP47zyePLrk+nreb97cBNao7U34lHDXeFQ+HqT8XvcE26g42SyQZmHFRlH2UZ0kohpcgm7Li2wAo0IHMre/0XfRV0HtarB6og11KC3Z7/RUcqKzEPA7ZEJQgZNgBZE02MFT702HN67p516Nvqkm0Gjx83wQdQMeqxlml8LDK0V5SdTdnatEK7C+bhiQ3CLRBupVuTeGYhJY/BbrqiE1SY1vdXZ2SFuvNbcrI6ErGJV8/qH1acDEtu58Cm9IYXlR4R//8FS+sjKjiIPcuzVQ+9bV25MODrRYTzxFJYbLhp2Um/HKOncgLdKHj7tOrMZfxR6CrV1qRAGh+vD5dMMDkqvh3RtFI8M/B+95gOm4879zLjARkfVycAOqjJdoBfgWjWNsJnafTkmc7B3nIQv/Doeol9zaGW/DlpeEHHLSCVAFpPcoRFbXqIB0NIfCnsKcK8GmaNVe1S1WmDjR9kV2WjYdDpu3d+gX3edjZ363f9jQEbUhFXtuRXOQv+gmYCubqBrqUoagUdP7xj0HIFEZg93/KZ2CrZfN9t0A6WcpUJBI5WLyoLnqf11jJxzi7XP7icTGifXh8HPdPwOvmb7A1BFcfY2H1yrgpQ9LL1WPc8f4dqfuE91BNq8DtcEql3/06rGk4gsNyWI77GnH9IKwUsAFlrpUmA3zzUPojorig8/2Cbd3TjsCKM9wxliCLyKPngKsM1KFkqM6bMFtyxYYrU2eewcxYM6RkLIzuCbt2tjjkrWkSVoIS5lGaeH9ACsgsCD8uBJTg2FG+jOXwTTSCvGIWOiSPmrIKKcqEISVvUcMWhHEeUKjXTMdtBmPl8s4WipwTYa2j7rmaa0RNf7IXAOT77NGep/q0h0KdWRo5UPERTufgAqHgtum1dZEPq6OH8ILA+nokd8MXPhCko+zgkNqNlrLQew5ugiVBI+TSaF0+Nh/0lIpsCoBQWlDacVD+Vx3x3aSXTbkp6URafBo7r4W0YMJYL0MnwFM5mzSBvH459mHAZ0yzT09dEXgjVW9/ggg2LxRO6yGo5FTpGQS5EwMSjG3crtd3U4X4CO+KX5W46TC5B/X/DpEipFhWLaE6rpYO0r44KwsS9Ge9H2dfFY3QNvXA1sWHN6WR25HgQ091u/FmxcmTXpvXerH0b5xRi1MwmGmrK4ZAT1TapoD8+smzXuW4xfFWkVDOL7zk9xNtB53A3+dJrIzc5OTB601UXSFtQkX3hWaSnhB0fIWaxp9w7vGQDYtDAeTTDigrLMhVNfLUpJcIxhrMjO0Amicb+Ubauev6gApJbByzVQRTWq047GGRSYgxukHnlk5+xWTYTi31cQQCJ9ILZRJ3tV05M1AIgNeeDW2H8IBJqkzSl9nnKSajGYOD7eMyjHHWbG4SEV8CvAH8Iew6SodPSlX4spOyb4O8XdYQ2bne98jMMolgBIbc8j1VfPhmdPcqVcmf5qMjZcC2VzGSMF9s4863hYPVGq86Huy5cmg6zBz+qDU3yje9vmEr3yJ6kZhF5z8UdlkJdjq/581O9VuCR2B3lyEAfQoUZot9HdVILawreyRxAy11JlpE3UoO/fi5/5omkUs0A7Gvb5+bsteFVIW+9l+qR2dINow47smAidv0bLLEr/yqKcUanjvixyzAQCM5CVzq0r7rDR9M7wjLxBq9eBWRVmyK9TfSJqXHjL8T3l8phqzWGZrkRC5oiPO6C5Wf59fFDP+ituUaiEqytebX0Feyu7U5Leql5gBMTdDPsmK7KUOyA5TuWxjGc7dN7kJKEYpro0VWRhjMArMIGbutu6vN2OSHb6nvd508S4Q34uCRKu96bSAD7YHASNVhzXv8N8jroYf5Y7E9s4wTpkvo3BZkkWqpF0M1vka3jjUC/JuZvw9V8avX+D9bciICl12vr/bQJxDe+TN9MQwDJwOe5HRWZKtCtH/1/2brHVDE381FF3JIILjZf20UTFL4MLwmZtFv3M88Bv1x6hEyoaAlZ5p5QEWzlw8bJBt8orARhiododtduYtJBSF7octT9JzbeKdozaif0LBWL/u9RjbeVNLZ8UV44Ye6Sz56Vn8QlwftWL01WoPryii3ZZ930Zx6Ins/HGvGQmHAD+2qvuKQAs8Y6ublb+Dvhp3Y2NNMjsuzOvb6m4YtkPzbhlctKadex8tBQuo0zhmSxfDIZm5VnEDdG2vZ6kcykYFxgAz3wrkVyXQnwxyQIeYMIHQYT+257jBWD0yJIiC3PqmohMzTC/65XVgSsowG2kgnlR7pYY18nBQ8aVfJ64D79rH2pymM4xMU1Zk/OS14XiDcldhO0c0RhQxiPSY72XYxpiaKVYmzOcEvI1PzQa7+LVZ6pBIwn8ffWvhqa38b3IskTs4RBkYs9i+i9/AqdAQg2IOeWv2fuo5tEcFyefI9nATJXQchbBEQO2Cj3kaBe2X+81o97B22kYSwjOkgZybf53qZFQ6p/N0dL/VnuL1cYTGi8k6rMpkKGx4j+Mc/fcHUVNXTKhyO10FkvHiN+qSbJGepJ/aLXoLZ8RET0Bshv/4hAQgzeS7yl0n74cedqdnmAeHmQ2CyXvMM0MWpEvA2ezZIKU+WvUSaGpTt1kvMloerqnqxHLfT01Yh2n3iD29EWnrQsyjedi1I5SUgvQKBM9G+oAai15cO1con2QFz3UK7w7ZgzM+vPmbk2QqR87fzlbdTSAhrLXzqVfLnWBA/4+5aC+0BRMZ6iX9lH3QXtKU9D01K3HprdilL456y5lsl38VQaMbz9hk0LgquziMY01Znz2WE4ClHG9cF/e7stVmn89oNFUE9NZ1RAc97KzDEWHLoKwlCG6L20/2Gj7/M6PDhsvhY+FMzYRg+v/0jo2gPT0UTCfaLBDRVvKQgUSYPMG1dr6ox7ohepBUS0msHq/V7A6Y9WfKDgSLatqTzwhOXnuXAoFc1LsdlV/Nv7XHqg5TAohZGa1mOn44SyY1fyPMCxL1QmxvhBC7mxDyj9DUnBpbjdAzrBW0mUzZ51brDVW3f0A8oKL6FYBf0mwK6YxDMJogq94OPgpZyKHKBYvJXMfs6u0pYnEn/jPeTVQMK6uY9Egww5setjqwdQmwi1ea0/uoNw7QKPorCWZohFt4VB+HUy/ObjCDdxryIg/y0wXGMwFyftSyf0v/ESOVaUNOHg1aA0SQ0KOwx/oqBneMvSoxZc7SqvQaHcx3ZLg7I0FQgQ9799KuVGTfGNgWvzIMnHqMNnCyCLJMNoNQK9XA4Wkq+6tVuCUREehKj+szE6KlaSwgAPfb6JeGqIyBrjJK/wNw2yPaYB9wHia3A56M5r4OplAvdVjO1vrsc4I8LAy1zqqpo0yM1hfixHeLNDG6ufXaX/4mWxYpqL3hBHpPbnox49P3jj/wGgdZFaJe1JTer036xd0Xak5qCI6SV86xqAdAChv6sj7ESw0SU7w0leCi/08lfYfucRQHdzjO3JkA7lvHw0ouMCSCweP+ms5HlStT1HLlgQ/pkLQ0HiDkuoPtTY6fDW0UPlH3ebKJKJsiIlEwAnWQ1ExfQhfs1IRdbEO6sgyC7u2YqSye9WFoH3s0+d4P2X78UPcUsRitbiSflMds3+5ixk47wEAbwHOouv3l0AUb9zZIP32hh+8n3fJx3LXT4wqErJXRmufydvyJuKW5IkA+rD7B5y3hJGUFrf+je8x2WEZ93MMZZjKF3R4hY4E82J7y0z9znWEXqtnGce0dejOBkrf6CbP1VCh4ixhRvmOXO9yA0A2XQqeWYNfk1eUkRWlybRDBiE5SOOtjudxOpqC6Hv0XRqdL58/dsrEItVoppvb13l9MrZRKzOe/vtw9JP9aAkOa7ra6MbT/3YE4LlEJ5ticKWKe+rOGibg+N20Vx6Vg7J3byZG9+hIpULnZWH4Tq3LmlMA+oUfgAbbzPl3twbDuQozSElI95KSsXaBWevUxIWPQdY+4eolMlTtLwn+51SP6BWFEiioYy+r2Rza4OqKJPMbx7t0CZCtpMKxYQ5JCowbAH7J4Y3Eh3C04j1H/2a7qH3cVo01mg0KjVVR59qENmLLCnQ4LNMS3i2XshEK7QAIvi4D+egZPpMUywog3s+tqRiaGXIEMFp3rd3TuvLXVT9tpJGxjgQLGMKXmGL1MVjoN97by2NaOn0JoIbOQqeBIHTVbBYNON5DD3XP+rStPIfVbuHd+90TJpGh8BlfV0dLneK2wDMnndVGVvQLhvaQxu6sL3XsvtxmQzeFWUSHLeAlmTc9yNQKkXtOJWS9faewS8yotiXdJQ6EI1vpVOHgh46gljSllVDRx9qlH7i2QFU/dKpaQEbpAFUBI/eSUGbpgT2ORGcUGXXDWjQJQo+nCkQVnIMRUCP367os5Iw4Rb3LDvOi+/mwcBozzUa4WkjVcSIURKO3RTFCiY9j3O6C5MBS6Y0WbBooC0nOzhKxL8xMIIaM/tnyEzIdlABrz3f9XlCiQ0hh+C7/bNp14eUvnjcHWjBOSw8E7BjzeXkRQkpIuZSOriwZ8PiOLZxCkXFOQ4hbXa4Tu69lccJ9Hd0F1lxkg5QnAhhfx5WdcTkBH3SibBUMCLPb/cYypz6s4GGDMV5smYibldp//j9gbCEhqanpxLsoexOMik4SOt879z21iz+8V3wgG8CicQsmxcsqCc5QUqOZhnpO4qAFgzHF+noxN835P4xf5EsOcPvYWwtzK3WEYVGy5tuvxE5WZB246SGIDgeC4sMge0B4p70Tse4b6NjlPHW+90GmqnySqY83r0ilaew46qmwi4RzmOcPehbn4YPCoISjQ44RURV++dfU53vcKhkSj6cWuh75tdSSUNMysFwoP+lN2gGTwxOfrha9wWxDPpimhEBVrt6dcBIvdoUbCLTDQDZuUOVVhZP4sATqq8z7Ai0STnGxzKmAHG+3I+/tvrDN/OOTHwR6W5aWSRj+M5wmS5hfdvimlus2z4pE6RV+l6scSEX3XjFUVgbSuuufln4qZfmgBxNvIZmkPtMh4WHAtuqRVdgDOLksqdhjqc9jrNVpRsYL4L5fXaKhNXYNJfTorxbaoSpoqj6ZEp05xsc4y4Qryx7BRs3iYvuHRbCUsiCPmmGdUPXDn6H7woEjiz1YeriH6NPF5au5aVrtcw0DvEgLLKMuVq6QvzE1mu+x9AFhhIEE3jVvzGWs7x+IBGJ2hfG8Kb57q5sDsPmddrc0s2doavGt3j59SpKkbETAVxcSwwHbpAEsYTNPM1KhVl7EPpQp+gNotyPx7hI11xG47CrYE7+4xlCFpaDwvf9FWescjE9qNrcgCXvSeme0GAOo6QjsttWQcRguwWZb6OG1VPN2xZcfyUeEGLHhPkrziDDf4SHNaCcXXJ9CtFdyRMVueZNWqaoSKhpFI91MMLSXju3pGbSzJlM8FPf/oxZbRADvlZZCyb8fbb4mQVBZZ3GWV4hj4PCrLA1qQvEqs9XLsRnoal9WaSQhWRzLJmCurnGGRc6wxyAAejp0pAR70k0M8R+ziXphTbSz5jU2xp2cFe1EhegrqPqjFAtYWbYwsm9X969oYf76RSVpD5DfI8iDfFILBkfvnZaZtHikQ2tfNY1T0QOYafZ+dfiQjWZxqrDxXDWbc/jYZSbOzpgJ0HvC9wodOgTk5d5d9dmNrnM0LH8bvtI4zgktUZdf/DkYM10EF8yMhbFqvpMTi+TaLBUNd9aLSzSGAqu41xsKxsEYHFPhxozYZMPCafc4U5t8Ja7k34czb9pTsN2JFnwl8AmZSpI39KzBoEcD8fz0CAcio2KlaDIhPF8V0HkEbwc2c0mkpBazhOMI1d4cxnKG15nlJ+haP4D9g/H1z7jIEHS7enL9st+r19iJpqLFuJiKD2NT7LXyBzaAcFxIJ/fo4roeZSvHUyfgqUjSVcPiszEAuk4Fgqjxih+ln6TZW8b5sbDIvrB1Ul++c1B63XbFgHdVJTaRPzIXeh5f5u+QYvfa7pHyQV0ZUIv4SnfFMvTC0g0/fdaaBd9rcpxu/CBpbobKZgCIyVRDZGdPlZs8UGyu7+Hxb64E/k0YIIyG0d7ZSIcU1dOwyAQt25Ow5B4W/oUhgU+Gf+qB/Eqf+V11+GylEkiyGag2sSabnAwgaqTr549u7USX8FH6EnKLv1g9jl2zIU7C6GM3aeDn8kP+9aBM0Agrl165RV4/UHaXPnrBjs3YOHlrMK9jziNkwwt6+rC5FPPvSm2uVuOQouD4+Rk/8X2VoT+8bijB9PNpfsOsNhiSOVgntu7dzfzJItraFExs2ylPt0vanTgZJP3SIxPvZsgaDSBNmxIh0KPLS+EZkJ1Xy0gY8WVOZDbYF9v0GJta6+GUy7ek8lisYumJ1nyw90NF5n7L6H1aFMYqA/WI2COJA7pWaf9Ugf5pniETIJNyNXtonwZOLeCG380p2a2m5Fs4WDJIbVCtkJ77ah+h3HMvJJ0fzW8OXfnZDuzbWB935lP5zr2+vOc7CL44LjNt8p2deJJKd+d8n1mwKwxWxUjkxJRVlpIqwq1a+Sfeu1oNGDaOXyS/LVoiWAi4/RFFK77j8sVBWyTeqc13DCYWKdEbHTgEcIdtBewm3fvU99V8J4gYLJijdis2O/D+3FBz8kG/SwAXwjzKgO1TmXuA3syLPxxfnEUxttkUPpzQJgAzcN6o79tpHr3QWX3TVy4USKZJPX/G7/sFv7TB2RKaM9LvG8518UTl/oNK6/mqMpSOqsv0xRVzNjumgamqz/e3LG3e1lkrW5SquqlrDJIrN90AProjO2hsva2vAv1ZNPbHVfvH6K8KnMmDbXcZImS+YAXafdXLVILS/Q0MSKuRaLPQABT6AsH1SpBlkiSLXyhT/gT5IbfD6Z1Jx0n7l33o2uGW4lgd8BRn8WUeEHBHEn2SCXVQwlREQtvN7iSC2y8qSngF4ytc3vgOucrGccauebyUn9sdKmkhMom+XHRGLg4yr7NW/ZAq8UDCTjimw0unj204NYoihtZTNdXwgmCpqzA6Y4a3S/braI7FEXELgpjVSnB+dqkyFq3Tny2G8lAz1OtN0TZdE3wgbqL8XtsE5Ut1NayTqmPNmEhJVC0f6ZfMop0HP5VawTxA+lq1XoeRAoIGH0ojuV+9O13sh2V2zoxj5jVyNGuZDtqZVlEeSIRI05PVi7nZfKw+EuT5YTkdX/qnx/AmQXABJR8mEbt5A8Oab2RqMdG+P0zvDI0gODnGDSO2w4ZOrD1zi5LnYaIljibbOMhpDWcwsd6Ry5eUmiLQ24OpaErO6a3/sYLybm9xOJLqfn7DNg/5SKBxEfKNyyUYP4KtkSMQI5Xo7dHcIhqH4l3CRK/gB7WtFU6bj0mReNJIitL8grYbUyZpqDuMDT5s5WQsWjOEmRSbMiH7HIkEIPvRu0WxMnRCJKjGFWdlKGqK96T7jlsEHCjsPjk/9VEQ4W5qB2tRAFGJ5YGgbmyYxqxGxduvkNdd3IZKcIbvtEtH4X7aHeyV4Dcn4wkEzUNRRhISM51Av5I1mwi2lj3DP8d6K9iFzNVDCSb+eb9pBu+SEqYrvFC8WKSi8OcZDj50KV871120hgz6n6OZy1KOh8OzKNuCKFt9mVlUfJKzD9gcuL53q+oTHGGIKFz4+4/zLC13N3l3y4Fn9dzM02uGyBGoJXmF3jrwW9OguOsh1FVykE1suM6kC/e005VRngkgcn29tixbfGSx7k8JzTId+5wTXE1HgKXCtGlwA7L6FxS+RUGGP2az1Em91D7THACjjqlVdoDOltQ7Yb4S8n4kG/m/CvtFfQB0e/e/JMgICLGKds6v5THENB7WYOdJ0P5s3GQzdbeXjUAG5Y2WCUBs5LZ6xDZzv1L7jfUHqBbmnHW7U4g+UTYB/tW7B0Ya0JAbpzWFSoVQH6CbY6q9fM8ccelwWdxeWdjZm+TcmBAHpje+emw8T5mUgl7Omvks7D2xk04/HjynzVyBN2dI3dBgxTkB1keL9tMN0WgyjY0ddKI8pigHP9lOa8hb7F2bZIa/FqS6JJPPHnlyPbVl+weIG7j4ocmWH/OkvaT4qtcbnafk2ocwOkjSqUob66ehit1UDMwKXreD2R92MZugTHNe/PWAZesANg9eBbm2p+4kqK52j8MW3AhqaffDN+kK195DUM4FLVYm8BQhOF+OWoM5tTD8LImCNRenutbU6qRxpaMDXCBU37/K3Y7eobcg/IaZaBuw44FteI67Hdgufk5VqCDjlK7jDBUtVq07hpPI9ymWW/m3nNLQlusNGDSBNYXOUBDRWNnHira/1eo9GEwVgpXn2tG1PUUxT15p/fbfGXCvpsj0QlzwErC0ge/Oqlsh7E0QhpqDAcvlBJOiXDD/bv01SkM269rmghWHJPUbmpq4trj7H6cCMXMIwWgOLaTXR0w3tamzJpReC8FXDNwkxSCbmg/ag17JdPyptz7mR3k6KvXor6tFCfEv85TW7CDWLEap1AC12Ym+LK9/CxdKPnXz9Qz4xNXGn3sG1wAfthifQfjDyiCnLo2uhuMzI9yKxH4PUTt52mReMLmnHFrrLpDYcPC+cU7ge55guYhGv/ANB92YzoXrI+Hs6gdXnnfE8GGhfydGwvKBKCtpDecGnu41Mz28j9/LTVtSV9WZEoxANMgPGo4BDbY2p69ixYGQWATdyg9TRDAK7f/Lrlubat60yuVZ9wcwqZ7NBP71mX6NEgdvfK1EgMnkZzsDQl/wWDHdAoOYCo4pKwY5I/V26cKTO4aMYcV/YDdgglOtas2KtIXBJAcgotsV4YfF+CDN4T5WdX808VdXh3/UXLrAdcMDF3QIXj1HyUHIOkXBH7DXICbJt9eNiowRXiuB0d1J/FqjPFe2IlNdXnwFwpRusB5PLSv0Lk/AdI1gQmao8wwLmnoh/L9riMbMMsWAOI+5B71d+lGTKlxx4hQn4ixRfedyZUUsRcpGrgAS1XqCKzggl0/LFuyQpe9BsgvZGkEHQ4ELkl6bcLtiHZ+7uFxmRjnV7v8PP1Whug1igIT3OTMnmb/dGJPuGKY5fRdvWoatxfNU3ABi+fY7eHiPqC0gQDpAC19twVfWBtBur+ST+y7fzmSE5Q0C3mcp8/31XIdqm7sEZJHtFnXBgaTyG+fWRGAY70K10IBvKH2TE6IMzm1k92/Cn2payTupKTtojgP3uaWIgFVgV0lD0WGR0PanqiKtrBFwqznvb/rz2PgpSjWd2BESLQpxY+6tmKXZnjvY9xfR12CQ8o/aKz1t+XxCSzy0uE5f/kaFUCrwxjL8gT7SEUJshp//5/yvPFJHgJlgsvXp+gRQCSzz+vS6rl3BhMsbj/HzwJYz8GsWppOQDGVswlOHEaFE/qhImhDrt2DUfNxtt21GW7KwJRn9/mtYIjlnnwgESPEpwoLyTru3SsVGzRxnZG6x+BiseUs57lTdb3H8KG7UPeH1SSjy9wZHELnar9x5cOtOR7lOvyjWm4Ab18Q+qoMxxLCFit0V8SmOu7AU8XGY3eSXb6Ly+kaQmDkRlOstgmcj+rD34KNz7LTvLL0O1Z9J/nCjp+1flOFgtbd7Yg0t5eNrPuppxYxJfSpnJRNL4S3YTffnV+x+zVsuioseET/On2wNi/TnL2rAQIKswi7Er3Sv48D/+PLsa2WJOSk6DqcCLmusILDiz0FwKEhMewrxtNyM2IAE0/6hiopIQoUgC6U8CLirhWbfVibSnCGZlF5uywIcaUlcEaYP/evokbi1NSquO62XNnWR4+fB3M1N7LaI5pwdHYOKEjg9OaSiTtEDypKGOVxZhdQS0jEvZ46foNS4SBpwZfPn60p6pQldNUmimhWeU5LUnEpZYjPJU6hmAsh4AKaLFfJANrZ9ou428yoEIFuiY9UgOYkqtSUocWxyijxK+NTtuDdbh7NJcyLIl6CUBWQjZiL34Bk0Qe3vmT9tpIKus3r5CvEdEu5Va2Wxm8CQJT9bESzuFBeH0QIRybKFAUVqNa9tCXukd1jwLXYKWsuMuFda8R1UjVG2cvAZ+R3lBV+nLksL4Ti6lubX3hKFcSyFsG5rK9pJt5nlSGIkBLP/HFqLL/KX0S96NdOo4CS+GYPBk+lBZxz6Yie12vvUj8l4t1ik/5PmvbLOTPCcaoPeZ7APUQIKIcxcNUDin3R1okbeAUGwt7Ja3G0ntQokBhlajisyXeqbfPLrTTKpTauclKp+DGdyBsbzFHEYtIqZnlLe5wjluF/UID6EgwWPGj0FVKM59Jom3+0Y1QTb+IKqHZv/0FIEEuVItlJHSixdza2w0UN80Hyc/eUGv6SBybC/EEs9cOcLBR1eeQXXe7p7hfIhtxxBrGhk9n7jom/4LXF125WzPmMCUiNyE8iO7sVSmRf/iSNFBveZWGPeCirfJ8a43fk5jCfA3NPEJyMAamu3Q5im0DKo8aonWXtye9iE8vraixlVTAGSXFMjP3+XiOE9jrnXTDzARnt7+9gvHctQpaAI0za6N7bq9R1lb55jILwmx4Ih4OA0K1/Xx7B9jytPFBRhEO8xqXLhxotsIRjnGRvnkMK/KJ1YhE9T2mNmclLYgMSn+7dzik8BzoHt+EcXstV8yNpTspqsnS96ATq3A66NbF449w9JqViBt4gWi7yVzt3kR4XSJ8iEB5anMqG+EsSyrMQVv0sMeEysGx+yYs6G2xPJw3zqTq4RzDQXPhYra/VMlt7E8zzl4D7L3HS3kkWf4ZkmFmnjcENPQdkmohl6p/gqkOg+8McyzNxxb5Fl19DsSr3MTuSMqhSKDn95ibzYCEdrZXJiKaqu7BFBuju+jSObOPchog2IsE/u/3U/UK2mntvSnD0qNkPYoRTskBnLJ3NJamL0V4sEbryX8NMr7MKMJ0+h2+xMKY4KERpvUrd0c6ABXWHqLdY1QTugC/5dhdoLy3+KwgG5FnL0MZw6qvOvHkKQRoQrcKLuwUld15s05QxurH67A9eAr02a/vUWNBIgP6vOa69ZZuZKElWttIerRDGIAkZ54fw7HBctSZtfspPxaliwbOEH/Laxot3ZQonzvXknSVodzZHA1Jw7BcNRsYvl+KJ0Y6pMRPpIbaN/QSuHtnjUoej+vlVhq5021xMUPKxCK/D8rSRbOmduHG85/JrIimgo5wXWP83lLvRaxwCxeTGVt44fTUqsfUARmQcS3f5DbHR9SZ4nJYIEvcCjIqLezJ3I6S7xBop57j3ZyMQX0Xxr5mc6IUmrlOXM9fJG5iDZQQ9rWsGZ0Y26GzTAEsD6pjPuDa1XAT1MRpxyZ8zN53sl1YEV0E0EHvZqcnBnqMTXRh6zC9PwDXEk3OHs2zLLIjBhY5+7lDxp1X0qcm8XtWorat33mUx+kEDDgaDUdpclQq/ZM6mMYoF433nKbCKDxCozugSPVaRjNPosMDy8FujvIJSb763XuBGBIYLS9x+HZhYiUa9xod0xKV9aRt7yczWWlLgfK8qn4fULHMBSP48m/wTWfDBdTH8uDAKt5WM033+2bCpxDhmZtE+d7XP65yBTOf9/EWaCG+Gs9/5kVbWS0JlfoDH6Si2tVCzCRGfV0XZAUWfXOMJ5F9dkMagbwaeqVqqbVONDQGg8zID5MUV7IkazdAz4JLOXsn1RuZnoZNIGV2Na15+dRKYUAmXFmkWBJpPMBwT8N4bd8VZwBnhm3WzH9S0sbpoP0sgf2OmPvQ6smMyfkVK+OLjXYubmtioAhdwDb5/pLRg3PGwfHEz6v9OOe4AK8iw2cma49tV44In8Rc9jGcqSQlFXPdlC8366ke4U/ITFy0/SQBl1vWvGk40KycwWGaLf8cCtEi/4X2W8961i6lYnpfNQhGcQyC8s2oIOW+Pw545Thq3ZBEyNC8YDr/pzCEmBI8U3A4IiQJoHiD9kUMNd8wfzysC2Kqc4OGeWYsJxmDev4Jn4HV+vqpgN6xxSEMABhRMdTteHiJAgnQEX9BR2V1sNqh5EcMvQNYYa5+bblQn7Rli1UFCtQkP6ECmGkxmPNkg2CGS2mmf0/WEuTZSyPMtbbrnftPgleOmJ3jSm0m1EU9fQHQo1NZti+KczpJ8mSYIVtXzXh4rNJcL3Fm7Bbftpjmj5UnuDpPk8HvqKOj2DGJyk4R0Md1x7umiH0DTOXaLwO0EI94k7n6R8nfqiwekgUQZ1rRek0HViM5YN0JLWp4f4NRE8ErcGNSHZd58+9Kx8lmkc9ogfQmX0rX1kB8QQzNbH+eVDee0jOQNUgQcew3y+0QbifXrtLHXDIxsqsej41Kz7vfcQRE1zUnY2phYNILK8a657zyHNMzPiRhxs28s1JX2kiCMEloubOXnc8BzU+n7LM9wztf63eFWN/eWHXVivSdCWg5DfWsk2CF8aFJrOP277QEPdkWlOlewCVEkLjyd5wUn9ZzaKOJKnDQDLfliiRLTKlU8TOeQj8jOU8FfpM9tayJTDpxw6sVlZuJRAILfxn+QAGIB/W1FGDjuuVu62hFDBdvzVSfge95Ebf9pclp0GrpV3S+gwBWn5J7aGiim/fRyIN7YVVXJsnAnVeq90vDdAV0XearTqjT2Ck/AMkBW6T/ls/6VUVnFWs01wxkahKR0tRwyLRKgHefm3RWie/pTVQpUMZw+/7ozQSW+7vuZd8lsvT1iX5rwlpiaFnOnDbHsr1As6vLETd5HVbcBCGbJHcS7ax9Byd50jdYyagUtjAaHYX8ryyuR/bDkw1o4j8+hXMfbzy+CVmgrfRDyl4dn+5LxrqRAXLoDKpQREAHqdLSsVSJh1s8KnZ/SsUVq27cq+O6LMSBmhT4X3E750rmWwCsoCre6bT//oFWYALjp2SbcxnULBaTvnYDHtfEbO1m/3c9nJk8ZO5KHQTV88ivTWN/S2EXwmisTPdcupMrvI8e48QZdkZu9WHyKron7MKhGFJw6Z0KZ3tleVrvvJo89siUwByPY+Hs4gkKPBQbLQOaedcv/xeM+Ih8rl1eHEC/C65xWVciToVqSGp9HfbhVzFSrO6kBnv7mJwnRLvMEwqiNankVdJJMw4icU3lKyw/ecNSWIUddqlbThYMiq8nHjRRufs+28cq0OI9zhpvxFvFgSZE/eAYvm0x+9lZO+EH9NkBngaqU1NMYhdombNuy3awUN9p0mJQ//e9L65YbShgoc+ZUlNy+c6F6gDEHXV0JrzevPIZFAe2RyRa2dNqzLvihAAMCszYueqszzXRkSyobx5+LTLK2V3lfg3wbS9DzP3QW7VHdHbjZcttQRvtjrGveJnNn2DE2ZDIbvkCrT0H8RzbGDdmIq4P1ey+hoY/W6NuZKOz4dv4HUNznxdKV1Wf3MvqUv35r2jTKvpPWBUWNm5fytX/QJwp6qkIOsSx7Y67BSCbCDVLM8/VcMG+T0j+INrgL9sfT1ICtACH8BI0G6ViUZPVzzCmQHW2oVIwZjAoFl6+meO/pD8teO1E+1y03mCpYfW9S8qhtH2GhlFlebPf4NbezVv9xbXKWz0xezRNQWqUqtYRTUbuzK7KTvjG4rQHfzBpVmK4wDLnSIwdSzTSk1fPNeY0WOpPZTLlvQ59xwgfFrb326vT2hS1JAZ9E6sujFtKTiJ7bxI6o4cBhDaX+adXREThhR+MwA4TqD7rga/o9iY7d6TVRe14CS2S3iSQsD0R6ApnhG/2Wa0A0AY2NtWTjmabdKU+KgIRDP9RQYVjXiF1qC+xyNVG03I9vpmEpY/G/zC4nLOKgXAZ/uTikHI9Afbkhfgfgo9arWbix5eH7WUo9RQygDzwCnVSjbXc7MihEufVj6WGbK963pw8VjY3RS8IH1cy2yZbIcKLO5CgAUcXJfF2+McnDLKtXxyZaf7SPA6KJq+zF2NHyfoeTOwHhGqNcnHVr1hT73pcoyXyfvCYBnG1Bp/aR9t8hoI7CXM3UZOisWGA1SHZ2jf7k9GlRnp3mF/c1AV+JjvUsnZrsybEOQJg/dn/9eJkyykQHjbF56zgcPX6DdMG03WKUMlYz+uOZ+5DZy9E9MZOZ9GMoLFdrIPPQQLjv+GlCMpoyHPXkzIODjHAID2PrnaRpqWVHh0rnieDILKq+Emrd5RnjgE9pDUXWTmHaKuqqYlcgEz4zbi46dbWrAAFBjsQq1rLHIiPJEcwFLCOY4JNlXRXQJqCUKXk2d1RSBGzDP6HDSpo863BhVRFFF6uIpjQV7j5ebFe3UkkO/+coIo2BTAcgBqOtQ134s9a4QJvofuqBYMGOBMsWZ+sn/2AOxDx6SfAnDFGw==`; + + +Uint8Array.from(atob(($06269ad78f3c5fdf$export$2e2bcd8739ae039)), (c)=>c.charCodeAt(0)); + + + +const $05f6997e4b65da14$var$bluenoiseBits = Uint8Array.from(atob(($06269ad78f3c5fdf$export$2e2bcd8739ae039)), (c)=>c.charCodeAt(0)); +/** + * + * @param {*} timerQuery + * @param {THREE.WebGLRenderer} gl + * @param {N8AOPass} pass + */ function $05f6997e4b65da14$var$checkTimerQuery(timerQuery, gl, pass) { + const available = gl.getQueryParameter(timerQuery, gl.QUERY_RESULT_AVAILABLE); + if (available) { + const elapsedTimeInNs = gl.getQueryParameter(timerQuery, gl.QUERY_RESULT); + const elapsedTimeInMs = elapsedTimeInNs / 1000000; + pass.lastTime = elapsedTimeInMs; + } else // If the result is not available yet, check again after a delay + setTimeout(()=>{ + $05f6997e4b65da14$var$checkTimerQuery(timerQuery, gl, pass); + }, 1); +} +class $05f6997e4b65da14$export$2d57db20b5eb5e0a extends (Pass) { + /** + * + * @param {THREE.Scene} scene + * @param {THREE.Camera} camera + * @param {number} width + * @param {number} height + * + * @property {THREE.Scene} scene + * @property {THREE.Camera} camera + * @property {number} width + * @property {number} height + */ constructor(scene, camera, width = 512, height = 512){ + super(); + this.width = width; + this.height = height; + this.clear = true; + this.camera = camera; + this.scene = scene; + /** + * @type {Proxy & { + * aoSamples: number, + * aoRadius: number, + * denoiseSamples: number, + * denoiseRadius: number, + * distanceFalloff: number, + * intensity: number, + * denoiseIterations: number, + * renderMode: 0 | 1 | 2 | 3 | 4, + * color: THREE.Color, + * gammaCorrection: boolean, + * logarithmicDepthBuffer: boolean + * screenSpaceRadius: boolean, + * halfRes: boolean, + * depthAwareUpsampling: boolean, + * autoRenderBeauty: boolean + * colorMultiply: boolean + * } + */ this.configuration = new Proxy({ + aoSamples: 16, + aoRadius: 5.0, + denoiseSamples: 8, + denoiseRadius: 12, + distanceFalloff: 1.0, + intensity: 5, + denoiseIterations: 2.0, + renderMode: 0, + color: new Color(0, 0, 0), + gammaCorrection: true, + logarithmicDepthBuffer: false, + screenSpaceRadius: false, + halfRes: false, + depthAwareUpsampling: true, + autoRenderBeauty: true, + colorMultiply: true, + transparencyAware: false, + stencil: false + }, { + set: (target, propName, value)=>{ + const oldProp = target[propName]; + target[propName] = value; + if (propName === "aoSamples" && oldProp !== value) this.configureAOPass(this.configuration.logarithmicDepthBuffer); + if (propName === "denoiseSamples" && oldProp !== value) this.configureDenoisePass(this.configuration.logarithmicDepthBuffer); + if (propName === "halfRes" && oldProp !== value) { + this.configureAOPass(this.configuration.logarithmicDepthBuffer); + this.configureHalfResTargets(); + this.configureEffectCompositer(this.configuration.logarithmicDepthBuffer); + this.setSize(this.width, this.height); + } + if (propName === "depthAwareUpsampling" && oldProp !== value) this.configureEffectCompositer(this.configuration.logarithmicDepthBuffer); + if (propName === "transparencyAware" && oldProp !== value) { + this.autoDetectTransparency = false; + this.configureTransparencyTarget(); + } + if (propName === "stencil" && oldProp !== value) { + /* this.beautyRenderTarget.stencilBuffer = value; + this.beautyRenderTarget.depthTexture.format = value ? THREE.DepthStencilFormat : THREE.DepthFormat; + this.beautyRenderTarget.depthTexture.type = value ? THREE.UnsignedInt248Type : THREE.UnsignedIntType; + this.beautyRenderTarget.depthTexture.needsUpdate = true; + this.beautyRenderTarget.needsUpdate = true;*/ this.beautyRenderTarget.dispose(); + this.beautyRenderTarget = new WebGLRenderTarget(this.width, this.height, { + minFilter: LinearFilter, + magFilter: NearestFilter, + type: HalfFloatType, + format: RGBAFormat, + stencilBuffer: value + }); + this.beautyRenderTarget.depthTexture = new DepthTexture(this.width, this.height, value ? UnsignedInt248Type : UnsignedIntType); + this.beautyRenderTarget.depthTexture.format = value ? DepthStencilFormat : DepthFormat; + } + return true; + } + }); + /** @type {THREE.Vector3[]} */ this.samples = []; + /** @type {THREE.Vector2[]} */ this.samplesDenoise = []; + this.autoDetectTransparency = true; + this.beautyRenderTarget = new WebGLRenderTarget(this.width, this.height, { + minFilter: LinearFilter, + magFilter: NearestFilter, + type: HalfFloatType, + format: RGBAFormat, + stencilBuffer: false + }); + this.beautyRenderTarget.depthTexture = new DepthTexture(this.width, this.height, UnsignedIntType); + this.beautyRenderTarget.depthTexture.format = DepthFormat; + this.configureEffectCompositer(this.configuration.logarithmicDepthBuffer); + this.configureSampleDependentPasses(); + this.configureHalfResTargets(); + this.detectTransparency(); + this.configureTransparencyTarget(); + this.writeTargetInternal = new WebGLRenderTarget(this.width, this.height, { + minFilter: LinearFilter, + magFilter: LinearFilter, + depthBuffer: false + }); + this.readTargetInternal = new WebGLRenderTarget(this.width, this.height, { + minFilter: LinearFilter, + magFilter: LinearFilter, + depthBuffer: false + }); + /** @type {THREE.DataTexture} */ this.bluenoise = new DataTexture($05f6997e4b65da14$var$bluenoiseBits, 128, 128); + this.bluenoise.colorSpace = NoColorSpace; + this.bluenoise.wrapS = RepeatWrapping; + this.bluenoise.wrapT = RepeatWrapping; + this.bluenoise.minFilter = NearestFilter; + this.bluenoise.magFilter = NearestFilter; + this.bluenoise.needsUpdate = true; + this.lastTime = 0; + this._r = new Vector2$1(); + this._c = new Color(); } - where(indexOrCrit) { - if (typeof indexOrCrit === 'string') - return new this.db.WhereClause(this, indexOrCrit); - if (isArray(indexOrCrit)) - return new this.db.WhereClause(this, `[${indexOrCrit.join('+')}]`); - const keyPaths = keys(indexOrCrit); - if (keyPaths.length === 1) - return this - .where(keyPaths[0]) - .equals(indexOrCrit[keyPaths[0]]); - const compoundIndex = this.schema.indexes.concat(this.schema.primKey).filter(ix => ix.compound && - keyPaths.every(keyPath => ix.keyPath.indexOf(keyPath) >= 0) && - ix.keyPath.every(keyPath => keyPaths.indexOf(keyPath) >= 0))[0]; - if (compoundIndex && this.db._maxKey !== maxString) - return this - .where(compoundIndex.name) - .equals(compoundIndex.keyPath.map(kp => indexOrCrit[kp])); - if (!compoundIndex && debug) - console.warn(`The query ${JSON.stringify(indexOrCrit)} on ${this.name} would benefit of a ` + - `compound index [${keyPaths.join('+')}]`); - const { idxByName } = this.schema; - const idb = this.db._deps.indexedDB; - function equals(a, b) { - try { - return idb.cmp(a, b) === 0; + configureHalfResTargets() { + if (this.configuration.halfRes) { + this.depthDownsampleTarget = /*new THREE.WebGLRenderTarget(this.width / 2, this.height / 2, { + minFilter: THREE.NearestFilter, + magFilter: THREE.NearestFilter, + depthBuffer: false, + format: THREE.RedFormat, + type: THREE.FloatType + });*/ new WebGLMultipleRenderTargets(this.width / 2, this.height / 2, 2); + this.depthDownsampleTarget.texture[0].format = RedFormat; + this.depthDownsampleTarget.texture[0].type = FloatType; + this.depthDownsampleTarget.texture[0].minFilter = NearestFilter; + this.depthDownsampleTarget.texture[0].magFilter = NearestFilter; + this.depthDownsampleTarget.texture[0].depthBuffer = false; + this.depthDownsampleTarget.texture[1].format = RGBAFormat; + this.depthDownsampleTarget.texture[1].type = HalfFloatType; + this.depthDownsampleTarget.texture[1].minFilter = NearestFilter; + this.depthDownsampleTarget.texture[1].magFilter = NearestFilter; + this.depthDownsampleTarget.texture[1].depthBuffer = false; + this.depthDownsampleQuad = new ($e4ca8dcb0218f846$export$dcd670d73db751f5)(new ShaderMaterial(($26aca173e0984d99$export$1efdf491687cd442))); + } else { + if (this.depthDownsampleTarget) { + this.depthDownsampleTarget.dispose(); + this.depthDownsampleTarget = null; } - catch (e) { - return false; + if (this.depthDownsampleQuad) { + this.depthDownsampleQuad.dispose(); + this.depthDownsampleQuad = null; } } - const [idx, filterFunction] = keyPaths.reduce(([prevIndex, prevFilterFn], keyPath) => { - const index = idxByName[keyPath]; - const value = indexOrCrit[keyPath]; - return [ - prevIndex || index, - prevIndex || !index ? - combine(prevFilterFn, index && index.multi ? - x => { - const prop = getByKeyPath(x, keyPath); - return isArray(prop) && prop.some(item => equals(value, item)); - } : x => equals(value, getByKeyPath(x, keyPath))) - : prevFilterFn - ]; - }, [null, null]); - return idx ? - this.where(idx.name).equals(indexOrCrit[idx.keyPath]) - .filter(filterFunction) : - compoundIndex ? - this.filter(filterFunction) : - this.where(keyPaths).equals(''); - } - filter(filterFunction) { - return this.toCollection().and(filterFunction); - } - count(thenShortcut) { - return this.toCollection().count(thenShortcut); - } - offset(offset) { - return this.toCollection().offset(offset); } - limit(numRows) { - return this.toCollection().limit(numRows); + detectTransparency() { + if (this.autoDetectTransparency) { + let isTransparency = false; + this.scene.traverse((obj)=>{ + if (obj.material && obj.material.transparent) isTransparency = true; + }); + this.configuration.transparencyAware = isTransparency; + } } - each(callback) { - return this.toCollection().each(callback); + configureTransparencyTarget() { + if (this.configuration.transparencyAware) { + this.transparencyRenderTargetDWFalse = new WebGLRenderTarget(this.width, this.height, { + minFilter: LinearFilter, + magFilter: NearestFilter, + type: HalfFloatType, + format: RGBAFormat + }); + this.transparencyRenderTargetDWTrue = new WebGLRenderTarget(this.width, this.height, { + minFilter: LinearFilter, + magFilter: NearestFilter, + type: HalfFloatType, + format: RGBAFormat + }); + this.transparencyRenderTargetDWTrue.depthTexture = new DepthTexture(this.width, this.height, UnsignedIntType); + this.depthCopyPass = new ($e4ca8dcb0218f846$export$dcd670d73db751f5)(new ShaderMaterial({ + uniforms: { + depthTexture: { + value: this.beautyRenderTarget.depthTexture + } + }, + vertexShader: /* glsl */ ` + varying vec2 vUv; + void main() { + vUv = uv; + gl_Position = vec4(position, 1); + }`, + fragmentShader: /* glsl */ ` + uniform sampler2D depthTexture; + varying vec2 vUv; + void main() { + gl_FragDepth = texture2D(depthTexture, vUv).r + 0.00001; + gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); + } + ` + })); + } else { + if (this.transparencyRenderTargetDWFalse) { + this.transparencyRenderTargetDWFalse.dispose(); + this.transparencyRenderTargetDWFalse = null; + } + if (this.transparencyRenderTargetDWTrue) { + this.transparencyRenderTargetDWTrue.dispose(); + this.transparencyRenderTargetDWTrue = null; + } + if (this.depthCopyPass) { + this.depthCopyPass.dispose(); + this.depthCopyPass = null; + } + } } - toArray(thenShortcut) { - return this.toCollection().toArray(thenShortcut); + renderTransparency(renderer) { + const oldBackground = this.scene.background; + const oldClearColor = renderer.getClearColor(new Color()); + const oldClearAlpha = renderer.getClearAlpha(); + const oldVisibility = new Map(); + const oldAutoClearDepth = renderer.autoClearDepth; + this.scene.traverse((obj)=>{ + oldVisibility.set(obj, obj.visible); + }); + // Override the state + this.scene.background = null; + renderer.autoClearDepth = false; + renderer.setClearColor(new Color(0, 0, 0), 0); + this.depthCopyPass.material.uniforms.depthTexture.value = this.beautyRenderTarget.depthTexture; + // Render out transparent objects WITHOUT depth write + renderer.setRenderTarget(this.transparencyRenderTargetDWFalse); + this.scene.traverse((obj)=>{ + if (obj.material) obj.visible = oldVisibility.get(obj) && obj.material.transparent && !obj.material.depthWrite && !obj.userData.treatAsOpaque; + }); + renderer.clear(true, true, true); + this.depthCopyPass.render(renderer); + renderer.render(this.scene, this.camera); + // Render out transparent objects WITH depth write + renderer.setRenderTarget(this.transparencyRenderTargetDWTrue); + this.scene.traverse((obj)=>{ + if (obj.material) obj.visible = oldVisibility.get(obj) && obj.material.transparent && obj.material.depthWrite && !obj.userData.treatAsOpaque; + }); + renderer.clear(true, true, true); + this.depthCopyPass.render(renderer); + renderer.render(this.scene, this.camera); + // Restore + this.scene.traverse((obj)=>{ + obj.visible = oldVisibility.get(obj); + }); + renderer.setClearColor(oldClearColor, oldClearAlpha); + this.scene.background = oldBackground; + renderer.autoClearDepth = oldAutoClearDepth; } - toCollection() { - return new this.db.Collection(new this.db.WhereClause(this)); + configureSampleDependentPasses() { + this.configureAOPass(this.configuration.logarithmicDepthBuffer); + this.configureDenoisePass(this.configuration.logarithmicDepthBuffer); } - orderBy(index) { - return new this.db.Collection(new this.db.WhereClause(this, isArray(index) ? - `[${index.join('+')}]` : - index)); + configureAOPass(logarithmicDepthBuffer = false) { + this.samples = this.generateHemisphereSamples(this.configuration.aoSamples); + const e = { + ...($1ed45968c1160c3c$export$c9b263b9a17dffd7) + }; + e.fragmentShader = e.fragmentShader.replace("16", this.configuration.aoSamples).replace("16.0", this.configuration.aoSamples + ".0"); + if (logarithmicDepthBuffer) e.fragmentShader = "#define LOGDEPTH\n" + e.fragmentShader; + if (this.configuration.halfRes) e.fragmentShader = "#define HALFRES\n" + e.fragmentShader; + if (this.effectShaderQuad) { + this.effectShaderQuad.material.dispose(); + this.effectShaderQuad.material = new ShaderMaterial(e); + } else this.effectShaderQuad = new ($e4ca8dcb0218f846$export$dcd670d73db751f5)(new ShaderMaterial(e)); } - reverse() { - return this.toCollection().reverse(); + configureDenoisePass(logarithmicDepthBuffer = false) { + this.samplesDenoise = this.generateDenoiseSamples(this.configuration.denoiseSamples, 11); + const p = { + ...($e52378cd0f5a973d$export$57856b59f317262e) + }; + p.fragmentShader = p.fragmentShader.replace("16", this.configuration.denoiseSamples); + if (logarithmicDepthBuffer) p.fragmentShader = "#define LOGDEPTH\n" + p.fragmentShader; + if (this.poissonBlurQuad) { + this.poissonBlurQuad.material.dispose(); + this.poissonBlurQuad.material = new ShaderMaterial(p); + } else this.poissonBlurQuad = new ($e4ca8dcb0218f846$export$dcd670d73db751f5)(new ShaderMaterial(p)); } - mapToClass(constructor) { - this.schema.mappedClass = constructor; - const readHook = obj => { - if (!obj) - return obj; - const res = Object.create(constructor.prototype); - for (var m in obj) - if (hasOwn(obj, m)) - try { - res[m] = obj[m]; - } - catch (_) { } - return res; + configureEffectCompositer(logarithmicDepthBuffer = false) { + const e = { + ...($12b21d24d1192a04$export$a815acccbd2c9a49) }; - if (this.schema.readHook) { - this.hook.reading.unsubscribe(this.schema.readHook); - } - this.schema.readHook = readHook; - this.hook("reading", readHook); - return constructor; + if (logarithmicDepthBuffer) e.fragmentShader = "#define LOGDEPTH\n" + e.fragmentShader; + if (this.configuration.halfRes && this.configuration.depthAwareUpsampling) e.fragmentShader = "#define HALFRES\n" + e.fragmentShader; + if (this.effectCompositerQuad) { + this.effectCompositerQuad.material.dispose(); + this.effectCompositerQuad.material = new ShaderMaterial(e); + } else this.effectCompositerQuad = new ($e4ca8dcb0218f846$export$dcd670d73db751f5)(new ShaderMaterial(e)); } - defineClass() { - function Class(content) { - extend(this, content); + /** + * + * @param {Number} n + * @returns {THREE.Vector3[]} + */ generateHemisphereSamples(n) { + const points = []; + for(let k = 0; k < n; k++){ + const theta = 2.399963 * k; + let r = Math.sqrt(k + 0.5) / Math.sqrt(n); + const x = r * Math.cos(theta); + const y = r * Math.sin(theta); + // Project to hemisphere + const z = Math.sqrt(1 - (x * x + y * y)); + points.push(new Vector3$1(x, y, z)); } - return this.mapToClass(Class); + return points; } - add(obj, key) { - const { auto, keyPath } = this.schema.primKey; - let objToAdd = obj; - if (keyPath && auto) { - objToAdd = workaroundForUndefinedPrimKey(keyPath)(obj); + /** + * + * @param {number} numSamples + * @param {number} numRings + * @returns {THREE.Vector2[]} + */ generateDenoiseSamples(numSamples, numRings) { + const angleStep = 2 * Math.PI * numRings / numSamples; + const invNumSamples = 1.0 / numSamples; + const radiusStep = invNumSamples; + const samples = []; + let radius = invNumSamples; + let angle = 0; + for(let i = 0; i < numSamples; i++){ + samples.push(new Vector2$1(Math.cos(angle), Math.sin(angle)).multiplyScalar(Math.pow(radius, 0.75))); + radius += radiusStep; + angle += angleStep; } - return this._trans('readwrite', trans => { - return this.core.mutate({ trans, type: 'add', keys: key != null ? [key] : null, values: [objToAdd] }); - }).then(res => res.numFailures ? DexiePromise.reject(res.failures[0]) : res.lastResult) - .then(lastResult => { - if (keyPath) { - try { - setByKeyPath(obj, keyPath, lastResult); - } - catch (_) { } - } - return lastResult; - }); + return samples; } - update(keyOrObject, modifications) { - if (typeof keyOrObject === 'object' && !isArray(keyOrObject)) { - const key = getByKeyPath(keyOrObject, this.schema.primKey.keyPath); - if (key === undefined) - return rejection(new exceptions.InvalidArgument("Given object does not contain its primary key")); - try { - if (typeof modifications !== "function") { - keys(modifications).forEach(keyPath => { - setByKeyPath(keyOrObject, keyPath, modifications[keyPath]); - }); - } - else { - modifications(keyOrObject, { value: keyOrObject, primKey: key }); - } - } - catch (_a) { - } - return this.where(":id").equals(key).modify(modifications); - } - else { - return this.where(":id").equals(keyOrObject).modify(modifications); + setSize(width, height) { + this.width = width; + this.height = height; + const c = this.configuration.halfRes ? 0.5 : 1; + this.beautyRenderTarget.setSize(width, height); + this.writeTargetInternal.setSize(width * c, height * c); + this.readTargetInternal.setSize(width * c, height * c); + if (this.configuration.halfRes) this.depthDownsampleTarget.setSize(width * c, height * c); + if (this.configuration.transparencyAware) { + this.transparencyRenderTargetDWFalse.setSize(width, height); + this.transparencyRenderTargetDWTrue.setSize(width, height); } } - put(obj, key) { - const { auto, keyPath } = this.schema.primKey; - let objToAdd = obj; - if (keyPath && auto) { - objToAdd = workaroundForUndefinedPrimKey(keyPath)(obj); + render(renderer, writeBuffer, readBuffer, deltaTime, maskActive) { + if (renderer.capabilities.logarithmicDepthBuffer !== this.configuration.logarithmicDepthBuffer) { + this.configuration.logarithmicDepthBuffer = renderer.capabilities.logarithmicDepthBuffer; + this.configureAOPass(this.configuration.logarithmicDepthBuffer); + this.configureDenoisePass(this.configuration.logarithmicDepthBuffer); + this.configureEffectCompositer(this.configuration.logarithmicDepthBuffer); } - return this._trans('readwrite', trans => this.core.mutate({ trans, type: 'put', values: [objToAdd], keys: key != null ? [key] : null })) - .then(res => res.numFailures ? DexiePromise.reject(res.failures[0]) : res.lastResult) - .then(lastResult => { - if (keyPath) { - try { - setByKeyPath(obj, keyPath, lastResult); - } - catch (_) { } + this.detectTransparency(); + let gl; + let ext; + let timerQuery; + if (this.debugMode) { + gl = renderer.getContext(); + ext = gl.getExtension("EXT_disjoint_timer_query_webgl2"); + if (ext === null) { + console.error("EXT_disjoint_timer_query_webgl2 not available, disabling debug mode."); + this.debugMode = false; } - return lastResult; - }); - } - delete(key) { - return this._trans('readwrite', trans => this.core.mutate({ trans, type: 'delete', keys: [key] })) - .then(res => res.numFailures ? DexiePromise.reject(res.failures[0]) : undefined); - } - clear() { - return this._trans('readwrite', trans => this.core.mutate({ trans, type: 'deleteRange', range: AnyRange })) - .then(res => res.numFailures ? DexiePromise.reject(res.failures[0]) : undefined); + } + if (this.configuration.autoRenderBeauty) { + renderer.setRenderTarget(this.beautyRenderTarget); + renderer.render(this.scene, this.camera); + if (this.configuration.transparencyAware) this.renderTransparency(renderer); + } + if (this.debugMode) { + timerQuery = gl.createQuery(); + gl.beginQuery(ext.TIME_ELAPSED_EXT, timerQuery); + } + const xrEnabled = renderer.xr.enabled; + renderer.xr.enabled = false; + this.camera.updateMatrixWorld(); + this._r.set(this.width, this.height); + let trueRadius = this.configuration.aoRadius; + if (this.configuration.halfRes && this.configuration.screenSpaceRadius) trueRadius *= 0.5; + if (this.configuration.halfRes) { + renderer.setRenderTarget(this.depthDownsampleTarget); + this.depthDownsampleQuad.material.uniforms.sceneDepth.value = this.beautyRenderTarget.depthTexture; + this.depthDownsampleQuad.material.uniforms.resolution.value = this._r; + this.depthDownsampleQuad.material.uniforms["near"].value = this.camera.near; + this.depthDownsampleQuad.material.uniforms["far"].value = this.camera.far; + this.depthDownsampleQuad.material.uniforms["projectionMatrixInv"].value = this.camera.projectionMatrixInverse; + this.depthDownsampleQuad.material.uniforms["viewMatrixInv"].value = this.camera.matrixWorld; + this.depthDownsampleQuad.material.uniforms["logDepth"].value = this.configuration.logarithmicDepthBuffer; + this.depthDownsampleQuad.render(renderer); + } + this.effectShaderQuad.material.uniforms["sceneDiffuse"].value = this.beautyRenderTarget.texture; + this.effectShaderQuad.material.uniforms["sceneDepth"].value = this.configuration.halfRes ? this.depthDownsampleTarget.texture[0] : this.beautyRenderTarget.depthTexture; + this.effectShaderQuad.material.uniforms["sceneNormal"].value = this.configuration.halfRes ? this.depthDownsampleTarget.texture[1] : null; + this.effectShaderQuad.material.uniforms["projMat"].value = this.camera.projectionMatrix; + this.effectShaderQuad.material.uniforms["viewMat"].value = this.camera.matrixWorldInverse; + this.effectShaderQuad.material.uniforms["projViewMat"].value = this.camera.projectionMatrix.clone().multiply(this.camera.matrixWorldInverse.clone()); + this.effectShaderQuad.material.uniforms["projectionMatrixInv"].value = this.camera.projectionMatrixInverse; + this.effectShaderQuad.material.uniforms["viewMatrixInv"].value = this.camera.matrixWorld; + this.effectShaderQuad.material.uniforms["cameraPos"].value = this.camera.getWorldPosition(new Vector3$1()); + this.effectShaderQuad.material.uniforms["resolution"].value = this.configuration.halfRes ? this._r.clone().multiplyScalar(0.5).floor() : this._r; + this.effectShaderQuad.material.uniforms["time"].value = performance.now() / 1000; + this.effectShaderQuad.material.uniforms["samples"].value = this.samples; + this.effectShaderQuad.material.uniforms["bluenoise"].value = this.bluenoise; + this.effectShaderQuad.material.uniforms["radius"].value = trueRadius; + this.effectShaderQuad.material.uniforms["distanceFalloff"].value = this.configuration.distanceFalloff; + this.effectShaderQuad.material.uniforms["near"].value = this.camera.near; + this.effectShaderQuad.material.uniforms["far"].value = this.camera.far; + this.effectShaderQuad.material.uniforms["logDepth"].value = renderer.capabilities.logarithmicDepthBuffer; + this.effectShaderQuad.material.uniforms["ortho"].value = this.camera.isOrthographicCamera; + this.effectShaderQuad.material.uniforms["screenSpaceRadius"].value = this.configuration.screenSpaceRadius; + // Start the AO + renderer.setRenderTarget(this.writeTargetInternal); + this.effectShaderQuad.render(renderer); + // End the AO + // Start the blur + for(let i = 0; i < this.configuration.denoiseIterations; i++){ + [this.writeTargetInternal, this.readTargetInternal] = [ + this.readTargetInternal, + this.writeTargetInternal + ]; + this.poissonBlurQuad.material.uniforms["tDiffuse"].value = this.readTargetInternal.texture; + this.poissonBlurQuad.material.uniforms["sceneDepth"].value = this.configuration.halfRes ? this.depthDownsampleTarget.texture[0] : this.beautyRenderTarget.depthTexture; + this.poissonBlurQuad.material.uniforms["projMat"].value = this.camera.projectionMatrix; + this.poissonBlurQuad.material.uniforms["viewMat"].value = this.camera.matrixWorldInverse; + this.poissonBlurQuad.material.uniforms["projectionMatrixInv"].value = this.camera.projectionMatrixInverse; + this.poissonBlurQuad.material.uniforms["viewMatrixInv"].value = this.camera.matrixWorld; + this.poissonBlurQuad.material.uniforms["cameraPos"].value = this.camera.getWorldPosition(new Vector3$1()); + this.poissonBlurQuad.material.uniforms["resolution"].value = this.configuration.halfRes ? this._r.clone().multiplyScalar(0.5).floor() : this._r; + this.poissonBlurQuad.material.uniforms["time"].value = performance.now() / 1000; + this.poissonBlurQuad.material.uniforms["blueNoise"].value = this.bluenoise; + this.poissonBlurQuad.material.uniforms["radius"].value = this.configuration.denoiseRadius * (this.configuration.halfRes ? 0.5 : 1); + this.poissonBlurQuad.material.uniforms["worldRadius"].value = trueRadius; + this.poissonBlurQuad.material.uniforms["distanceFalloff"].value = this.configuration.distanceFalloff; + this.poissonBlurQuad.material.uniforms["index"].value = i; + this.poissonBlurQuad.material.uniforms["poissonDisk"].value = this.samplesDenoise; + this.poissonBlurQuad.material.uniforms["near"].value = this.camera.near; + this.poissonBlurQuad.material.uniforms["far"].value = this.camera.far; + this.poissonBlurQuad.material.uniforms["logDepth"].value = renderer.capabilities.logarithmicDepthBuffer; + this.poissonBlurQuad.material.uniforms["screenSpaceRadius"].value = this.configuration.screenSpaceRadius; + renderer.setRenderTarget(this.writeTargetInternal); + this.poissonBlurQuad.render(renderer); + } + // Now, we have the blurred AO in writeTargetInternal + // End the blur + // Start the composition + if (this.configuration.transparencyAware) { + this.effectCompositerQuad.material.uniforms["transparencyDWFalse"].value = this.transparencyRenderTargetDWFalse.texture; + this.effectCompositerQuad.material.uniforms["transparencyDWTrue"].value = this.transparencyRenderTargetDWTrue.texture; + this.effectCompositerQuad.material.uniforms["transparencyDWTrueDepth"].value = this.transparencyRenderTargetDWTrue.depthTexture; + this.effectCompositerQuad.material.uniforms["transparencyAware"].value = true; + } + this.effectCompositerQuad.material.uniforms["sceneDiffuse"].value = this.beautyRenderTarget.texture; + this.effectCompositerQuad.material.uniforms["sceneDepth"].value = this.beautyRenderTarget.depthTexture; + this.effectCompositerQuad.material.uniforms["near"].value = this.camera.near; + this.effectCompositerQuad.material.uniforms["far"].value = this.camera.far; + this.effectCompositerQuad.material.uniforms["projectionMatrixInv"].value = this.camera.projectionMatrixInverse; + this.effectCompositerQuad.material.uniforms["viewMatrixInv"].value = this.camera.matrixWorld; + this.effectCompositerQuad.material.uniforms["logDepth"].value = renderer.capabilities.logarithmicDepthBuffer; + this.effectCompositerQuad.material.uniforms["ortho"].value = this.camera.isOrthographicCamera; + this.effectCompositerQuad.material.uniforms["downsampledDepth"].value = this.configuration.halfRes ? this.depthDownsampleTarget.texture[0] : this.beautyRenderTarget.depthTexture; + this.effectCompositerQuad.material.uniforms["resolution"].value = this._r; + this.effectCompositerQuad.material.uniforms["blueNoise"].value = this.bluenoise; + this.effectCompositerQuad.material.uniforms["intensity"].value = this.configuration.intensity; + this.effectCompositerQuad.material.uniforms["renderMode"].value = this.configuration.renderMode; + this.effectCompositerQuad.material.uniforms["screenSpaceRadius"].value = this.configuration.screenSpaceRadius; + this.effectCompositerQuad.material.uniforms["radius"].value = trueRadius; + this.effectCompositerQuad.material.uniforms["distanceFalloff"].value = this.configuration.distanceFalloff; + this.effectCompositerQuad.material.uniforms["gammaCorrection"].value = this.configuration.gammaCorrection; + this.effectCompositerQuad.material.uniforms["tDiffuse"].value = this.writeTargetInternal.texture; + this.effectCompositerQuad.material.uniforms["color"].value = this._c.copy(this.configuration.color).convertSRGBToLinear(); + this.effectCompositerQuad.material.uniforms["colorMultiply"].value = this.configuration.colorMultiply; + this.effectCompositerQuad.material.uniforms["cameraPos"].value = this.camera.getWorldPosition(new Vector3$1()); + this.effectCompositerQuad.material.uniforms["fog"].value = !!this.scene.fog; + if (this.scene.fog) { + if (this.scene.fog.isFog) { + this.effectCompositerQuad.material.uniforms["fogExp"].value = false; + this.effectCompositerQuad.material.uniforms["fogNear"].value = this.scene.fog.near; + this.effectCompositerQuad.material.uniforms["fogFar"].value = this.scene.fog.far; + } else if (this.scene.fog.isFogExp2) { + this.effectCompositerQuad.material.uniforms["fogExp"].value = true; + this.effectCompositerQuad.material.uniforms["fogDensity"].value = this.scene.fog.density; + } else console.error(`Unsupported fog type ${this.scene.fog.constructor.name} in SSAOPass.`); + } + renderer.setRenderTarget(this.renderToScreen ? null : writeBuffer); + this.effectCompositerQuad.render(renderer); + if (this.debugMode) { + gl.endQuery(ext.TIME_ELAPSED_EXT); + $05f6997e4b65da14$var$checkTimerQuery(timerQuery, gl, this); + } + renderer.xr.enabled = xrEnabled; } - bulkGet(keys) { - return this._trans('readonly', trans => { - return this.core.getMany({ - keys, - trans - }).then(result => result.map(res => this.hook.reading.fire(res))); - }); + /** + * Enables the debug mode of the AO, meaning the lastTime value will be updated. + */ enableDebugMode() { + this.debugMode = true; } - bulkAdd(objects, keysOrOptions, options) { - const keys = Array.isArray(keysOrOptions) ? keysOrOptions : undefined; - options = options || (keys ? undefined : keysOrOptions); - const wantResults = options ? options.allKeys : undefined; - return this._trans('readwrite', trans => { - const { auto, keyPath } = this.schema.primKey; - if (keyPath && keys) - throw new exceptions.InvalidArgument("bulkAdd(): keys argument invalid on tables with inbound keys"); - if (keys && keys.length !== objects.length) - throw new exceptions.InvalidArgument("Arguments objects and keys must have the same length"); - const numObjects = objects.length; - let objectsToAdd = keyPath && auto ? - objects.map(workaroundForUndefinedPrimKey(keyPath)) : - objects; - return this.core.mutate({ trans, type: 'add', keys: keys, values: objectsToAdd, wantResults }) - .then(({ numFailures, results, lastResult, failures }) => { - const result = wantResults ? results : lastResult; - if (numFailures === 0) - return result; - throw new BulkError(`${this.name}.bulkAdd(): ${numFailures} of ${numObjects} operations failed`, failures); - }); - }); + /** + * Disables the debug mode of the AO, meaning the lastTime value will not be updated. + */ disableDebugMode() { + this.debugMode = false; } - bulkPut(objects, keysOrOptions, options) { - const keys = Array.isArray(keysOrOptions) ? keysOrOptions : undefined; - options = options || (keys ? undefined : keysOrOptions); - const wantResults = options ? options.allKeys : undefined; - return this._trans('readwrite', trans => { - const { auto, keyPath } = this.schema.primKey; - if (keyPath && keys) - throw new exceptions.InvalidArgument("bulkPut(): keys argument invalid on tables with inbound keys"); - if (keys && keys.length !== objects.length) - throw new exceptions.InvalidArgument("Arguments objects and keys must have the same length"); - const numObjects = objects.length; - let objectsToPut = keyPath && auto ? - objects.map(workaroundForUndefinedPrimKey(keyPath)) : - objects; - return this.core.mutate({ trans, type: 'put', keys: keys, values: objectsToPut, wantResults }) - .then(({ numFailures, results, lastResult, failures }) => { - const result = wantResults ? results : lastResult; - if (numFailures === 0) - return result; - throw new BulkError(`${this.name}.bulkPut(): ${numFailures} of ${numObjects} operations failed`, failures); - }); - }); + /** + * Sets the display mode of the AO + * @param {"Combined" | "AO" | "No AO" | "Split" | "Split AO"} mode - The display mode. + */ setDisplayMode(mode) { + this.configuration.renderMode = [ + "Combined", + "AO", + "No AO", + "Split", + "Split AO" + ].indexOf(mode); } - bulkDelete(keys) { - const numKeys = keys.length; - return this._trans('readwrite', trans => { - return this.core.mutate({ trans, type: 'delete', keys: keys }); - }).then(({ numFailures, lastResult, failures }) => { - if (numFailures === 0) - return lastResult; - throw new BulkError(`${this.name}.bulkDelete(): ${numFailures} of ${numKeys} operations failed`, failures); - }); + /** + * + * @param {"Performance" | "Low" | "Medium" | "High" | "Ultra"} mode + */ setQualityMode(mode) { + if (mode === "Performance") { + this.configuration.aoSamples = 8; + this.configuration.denoiseSamples = 4; + this.configuration.denoiseRadius = 12; + } else if (mode === "Low") { + this.configuration.aoSamples = 16; + this.configuration.denoiseSamples = 4; + this.configuration.denoiseRadius = 12; + } else if (mode === "Medium") { + this.configuration.aoSamples = 16; + this.configuration.denoiseSamples = 8; + this.configuration.denoiseRadius = 12; + } else if (mode === "High") { + this.configuration.aoSamples = 64; + this.configuration.denoiseSamples = 8; + this.configuration.denoiseRadius = 6; + } else if (mode === "Ultra") { + this.configuration.aoSamples = 64; + this.configuration.denoiseSamples = 16; + this.configuration.denoiseRadius = 6; + } } +} + +/** + * Gamma Correction Shader + * http://en.wikipedia.org/wiki/gamma_correction + */ + +const GammaCorrectionShader = { + + uniforms: { + + 'tDiffuse': { value: null } + + }, + + vertexShader: /* glsl */` + + varying vec2 vUv; + + void main() { + + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + + }`, + + fragmentShader: /* glsl */` + + uniform sampler2D tDiffuse; + + varying vec2 vUv; + + void main() { + + vec4 tex = texture2D( tDiffuse, vUv ); + + gl_FragColor = LinearTosRGB( tex ); + + }` + }; -function Events(ctx) { - var evs = {}; - var rv = function (eventName, subscriber) { - if (subscriber) { - var i = arguments.length, args = new Array(i - 1); - while (--i) - args[i - 1] = arguments[i]; - evs[eventName].subscribe.apply(null, args); - return ctx; +/** + * Object to control the {@link CameraProjection} of the {@link OrthoPerspectiveCamera}. + */ +class ProjectionManager { + get projection() { + return this._currentProjection; + } + constructor(components, camera) { + this.components = components; + this._previousDistance = -1; + this.matchOrthoDistanceEnabled = false; + this._camera = camera; + const perspective = "Perspective"; + this._currentCamera = camera.get(perspective); + this._currentProjection = perspective; + } + /** + * Sets the {@link CameraProjection} of the {@link OrthoPerspectiveCamera}. + * + * @param projection - the new projection to set. If it is the current projection, + * it will have no effect. + */ + async setProjection(projection) { + if (this.projection === projection) + return; + if (projection === "Orthographic") { + this.setOrthoCamera(); } - else if (typeof (eventName) === 'string') { - return evs[eventName]; + else { + await this.setPerspectiveCamera(); } - }; - rv.addEventType = add; - for (var i = 1, l = arguments.length; i < l; ++i) { - add(arguments[i]); + await this.updateActiveCamera(); } - return rv; - function add(eventName, chainFunction, defaultFunction) { - if (typeof eventName === 'object') - return addConfiguredEvents(eventName); - if (!chainFunction) - chainFunction = reverseStoppableEventChain; - if (!defaultFunction) - defaultFunction = nop; - var context = { - subscribers: [], - fire: defaultFunction, - subscribe: function (cb) { - if (context.subscribers.indexOf(cb) === -1) { - context.subscribers.push(cb); - context.fire = chainFunction(context.fire, cb); - } - }, - unsubscribe: function (cb) { - context.subscribers = context.subscribers.filter(function (fn) { return fn !== cb; }); - context.fire = context.subscribers.reduce(chainFunction, defaultFunction); - } - }; - evs[eventName] = rv[eventName] = context; - return context; + setOrthoCamera() { + // Matching orthographic camera to perspective camera + // Resource: https://stackoverflow.com/questions/48758959/what-is-required-to-convert-threejs-perspective-camera-to-orthographic + if (this._camera.currentMode.id === "FirstPerson") { + return; + } + this._previousDistance = this._camera.controls.distance; + this._camera.controls.distance = 200; + const { width, height } = this.getDims(); + this.setupOrthoCamera(height, width); + this._currentCamera = this._camera.get("Orthographic"); + this._currentProjection = "Orthographic"; } - function addConfiguredEvents(cfg) { - keys(cfg).forEach(function (eventName) { - var args = cfg[eventName]; - if (isArray(args)) { - add(eventName, cfg[eventName][0], cfg[eventName][1]); - } - else if (args === 'asap') { - var context = add(eventName, mirror, function fire() { - var i = arguments.length, args = new Array(i); - while (i--) - args[i] = arguments[i]; - context.subscribers.forEach(function (fn) { - asap$1(function fireEvent() { - fn.apply(null, args); - }); - }); - }); - } - else - throw new exceptions.InvalidArgument("Invalid event config"); + // This small delay is needed to hide weirdness during the transition + async updateActiveCamera() { + await new Promise((resolve) => { + setTimeout(() => { + this._camera.activeCamera = this._currentCamera; + resolve(); + }, 50); }); } -} - -function makeClassConstructor(prototype, constructor) { - derive(constructor).from({ prototype }); - return constructor; -} - -function createTableConstructor(db) { - return makeClassConstructor(Table$3.prototype, function Table(name, tableSchema, trans) { - this.db = db; - this._tx = trans; - this.name = name; - this.schema = tableSchema; - this.hook = db._allTables[name] ? db._allTables[name].hook : Events(null, { - "creating": [hookCreatingChain, nop], - "reading": [pureFunctionChain, mirror], - "updating": [hookUpdatingChain, nop], - "deleting": [hookDeletingChain, nop] - }); - }); -} - -function isPlainKeyRange(ctx, ignoreLimitFilter) { - return !(ctx.filter || ctx.algorithm || ctx.or) && - (ignoreLimitFilter ? ctx.justLimit : !ctx.replayFilter); -} -function addFilter(ctx, fn) { - ctx.filter = combine(ctx.filter, fn); -} -function addReplayFilter(ctx, factory, isLimitFilter) { - var curr = ctx.replayFilter; - ctx.replayFilter = curr ? () => combine(curr(), factory()) : factory; - ctx.justLimit = isLimitFilter && !curr; -} -function addMatchFilter(ctx, fn) { - ctx.isMatch = combine(ctx.isMatch, fn); -} -function getIndexOrStore(ctx, coreSchema) { - if (ctx.isPrimKey) - return coreSchema.primaryKey; - const index = coreSchema.getIndexByKeyPath(ctx.index); - if (!index) - throw new exceptions.Schema("KeyPath " + ctx.index + " on object store " + coreSchema.name + " is not indexed"); - return index; -} -function openCursor(ctx, coreTable, trans) { - const index = getIndexOrStore(ctx, coreTable.schema); - return coreTable.openCursor({ - trans, - values: !ctx.keysOnly, - reverse: ctx.dir === 'prev', - unique: !!ctx.unique, - query: { - index, - range: ctx.range - } - }); -} -function iter(ctx, fn, coreTrans, coreTable) { - const filter = ctx.replayFilter ? combine(ctx.filter, ctx.replayFilter()) : ctx.filter; - if (!ctx.or) { - return iterate(openCursor(ctx, coreTable, coreTrans), combine(ctx.algorithm, filter), fn, !ctx.keysOnly && ctx.valueMapper); + getDims() { + const lineOfSight = new THREE$1.Vector3(); + this._camera.get("Perspective").getWorldDirection(lineOfSight); + const target = new THREE$1.Vector3(); + this._camera.controls.getTarget(target); + const distance = target + .clone() + .sub(this._camera.get("Perspective").position); + const depth = distance.dot(lineOfSight); + const dims = this.components.renderer.getSize(); + const aspect = dims.x / dims.y; + const camera = this._camera.get("Perspective"); + const height = depth * 2 * Math.atan((camera.fov * (Math.PI / 180)) / 2); + const width = height * aspect; + return { width, height }; } - else { - const set = {}; - const union = (item, cursor, advance) => { - if (!filter || filter(cursor, advance, result => cursor.stop(result), err => cursor.fail(err))) { - var primaryKey = cursor.primaryKey; - var key = '' + primaryKey; - if (key === '[object ArrayBuffer]') - key = '' + new Uint8Array(primaryKey); - if (!hasOwn(set, key)) { - set[key] = true; - fn(item, cursor, advance); - } - } - }; - return Promise.all([ - ctx.or._iterate(union, coreTrans), - iterate(openCursor(ctx, coreTable, coreTrans), ctx.algorithm, union, !ctx.keysOnly && ctx.valueMapper) - ]); + setupOrthoCamera(height, width) { + this._camera.controls.mouseButtons.wheel = CameraControls.ACTION.ZOOM; + this._camera.controls.mouseButtons.middle = CameraControls.ACTION.ZOOM; + const pCamera = this._camera.get("Perspective"); + const oCamera = this._camera.get("Orthographic"); + oCamera.zoom = 1; + oCamera.left = width / -2; + oCamera.right = width / 2; + oCamera.top = height / 2; + oCamera.bottom = height / -2; + oCamera.updateProjectionMatrix(); + oCamera.position.copy(pCamera.position); + oCamera.quaternion.copy(pCamera.quaternion); + this._camera.controls.camera = oCamera; } -} -function iterate(cursorPromise, filter, fn, valueMapper) { - var mappedFn = valueMapper ? (x, c, a) => fn(valueMapper(x), c, a) : fn; - var wrappedFn = wrap(mappedFn); - return cursorPromise.then(cursor => { - if (cursor) { - return cursor.start(() => { - var c = () => cursor.continue(); - if (!filter || filter(cursor, advancer => c = advancer, val => { cursor.stop(val); c = nop; }, e => { cursor.fail(e); c = nop; })) - wrappedFn(cursor.value, cursor, advancer => c = advancer); - c(); - }); - } - }); -} - -function cmp(a, b) { - try { - const ta = type(a); - const tb = type(b); - if (ta !== tb) { - if (ta === 'Array') - return 1; - if (tb === 'Array') - return -1; - if (ta === 'binary') - return 1; - if (tb === 'binary') - return -1; - if (ta === 'string') - return 1; - if (tb === 'string') - return -1; - if (ta === 'Date') - return 1; - if (tb !== 'Date') - return NaN; - return -1; + getDistance() { + // this handles ortho zoom to perpective distance + const pCamera = this._camera.get("Perspective"); + const oCamera = this._camera.get("Orthographic"); + // this is the reverse of + // const height = depth * 2 * Math.atan((pCamera.fov * (Math.PI / 180)) / 2); + // accounting for zoom + const depth = (oCamera.top - oCamera.bottom) / + oCamera.zoom / + (2 * Math.atan((pCamera.fov * (Math.PI / 180)) / 2)); + return depth; + } + async setPerspectiveCamera() { + this._camera.controls.mouseButtons.wheel = CameraControls.ACTION.DOLLY; + this._camera.controls.mouseButtons.middle = CameraControls.ACTION.DOLLY; + const pCamera = this._camera.get("Perspective"); + const oCamera = this._camera.get("Orthographic"); + pCamera.position.copy(oCamera.position); + pCamera.quaternion.copy(oCamera.quaternion); + this._camera.controls.mouseButtons.wheel = CameraControls.ACTION.DOLLY; + if (this.matchOrthoDistanceEnabled) { + this._camera.controls.distance = this.getDistance(); } - switch (ta) { - case 'number': - case 'Date': - case 'string': - return a > b ? 1 : a < b ? -1 : 0; - case 'binary': { - return compareUint8Arrays(getUint8Array(a), getUint8Array(b)); - } - case 'Array': - return compareArrays(a, b); + else { + this._camera.controls.distance = this._previousDistance; } + await this._camera.controls.zoomTo(1); + pCamera.updateProjectionMatrix(); + this._camera.controls.camera = pCamera; + this._currentCamera = pCamera; + this._currentProjection = "Perspective"; } - catch (_a) { } - return NaN; -} -function compareArrays(a, b) { - const al = a.length; - const bl = b.length; - const l = al < bl ? al : bl; - for (let i = 0; i < l; ++i) { - const res = cmp(a[i], b[i]); - if (res !== 0) - return res; - } - return al === bl ? 0 : al < bl ? -1 : 1; -} -function compareUint8Arrays(a, b) { - const al = a.length; - const bl = b.length; - const l = al < bl ? al : bl; - for (let i = 0; i < l; ++i) { - if (a[i] !== b[i]) - return a[i] < b[i] ? -1 : 1; - } - return al === bl ? 0 : al < bl ? -1 : 1; -} -function type(x) { - const t = typeof x; - if (t !== 'object') - return t; - if (ArrayBuffer.isView(x)) - return 'binary'; - const tsTag = toStringTag(x); - return tsTag === 'ArrayBuffer' ? 'binary' : tsTag; -} -function getUint8Array(a) { - if (a instanceof Uint8Array) - return a; - if (ArrayBuffer.isView(a)) - return new Uint8Array(a.buffer, a.byteOffset, a.byteLength); - return new Uint8Array(a); } -class Collection { - _read(fn, cb) { - var ctx = this._ctx; - return ctx.error ? - ctx.table._trans(null, rejection.bind(null, ctx.error)) : - ctx.table._trans('readonly', fn).then(cb); - } - _write(fn) { - var ctx = this._ctx; - return ctx.error ? - ctx.table._trans(null, rejection.bind(null, ctx.error)) : - ctx.table._trans('readwrite', fn, "locked"); - } - _addAlgorithm(fn) { - var ctx = this._ctx; - ctx.algorithm = combine(ctx.algorithm, fn); - } - _iterate(fn, coreTrans) { - return iter(this._ctx, fn, coreTrans, this._ctx.table.core); +/** + * A {@link NavigationMode} that allows 3D navigation and panning + * like in many 3D and CAD softwares. + */ +class OrbitMode { + constructor(camera) { + this.camera = camera; + /** {@link NavigationMode.enabled} */ + this.enabled = true; + /** {@link NavigationMode.id} */ + this.id = "Orbit"; + /** {@link NavigationMode.projectionChanged} */ + this.projectionChanged = new Event(); + this.activateOrbitControls(); } - clone(props) { - var rv = Object.create(this.constructor.prototype), ctx = Object.create(this._ctx); - if (props) - extend(ctx, props); - rv._ctx = ctx; - return rv; + /** {@link NavigationMode.toggle} */ + toggle(active) { + this.enabled = active; + if (active) { + this.activateOrbitControls(); + } } - raw() { - this._ctx.valueMapper = null; - return this; + activateOrbitControls() { + const controls = this.camera.controls; + controls.minDistance = 1; + controls.maxDistance = 300; + const position = new THREE$1.Vector3(); + controls.getPosition(position); + const distance = position.length(); + controls.distance = distance; + controls.truckSpeed = 2; + const { rotation } = this.camera.get(); + const direction = new THREE$1.Vector3(0, 0, -1).applyEuler(rotation); + const target = position.addScaledVector(direction, distance); + controls.moveTo(target.x, target.y, target.z); } - each(fn) { - var ctx = this._ctx; - return this._read(trans => iter(ctx, fn, trans, ctx.table.core)); +} + +/** + * A {@link NavigationMode} that allows first person navigation, + * simulating FPS video games. + */ +class FirstPersonMode { + constructor(camera) { + this.camera = camera; + /** {@link NavigationMode.enabled} */ + this.enabled = false; + /** {@link NavigationMode.id} */ + this.id = "FirstPerson"; + /** {@link NavigationMode.projectionChanged} */ + this.projectionChanged = new Event(); } - count(cb) { - return this._read(trans => { - const ctx = this._ctx; - const coreTable = ctx.table.core; - if (isPlainKeyRange(ctx, true)) { - return coreTable.count({ - trans, - query: { - index: getIndexOrStore(ctx, coreTable.schema), - range: ctx.range - } - }).then(count => Math.min(count, ctx.limit)); - } - else { - var count = 0; - return iter(ctx, () => { ++count; return false; }, trans, coreTable) - .then(() => count); + /** {@link NavigationMode.toggle} */ + toggle(active) { + this.enabled = active; + if (active) { + const projection = this.camera.getProjection(); + if (projection !== "Perspective") { + this.camera.setNavigationMode("Orbit"); + return; } - }).then(cb); - } - sortBy(keyPath, cb) { - const parts = keyPath.split('.').reverse(), lastPart = parts[0], lastIndex = parts.length - 1; - function getval(obj, i) { - if (i) - return getval(obj[parts[i]], i - 1); - return obj[lastPart]; - } - var order = this._ctx.dir === "next" ? 1 : -1; - function sorter(a, b) { - var aVal = getval(a, lastIndex), bVal = getval(b, lastIndex); - return aVal < bVal ? -order : aVal > bVal ? order : 0; + this.setupFirstPersonCamera(); } - return this.toArray(function (a) { - return a.sort(sorter); - }).then(cb); } - toArray(cb) { - return this._read(trans => { - var ctx = this._ctx; - if (ctx.dir === 'next' && isPlainKeyRange(ctx, true) && ctx.limit > 0) { - const { valueMapper } = ctx; - const index = getIndexOrStore(ctx, ctx.table.core.schema); - return ctx.table.core.query({ - trans, - limit: ctx.limit, - values: true, - query: { - index, - range: ctx.range - } - }).then(({ result }) => valueMapper ? result.map(valueMapper) : result); - } - else { - const a = []; - return iter(ctx, item => a.push(item), trans, ctx.table.core).then(() => a); - } - }, cb); + setupFirstPersonCamera() { + const controls = this.camera.controls; + const newTargetPosition = new THREE$1.Vector3(); + controls.distance--; + controls.getPosition(newTargetPosition); + controls.minDistance = 1; + controls.maxDistance = 1; + controls.distance = 1; + controls.moveTo(newTargetPosition.x, newTargetPosition.y, newTargetPosition.z); + controls.truckSpeed = 50; + controls.mouseButtons.wheel = CameraControls.ACTION.DOLLY; + controls.touches.two = CameraControls.ACTION.TOUCH_ZOOM_TRUCK; } - offset(offset) { - var ctx = this._ctx; - if (offset <= 0) - return this; - ctx.offset += offset; - if (isPlainKeyRange(ctx)) { - addReplayFilter(ctx, () => { - var offsetLeft = offset; - return (cursor, advance) => { - if (offsetLeft === 0) - return true; - if (offsetLeft === 1) { - --offsetLeft; - return false; - } - advance(() => { - cursor.advance(offsetLeft); - offsetLeft = 0; - }); - return false; - }; - }); +} + +/** + * A {@link NavigationMode} that allows to navigate floorplans in 2D, + * like many BIM tools. + */ +class PlanMode { + constructor(camera) { + this.camera = camera; + /** {@link NavigationMode.enabled} */ + this.enabled = false; + /** {@link NavigationMode.id} */ + this.id = "Plan"; + /** {@link NavigationMode.projectionChanged} */ + this.projectionChanged = new Event(); + this.mouseInitialized = false; + this.defaultAzimuthSpeed = camera.controls.azimuthRotateSpeed; + this.defaultPolarSpeed = camera.controls.polarRotateSpeed; + } + /** {@link NavigationMode.toggle} */ + toggle(active) { + this.enabled = active; + const controls = this.camera.controls; + controls.azimuthRotateSpeed = active ? 0 : this.defaultAzimuthSpeed; + controls.polarRotateSpeed = active ? 0 : this.defaultPolarSpeed; + if (!this.mouseInitialized) { + this.mouseAction1 = controls.touches.one; + this.mouseAction2 = controls.touches.two; + this.mouseInitialized = true; + } + if (active) { + controls.mouseButtons.left = CameraControls.ACTION.TRUCK; + controls.touches.one = CameraControls.ACTION.TOUCH_TRUCK; + controls.touches.two = CameraControls.ACTION.TOUCH_ZOOM; } else { - addReplayFilter(ctx, () => { - var offsetLeft = offset; - return () => (--offsetLeft < 0); - }); + controls.mouseButtons.left = CameraControls.ACTION.ROTATE; + controls.touches.one = this.mouseAction1; + controls.touches.two = this.mouseAction2; } - return this; } - limit(numRows) { - this._ctx.limit = Math.min(this._ctx.limit, numRows); - addReplayFilter(this._ctx, () => { - var rowsLeft = numRows; - return function (cursor, advance, resolve) { - if (--rowsLeft <= 0) - advance(resolve); - return rowsLeft >= 0; - }; - }, true); - return this; +} + +/** + * A flexible camera that uses + * [yomotsu's cameracontrols](https://github.com/yomotsu/camera-controls) to + * easily control the camera in 2D and 3D. It supports multiple navigation + * modes, such as 2D floor plan navigation, first person and 3D orbit. + */ +class OrthoPerspectiveCamera extends SimpleCamera { + constructor(components) { + super(components); + /** + * Event that fires when the {@link CameraProjection} changes. + */ + this.projectionChanged = new Event(); + this._userInputButtons = {}; + this._frustumSize = 50; + this._navigationModes = new Map(); + this.uiElement = new UIElement(); + this._orthoCamera = this.newOrthoCamera(); + this._navigationModes.set("Orbit", new OrbitMode(this)); + this._navigationModes.set("FirstPerson", new FirstPersonMode(this)); + this._navigationModes.set("Plan", new PlanMode(this)); + this.currentMode = this._navigationModes.get("Orbit"); + this.currentMode.toggle(true, { preventTargetAdjustment: true }); + this.toggleEvents(true); + this._projectionManager = new ProjectionManager(components, this); + components.onInitialized.add(() => { + if (components.uiEnabled) + this.setUI(); + }); + this.onAspectUpdated.add(() => this.setOrthoCameraAspect()); } - until(filterFunction, bIncludeStopEntry) { - addFilter(this._ctx, function (cursor, advance, resolve) { - if (filterFunction(cursor.value)) { - advance(resolve); - return bIncludeStopEntry; + setUI() { + const mainButton = new Button(this.components); + mainButton.materialIcon = "video_camera_back"; + mainButton.tooltip = "Camera"; + const projection = new Button(this.components, { + materialIconName: "camera", + name: "Projection", + }); + const perspective = new Button(this.components, { name: "Perspective" }); + perspective.active = true; + perspective.onClick.add(() => this.setProjection("Perspective")); + const orthographic = new Button(this.components, { name: "Orthographic" }); + orthographic.onClick.add(() => this.setProjection("Orthographic")); + projection.addChild(perspective, orthographic); + const navigation = new Button(this.components, { + materialIconName: "open_with", + name: "Navigation", + }); + const orbit = new Button(this.components, { name: "Orbit Around" }); + orbit.onClick.add(() => this.setNavigationMode("Orbit")); + const plan = new Button(this.components, { name: "Plan View" }); + plan.onClick.add(() => this.setNavigationMode("Plan")); + const firstPerson = new Button(this.components, { name: "First person" }); + firstPerson.onClick.add(() => this.setNavigationMode("FirstPerson")); + navigation.addChild(orbit, plan, firstPerson); + mainButton.addChild(navigation, projection); + this.projectionChanged.add((camera) => { + if (camera instanceof THREE$1.PerspectiveCamera) { + perspective.active = true; + orthographic.active = false; } else { - return true; + perspective.active = false; + orthographic.active = true; } }); - return this; - } - first(cb) { - return this.limit(1).toArray(function (a) { return a[0]; }).then(cb); - } - last(cb) { - return this.reverse().first(cb); + this.uiElement.set({ main: mainButton }); } - filter(filterFunction) { - addFilter(this._ctx, function (cursor) { - return filterFunction(cursor.value); - }); - addMatchFilter(this._ctx, filterFunction); - return this; + /** {@link Disposable.dispose} */ + async dispose() { + await super.dispose(); + this.toggleEvents(false); + this._orthoCamera.removeFromParent(); } - and(filter) { - return this.filter(filter); - } - or(indexName) { - return new this.db.WhereClause(this._ctx.table, indexName, this); + /** + * Similar to {@link Component.get}, but with an optional argument + * to specify which camera to get. + * + * @param projection - The camera corresponding to the + * {@link CameraProjection} specified. If no projection is specified, + * the active camera will be returned. + */ + get(projection) { + if (!projection) { + return this.activeCamera; + } + return projection === "Orthographic" + ? this._orthoCamera + : this._perspectiveCamera; } - reverse() { - this._ctx.dir = (this._ctx.dir === "prev" ? "next" : "prev"); - if (this._ondirectionchange) - this._ondirectionchange(this._ctx.dir); - return this; + /** Returns the current {@link CameraProjection}. */ + getProjection() { + return this._projectionManager.projection; } - desc() { - return this.reverse(); + /** Match Ortho zoom with Perspective distance when changing projection mode */ + set matchOrthoDistanceEnabled(value) { + this._projectionManager.matchOrthoDistanceEnabled = value; } - eachKey(cb) { - var ctx = this._ctx; - ctx.keysOnly = !ctx.isMatch; - return this.each(function (val, cursor) { cb(cursor.key, cursor); }); + /** + * Changes the current {@link CameraProjection} from Ortographic to Perspective + * and Viceversa. + */ + async toggleProjection() { + const projection = this.getProjection(); + const newProjection = projection === "Perspective" ? "Orthographic" : "Perspective"; + await this.setProjection(newProjection); } - eachUniqueKey(cb) { - this._ctx.unique = "unique"; - return this.eachKey(cb); + /** + * Sets the current {@link CameraProjection}. This triggers the event + * {@link projectionChanged}. + * + * @param projection - The new {@link CameraProjection} to set. + */ + async setProjection(projection) { + await this._projectionManager.setProjection(projection); + await this.projectionChanged.trigger(this.activeCamera); } - eachPrimaryKey(cb) { - var ctx = this._ctx; - ctx.keysOnly = !ctx.isMatch; - return this.each(function (val, cursor) { cb(cursor.primaryKey, cursor); }); + /** + * Allows or prevents all user input. + * + * @param active - whether to enable or disable user inputs. + */ + toggleUserInput(active) { + if (active) { + this.enableUserInput(); + } + else { + this.disableUserInput(); + } } - keys(cb) { - var ctx = this._ctx; - ctx.keysOnly = !ctx.isMatch; - var a = []; - return this.each(function (item, cursor) { - a.push(cursor.key); - }).then(function () { - return a; - }).then(cb); + /** + * Sets a new {@link NavigationMode} and disables the previous one. + * + * @param mode - The {@link NavigationMode} to set. + */ + setNavigationMode(mode) { + if (this.currentMode.id === mode) + return; + this.currentMode.toggle(false); + if (!this._navigationModes.has(mode)) { + throw new Error("The specified mode does not exist!"); + } + this.currentMode = this._navigationModes.get(mode); + this.currentMode.toggle(true); } - primaryKeys(cb) { - var ctx = this._ctx; - if (ctx.dir === 'next' && isPlainKeyRange(ctx, true) && ctx.limit > 0) { - return this._read(trans => { - var index = getIndexOrStore(ctx, ctx.table.core.schema); - return ctx.table.core.query({ - trans, - values: false, - limit: ctx.limit, - query: { - index, - range: ctx.range - } - }); - }).then(({ result }) => result).then(cb); + /** + * Make the camera view fit all the specified meshes. + * + * @param meshes the meshes to fit. If it is not defined, it will + * evaluate {@link Components.meshes}. + * @param offset the distance to the fit object + */ + async fit(meshes = this.components.meshes, offset = 1.5) { + if (!this.enabled) + return; + const maxNum = Number.MAX_VALUE; + const minNum = Number.MIN_VALUE; + const min = new THREE$1.Vector3(maxNum, maxNum, maxNum); + const max = new THREE$1.Vector3(minNum, minNum, minNum); + for (const mesh of meshes) { + const box = new THREE$1.Box3().setFromObject(mesh); + if (box.min.x < min.x) + min.x = box.min.x; + if (box.min.y < min.y) + min.y = box.min.y; + if (box.min.z < min.z) + min.z = box.min.z; + if (box.max.x > max.x) + max.x = box.max.x; + if (box.max.y > max.y) + max.y = box.max.y; + if (box.max.z > max.z) + max.z = box.max.z; } - ctx.keysOnly = !ctx.isMatch; - var a = []; - return this.each(function (item, cursor) { - a.push(cursor.primaryKey); - }).then(function () { - return a; - }).then(cb); + const box = new THREE$1.Box3(min, max); + const sceneSize = new THREE$1.Vector3(); + box.getSize(sceneSize); + const sceneCenter = new THREE$1.Vector3(); + box.getCenter(sceneCenter); + const radius = Math.max(sceneSize.x, sceneSize.y, sceneSize.z) * offset; + const sphere = new THREE$1.Sphere(sceneCenter, radius); + await this.controls.fitToSphere(sphere, true); } - uniqueKeys(cb) { - this._ctx.unique = "unique"; - return this.keys(cb); + disableUserInput() { + this._userInputButtons.left = this.controls.mouseButtons.left; + this._userInputButtons.right = this.controls.mouseButtons.right; + this._userInputButtons.middle = this.controls.mouseButtons.middle; + this._userInputButtons.wheel = this.controls.mouseButtons.wheel; + this.controls.mouseButtons.left = 0; + this.controls.mouseButtons.right = 0; + this.controls.mouseButtons.middle = 0; + this.controls.mouseButtons.wheel = 0; } - firstKey(cb) { - return this.limit(1).keys(function (a) { return a[0]; }).then(cb); + enableUserInput() { + if (Object.keys(this._userInputButtons).length === 0) + return; + this.controls.mouseButtons.left = this._userInputButtons.left; + this.controls.mouseButtons.right = this._userInputButtons.right; + this.controls.mouseButtons.middle = this._userInputButtons.middle; + this.controls.mouseButtons.wheel = this._userInputButtons.wheel; } - lastKey(cb) { - return this.reverse().firstKey(cb); + newOrthoCamera() { + const dims = this.components.renderer.getSize(); + const aspect = dims.x / dims.y; + return new THREE$1.OrthographicCamera((this._frustumSize * aspect) / -2, (this._frustumSize * aspect) / 2, this._frustumSize / 2, this._frustumSize / -2, 0.1, 1000); } - distinct() { - var ctx = this._ctx, idx = ctx.index && ctx.table.schema.idxByName[ctx.index]; - if (!idx || !idx.multi) - return this; - var set = {}; - addFilter(this._ctx, function (cursor) { - var strKey = cursor.primaryKey.toString(); - var found = hasOwn(set, strKey); - set[strKey] = true; - return !found; - }); - return this; + setOrthoCameraAspect() { + const size = this.components.renderer.getSize(); + const aspect = size.x / size.y; + this._orthoCamera.left = (-this._frustumSize * aspect) / 2; + this._orthoCamera.right = (this._frustumSize * aspect) / 2; + this._orthoCamera.top = this._frustumSize / 2; + this._orthoCamera.bottom = -this._frustumSize / 2; + this._orthoCamera.updateProjectionMatrix(); } - modify(changes) { - var ctx = this._ctx; - return this._write(trans => { - var modifyer; - if (typeof changes === 'function') { - modifyer = changes; + toggleEvents(active) { + const modes = Object.values(this._navigationModes); + for (const mode of modes) { + if (active) { + mode.projectionChanged.on(this.projectionChanged.trigger); } else { - var keyPaths = keys(changes); - var numKeys = keyPaths.length; - modifyer = function (item) { - var anythingModified = false; - for (var i = 0; i < numKeys; ++i) { - var keyPath = keyPaths[i], val = changes[keyPath]; - if (getByKeyPath(item, keyPath) !== val) { - setByKeyPath(item, keyPath, val); - anythingModified = true; - } - } - return anythingModified; - }; + mode.projectionChanged.reset(); } - const coreTable = ctx.table.core; - const { outbound, extractKey } = coreTable.schema.primaryKey; - const limit = this.db._options.modifyChunkSize || 200; - const totalFailures = []; - let successCount = 0; - const failedKeys = []; - const applyMutateResult = (expectedCount, res) => { - const { failures, numFailures } = res; - successCount += expectedCount - numFailures; - for (let pos of keys(failures)) { - totalFailures.push(failures[pos]); - } - }; - return this.clone().primaryKeys().then(keys => { - const nextChunk = (offset) => { - const count = Math.min(limit, keys.length - offset); - return coreTable.getMany({ - trans, - keys: keys.slice(offset, offset + count), - cache: "immutable" - }).then(values => { - const addValues = []; - const putValues = []; - const putKeys = outbound ? [] : null; - const deleteKeys = []; - for (let i = 0; i < count; ++i) { - const origValue = values[i]; - const ctx = { - value: deepClone(origValue), - primKey: keys[offset + i] - }; - if (modifyer.call(ctx, ctx.value, ctx) !== false) { - if (ctx.value == null) { - deleteKeys.push(keys[offset + i]); - } - else if (!outbound && cmp(extractKey(origValue), extractKey(ctx.value)) !== 0) { - deleteKeys.push(keys[offset + i]); - addValues.push(ctx.value); - } - else { - putValues.push(ctx.value); - if (outbound) - putKeys.push(keys[offset + i]); - } - } - } - const criteria = isPlainKeyRange(ctx) && - ctx.limit === Infinity && - (typeof changes !== 'function' || changes === deleteCallback) && { - index: ctx.index, - range: ctx.range - }; - return Promise.resolve(addValues.length > 0 && - coreTable.mutate({ trans, type: 'add', values: addValues }) - .then(res => { - for (let pos in res.failures) { - deleteKeys.splice(parseInt(pos), 1); - } - applyMutateResult(addValues.length, res); - })).then(() => (putValues.length > 0 || (criteria && typeof changes === 'object')) && - coreTable.mutate({ - trans, - type: 'put', - keys: putKeys, - values: putValues, - criteria, - changeSpec: typeof changes !== 'function' - && changes - }).then(res => applyMutateResult(putValues.length, res))).then(() => (deleteKeys.length > 0 || (criteria && changes === deleteCallback)) && - coreTable.mutate({ - trans, - type: 'delete', - keys: deleteKeys, - criteria - }).then(res => applyMutateResult(deleteKeys.length, res))).then(() => { - return keys.length > offset + count && nextChunk(offset + limit); - }); - }); - }; - return nextChunk(0).then(() => { - if (totalFailures.length > 0) - throw new ModifyError("Error modifying one or more objects", totalFailures, successCount, failedKeys); - return keys.length; - }); - }); - }); - } - delete() { - var ctx = this._ctx, range = ctx.range; - if (isPlainKeyRange(ctx) && - ((ctx.isPrimKey && !hangsOnDeleteLargeKeyRange) || range.type === 3 )) - { - return this._write(trans => { - const { primaryKey } = ctx.table.core.schema; - const coreRange = range; - return ctx.table.core.count({ trans, query: { index: primaryKey, range: coreRange } }).then(count => { - return ctx.table.core.mutate({ trans, type: 'deleteRange', range: coreRange }) - .then(({ failures, lastResult, results, numFailures }) => { - if (numFailures) - throw new ModifyError("Could not delete some values", Object.keys(failures).map(pos => failures[pos]), count - numFailures); - return count - numFailures; - }); - }); - }); } - return this.modify(deleteCallback); } } -const deleteCallback = (value, ctx) => ctx.value = null; -function createCollectionConstructor(db) { - return makeClassConstructor(Collection.prototype, function Collection(whereClause, keyRangeGenerator) { - this.db = db; - let keyRange = AnyRange, error = null; - if (keyRangeGenerator) - try { - keyRange = keyRangeGenerator(); - } - catch (ex) { - error = ex; - } - const whereCtx = whereClause._ctx; - const table = whereCtx.table; - const readingHook = table.hook.reading.fire; - this._ctx = { - table: table, - index: whereCtx.index, - isPrimKey: (!whereCtx.index || (table.schema.primKey.keyPath && whereCtx.index === table.schema.primKey.name)), - range: keyRange, - keysOnly: false, - dir: "next", - unique: "", - algorithm: null, - filter: null, - replayFilter: null, - justLimit: true, - isMatch: null, - offset: 0, - limit: Infinity, - error: error, - or: whereCtx.or, - valueMapper: readingHook !== mirror ? readingHook : null - }; +// Gets the plane information (ax + by + cz = d) of each face, where: +// - (a, b, c) is the normal vector of the plane +// - d is the signed distance to the origin +function getPlaneDistanceMaterial() { + return new THREE$1.ShaderMaterial({ + side: 2, + clipping: true, + uniforms: {}, + vertexShader: ` + varying vec4 vColor; + + #include + + void main() { + #include + + vec4 absPosition = vec4(position, 1.0); + vec3 trueNormal = normal; + + #ifdef USE_INSTANCING + absPosition = instanceMatrix * absPosition; + trueNormal = (instanceMatrix * vec4(normal, 0.)).xyz; + #endif + + absPosition = modelMatrix * absPosition; + trueNormal = (normalize(modelMatrix * vec4(trueNormal, 0.))).xyz; + + vec3 planePosition = absPosition.xyz / 40.; + float d = abs(dot(trueNormal, planePosition)); + vColor = vec4(abs(trueNormal), d); + gl_Position = projectionMatrix * viewMatrix * absPosition; + + #include + #include + } + `, + fragmentShader: ` + varying vec4 vColor; + + #include + + void main() { + #include + gl_FragColor = vColor; + } + `, }); } -function simpleCompare(a, b) { - return a < b ? -1 : a === b ? 0 : 1; -} -function simpleCompareReverse(a, b) { - return a > b ? -1 : a === b ? 0 : 1; +// Gets the plane information (ax + by + cz = d) of each face, where: +// - (a, b, c) is the normal vector of the plane +// - d is the signed distance to the origin +function getProjectedNormalMaterial() { + return new THREE$1.ShaderMaterial({ + side: 2, + clipping: true, + uniforms: {}, + vertexShader: ` + varying vec3 vCameraPosition; + varying vec3 vPosition; + varying vec3 vNormal; + + #include + + void main() { + #include + + vec4 absPosition = vec4(position, 1.0); + vNormal = normal; + + #ifdef USE_INSTANCING + absPosition = instanceMatrix * absPosition; + vNormal = (instanceMatrix * vec4(normal, 0.)).xyz; + #endif + + absPosition = modelMatrix * absPosition; + vNormal = (normalize(modelMatrix * vec4(vNormal, 0.))).xyz; + + gl_Position = projectionMatrix * viewMatrix * absPosition; + + vCameraPosition = cameraPosition; + vPosition = absPosition.xyz; + + #include + #include + } + `, + fragmentShader: ` + varying vec3 vCameraPosition; + varying vec3 vPosition; + varying vec3 vNormal; + + #include + + void main() { + #include + vec3 cameraPixelVec = normalize(vCameraPosition - vPosition); + float difference = abs(dot(vNormal, cameraPixelVec)); + + // This achieves a double gloss effect: when the surface is perpendicular and when it's parallel + difference = abs((difference * 2.) - 1.); + + gl_FragColor = vec4(difference, difference, difference, 1.); + } + `, + }); } -function fail(collectionOrWhereClause, err, T) { - var collection = collectionOrWhereClause instanceof WhereClause ? - new collectionOrWhereClause.Collection(collectionOrWhereClause) : - collectionOrWhereClause; - collection._ctx.error = T ? new T(err) : new TypeError(err); - return collection; -} -function emptyCollection(whereClause) { - return new whereClause.Collection(whereClause, () => rangeEqual("")).limit(0); -} -function upperFactory(dir) { - return dir === "next" ? - (s) => s.toUpperCase() : - (s) => s.toLowerCase(); -} -function lowerFactory(dir) { - return dir === "next" ? - (s) => s.toLowerCase() : - (s) => s.toUpperCase(); -} -function nextCasing(key, lowerKey, upperNeedle, lowerNeedle, cmp, dir) { - var length = Math.min(key.length, lowerNeedle.length); - var llp = -1; - for (var i = 0; i < length; ++i) { - var lwrKeyChar = lowerKey[i]; - if (lwrKeyChar !== lowerNeedle[i]) { - if (cmp(key[i], upperNeedle[i]) < 0) - return key.substr(0, i) + upperNeedle[i] + upperNeedle.substr(i + 1); - if (cmp(key[i], lowerNeedle[i]) < 0) - return key.substr(0, i) + lowerNeedle[i] + upperNeedle.substr(i + 1); - if (llp >= 0) - return key.substr(0, llp) + lowerKey[llp] + upperNeedle.substr(llp + 1); - return null; - } - if (cmp(key[i], lwrKeyChar) < 0) - llp = i; +// Follows the structure of +// https://github.com/mrdoob/three.js/blob/master/examples/jsm/postprocessing/OutlinePass.js +class CustomEffectsPass extends Pass { + get lineColor() { + return this._lineColor; } - if (length < lowerNeedle.length && dir === "next") - return key + upperNeedle.substr(key.length); - if (length < key.length && dir === "prev") - return key.substr(0, upperNeedle.length); - return (llp < 0 ? null : key.substr(0, llp) + lowerNeedle[llp] + upperNeedle.substr(llp + 1)); -} -function addIgnoreCaseAlgorithm(whereClause, match, needles, suffix) { - var upper, lower, compare, upperNeedles, lowerNeedles, direction, nextKeySuffix, needlesLen = needles.length; - if (!needles.every(s => typeof s === 'string')) { - return fail(whereClause, STRING_EXPECTED); + set lineColor(lineColor) { + this._lineColor = lineColor; + const material = this.fsQuad.material; + material.uniforms.lineColor.value.set(lineColor); } - function initDirection(dir) { - upper = upperFactory(dir); - lower = lowerFactory(dir); - compare = (dir === "next" ? simpleCompare : simpleCompareReverse); - var needleBounds = needles.map(function (needle) { - return { lower: lower(needle), upper: upper(needle) }; - }).sort(function (a, b) { - return compare(a.lower, b.lower); - }); - upperNeedles = needleBounds.map(function (nb) { return nb.upper; }); - lowerNeedles = needleBounds.map(function (nb) { return nb.lower; }); - direction = dir; - nextKeySuffix = (dir === "next" ? "" : suffix); + get tolerance() { + return this._tolerance; } - initDirection("next"); - var c = new whereClause.Collection(whereClause, () => createRange(upperNeedles[0], lowerNeedles[needlesLen - 1] + suffix)); - c._ondirectionchange = function (direction) { - initDirection(direction); - }; - var firstPossibleNeedle = 0; - c._addAlgorithm(function (cursor, advance, resolve) { - var key = cursor.key; - if (typeof key !== 'string') - return false; - var lowerKey = lower(key); - if (match(lowerKey, lowerNeedles, firstPossibleNeedle)) { - return true; - } - else { - var lowestPossibleCasing = null; - for (var i = firstPossibleNeedle; i < needlesLen; ++i) { - var casing = nextCasing(key, lowerKey, upperNeedles[i], lowerNeedles[i], compare, direction); - if (casing === null && lowestPossibleCasing === null) - firstPossibleNeedle = i + 1; - else if (lowestPossibleCasing === null || compare(lowestPossibleCasing, casing) > 0) { - lowestPossibleCasing = casing; - } - } - if (lowestPossibleCasing !== null) { - advance(function () { cursor.continue(lowestPossibleCasing + nextKeySuffix); }); - } - else { - advance(resolve); - } - return false; - } - }); - return c; -} -function createRange(lower, upper, lowerOpen, upperOpen) { - return { - type: 2 , - lower, - upper, - lowerOpen, - upperOpen - }; -} -function rangeEqual(value) { - return { - type: 1 , - lower: value, - upper: value - }; -} - -class WhereClause { - get Collection() { - return this._ctx.table.db.Collection; + set tolerance(value) { + this._tolerance = value; + const material = this.fsQuad.material; + material.uniforms.tolerance.value = value; } - between(lower, upper, includeLower, includeUpper) { - includeLower = includeLower !== false; - includeUpper = includeUpper === true; - try { - if ((this._cmp(lower, upper) > 0) || - (this._cmp(lower, upper) === 0 && (includeLower || includeUpper) && !(includeLower && includeUpper))) - return emptyCollection(this); - return new this.Collection(this, () => createRange(lower, upper, !includeLower, !includeUpper)); - } - catch (e) { - return fail(this, INVALID_KEY_ARGUMENT); - } + get opacity() { + return this._opacity; } - equals(value) { - if (value == null) - return fail(this, INVALID_KEY_ARGUMENT); - return new this.Collection(this, () => rangeEqual(value)); + set opacity(value) { + this._opacity = value; + const material = this.fsQuad.material; + material.uniforms.opacity.value = value; } - above(value) { - if (value == null) - return fail(this, INVALID_KEY_ARGUMENT); - return new this.Collection(this, () => createRange(value, undefined, true)); + get glossEnabled() { + return this._glossEnabled; } - aboveOrEqual(value) { - if (value == null) - return fail(this, INVALID_KEY_ARGUMENT); - return new this.Collection(this, () => createRange(value, undefined, false)); + set glossEnabled(active) { + if (active === this._glossEnabled) + return; + this._glossEnabled = active; + const material = this.fsQuad.material; + material.uniforms.glossEnabled.value = active ? 1 : 0; } - below(value) { - if (value == null) - return fail(this, INVALID_KEY_ARGUMENT); - return new this.Collection(this, () => createRange(undefined, value, false, true)); + get glossExponent() { + return this._glossExponent; } - belowOrEqual(value) { - if (value == null) - return fail(this, INVALID_KEY_ARGUMENT); - return new this.Collection(this, () => createRange(undefined, value)); + set glossExponent(value) { + this._glossExponent = value; + const material = this.fsQuad.material; + material.uniforms.glossExponent.value = value; } - startsWith(str) { - if (typeof str !== 'string') - return fail(this, STRING_EXPECTED); - return this.between(str, str + maxString, true, true); + get minGloss() { + return this._minGloss; } - startsWithIgnoreCase(str) { - if (str === "") - return this.startsWith(str); - return addIgnoreCaseAlgorithm(this, (x, a) => x.indexOf(a[0]) === 0, [str], maxString); + set minGloss(value) { + this._minGloss = value; + const material = this.fsQuad.material; + material.uniforms.minGloss.value = value; } - equalsIgnoreCase(str) { - return addIgnoreCaseAlgorithm(this, (x, a) => x === a[0], [str], ""); + get maxGloss() { + new THREE$1.MeshBasicMaterial().color.convertLinearToSRGB(); + return this._maxGloss; } - anyOfIgnoreCase() { - var set = getArrayOf.apply(NO_CHAR_ARRAY, arguments); - if (set.length === 0) - return emptyCollection(this); - return addIgnoreCaseAlgorithm(this, (x, a) => a.indexOf(x) !== -1, set, ""); + set maxGloss(value) { + this._maxGloss = value; + const material = this.fsQuad.material; + material.uniforms.maxGloss.value = value; } - startsWithAnyOfIgnoreCase() { - var set = getArrayOf.apply(NO_CHAR_ARRAY, arguments); - if (set.length === 0) - return emptyCollection(this); - return addIgnoreCaseAlgorithm(this, (x, a) => a.some(n => x.indexOf(n) === 0), set, maxString); + get outlineEnabled() { + return this._outlineEnabled; } - anyOf() { - const set = getArrayOf.apply(NO_CHAR_ARRAY, arguments); - let compare = this._cmp; - try { - set.sort(compare); - } - catch (e) { - return fail(this, INVALID_KEY_ARGUMENT); - } - if (set.length === 0) - return emptyCollection(this); - const c = new this.Collection(this, () => createRange(set[0], set[set.length - 1])); - c._ondirectionchange = direction => { - compare = (direction === "next" ? - this._ascending : - this._descending); - set.sort(compare); - }; - let i = 0; - c._addAlgorithm((cursor, advance, resolve) => { - const key = cursor.key; - while (compare(key, set[i]) > 0) { - ++i; - if (i === set.length) { - advance(resolve); - return false; - } - } - if (compare(key, set[i]) === 0) { - return true; - } - else { - advance(() => { cursor.continue(set[i]); }); - return false; - } - }); - return c; + set outlineEnabled(active) { + if (active === this._outlineEnabled) + return; + this._outlineEnabled = active; + const material = this.fsQuad.material; + material.uniforms.outlineEnabled.value = active ? 1 : 0; } - notEqual(value) { - return this.inAnyRange([[minKey, value], [value, this.db._maxKey]], { includeLowers: false, includeUppers: false }); + constructor(resolution, components, scene, camera) { + super(); + this.excludedMeshes = []; + this.outlinedMeshes = {}; + this._outlineScene = new THREE$1.Scene(); + this._outlineEnabled = false; + this._lineColor = 0x999999; + this._opacity = 0.4; + this._tolerance = 3; + this._glossEnabled = true; + this._glossExponent = 1.9; + this._minGloss = -0.1; + this._maxGloss = 0.1; + this._outlinesNeedsUpdate = false; + this.components = components; + this.renderScene = scene; + this.renderCamera = camera; + this.resolution = new THREE$1.Vector2(resolution.x, resolution.y); + this.fsQuad = new FullScreenQuad(); + this.fsQuad.material = this.createOutlinePostProcessMaterial(); + this.planeBuffer = this.newRenderTarget(); + this.glossBuffer = this.newRenderTarget(); + this.outlineBuffer = this.newRenderTarget(); + const normalMaterial = getPlaneDistanceMaterial(); + normalMaterial.clippingPlanes = components.renderer.clippingPlanes; + this.normalOverrideMaterial = normalMaterial; + const glossMaterial = getProjectedNormalMaterial(); + glossMaterial.clippingPlanes = components.renderer.clippingPlanes; + this.glossOverrideMaterial = glossMaterial; } - noneOf() { - const set = getArrayOf.apply(NO_CHAR_ARRAY, arguments); - if (set.length === 0) - return new this.Collection(this); - try { - set.sort(this._ascending); - } - catch (e) { - return fail(this, INVALID_KEY_ARGUMENT); + async dispose() { + this.planeBuffer.dispose(); + this.glossBuffer.dispose(); + this.outlineBuffer.dispose(); + this.normalOverrideMaterial.dispose(); + this.glossOverrideMaterial.dispose(); + this.fsQuad.dispose(); + this.excludedMeshes = []; + this._outlineScene.children = []; + const disposer = await this.components.tools.get(Disposer); + for (const name in this.outlinedMeshes) { + const style = this.outlinedMeshes[name]; + for (const mesh of style.meshes) { + disposer.destroy(mesh, true, true); + } + style.material.dispose(); } - const ranges = set.reduce((res, val) => res ? - res.concat([[res[res.length - 1][1], val]]) : - [[minKey, val]], null); - ranges.push([set[set.length - 1], this.db._maxKey]); - return this.inAnyRange(ranges, { includeLowers: false, includeUppers: false }); } - inAnyRange(ranges, options) { - const cmp = this._cmp, ascending = this._ascending, descending = this._descending, min = this._min, max = this._max; - if (ranges.length === 0) - return emptyCollection(this); - if (!ranges.every(range => range[0] !== undefined && - range[1] !== undefined && - ascending(range[0], range[1]) <= 0)) { - return fail(this, "First argument to inAnyRange() must be an Array of two-value Arrays [lower,upper] where upper must not be lower than lower", exceptions.InvalidArgument); + setSize(width, height) { + this.planeBuffer.setSize(width, height); + this.glossBuffer.setSize(width, height); + this.outlineBuffer.setSize(width, height); + this.resolution.set(width, height); + const material = this.fsQuad.material; + material.uniforms.screenSize.value.set(this.resolution.x, this.resolution.y, 1 / this.resolution.x, 1 / this.resolution.y); + } + render(renderer, writeBuffer, readBuffer) { + // Turn off writing to the depth buffer + // because we need to read from it in the subsequent passes. + const depthBufferValue = writeBuffer.depthBuffer; + writeBuffer.depthBuffer = false; + // 1. Re-render the scene to capture all normals in a texture. + const previousOverrideMaterial = this.renderScene.overrideMaterial; + const previousBackground = this.renderScene.background; + this.renderScene.background = null; + for (const mesh of this.excludedMeshes) { + mesh.visible = false; } - const includeLowers = !options || options.includeLowers !== false; - const includeUppers = options && options.includeUppers === true; - function addRange(ranges, newRange) { - let i = 0, l = ranges.length; - for (; i < l; ++i) { - const range = ranges[i]; - if (cmp(newRange[0], range[1]) < 0 && cmp(newRange[1], range[0]) > 0) { - range[0] = min(range[0], newRange[0]); - range[1] = max(range[1], newRange[1]); - break; + // Render normal pass + renderer.setRenderTarget(this.planeBuffer); + this.renderScene.overrideMaterial = this.normalOverrideMaterial; + renderer.render(this.renderScene, this.renderCamera); + // Render gloss pass + if (this._glossEnabled) { + renderer.setRenderTarget(this.glossBuffer); + this.renderScene.overrideMaterial = this.glossOverrideMaterial; + renderer.render(this.renderScene, this.renderCamera); + } + this.renderScene.overrideMaterial = previousOverrideMaterial; + // Render outline pass + if (this._outlineEnabled) { + let outlinedMeshesFound = false; + for (const name in this.outlinedMeshes) { + const style = this.outlinedMeshes[name]; + for (const mesh of style.meshes) { + outlinedMeshesFound = true; + mesh.userData.materialPreOutline = mesh.material; + mesh.material = style.material; + mesh.userData.groupsPreOutline = mesh.geometry.groups; + mesh.geometry.groups = []; + if (mesh instanceof THREE$1.InstancedMesh) { + mesh.userData.colorPreOutline = mesh.instanceColor; + mesh.instanceColor = null; + } + mesh.userData.parentPreOutline = mesh.parent; + this._outlineScene.add(mesh); + } + } + // This way, when there are no outlines meshes, it clears the outlines buffer only once + // and then skips this render + if (outlinedMeshesFound || this._outlinesNeedsUpdate) { + renderer.setRenderTarget(this.outlineBuffer); + renderer.render(this._outlineScene, this.renderCamera); + this._outlinesNeedsUpdate = outlinedMeshesFound; + } + for (const name in this.outlinedMeshes) { + const style = this.outlinedMeshes[name]; + for (const mesh of style.meshes) { + mesh.material = mesh.userData.materialPreOutline; + mesh.geometry.groups = mesh.userData.groupsPreOutline; + if (mesh instanceof THREE$1.InstancedMesh) { + mesh.instanceColor = mesh.userData.colorPreOutline; + } + if (mesh.userData.parentPreOutline) { + mesh.userData.parentPreOutline.add(mesh); + } + mesh.userData.materialPreOutline = undefined; + mesh.userData.groupsPreOutline = undefined; + mesh.userData.colorPreOutline = undefined; + mesh.userData.parentPreOutline = undefined; } } - if (i === l) - ranges.push(newRange); - return ranges; } - let sortDirection = ascending; - function rangeSorter(a, b) { return sortDirection(a[0], b[0]); } - let set; - try { - set = ranges.reduce(addRange, []); - set.sort(rangeSorter); + for (const mesh of this.excludedMeshes) { + mesh.visible = true; } - catch (ex) { - return fail(this, INVALID_KEY_ARGUMENT); + this.renderScene.background = previousBackground; + const material = this.fsQuad.material; + material.uniforms.planeBuffer.value = this.planeBuffer.texture; + material.uniforms.glossBuffer.value = this.glossBuffer.texture; + material.uniforms.outlineBuffer.value = this.outlineBuffer.texture; + material.uniforms.sceneColorBuffer.value = readBuffer.texture; + if (this.renderToScreen) { + // If this is the last effect, then renderToScreen is true. + // So we should render to the screen by setting target null + // Otherwise, just render into the writeBuffer that the next effect will use as its read buffer. + renderer.setRenderTarget(null); + this.fsQuad.render(renderer); } - let rangePos = 0; - const keyIsBeyondCurrentEntry = includeUppers ? - key => ascending(key, set[rangePos][1]) > 0 : - key => ascending(key, set[rangePos][1]) >= 0; - const keyIsBeforeCurrentEntry = includeLowers ? - key => descending(key, set[rangePos][0]) > 0 : - key => descending(key, set[rangePos][0]) >= 0; - function keyWithinCurrentRange(key) { - return !keyIsBeyondCurrentEntry(key) && !keyIsBeforeCurrentEntry(key); + else { + renderer.setRenderTarget(writeBuffer); + this.fsQuad.render(renderer); } - let checkKey = keyIsBeyondCurrentEntry; - const c = new this.Collection(this, () => createRange(set[0][0], set[set.length - 1][1], !includeLowers, !includeUppers)); - c._ondirectionchange = direction => { - if (direction === "next") { - checkKey = keyIsBeyondCurrentEntry; - sortDirection = ascending; - } - else { - checkKey = keyIsBeforeCurrentEntry; - sortDirection = descending; - } - set.sort(rangeSorter); - }; - c._addAlgorithm((cursor, advance, resolve) => { - var key = cursor.key; - while (checkKey(key)) { - ++rangePos; - if (rangePos === set.length) { - advance(resolve); - return false; - } - } - if (keyWithinCurrentRange(key)) { - return true; - } - else if (this._cmp(key, set[rangePos][1]) === 0 || this._cmp(key, set[rangePos][0]) === 0) { - return false; - } - else { - advance(() => { - if (sortDirection === ascending) - cursor.continue(set[rangePos][0]); - else - cursor.continue(set[rangePos][1]); - }); - return false; - } - }); - return c; + // Reset the depthBuffer value so we continue writing to it in the next render. + writeBuffer.depthBuffer = depthBufferValue; } - startsWithAnyOf() { - const set = getArrayOf.apply(NO_CHAR_ARRAY, arguments); - if (!set.every(s => typeof s === 'string')) { - return fail(this, "startsWithAnyOf() only works with strings"); - } - if (set.length === 0) - return emptyCollection(this); - return this.inAnyRange(set.map((str) => [str, str + maxString])); + get vertexShader() { + return ` + varying vec2 vUv; + void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); + } + `; } -} + get fragmentShader() { + return ` + uniform sampler2D sceneColorBuffer; + uniform sampler2D planeBuffer; + uniform sampler2D glossBuffer; + uniform sampler2D outlineBuffer; + uniform vec4 screenSize; + uniform vec3 lineColor; + + uniform float outlineEnabled; + + uniform int width; + uniform float opacity; + uniform float tolerance; + uniform float glossExponent; + uniform float minGloss; + uniform float maxGloss; + uniform float glossEnabled; -function createWhereClauseConstructor(db) { - return makeClassConstructor(WhereClause.prototype, function WhereClause(table, index, orCollection) { - this.db = db; - this._ctx = { - table: table, - index: index === ":id" ? null : index, - or: orCollection - }; - const indexedDB = db._deps.indexedDB; - if (!indexedDB) - throw new exceptions.MissingAPI(); - this._cmp = this._ascending = indexedDB.cmp.bind(indexedDB); - this._descending = (a, b) => indexedDB.cmp(b, a); - this._max = (a, b) => indexedDB.cmp(a, b) > 0 ? a : b; - this._min = (a, b) => indexedDB.cmp(a, b) < 0 ? a : b; - this._IDBKeyRange = db._deps.IDBKeyRange; - }); -} + varying vec2 vUv; -function eventRejectHandler(reject) { - return wrap(function (event) { - preventDefault(event); - reject(event.target.error); - return false; - }); -} -function preventDefault(event) { - if (event.stopPropagation) - event.stopPropagation(); - if (event.preventDefault) - event.preventDefault(); -} + vec4 getValue(sampler2D buffer, int x, int y) { + return texture2D(buffer, vUv + screenSize.zw * vec2(x, y)); + } -const DEXIE_STORAGE_MUTATED_EVENT_NAME = 'storagemutated'; -const STORAGE_MUTATED_DOM_EVENT_NAME = 'x-storagemutated-1'; -const globalEvents = Events(null, DEXIE_STORAGE_MUTATED_EVENT_NAME); + float normalDiff(vec3 normal1, vec3 normal2) { + return ((dot(normal1, normal2) - 1.) * -1.) / 2.; + } -class Transaction { - _lock() { - assert(!PSD.global); - ++this._reculock; - if (this._reculock === 1 && !PSD.global) - PSD.lockOwnerFor = this; - return this; + // Returns 0 if it's background, 1 if it's not + float getIsBackground(vec3 normal) { + float background = 1.0; + background *= step(normal.x, 0.); + background *= step(normal.y, 0.); + background *= step(normal.z, 0.); + background = (background - 1.) * -1.; + return background; + } + + void main() { + + vec4 sceneColor = getValue(sceneColorBuffer, 0, 0); + vec3 normSceneColor = normalize(sceneColor.rgb); + + vec4 plane = getValue(planeBuffer, 0, 0); + vec3 normal = plane.xyz; + float distance = plane.w; + + vec3 normalTop = getValue(planeBuffer, 0, width).rgb; + vec3 normalBottom = getValue(planeBuffer, 0, -width).rgb; + vec3 normalRight = getValue(planeBuffer, width, 0).rgb; + vec3 normalLeft = getValue(planeBuffer, -width, 0).rgb; + vec3 normalTopRight = getValue(planeBuffer, width, width).rgb; + vec3 normalTopLeft = getValue(planeBuffer, -width, width).rgb; + vec3 normalBottomRight = getValue(planeBuffer, width, -width).rgb; + vec3 normalBottomLeft = getValue(planeBuffer, -width, -width).rgb; + + float distanceTop = getValue(planeBuffer, 0, width).a; + float distanceBottom = getValue(planeBuffer, 0, -width).a; + float distanceRight = getValue(planeBuffer, width, 0).a; + float distanceLeft = getValue(planeBuffer, -width, 0).a; + float distanceTopRight = getValue(planeBuffer, width, width).a; + float distanceTopLeft = getValue(planeBuffer, -width, width).a; + float distanceBottomRight = getValue(planeBuffer, width, -width).a; + float distanceBottomLeft = getValue(planeBuffer, -width, -width).a; + + vec3 sceneColorTop = normalize(getValue(sceneColorBuffer, 1, 0).rgb); + vec3 sceneColorBottom = normalize(getValue(sceneColorBuffer, -1, 0).rgb); + vec3 sceneColorLeft = normalize(getValue(sceneColorBuffer, 0, -1).rgb); + vec3 sceneColorRight = normalize(getValue(sceneColorBuffer, 0, 1).rgb); + vec3 sceneColorTopRight = normalize(getValue(sceneColorBuffer, 1, 1).rgb); + vec3 sceneColorBottomRight = normalize(getValue(sceneColorBuffer, -1, 1).rgb); + vec3 sceneColorTopLeft = normalize(getValue(sceneColorBuffer, 1, 1).rgb); + vec3 sceneColorBottomLeft = normalize(getValue(sceneColorBuffer, -1, 1).rgb); + + // Checks if the planes of this texel and the neighbour texels are different + + float planeDiff = 0.0; + + planeDiff += step(0.001, normalDiff(normal, normalTop)); + planeDiff += step(0.001, normalDiff(normal, normalBottom)); + planeDiff += step(0.001, normalDiff(normal, normalLeft)); + planeDiff += step(0.001, normalDiff(normal, normalRight)); + planeDiff += step(0.001, normalDiff(normal, normalTopRight)); + planeDiff += step(0.001, normalDiff(normal, normalTopLeft)); + planeDiff += step(0.001, normalDiff(normal, normalBottomRight)); + planeDiff += step(0.001, normalDiff(normal, normalBottomLeft)); + + planeDiff += step(0.001, normalDiff(normSceneColor, sceneColorTop)); + planeDiff += step(0.001, normalDiff(normSceneColor, sceneColorBottom)); + planeDiff += step(0.001, normalDiff(normSceneColor, sceneColorLeft)); + planeDiff += step(0.001, normalDiff(normSceneColor, sceneColorRight)); + planeDiff += step(0.001, normalDiff(normSceneColor, sceneColorTopRight)); + planeDiff += step(0.001, normalDiff(normSceneColor, sceneColorTopLeft)); + planeDiff += step(0.001, normalDiff(normSceneColor, sceneColorBottomRight)); + planeDiff += step(0.001, normalDiff(normSceneColor, sceneColorBottomLeft)); + + planeDiff += step(0.001, abs(distance - distanceTop)); + planeDiff += step(0.001, abs(distance - distanceBottom)); + planeDiff += step(0.001, abs(distance - distanceLeft)); + planeDiff += step(0.001, abs(distance - distanceRight)); + planeDiff += step(0.001, abs(distance - distanceTopRight)); + planeDiff += step(0.001, abs(distance - distanceTopLeft)); + planeDiff += step(0.001, abs(distance - distanceBottomRight)); + planeDiff += step(0.001, abs(distance - distanceBottomLeft)); + + // Add extra background outline + + int width2 = width + 1; + vec3 normalTop2 = getValue(planeBuffer, 0, width2).rgb; + vec3 normalBottom2 = getValue(planeBuffer, 0, -width2).rgb; + vec3 normalRight2 = getValue(planeBuffer, width2, 0).rgb; + vec3 normalLeft2 = getValue(planeBuffer, -width2, 0).rgb; + vec3 normalTopRight2 = getValue(planeBuffer, width2, width2).rgb; + vec3 normalTopLeft2 = getValue(planeBuffer, -width2, width2).rgb; + vec3 normalBottomRight2 = getValue(planeBuffer, width2, -width2).rgb; + vec3 normalBottomLeft2 = getValue(planeBuffer, -width2, -width2).rgb; + + planeDiff += -(getIsBackground(normalTop2) - 1.); + planeDiff += -(getIsBackground(normalBottom2) - 1.); + planeDiff += -(getIsBackground(normalRight2) - 1.); + planeDiff += -(getIsBackground(normalLeft2) - 1.); + planeDiff += -(getIsBackground(normalTopRight2) - 1.); + planeDiff += -(getIsBackground(normalBottomRight2) - 1.); + planeDiff += -(getIsBackground(normalBottomRight2) - 1.); + planeDiff += -(getIsBackground(normalBottomLeft2) - 1.); + + // Tolerance sets the minimum amount of differences to consider + // this texel an edge + + float line = step(tolerance, planeDiff); + + // Exclude background and apply opacity + + float background = getIsBackground(normal); + line *= background; + line *= opacity; + + // Add gloss + + vec3 gloss = getValue(glossBuffer, 0, 0).xyz; + float diffGloss = abs(maxGloss - minGloss); + vec3 glossExpVector = vec3(glossExponent,glossExponent,glossExponent); + gloss = min(pow(gloss, glossExpVector), vec3(1.,1.,1.)); + gloss *= diffGloss; + gloss += minGloss; + vec4 glossedColor = sceneColor + vec4(gloss, 1.) * glossEnabled; + + vec4 corrected = mix(sceneColor, glossedColor, background); + + // Draw lines + + corrected = mix(corrected, vec4(lineColor, 1.), line); + + // Add outline + + vec4 outlinePreview =getValue(outlineBuffer, 0, 0); + float outlineColorCorrection = 1. / max(0.2, outlinePreview.a); + vec3 outlineColor = outlinePreview.rgb * outlineColorCorrection; + + // thickness between 10 and 2, opacity between 1 and 0.2 + int outlineThickness = int(outlinePreview.a * 10.); + + float outlineDiff = 0.; + + outlineDiff += step(0.1, getValue(outlineBuffer, 0, 0).a); + outlineDiff += step(0.1, getValue(outlineBuffer, 1, 0).a); + outlineDiff += step(0.1, getValue(outlineBuffer, -1, 0).a); + outlineDiff += step(0.1, getValue(outlineBuffer, 0, -1).a); + outlineDiff += step(0.1, getValue(outlineBuffer, 0, 1).a); + outlineDiff += step(0.1, getValue(outlineBuffer, outlineThickness, 0).a); + outlineDiff += step(0.1, getValue(outlineBuffer, -outlineThickness, 0).a); + outlineDiff += step(0.1, getValue(outlineBuffer, 0, -outlineThickness).a); + outlineDiff += step(0.1, getValue(outlineBuffer, 0, outlineThickness).a); + outlineDiff += step(0.1, getValue(outlineBuffer, outlineThickness, outlineThickness).a); + outlineDiff += step(0.1, getValue(outlineBuffer, -outlineThickness, outlineThickness).a); + outlineDiff += step(0.1, getValue(outlineBuffer, -outlineThickness, -outlineThickness).a); + outlineDiff += step(0.1, getValue(outlineBuffer, outlineThickness, -outlineThickness).a); + + float outLine = step(4., outlineDiff) * step(outlineDiff, 12.) * outlineEnabled; + corrected = mix(corrected, vec4(outlineColor, 1.), outLine); + + gl_FragColor = corrected; + } + `; } - _unlock() { - assert(!PSD.global); - if (--this._reculock === 0) { - if (!PSD.global) - PSD.lockOwnerFor = null; - while (this._blockedFuncs.length > 0 && !this._locked()) { - var fnAndPSD = this._blockedFuncs.shift(); - try { - usePSD(fnAndPSD[1], fnAndPSD[0]); - } - catch (e) { } - } + createOutlinePostProcessMaterial() { + return new THREE$1.ShaderMaterial({ + uniforms: { + opacity: { value: this._opacity }, + debugVisualize: { value: 0 }, + sceneColorBuffer: { value: null }, + tolerance: { value: this._tolerance }, + planeBuffer: { value: null }, + glossBuffer: { value: null }, + outlineBuffer: { value: null }, + glossEnabled: { value: 1 }, + minGloss: { value: this._minGloss }, + maxGloss: { value: this._maxGloss }, + outlineEnabled: { value: 0 }, + glossExponent: { value: this._glossExponent }, + width: { value: 1 }, + lineColor: { value: new THREE$1.Color(this._lineColor) }, + screenSize: { + value: new THREE$1.Vector4(this.resolution.x, this.resolution.y, 1 / this.resolution.x, 1 / this.resolution.y), + }, + }, + vertexShader: this.vertexShader, + fragmentShader: this.fragmentShader, + }); + } + newRenderTarget() { + const planeBuffer = new THREE$1.WebGLRenderTarget(this.resolution.x, this.resolution.y); + planeBuffer.texture.colorSpace = "srgb-linear"; + planeBuffer.texture.format = THREE$1.RGBAFormat; + planeBuffer.texture.type = THREE$1.HalfFloatType; + planeBuffer.texture.minFilter = THREE$1.NearestFilter; + planeBuffer.texture.magFilter = THREE$1.NearestFilter; + planeBuffer.texture.generateMipmaps = false; + planeBuffer.stencilBuffer = false; + return planeBuffer; + } +} + +// source: https://discourse.threejs.org/t/how-to-render-full-outlines-as-a-post-process-tutorial/22674 +class Postproduction { + get basePass() { + if (!this._basePass) { + throw new Error("Custom effects not initialized!"); } - return this; + return this._basePass; } - _locked() { - return this._reculock && PSD.lockOwnerFor !== this; + get gammaPass() { + if (!this._gammaPass) { + throw new Error("Custom effects not initialized!"); + } + return this._gammaPass; } - create(idbtrans) { - if (!this.mode) - return this; - const idbdb = this.db.idbdb; - const dbOpenError = this.db._state.dbOpenError; - assert(!this.idbtrans); - if (!idbtrans && !idbdb) { - switch (dbOpenError && dbOpenError.name) { - case "DatabaseClosedError": - throw new exceptions.DatabaseClosed(dbOpenError); - case "MissingAPIError": - throw new exceptions.MissingAPI(dbOpenError.message, dbOpenError); - default: - throw new exceptions.OpenFailed(dbOpenError); - } + get customEffects() { + if (!this._customEffects) { + throw new Error("Custom effects not initialized!"); } - if (!this.active) - throw new exceptions.TransactionInactive(); - assert(this._completion._state === null); - idbtrans = this.idbtrans = idbtrans || - (this.db.core - ? this.db.core.transaction(this.storeNames, this.mode, { durability: this.chromeTransactionDurability }) - : idbdb.transaction(this.storeNames, this.mode, { durability: this.chromeTransactionDurability })); - idbtrans.onerror = wrap(ev => { - preventDefault(ev); - this._reject(idbtrans.error); - }); - idbtrans.onabort = wrap(ev => { - preventDefault(ev); - this.active && this._reject(new exceptions.Abort(idbtrans.error)); - this.active = false; - this.on("abort").fire(ev); - }); - idbtrans.oncomplete = wrap(() => { - this.active = false; - this._resolve(); - if ('mutatedParts' in idbtrans) { - globalEvents.storagemutated.fire(idbtrans["mutatedParts"]); - } - }); - return this; + return this._customEffects; } - _promise(mode, fn, bWriteLock) { - if (mode === 'readwrite' && this.mode !== 'readwrite') - return rejection(new exceptions.ReadOnly("Transaction is readonly")); - if (!this.active) - return rejection(new exceptions.TransactionInactive()); - if (this._locked()) { - return new DexiePromise((resolve, reject) => { - this._blockedFuncs.push([() => { - this._promise(mode, fn, bWriteLock).then(resolve, reject); - }, PSD]); - }); + get n8ao() { + if (!this._n8ao) { + throw new Error("Custom effects not initialized!"); } - else if (bWriteLock) { - return newScope(() => { - var p = new DexiePromise((resolve, reject) => { - this._lock(); - const rv = fn(resolve, reject, this); - if (rv && rv.then) - rv.then(resolve, reject); - }); - p.finally(() => this._unlock()); - p._lib = true; - return p; - }); + return this._n8ao; + } + get enabled() { + return this._enabled; + } + set enabled(active) { + if (!this._initialized) { + this.initialize(); } - else { - var p = new DexiePromise((resolve, reject) => { - var rv = fn(resolve, reject, this); - if (rv && rv.then) - rv.then(resolve, reject); - }); - p._lib = true; - return p; + this._enabled = active; + } + get settings() { + return { ...this._settings }; + } + constructor(components, renderer) { + this.components = components; + this.renderer = renderer; + this.excludedItems = new Set(); + this.overrideClippingPlanes = false; + this._enabled = false; + this._initialized = false; + this._settings = { + gamma: true, + custom: true, + ao: false, + }; + this._renderTarget = new THREE$1.WebGLRenderTarget(window.innerWidth, window.innerHeight); + this._renderTarget.texture.colorSpace = "srgb-linear"; + this.composer = new EffectComposer(this.renderer, this._renderTarget); + this.composer.setSize(window.innerWidth, window.innerHeight); + } + async dispose() { + var _a, _b, _c, _d; + this._renderTarget.dispose(); + (_a = this._depthTexture) === null || _a === void 0 ? void 0 : _a.dispose(); + await ((_b = this._customEffects) === null || _b === void 0 ? void 0 : _b.dispose()); + (_c = this._gammaPass) === null || _c === void 0 ? void 0 : _c.dispose(); + (_d = this._n8ao) === null || _d === void 0 ? void 0 : _d.dispose(); + this.excludedItems.clear(); + } + setPasses(settings) { + // This check can prevent some bugs + let settingsChanged = false; + for (const name in settings) { + const key = name; + if (this.settings[key] !== settings[key]) { + settingsChanged = true; + break; + } + } + if (!settingsChanged) { + return; + } + for (const name in settings) { + const key = name; + if (this._settings[key] !== undefined) { + this._settings[key] = settings[key]; + } } + this.updatePasses(); } - _root() { - return this.parent ? this.parent._root() : this; + setSize(width, height) { + if (this._initialized) { + this.composer.setSize(width, height); + this.basePass.setSize(width, height); + this.n8ao.setSize(width, height); + this.customEffects.setSize(width, height); + this.gammaPass.setSize(width, height); + } } - waitFor(promiseLike) { - var root = this._root(); - const promise = DexiePromise.resolve(promiseLike); - if (root._waitingFor) { - root._waitingFor = root._waitingFor.then(() => promise); + update() { + if (!this._enabled) + return; + this.composer.render(); + } + updateCamera() { + const camera = this.components.camera.get(); + if (this._n8ao) { + this._n8ao.camera = camera; } - else { - root._waitingFor = promise; - root._waitingQueue = []; - var store = root.idbtrans.objectStore(root.storeNames[0]); - (function spin() { - ++root._spinCount; - while (root._waitingQueue.length) - (root._waitingQueue.shift())(); - if (root._waitingFor) - store.get(-Infinity).onsuccess = spin; - }()); + if (this._customEffects) { + this._customEffects.renderCamera = camera; } - var currentWaitPromise = root._waitingFor; - return new DexiePromise((resolve, reject) => { - promise.then(res => root._waitingQueue.push(wrap(resolve.bind(null, res))), err => root._waitingQueue.push(wrap(reject.bind(null, err)))).finally(() => { - if (root._waitingFor === currentWaitPromise) { - root._waitingFor = null; - } + if (this._basePass) { + this._basePass.camera = camera; + } + } + initialize() { + const scene = this.overrideScene || this.components.scene.get(); + const camera = this.overrideCamera || this.components.camera.get(); + if (!scene || !camera) + return; + if (this.components.camera instanceof OrthoPerspectiveCamera) { + this.components.camera.projectionChanged.add(() => { + this.updateCamera(); }); + } + const renderer = this.components.renderer; + if (!this.overrideClippingPlanes) { + this.renderer.clippingPlanes = renderer.clippingPlanes; + } + this.renderer.outputColorSpace = "srgb"; + this.renderer.toneMapping = THREE$1.NoToneMapping; + this.newBasePass(scene, camera); + this.newSaoPass(scene, camera); + this.newGammaPass(); + this.newCustomPass(scene, camera); + this._initialized = true; + this.updatePasses(); + } + updateProjection(camera) { + this.composer.passes.forEach((pass) => { + // @ts-ignore + pass.camera = camera; }); + this.update(); } - abort() { - if (this.active) { - this.active = false; - if (this.idbtrans) - this.idbtrans.abort(); - this._reject(new exceptions.Abort()); + updatePasses() { + for (const pass of this.composer.passes) { + this.composer.removePass(pass); } - } - table(tableName) { - const memoizedTables = (this._memoizedTables || (this._memoizedTables = {})); - if (hasOwn(memoizedTables, tableName)) - return memoizedTables[tableName]; - const tableSchema = this.schema[tableName]; - if (!tableSchema) { - throw new exceptions.NotFound("Table " + tableName + " not part of transaction"); + if (this._basePass) { + this.composer.addPass(this.basePass); } - const transactionBoundTable = new this.db.Table(tableName, tableSchema, this); - transactionBoundTable.core = this.db.core.table(tableName); - memoizedTables[tableName] = transactionBoundTable; - return transactionBoundTable; + if (this._settings.gamma) { + this.composer.addPass(this.gammaPass); + } + if (this._settings.ao) { + this.composer.addPass(this.n8ao); + } + if (this._settings.custom) { + this.composer.addPass(this.customEffects); + } + } + newCustomPass(scene, camera) { + this._customEffects = new CustomEffectsPass(new THREE$1.Vector2(window.innerWidth, window.innerHeight), this.components, scene, camera); + } + newGammaPass() { + this._gammaPass = new ShaderPass(GammaCorrectionShader); + } + newSaoPass(scene, camera) { + const { width, height } = this.components.renderer.getSize(); + this._n8ao = new $05f6997e4b65da14$export$2d57db20b5eb5e0a(scene, camera, width, height); + // this.composer.addPass(this.n8ao); + const { configuration } = this._n8ao; + configuration.aoSamples = 16; + configuration.denoiseSamples = 1; + configuration.denoiseRadius = 13; + configuration.aoRadius = 1; + configuration.distanceFalloff = 4; + configuration.aoRadius = 1; + configuration.intensity = 4; + configuration.halfRes = true; + configuration.color = new THREE$1.Color().setHex(0xcccccc, "srgb-linear"); + } + newBasePass(scene, camera) { + this._basePass = new RenderPass(scene, camera); } } -function createTransactionConstructor(db) { - return makeClassConstructor(Transaction.prototype, function Transaction(mode, storeNames, dbschema, chromeTransactionDurability, parent) { - this.db = db; - this.mode = mode; - this.storeNames = storeNames; - this.schema = dbschema; - this.chromeTransactionDurability = chromeTransactionDurability; - this.idbtrans = null; - this.on = Events(this, "complete", "error", "abort"); - this.parent = parent || null; - this.active = true; - this._reculock = 0; - this._blockedFuncs = []; - this._resolve = null; - this._reject = null; - this._waitingFor = null; - this._waitingQueue = null; - this._spinCount = 0; - this._completion = new DexiePromise((resolve, reject) => { - this._resolve = resolve; - this._reject = reject; - }); - this._completion.then(() => { - this.active = false; - this.on.complete.fire(); - }, e => { - var wasActive = this.active; - this.active = false; - this.on.error.fire(e); - this.parent ? - this.parent._reject(e) : - wasActive && this.idbtrans && this.idbtrans.abort(); - return rejection(e); - }); - }); -} - -function createIndexSpec(name, keyPath, unique, multi, auto, compound, isPrimKey) { - return { - name, - keyPath, - unique, - multi, - auto, - compound, - src: (unique && !isPrimKey ? '&' : '') + (multi ? '*' : '') + (auto ? "++" : "") + nameFromKeyPath(keyPath) - }; -} -function nameFromKeyPath(keyPath) { - return typeof keyPath === 'string' ? - keyPath : - keyPath ? ('[' + [].join.call(keyPath, '+') + ']') : ""; -} - -function createTableSchema(name, primKey, indexes) { - return { - name, - primKey, - indexes, - mappedClass: null, - idxByName: arrayToObject(indexes, index => [index.name, index]) - }; +/** + * Renderer that uses efficient postproduction effects (e.g. Ambient Occlusion). + */ +class PostproductionRenderer extends SimpleRenderer { + constructor(components, container, parameters) { + super(components, container, parameters); + this.postproduction = new Postproduction(components, this._renderer); + this.setPostproductionSize(); + this.onResize.add((size) => this.resizePostproduction(size)); + } + /** {@link Updateable.update} */ + async update() { + if (!this.enabled) + return; + await this.onBeforeUpdate.trigger(); + const scene = this.overrideScene || this.components.scene.get(); + const camera = this.overrideCamera || this.components.camera.get(); + if (!scene || !camera) + return; + if (this.postproduction.enabled) { + this.postproduction.composer.render(); + } + else { + this._renderer.render(scene, camera); + } + this._renderer2D.render(scene, camera); + await this.onAfterUpdate.trigger(); + } + /** {@link Disposable.dispose}. */ + async dispose() { + await super.dispose(); + await this.postproduction.dispose(); + } + resizePostproduction(size) { + if (this.postproduction) { + this.setPostproductionSize(size); + } + } + setPostproductionSize(size) { + if (!this.container) + return; + const width = size ? size.x : this.container.clientWidth; + const height = size ? size.y : this.container.clientHeight; + this.postproduction.setSize(width, height); + } } -function safariMultiStoreFix(storeNames) { - return storeNames.length === 1 ? storeNames[0] : storeNames; -} -let getMaxKey = (IdbKeyRange) => { - try { - IdbKeyRange.only([[]]); - getMaxKey = () => [[]]; - return [[]]; +class FragmentHighlighter extends Component { + get outlineEnabled() { + return this._outlineEnabled; } - catch (e) { - getMaxKey = () => maxString; - return maxString; + set outlineEnabled(value) { + this._outlineEnabled = value; + if (!value) { + delete this._postproduction.customEffects.outlinedMeshes.fragments; + } } -}; - -function getKeyExtractor(keyPath) { - if (keyPath == null) { - return () => undefined; + get _postproduction() { + if (!(this.components.renderer instanceof PostproductionRenderer)) { + throw new Error("Postproduction renderer is needed for outlines!"); + } + const renderer = this.components.renderer; + return renderer.postproduction; } - else if (typeof keyPath === 'string') { - return getSinglePathKeyExtractor(keyPath); + constructor(components) { + super(components); + /** {@link Disposable.onDisposed} */ + this.onDisposed = new Event(); + /** {@link Updateable.onBeforeUpdate} */ + this.onBeforeUpdate = new Event(); + /** {@link Updateable.onAfterUpdate} */ + this.onAfterUpdate = new Event(); + this.enabled = true; + this.highlightMats = {}; + this.events = {}; + this.multiple = "ctrlKey"; + this.zoomFactor = 1.5; + this.zoomToSelection = false; + this.selection = {}; + this.excludeOutline = new Set(); + this.fillEnabled = true; + this.outlineMaterial = new THREE$1.MeshBasicMaterial({ + color: "white", + transparent: true, + depthTest: false, + depthWrite: false, + opacity: 0.4, + }); + this._eventsActive = false; + this._outlineEnabled = true; + this._outlinedMeshes = {}; + this._invisibleMaterial = new THREE$1.MeshBasicMaterial({ visible: false }); + this._tempMatrix = new THREE$1.Matrix4(); + this.config = { + selectName: "select", + hoverName: "hover", + selectionMaterial: new THREE$1.MeshBasicMaterial({ + color: "#BCF124", + transparent: true, + opacity: 0.85, + depthTest: true, + }), + hoverMaterial: new THREE$1.MeshBasicMaterial({ + color: "#6528D7", + transparent: true, + opacity: 0.2, + depthTest: true, + }), + autoHighlightOnClick: true, + cullHighlightMeshes: true, + }; + this._mouseState = { + down: false, + moved: false, + }; + this.onFragmentsDisposed = (data) => { + this.disposeOutlinedMeshes(data.fragmentIDs); + }; + this.onSetup = new Event(); + this.onMouseDown = () => { + if (!this.enabled) + return; + this._mouseState.down = true; + }; + this.onMouseUp = async (event) => { + if (!this.enabled) + return; + if (event.target !== this.components.renderer.get().domElement) + return; + this._mouseState.down = false; + if (this._mouseState.moved || event.button !== 0) { + this._mouseState.moved = false; + return; + } + this._mouseState.moved = false; + if (this.config.autoHighlightOnClick) { + const mult = this.multiple === "none" ? true : !event[this.multiple]; + await this.highlight(this.config.selectName, mult, this.zoomToSelection); + } + }; + this.onMouseMove = async () => { + if (!this.enabled) + return; + if (this._mouseState.moved) { + await this.clearFills(this.config.hoverName); + return; + } + this._mouseState.moved = this._mouseState.down; + await this.highlight(this.config.hoverName, true, false); + }; + this.components.tools.add(FragmentHighlighter.uuid, this); + const fragmentManager = components.tools.get(FragmentManager); + fragmentManager.onFragmentsDisposed.add(this.onFragmentsDisposed); } - else { - return obj => getByKeyPath(obj, keyPath); + get() { + return this.highlightMats; } -} -function getSinglePathKeyExtractor(keyPath) { - const split = keyPath.split('.'); - if (split.length === 1) { - return obj => obj[keyPath]; + getHoveredSelection() { + return this.selection[this.config.hoverName]; } - else { - return obj => getByKeyPath(obj, keyPath); + disposeOutlinedMeshes(fragmentIDs) { + for (const id of fragmentIDs) { + const mesh = this._outlinedMeshes[id]; + if (!mesh) + continue; + mesh.geometry.dispose(); + delete this._outlinedMeshes[id]; + } } -} - -function arrayify(arrayLike) { - return [].slice.call(arrayLike); -} -let _id_counter = 0; -function getKeyPathAlias(keyPath) { - return keyPath == null ? - ":id" : - typeof keyPath === 'string' ? - keyPath : - `[${keyPath.join('+')}]`; -} -function createDBCore(db, IdbKeyRange, tmpTrans) { - function extractSchema(db, trans) { - const tables = arrayify(db.objectStoreNames); - return { - schema: { - name: db.name, - tables: tables.map(table => trans.objectStore(table)).map(store => { - const { keyPath, autoIncrement } = store; - const compound = isArray(keyPath); - const outbound = keyPath == null; - const indexByKeyPath = {}; - const result = { - name: store.name, - primaryKey: { - name: null, - isPrimaryKey: true, - outbound, - compound, - keyPath, - autoIncrement, - unique: true, - extractKey: getKeyExtractor(keyPath) - }, - indexes: arrayify(store.indexNames).map(indexName => store.index(indexName)) - .map(index => { - const { name, unique, multiEntry, keyPath } = index; - const compound = isArray(keyPath); - const result = { - name, - compound, - keyPath, - unique, - multiEntry, - extractKey: getKeyExtractor(keyPath) - }; - indexByKeyPath[getKeyPathAlias(keyPath)] = result; - return result; - }), - getIndexByKeyPath: (keyPath) => indexByKeyPath[getKeyPathAlias(keyPath)] - }; - indexByKeyPath[":id"] = result.primaryKey; - if (keyPath != null) { - indexByKeyPath[getKeyPathAlias(keyPath)] = result.primaryKey; - } - return result; - }) - }, - hasGetAll: tables.length > 0 && ('getAll' in trans.objectStore(tables[0])) && - !(typeof navigator !== 'undefined' && /Safari/.test(navigator.userAgent) && - !/(Chrome\/|Edge\/)/.test(navigator.userAgent) && - [].concat(navigator.userAgent.match(/Safari\/(\d*)/))[1] < 604) + async dispose() { + this.setupEvents(false); + this.config.hoverMaterial.dispose(); + this.config.selectionMaterial.dispose(); + this.onBeforeUpdate.reset(); + this.onAfterUpdate.reset(); + for (const matID in this.highlightMats) { + const mats = this.highlightMats[matID] || []; + for (const mat of mats) { + mat.dispose(); + } + } + this.disposeOutlinedMeshes(Object.keys(this._outlinedMeshes)); + this.outlineMaterial.dispose(); + this._invisibleMaterial.dispose(); + this.highlightMats = {}; + this.selection = {}; + for (const name in this.events) { + this.events[name].onClear.reset(); + this.events[name].onHighlight.reset(); + } + this.onSetup.reset(); + const fragmentManager = this.components.tools.get(FragmentManager); + fragmentManager.onFragmentsDisposed.remove(this.onFragmentsDisposed); + this.events = {}; + await this.onDisposed.trigger(FragmentHighlighter.uuid); + this.onDisposed.reset(); + } + async add(name, material) { + if (this.highlightMats[name]) { + throw new Error("A highlight with this name already exists."); + } + this.highlightMats[name] = material; + this.selection[name] = {}; + this.events[name] = { + onHighlight: new Event(), + onClear: new Event(), }; + await this.update(); } - function makeIDBKeyRange(range) { - if (range.type === 3 ) - return null; - if (range.type === 4 ) - throw new Error("Cannot convert never type to IDBKeyRange"); - const { lower, upper, lowerOpen, upperOpen } = range; - const idbRange = lower === undefined ? - upper === undefined ? - null : - IdbKeyRange.upperBound(upper, !!upperOpen) : - upper === undefined ? - IdbKeyRange.lowerBound(lower, !!lowerOpen) : - IdbKeyRange.bound(lower, upper, !!lowerOpen, !!upperOpen); - return idbRange; + /** {@link Updateable.update} */ + async update() { + if (!this.fillEnabled) { + return; + } + this.onBeforeUpdate.trigger(this); + const fragments = this.components.tools.get(FragmentManager); + for (const fragmentID in fragments.list) { + const fragment = fragments.list[fragmentID]; + this.addHighlightToFragment(fragment); + const outlinedMesh = this._outlinedMeshes[fragmentID]; + if (outlinedMesh) { + fragment.mesh.updateMatrixWorld(true); + outlinedMesh.applyMatrix4(fragment.mesh.matrixWorld); + } + } + this.onAfterUpdate.trigger(this); } - function createDbCoreTable(tableSchema) { - const tableName = tableSchema.name; - function mutate({ trans, type, keys, values, range }) { - return new Promise((resolve, reject) => { - resolve = wrap(resolve); - const store = trans.objectStore(tableName); - const outbound = store.keyPath == null; - const isAddOrPut = type === "put" || type === "add"; - if (!isAddOrPut && type !== 'delete' && type !== 'deleteRange') - throw new Error("Invalid operation type: " + type); - const { length } = keys || values || { length: 1 }; - if (keys && values && keys.length !== values.length) { - throw new Error("Given keys array must have same length as given values array."); - } - if (length === 0) - return resolve({ numFailures: 0, failures: {}, results: [], lastResult: undefined }); - let req; - const reqs = []; - const failures = []; - let numFailures = 0; - const errorHandler = event => { - ++numFailures; - preventDefault(event); - }; - if (type === 'deleteRange') { - if (range.type === 4 ) - return resolve({ numFailures, failures, results: [], lastResult: undefined }); - if (range.type === 3 ) - reqs.push(req = store.clear()); - else - reqs.push(req = store.delete(makeIDBKeyRange(range))); - } - else { - const [args1, args2] = isAddOrPut ? - outbound ? - [values, keys] : - [values, null] : - [keys, null]; - if (isAddOrPut) { - for (let i = 0; i < length; ++i) { - reqs.push(req = (args2 && args2[i] !== undefined ? - store[type](args1[i], args2[i]) : - store[type](args1[i]))); - req.onerror = errorHandler; - } - } - else { - for (let i = 0; i < length; ++i) { - reqs.push(req = store[type](args1[i])); - req.onerror = errorHandler; - } - } + async highlight(name, removePrevious = true, zoomToSelection = this.zoomToSelection) { + var _a; + if (!this.enabled) + return null; + this.checkSelection(name); + const fragments = this.components.tools.get(FragmentManager); + const fragList = []; + const meshes = fragments.meshes; + const result = this.components.raycaster.castRay(meshes); + if (!result) { + await this.clear(name); + return null; + } + const mesh = result.object; + const geometry = mesh.geometry; + const index = (_a = result.face) === null || _a === void 0 ? void 0 : _a.a; + const instanceID = result.instanceId; + if (!geometry || index === undefined || instanceID === undefined) { + return null; + } + if (removePrevious) { + await this.clear(name); + } + if (!this.selection[name][mesh.uuid]) { + this.selection[name][mesh.uuid] = new Set(); + } + fragList.push(mesh.fragment); + const blockID = mesh.fragment.getVertexBlockID(geometry, index); + const itemID = mesh.fragment + .getItemID(instanceID, blockID) + .replace(/\..*/, ""); + const idNum = parseInt(itemID, 10); + this.selection[name][mesh.uuid].add(itemID); + this.addComposites(mesh, idNum, name); + await this.regenerate(name, mesh.uuid); + const group = mesh.fragment.group; + if (group) { + const keys = group.data[idNum][0]; + for (let i = 0; i < keys.length; i++) { + const fragKey = keys[i]; + const fragID = group.keyFragments[fragKey]; + const fragment = fragments.list[fragID]; + fragList.push(fragment); + if (!this.selection[name][fragID]) { + this.selection[name][fragID] = new Set(); } - const done = event => { - const lastResult = event.target.result; - reqs.forEach((req, i) => req.error != null && (failures[i] = req.error)); - resolve({ - numFailures, - failures, - results: type === "delete" ? keys : reqs.map(req => req.result), - lastResult - }); - }; - req.onerror = event => { - errorHandler(event); - done(event); - }; - req.onsuccess = done; - }); + this.selection[name][fragID].add(itemID); + this.addComposites(fragment.mesh, idNum, name); + await this.regenerate(name, fragID); + } } - function openCursor({ trans, values, query, reverse, unique }) { - return new Promise((resolve, reject) => { - resolve = wrap(resolve); - const { index, range } = query; - const store = trans.objectStore(tableName); - const source = index.isPrimaryKey ? - store : - store.index(index.name); - const direction = reverse ? - unique ? - "prevunique" : - "prev" : - unique ? - "nextunique" : - "next"; - const req = values || !('openKeyCursor' in source) ? - source.openCursor(makeIDBKeyRange(range), direction) : - source.openKeyCursor(makeIDBKeyRange(range), direction); - req.onerror = eventRejectHandler(reject); - req.onsuccess = wrap(ev => { - const cursor = req.result; - if (!cursor) { - resolve(null); - return; - } - cursor.___id = ++_id_counter; - cursor.done = false; - const _cursorContinue = cursor.continue.bind(cursor); - let _cursorContinuePrimaryKey = cursor.continuePrimaryKey; - if (_cursorContinuePrimaryKey) - _cursorContinuePrimaryKey = _cursorContinuePrimaryKey.bind(cursor); - const _cursorAdvance = cursor.advance.bind(cursor); - const doThrowCursorIsNotStarted = () => { throw new Error("Cursor not started"); }; - const doThrowCursorIsStopped = () => { throw new Error("Cursor not stopped"); }; - cursor.trans = trans; - cursor.stop = cursor.continue = cursor.continuePrimaryKey = cursor.advance = doThrowCursorIsNotStarted; - cursor.fail = wrap(reject); - cursor.next = function () { - let gotOne = 1; - return this.start(() => gotOne-- ? this.continue() : this.stop()).then(() => this); - }; - cursor.start = (callback) => { - const iterationPromise = new Promise((resolveIteration, rejectIteration) => { - resolveIteration = wrap(resolveIteration); - req.onerror = eventRejectHandler(rejectIteration); - cursor.fail = rejectIteration; - cursor.stop = value => { - cursor.stop = cursor.continue = cursor.continuePrimaryKey = cursor.advance = doThrowCursorIsStopped; - resolveIteration(value); - }; - }); - const guardedCallback = () => { - if (req.result) { - try { - callback(); - } - catch (err) { - cursor.fail(err); - } - } - else { - cursor.done = true; - cursor.start = () => { throw new Error("Cursor behind last entry"); }; - cursor.stop(); - } - }; - req.onsuccess = wrap(ev => { - req.onsuccess = guardedCallback; - guardedCallback(); - }); - cursor.continue = _cursorContinue; - cursor.continuePrimaryKey = _cursorContinuePrimaryKey; - cursor.advance = _cursorAdvance; - guardedCallback(); - return iterationPromise; - }; - resolve(cursor); - }, reject); - }); + await this.events[name].onHighlight.trigger(this.selection[name]); + if (zoomToSelection) { + await this.zoomSelection(name); } - function query(hasGetAll) { - return (request) => { - return new Promise((resolve, reject) => { - resolve = wrap(resolve); - const { trans, values, limit, query } = request; - const nonInfinitLimit = limit === Infinity ? undefined : limit; - const { index, range } = query; - const store = trans.objectStore(tableName); - const source = index.isPrimaryKey ? store : store.index(index.name); - const idbKeyRange = makeIDBKeyRange(range); - if (limit === 0) - return resolve({ result: [] }); - if (hasGetAll) { - const req = values ? - source.getAll(idbKeyRange, nonInfinitLimit) : - source.getAllKeys(idbKeyRange, nonInfinitLimit); - req.onsuccess = event => resolve({ result: event.target.result }); - req.onerror = eventRejectHandler(reject); - } - else { - let count = 0; - const req = values || !('openKeyCursor' in source) ? - source.openCursor(idbKeyRange) : - source.openKeyCursor(idbKeyRange); - const result = []; - req.onsuccess = event => { - const cursor = req.result; - if (!cursor) - return resolve({ result }); - result.push(values ? cursor.value : cursor.primaryKey); - if (++count === limit) - return resolve({ result }); - cursor.continue(); - }; - req.onerror = eventRejectHandler(reject); - } - }); - }; + return { id: itemID, fragments: fragList }; + } + async highlightByID(name, ids, removePrevious = true, zoomToSelection = this.zoomToSelection) { + if (!this.enabled) + return; + if (removePrevious) { + await this.clear(name); } - return { - name: tableName, - schema: tableSchema, - mutate, - getMany({ trans, keys }) { - return new Promise((resolve, reject) => { - resolve = wrap(resolve); - const store = trans.objectStore(tableName); - const length = keys.length; - const result = new Array(length); - let keyCount = 0; - let callbackCount = 0; - let req; - const successHandler = event => { - const req = event.target; - if ((result[req._pos] = req.result) != null) - ; - if (++callbackCount === keyCount) - resolve(result); - }; - const errorHandler = eventRejectHandler(reject); - for (let i = 0; i < length; ++i) { - const key = keys[i]; - if (key != null) { - req = store.get(keys[i]); - req._pos = i; - req.onsuccess = successHandler; - req.onerror = errorHandler; - ++keyCount; - } - } - if (keyCount === 0) - resolve(result); - }); - }, - get({ trans, key }) { - return new Promise((resolve, reject) => { - resolve = wrap(resolve); - const store = trans.objectStore(tableName); - const req = store.get(key); - req.onsuccess = event => resolve(event.target.result); - req.onerror = eventRejectHandler(reject); - }); - }, - query: query(hasGetAll), - openCursor, - count({ query, trans }) { - const { index, range } = query; - return new Promise((resolve, reject) => { - const store = trans.objectStore(tableName); - const source = index.isPrimaryKey ? store : store.index(index.name); - const idbKeyRange = makeIDBKeyRange(range); - const req = idbKeyRange ? source.count(idbKeyRange) : source.count(); - req.onsuccess = wrap(ev => resolve(ev.target.result)); - req.onerror = eventRejectHandler(reject); - }); + const styles = this.selection[name]; + for (const fragID in ids) { + if (!styles[fragID]) { + styles[fragID] = new Set(); } - }; - } - const { schema, hasGetAll } = extractSchema(db, tmpTrans); - const tables = schema.tables.map(tableSchema => createDbCoreTable(tableSchema)); - const tableMap = {}; - tables.forEach(table => tableMap[table.name] = table); - return { - stack: "dbcore", - transaction: db.transaction.bind(db), - table(name) { - const result = tableMap[name]; - if (!result) - throw new Error(`Table '${name}' not found`); - return tableMap[name]; - }, - MIN_KEY: -Infinity, - MAX_KEY: getMaxKey(IdbKeyRange), - schema - }; -} - -function createMiddlewareStack(stackImpl, middlewares) { - return middlewares.reduce((down, { create }) => ({ ...down, ...create(down) }), stackImpl); -} -function createMiddlewareStacks(middlewares, idbdb, { IDBKeyRange, indexedDB }, tmpTrans) { - const dbcore = createMiddlewareStack(createDBCore(idbdb, IDBKeyRange, tmpTrans), middlewares.dbcore); - return { - dbcore - }; -} -function generateMiddlewareStacks({ _novip: db }, tmpTrans) { - const idbdb = tmpTrans.db; - const stacks = createMiddlewareStacks(db._middlewares, idbdb, db._deps, tmpTrans); - db.core = stacks.dbcore; - db.tables.forEach(table => { - const tableName = table.name; - if (db.core.schema.tables.some(tbl => tbl.name === tableName)) { - table.core = db.core.table(tableName); - if (db[tableName] instanceof db.Table) { - db[tableName].core = table.core; + const fragments = this.components.tools.get(FragmentManager); + const fragment = fragments.list[fragID]; + const idsNum = new Set(); + for (const id of ids[fragID]) { + styles[fragID].add(id); + idsNum.add(parseInt(id, 10)); } - } - }); -} - -function setApiOnPlace({ _novip: db }, objs, tableNames, dbschema) { - tableNames.forEach(tableName => { - const schema = dbschema[tableName]; - objs.forEach(obj => { - const propDesc = getPropertyDescriptor(obj, tableName); - if (!propDesc || ("value" in propDesc && propDesc.value === undefined)) { - if (obj === db.Transaction.prototype || obj instanceof db.Transaction) { - setProp(obj, tableName, { - get() { return this.table(tableName); }, - set(value) { - defineProperty(this, tableName, { value, writable: true, configurable: true, enumerable: true }); - } - }); - } - else { - obj[tableName] = new db.Table(tableName, schema); - } + for (const id of idsNum) { + this.addComposites(fragment.mesh, id, name); } - }); - }); -} -function removeTablesApi({ _novip: db }, objs) { - objs.forEach(obj => { - for (let key in obj) { - if (obj[key] instanceof db.Table) - delete obj[key]; + await this.regenerate(name, fragID); } - }); -} -function lowerVersionFirst(a, b) { - return a._cfg.version - b._cfg.version; -} -function runUpgraders(db, oldVersion, idbUpgradeTrans, reject) { - const globalSchema = db._dbSchema; - const trans = db._createTransaction('readwrite', db._storeNames, globalSchema); - trans.create(idbUpgradeTrans); - trans._completion.catch(reject); - const rejectTransaction = trans._reject.bind(trans); - const transless = PSD.transless || PSD; - newScope(() => { - PSD.trans = trans; - PSD.transless = transless; - if (oldVersion === 0) { - keys(globalSchema).forEach(tableName => { - createTable(idbUpgradeTrans, tableName, globalSchema[tableName].primKey, globalSchema[tableName].indexes); - }); - generateMiddlewareStacks(db, idbUpgradeTrans); - DexiePromise.follow(() => db.on.populate.fire(trans)).catch(rejectTransaction); + await this.events[name].onHighlight.trigger(this.selection[name]); + if (zoomToSelection) { + await this.zoomSelection(name); } - else - updateTablesAndIndexes(db, oldVersion, trans, idbUpgradeTrans).catch(rejectTransaction); - }); -} -function updateTablesAndIndexes({ _novip: db }, oldVersion, trans, idbUpgradeTrans) { - const queue = []; - const versions = db._versions; - let globalSchema = db._dbSchema = buildGlobalSchema(db, db.idbdb, idbUpgradeTrans); - let anyContentUpgraderHasRun = false; - const versToRun = versions.filter(v => v._cfg.version >= oldVersion); - versToRun.forEach(version => { - queue.push(() => { - const oldSchema = globalSchema; - const newSchema = version._cfg.dbschema; - adjustToExistingIndexNames(db, oldSchema, idbUpgradeTrans); - adjustToExistingIndexNames(db, newSchema, idbUpgradeTrans); - globalSchema = db._dbSchema = newSchema; - const diff = getSchemaDiff(oldSchema, newSchema); - diff.add.forEach(tuple => { - createTable(idbUpgradeTrans, tuple[0], tuple[1].primKey, tuple[1].indexes); - }); - diff.change.forEach(change => { - if (change.recreate) { - throw new exceptions.Upgrade("Not yet support for changing primary key"); - } - else { - const store = idbUpgradeTrans.objectStore(change.name); - change.add.forEach(idx => addIndex(store, idx)); - change.change.forEach(idx => { - store.deleteIndex(idx.name); - addIndex(store, idx); - }); - change.del.forEach(idxName => store.deleteIndex(idxName)); - } - }); - const contentUpgrade = version._cfg.contentUpgrade; - if (contentUpgrade && version._cfg.version > oldVersion) { - generateMiddlewareStacks(db, idbUpgradeTrans); - trans._memoizedTables = {}; - anyContentUpgraderHasRun = true; - let upgradeSchema = shallowClone(newSchema); - diff.del.forEach(table => { - upgradeSchema[table] = oldSchema[table]; - }); - removeTablesApi(db, [db.Transaction.prototype]); - setApiOnPlace(db, [db.Transaction.prototype], keys(upgradeSchema), upgradeSchema); - trans.schema = upgradeSchema; - const contentUpgradeIsAsync = isAsyncFunction(contentUpgrade); - if (contentUpgradeIsAsync) { - incrementExpectedAwaits(); - } - let returnValue; - const promiseFollowed = DexiePromise.follow(() => { - returnValue = contentUpgrade(trans); - if (returnValue) { - if (contentUpgradeIsAsync) { - var decrementor = decrementExpectedAwaits.bind(null, null); - returnValue.then(decrementor, decrementor); - } - } - }); - return (returnValue && typeof returnValue.then === 'function' ? - DexiePromise.resolve(returnValue) : promiseFollowed.then(() => returnValue)); - } - }); - queue.push(idbtrans => { - if (!anyContentUpgraderHasRun || !hasIEDeleteObjectStoreBug) { - const newSchema = version._cfg.dbschema; - deleteRemovedTables(newSchema, idbtrans); - } - removeTablesApi(db, [db.Transaction.prototype]); - setApiOnPlace(db, [db.Transaction.prototype], db._storeNames, db._dbSchema); - trans.schema = db._dbSchema; - }); - }); - function runQueue() { - return queue.length ? DexiePromise.resolve(queue.shift()(trans.idbtrans)).then(runQueue) : - DexiePromise.resolve(); } - return runQueue().then(() => { - createMissingTables(globalSchema, idbUpgradeTrans); - }); -} -function getSchemaDiff(oldSchema, newSchema) { - const diff = { - del: [], - add: [], - change: [] - }; - let table; - for (table in oldSchema) { - if (!newSchema[table]) - diff.del.push(table); + /** + * Clears any selection previously made by calling {@link highlight}. + */ + async clear(name) { + await this.clearFills(name); + if (!name || !this.excludeOutline.has(name)) { + await this.clearOutlines(); + } } - for (table in newSchema) { - const oldDef = oldSchema[table], newDef = newSchema[table]; - if (!oldDef) { - diff.add.push([table, newDef]); + async setup(config) { + if (config === null || config === void 0 ? void 0 : config.selectionMaterial) { + this.config.selectionMaterial.dispose(); } - else { - const change = { - name: table, - def: newDef, - recreate: false, - del: [], - add: [], - change: [] - }; - if (( - '' + (oldDef.primKey.keyPath || '')) !== ('' + (newDef.primKey.keyPath || '')) || - (oldDef.primKey.auto !== newDef.primKey.auto && !isIEOrEdge)) - { - change.recreate = true; - diff.change.push(change); - } - else { - const oldIndexes = oldDef.idxByName; - const newIndexes = newDef.idxByName; - let idxName; - for (idxName in oldIndexes) { - if (!newIndexes[idxName]) - change.del.push(idxName); - } - for (idxName in newIndexes) { - const oldIdx = oldIndexes[idxName], newIdx = newIndexes[idxName]; - if (!oldIdx) - change.add.push(newIdx); - else if (oldIdx.src !== newIdx.src) - change.change.push(newIdx); - } - if (change.del.length > 0 || change.add.length > 0 || change.change.length > 0) { - diff.change.push(change); - } - } + if (config === null || config === void 0 ? void 0 : config.hoverMaterial) { + this.config.hoverMaterial.dispose(); } + this.config = { ...this.config, ...config }; + this.outlineMaterial.color.set(0xf0ff7a); + this.excludeOutline.add(this.config.hoverName); + await this.add(this.config.selectName, [this.config.selectionMaterial]); + await this.add(this.config.hoverName, [this.config.hoverMaterial]); + this.setupEvents(true); + this.enabled = true; + this.onSetup.trigger(this); } - return diff; -} -function createTable(idbtrans, tableName, primKey, indexes) { - const store = idbtrans.db.createObjectStore(tableName, primKey.keyPath ? - { keyPath: primKey.keyPath, autoIncrement: primKey.auto } : - { autoIncrement: primKey.auto }); - indexes.forEach(idx => addIndex(store, idx)); - return store; -} -function createMissingTables(newSchema, idbtrans) { - keys(newSchema).forEach(tableName => { - if (!idbtrans.db.objectStoreNames.contains(tableName)) { - createTable(idbtrans, tableName, newSchema[tableName].primKey, newSchema[tableName].indexes); + async regenerate(name, fragID) { + if (this.fillEnabled) { + await this.updateFragmentFill(name, fragID); } - }); -} -function deleteRemovedTables(newSchema, idbtrans) { - [].slice.call(idbtrans.db.objectStoreNames).forEach(storeName => newSchema[storeName] == null && idbtrans.db.deleteObjectStore(storeName)); -} -function addIndex(store, idx) { - store.createIndex(idx.name, idx.keyPath, { unique: idx.unique, multiEntry: idx.multi }); -} -function buildGlobalSchema(db, idbdb, tmpTrans) { - const globalSchema = {}; - const dbStoreNames = slice(idbdb.objectStoreNames, 0); - dbStoreNames.forEach(storeName => { - const store = tmpTrans.objectStore(storeName); - let keyPath = store.keyPath; - const primKey = createIndexSpec(nameFromKeyPath(keyPath), keyPath || "", false, false, !!store.autoIncrement, keyPath && typeof keyPath !== "string", true); - const indexes = []; - for (let j = 0; j < store.indexNames.length; ++j) { - const idbindex = store.index(store.indexNames[j]); - keyPath = idbindex.keyPath; - var index = createIndexSpec(idbindex.name, keyPath, !!idbindex.unique, !!idbindex.multiEntry, false, keyPath && typeof keyPath !== "string", false); - indexes.push(index); + if (this._outlineEnabled) { + await this.updateFragmentOutline(name, fragID); } - globalSchema[storeName] = createTableSchema(storeName, primKey, indexes); - }); - return globalSchema; -} -function readGlobalSchema({ _novip: db }, idbdb, tmpTrans) { - db.verno = idbdb.version / 10; - const globalSchema = db._dbSchema = buildGlobalSchema(db, idbdb, tmpTrans); - db._storeNames = slice(idbdb.objectStoreNames, 0); - setApiOnPlace(db, [db._allTables], keys(globalSchema), globalSchema); -} -function verifyInstalledSchema(db, tmpTrans) { - const installedSchema = buildGlobalSchema(db, db.idbdb, tmpTrans); - const diff = getSchemaDiff(installedSchema, db._dbSchema); - return !(diff.add.length || diff.change.some(ch => ch.add.length || ch.change.length)); -} -function adjustToExistingIndexNames({ _novip: db }, schema, idbtrans) { - const storeNames = idbtrans.db.objectStoreNames; - for (let i = 0; i < storeNames.length; ++i) { - const storeName = storeNames[i]; - const store = idbtrans.objectStore(storeName); - db._hasGetAll = 'getAll' in store; - for (let j = 0; j < store.indexNames.length; ++j) { - const indexName = store.indexNames[j]; - const keyPath = store.index(indexName).keyPath; - const dexieName = typeof keyPath === 'string' ? keyPath : "[" + slice(keyPath).join('+') + "]"; - if (schema[storeName]) { - const indexSpec = schema[storeName].idxByName[dexieName]; - if (indexSpec) { - indexSpec.name = indexName; - delete schema[storeName].idxByName[dexieName]; - schema[storeName].idxByName[indexName] = indexSpec; + } + async zoomSelection(name) { + if (!this.fillEnabled && !this._outlineEnabled) { + return; + } + const bbox = this.components.tools.get(FragmentBoundingBox); + const fragments = this.components.tools.get(FragmentManager); + bbox.reset(); + const selected = this.selection[name]; + if (!Object.keys(selected).length) { + return; + } + for (const fragID in selected) { + const fragment = fragments.list[fragID]; + if (this.fillEnabled) { + const highlight = fragment.fragments[name]; + if (highlight) { + bbox.addMesh(highlight.mesh); } } + if (this._outlineEnabled && this._outlinedMeshes[fragID]) { + bbox.addMesh(this._outlinedMeshes[fragID]); + } } + const sphere = bbox.getSphere(); + sphere.radius *= this.zoomFactor; + const camera = this.components.camera; + await camera.controls.fitToSphere(sphere, true); } - if (typeof navigator !== 'undefined' && /Safari/.test(navigator.userAgent) && - !/(Chrome\/|Edge\/)/.test(navigator.userAgent) && - _global.WorkerGlobalScope && _global instanceof _global.WorkerGlobalScope && - [].concat(navigator.userAgent.match(/Safari\/(\d*)/))[1] < 604) { - db._hasGetAll = false; - } -} -function parseIndexSyntax(primKeyAndIndexes) { - return primKeyAndIndexes.split(',').map((index, indexNum) => { - index = index.trim(); - const name = index.replace(/([&*]|\+\+)/g, ""); - const keyPath = /^\[/.test(name) ? name.match(/^\[(.*)\]$/)[1].split('+') : name; - return createIndexSpec(name, keyPath || null, /\&/.test(index), /\*/.test(index), /\+\+/.test(index), isArray(keyPath), indexNum === 0); - }); -} - -class Version { - _parseStoresSpec(stores, outSchema) { - keys(stores).forEach(tableName => { - if (stores[tableName] !== null) { - var indexes = parseIndexSyntax(stores[tableName]); - var primKey = indexes.shift(); - if (primKey.multi) - throw new exceptions.Schema("Primary key cannot be multi-valued"); - indexes.forEach(idx => { - if (idx.auto) - throw new exceptions.Schema("Only primary key can be marked as autoIncrement (++)"); - if (!idx.keyPath) - throw new exceptions.Schema("Index must have a name and cannot be an empty string"); - }); - outSchema[tableName] = createTableSchema(tableName, primKey, indexes); + addComposites(mesh, itemID, name) { + const composites = mesh.fragment.composites[itemID]; + if (composites) { + for (let i = 1; i < composites; i++) { + const compositeID = toCompositeID(itemID, i); + this.selection[name][mesh.uuid].add(compositeID); } - }); - } - stores(stores) { - const db = this.db; - this._cfg.storesSource = this._cfg.storesSource ? - extend(this._cfg.storesSource, stores) : - stores; - const versions = db._versions; - const storesSpec = {}; - let dbschema = {}; - versions.forEach(version => { - extend(storesSpec, version._cfg.storesSource); - dbschema = (version._cfg.dbschema = {}); - version._parseStoresSpec(storesSpec, dbschema); - }); - db._dbSchema = dbschema; - removeTablesApi(db, [db._allTables, db, db.Transaction.prototype]); - setApiOnPlace(db, [db._allTables, db, db.Transaction.prototype, this._cfg.tables], keys(dbschema), dbschema); - db._storeNames = keys(dbschema); - return this; + } } - upgrade(upgradeFunction) { - this._cfg.contentUpgrade = promisableChain(this._cfg.contentUpgrade || nop, upgradeFunction); - return this; + async clearStyle(name) { + const fragments = this.components.tools.get(FragmentManager); + for (const fragID in this.selection[name]) { + const fragment = fragments.list[fragID]; + if (!fragment) + continue; + const selection = fragment.fragments[name]; + if (selection) { + selection.mesh.removeFromParent(); + } + } + await this.events[name].onClear.trigger(null); + this.selection[name] = {}; } -} - -function createVersionConstructor(db) { - return makeClassConstructor(Version.prototype, function Version(versionNumber) { - this.db = db; - this._cfg = { - version: versionNumber, - storesSource: null, - dbschema: {}, - tables: {}, - contentUpgrade: null - }; - }); -} - -function getDbNamesTable(indexedDB, IDBKeyRange) { - let dbNamesDB = indexedDB["_dbNamesDB"]; - if (!dbNamesDB) { - dbNamesDB = indexedDB["_dbNamesDB"] = new Dexie$1(DBNAMES_DB, { - addons: [], - indexedDB, - IDBKeyRange, - }); - dbNamesDB.version(1).stores({ dbnames: "name" }); + async updateFragmentFill(name, fragmentID) { + const fragments = this.components.tools.get(FragmentManager); + const ids = this.selection[name][fragmentID]; + const fragment = fragments.list[fragmentID]; + if (!fragment) + return; + const selection = fragment.fragments[name]; + if (!selection) + return; + const fragmentParent = fragment.mesh.parent; + if (!fragmentParent) + return; + fragmentParent.add(selection.mesh); + const isBlockFragment = selection.blocks.count > 1; + if (isBlockFragment) { + fragment.getInstance(0, this._tempMatrix); + selection.setInstance(0, { + ids: Array.from(fragment.ids), + transform: this._tempMatrix, + }); + selection.blocks.setVisibility(true, ids, true); + } + else { + let i = 0; + for (const id of ids) { + selection.mesh.count = i + 1; + const { instanceID } = fragment.getInstanceAndBlockID(id); + fragment.getInstance(instanceID, this._tempMatrix); + selection.setInstance(i, { ids: [id], transform: this._tempMatrix }); + i++; + } + } } - return dbNamesDB.table("dbnames"); -} -function hasDatabasesNative(indexedDB) { - return indexedDB && typeof indexedDB.databases === "function"; -} -function getDatabaseNames({ indexedDB, IDBKeyRange, }) { - return hasDatabasesNative(indexedDB) - ? Promise.resolve(indexedDB.databases()).then((infos) => infos - .map((info) => info.name) - .filter((name) => name !== DBNAMES_DB)) - : getDbNamesTable(indexedDB, IDBKeyRange).toCollection().primaryKeys(); -} -function _onDatabaseCreated({ indexedDB, IDBKeyRange }, name) { - !hasDatabasesNative(indexedDB) && - name !== DBNAMES_DB && - getDbNamesTable(indexedDB, IDBKeyRange).put({ name }).catch(nop); -} -function _onDatabaseDeleted({ indexedDB, IDBKeyRange }, name) { - !hasDatabasesNative(indexedDB) && - name !== DBNAMES_DB && - getDbNamesTable(indexedDB, IDBKeyRange).delete(name).catch(nop); -} - -function vip(fn) { - return newScope(function () { - PSD.letThrough = true; - return fn(); - }); -} - -function idbReady() { - var isSafari = !navigator.userAgentData && - /Safari\//.test(navigator.userAgent) && - !/Chrom(e|ium)\//.test(navigator.userAgent); - if (!isSafari || !indexedDB.databases) - return Promise.resolve(); - var intervalId; - return new Promise(function (resolve) { - var tryIdb = function () { return indexedDB.databases().finally(resolve); }; - intervalId = setInterval(tryIdb, 100); - tryIdb(); - }).finally(function () { return clearInterval(intervalId); }); -} - -function dexieOpen(db) { - const state = db._state; - const { indexedDB } = db._deps; - if (state.isBeingOpened || db.idbdb) - return state.dbReadyPromise.then(() => state.dbOpenError ? - rejection(state.dbOpenError) : - db); - debug && (state.openCanceller._stackHolder = getErrorWithStack()); - state.isBeingOpened = true; - state.dbOpenError = null; - state.openComplete = false; - const openCanceller = state.openCanceller; - function throwIfCancelled() { - if (state.openCanceller !== openCanceller) - throw new exceptions.DatabaseClosed('db.open() was cancelled'); + checkSelection(name) { + if (!this.selection[name]) { + throw new Error(`Selection ${name} does not exist.`); + } } - let resolveDbReady = state.dbReadyResolve, - upgradeTransaction = null, wasCreated = false; - return DexiePromise.race([openCanceller, (typeof navigator === 'undefined' ? DexiePromise.resolve() : idbReady()).then(() => new DexiePromise((resolve, reject) => { - throwIfCancelled(); - if (!indexedDB) - throw new exceptions.MissingAPI(); - const dbName = db.name; - const req = state.autoSchema ? - indexedDB.open(dbName) : - indexedDB.open(dbName, Math.round(db.verno * 10)); - if (!req) - throw new exceptions.MissingAPI(); - req.onerror = eventRejectHandler(reject); - req.onblocked = wrap(db._fireOnBlocked); - req.onupgradeneeded = wrap(e => { - upgradeTransaction = req.transaction; - if (state.autoSchema && !db._options.allowEmptyDB) { - req.onerror = preventDefault; - upgradeTransaction.abort(); - req.result.close(); - const delreq = indexedDB.deleteDatabase(dbName); - delreq.onsuccess = delreq.onerror = wrap(() => { - reject(new exceptions.NoSuchDatabase(`Database ${dbName} doesnt exist`)); + addHighlightToFragment(fragment) { + for (const name in this.highlightMats) { + if (!fragment.fragments[name]) { + const material = this.highlightMats[name]; + const subFragment = fragment.addFragment(name, material); + subFragment.group = fragment.group; + if (fragment.blocks.count > 1) { + subFragment.setInstance(0, { + ids: Array.from(fragment.ids), + transform: this._tempMatrix, }); + subFragment.blocks.setVisibility(false); } - else { - upgradeTransaction.onerror = eventRejectHandler(reject); - var oldVer = e.oldVersion > Math.pow(2, 62) ? 0 : e.oldVersion; - wasCreated = oldVer < 1; - db._novip.idbdb = req.result; - runUpgraders(db, oldVer / 10, upgradeTransaction, reject); - } - }, reject); - req.onsuccess = wrap(() => { - upgradeTransaction = null; - const idbdb = db._novip.idbdb = req.result; - const objectStoreNames = slice(idbdb.objectStoreNames); - if (objectStoreNames.length > 0) - try { - const tmpTrans = idbdb.transaction(safariMultiStoreFix(objectStoreNames), 'readonly'); - if (state.autoSchema) - readGlobalSchema(db, idbdb, tmpTrans); - else { - adjustToExistingIndexNames(db, db._dbSchema, tmpTrans); - if (!verifyInstalledSchema(db, tmpTrans)) { - console.warn(`Dexie SchemaDiff: Schema was extended without increasing the number passed to db.version(). Some queries may fail.`); - } - } - generateMiddlewareStacks(db, tmpTrans); - } - catch (e) { - } - connections.push(db); - idbdb.onversionchange = wrap(ev => { - state.vcFired = true; - db.on("versionchange").fire(ev); - }); - idbdb.onclose = wrap(ev => { - db.on("close").fire(ev); - }); - if (wasCreated) - _onDatabaseCreated(db._deps, dbName); - resolve(); - }, reject); - }))]).then(() => { - throwIfCancelled(); - state.onReadyBeingFired = []; - return DexiePromise.resolve(vip(() => db.on.ready.fire(db.vip))).then(function fireRemainders() { - if (state.onReadyBeingFired.length > 0) { - let remainders = state.onReadyBeingFired.reduce(promisableChain, nop); - state.onReadyBeingFired = []; - return DexiePromise.resolve(vip(() => remainders(db.vip))).then(fireRemainders); + subFragment.mesh.renderOrder = 2; + subFragment.mesh.frustumCulled = false; } - }); - }).finally(() => { - state.onReadyBeingFired = null; - state.isBeingOpened = false; - }).then(() => { - return db; - }).catch(err => { - state.dbOpenError = err; - try { - upgradeTransaction && upgradeTransaction.abort(); } - catch (_a) { } - if (openCanceller === state.openCanceller) { - db._close(); + } + async clearFills(name) { + const names = name ? [name] : Object.keys(this.selection); + for (const name of names) { + await this.clearStyle(name); } - return rejection(err); - }).finally(() => { - state.openComplete = true; - resolveDbReady(); - }); -} - -function awaitIterator(iterator) { - var callNext = result => iterator.next(result), doThrow = error => iterator.throw(error), onSuccess = step(callNext), onError = step(doThrow); - function step(getNext) { - return (val) => { - var next = getNext(val), value = next.value; - return next.done ? value : - (!value || typeof value.then !== 'function' ? - isArray(value) ? Promise.all(value).then(onSuccess, onError) : onSuccess(value) : - value.then(onSuccess, onError)); - }; } - return step(callNext)(); -} - -function extractTransactionArgs(mode, _tableArgs_, scopeFunc) { - var i = arguments.length; - if (i < 2) - throw new exceptions.InvalidArgument("Too few arguments"); - var args = new Array(i - 1); - while (--i) - args[i - 1] = arguments[i]; - scopeFunc = args.pop(); - var tables = flatten(args); - return [mode, tables, scopeFunc]; -} -function enterTransactionScope(db, mode, storeNames, parentTransaction, scopeFunc) { - return DexiePromise.resolve().then(() => { - const transless = PSD.transless || PSD; - const trans = db._createTransaction(mode, storeNames, db._dbSchema, parentTransaction); - const zoneProps = { - trans: trans, - transless: transless - }; - if (parentTransaction) { - trans.idbtrans = parentTransaction.idbtrans; + async clearOutlines() { + const fragments = this.components.tools.get(FragmentManager); + const effects = this._postproduction.customEffects; + const fragmentsOutline = effects.outlinedMeshes.fragments; + if (fragmentsOutline) { + fragmentsOutline.meshes.clear(); } - else { - try { - trans.create(); - db._state.PR1398_maxLoop = 3; + for (const fragID in this._outlinedMeshes) { + const fragment = fragments.list[fragID]; + const isBlockFragment = fragment.blocks.count > 1; + const mesh = this._outlinedMeshes[fragID]; + if (isBlockFragment) { + mesh.geometry.setIndex([]); } - catch (ex) { - if (ex.name === errnames.InvalidState && db.isOpen() && --db._state.PR1398_maxLoop > 0) { - console.warn('Dexie: Need to reopen db'); - db._close(); - return db.open().then(() => enterTransactionScope(db, mode, storeNames, null, scopeFunc)); - } - return rejection(ex); + else { + mesh.count = 0; } } - const scopeFuncIsAsync = isAsyncFunction(scopeFunc); - if (scopeFuncIsAsync) { - incrementExpectedAwaits(); + } + async updateFragmentOutline(name, fragmentID) { + const fragments = this.components.tools.get(FragmentManager); + if (!this.selection[name][fragmentID]) { + return; } - let returnValue; - const promiseFollowed = DexiePromise.follow(() => { - returnValue = scopeFunc.call(trans, trans); - if (returnValue) { - if (scopeFuncIsAsync) { - var decrementor = decrementExpectedAwaits.bind(null, null); - returnValue.then(decrementor, decrementor); - } - else if (typeof returnValue.next === 'function' && typeof returnValue.throw === 'function') { - returnValue = awaitIterator(returnValue); + if (this.excludeOutline.has(name)) { + return; + } + const ids = this.selection[name][fragmentID]; + const fragment = fragments.list[fragmentID]; + if (!fragment) + return; + const geometry = fragment.mesh.geometry; + const customEffects = this._postproduction.customEffects; + if (!customEffects.outlinedMeshes.fragments) { + customEffects.outlinedMeshes.fragments = { + meshes: new Set(), + material: this.outlineMaterial, + }; + } + const outlineEffect = customEffects.outlinedMeshes.fragments; + // Create a copy of the original fragment mesh for outline + if (!this._outlinedMeshes[fragmentID]) { + const newGeometry = new THREE$1.BufferGeometry(); + newGeometry.attributes = geometry.attributes; + newGeometry.index = geometry.index; + const newMesh = new THREE$1.InstancedMesh(newGeometry, this._invisibleMaterial, fragment.capacity); + newMesh.frustumCulled = false; + newMesh.renderOrder = 999; + fragment.mesh.updateMatrixWorld(true); + newMesh.applyMatrix4(fragment.mesh.matrixWorld); + this._outlinedMeshes[fragmentID] = newMesh; + const scene = this.components.scene.get(); + scene.add(newMesh); + } + const outlineMesh = this._outlinedMeshes[fragmentID]; + outlineEffect.meshes.add(outlineMesh); + const isBlockFragment = fragment.blocks.count > 1; + if (isBlockFragment) { + const indices = fragment.mesh.geometry.index.array; + const newIndex = []; + const idsSet = new Set(ids); + for (let i = 0; i < indices.length - 2; i += 3) { + const index = indices[i]; + const blockID = fragment.mesh.geometry.attributes.blockID.array; + const block = blockID[index]; + const itemID = fragment.mesh.fragment.getItemID(0, block); + if (idsSet.has(itemID)) { + newIndex.push(indices[i], indices[i + 1], indices[i + 2]); } } - }, zoneProps); - return (returnValue && typeof returnValue.then === 'function' ? - DexiePromise.resolve(returnValue).then(x => trans.active ? - x - : rejection(new exceptions.PrematureCommit("Transaction committed too early. See http://bit.ly/2kdckMn"))) - : promiseFollowed.then(() => returnValue)).then(x => { - if (parentTransaction) - trans._resolve(); - return trans._completion.then(() => x); - }).catch(e => { - trans._reject(e); - return rejection(e); - }); - }); + outlineMesh.geometry.setIndex(newIndex); + } + else { + let counter = 0; + for (const id of ids) { + const { instanceID } = fragment.getInstanceAndBlockID(id); + fragment.mesh.getMatrixAt(instanceID, this._tempMatrix); + outlineMesh.setMatrixAt(counter++, this._tempMatrix); + } + outlineMesh.count = counter; + outlineMesh.instanceMatrix.needsUpdate = true; + } + } + setupEvents(active) { + const container = this.components.renderer.get().domElement; + if (active === this._eventsActive) { + return; + } + this._eventsActive = active; + if (active) { + container.addEventListener("mousedown", this.onMouseDown); + container.addEventListener("mouseup", this.onMouseUp); + container.addEventListener("mousemove", this.onMouseMove); + } + else { + container.removeEventListener("mousedown", this.onMouseDown); + container.removeEventListener("mouseup", this.onMouseUp); + container.removeEventListener("mousemove", this.onMouseMove); + } + } } +FragmentHighlighter.uuid = "cb8a76f2-654a-4b50-80c6-66fd83cafd77"; +ToolComponent.libraryUUIDs.add(FragmentHighlighter.uuid); -function pad(a, value, count) { - const result = isArray(a) ? a.slice() : [a]; - for (let i = 0; i < count; ++i) - result.push(value); - return result; -} -function createVirtualIndexMiddleware(down) { - return { - ...down, - table(tableName) { - const table = down.table(tableName); - const { schema } = table; - const indexLookup = {}; - const allVirtualIndexes = []; - function addVirtualIndexes(keyPath, keyTail, lowLevelIndex) { - const keyPathAlias = getKeyPathAlias(keyPath); - const indexList = (indexLookup[keyPathAlias] = indexLookup[keyPathAlias] || []); - const keyLength = keyPath == null ? 0 : typeof keyPath === 'string' ? 1 : keyPath.length; - const isVirtual = keyTail > 0; - const virtualIndex = { - ...lowLevelIndex, - isVirtual, - keyTail, - keyLength, - extractKey: getKeyExtractor(keyPath), - unique: !isVirtual && lowLevelIndex.unique - }; - indexList.push(virtualIndex); - if (!virtualIndex.isPrimaryKey) { - allVirtualIndexes.push(virtualIndex); - } - if (keyLength > 1) { - const virtualKeyPath = keyLength === 2 ? - keyPath[0] : - keyPath.slice(0, keyLength - 1); - addVirtualIndexes(virtualKeyPath, keyTail + 1, lowLevelIndex); - } - indexList.sort((a, b) => a.keyTail - b.keyTail); - return virtualIndex; - } - const primaryKey = addVirtualIndexes(schema.primaryKey.keyPath, 0, schema.primaryKey); - indexLookup[":id"] = [primaryKey]; - for (const index of schema.indexes) { - addVirtualIndexes(index.keyPath, 0, index); - } - function findBestIndex(keyPath) { - const result = indexLookup[getKeyPathAlias(keyPath)]; - return result && result[0]; - } - function translateRange(range, keyTail) { - return { - type: range.type === 1 ? - 2 : - range.type, - lower: pad(range.lower, range.lowerOpen ? down.MAX_KEY : down.MIN_KEY, keyTail), - lowerOpen: true, - upper: pad(range.upper, range.upperOpen ? down.MIN_KEY : down.MAX_KEY, keyTail), - upperOpen: true - }; +/** + * A tool to handle big scenes efficiently by automatically hiding the objects + * that are not visible to the camera. + */ +class ScreenCuller extends Component { + constructor(components) { + super(components); + /** {@link Disposable.onDisposed} */ + this.onDisposed = new Event(); + /** Fires after hiding the objects that were not visible to the camera. */ + this.onViewUpdated = new Event(); + /** {@link Component.enabled} */ + this.enabled = true; + /** + * Needs to check whether there are objects that need to be hidden or shown. + * You can bind this to the camera movement, to a certain interval, etc. + */ + this.needsUpdate = false; + /** + * Render the internal scene used to determine the object visibility. Used + * for debugging purposes. + */ + this.renderDebugFrame = false; + this.renderTarget = null; + this.bufferSize = null; + this._meshColorMap = new Map(); + this._visibleMeshes = []; + this._colorMeshes = new Map(); + this._meshes = new Map(); + this._currentVisibleMeshes = new Set(); + this._recentlyHiddenMeshes = new Set(); + this._transparentMat = new THREE$1.MeshBasicMaterial({ + transparent: true, + opacity: 0, + }); + this._colors = { r: 0, g: 0, b: 0, i: 0 }; + // Alternative scene and meshes to make the visibility check + this._scene = new THREE$1.Scene(); + this._buffer = null; + this.config = { + updateInterval: 1000, + rtWidth: 512, + rtHeight: 512, + autoUpdate: true, + }; + this.onSetup = new Event(); + /** + * The function that the culler uses to reprocess the scene. Generally it's + * better to call needsUpdate, but you can also call this to force it. + * @param force if true, it will refresh the scene even if needsUpdate is + * not true. + */ + this.updateVisibility = async (force) => { + if (!(this.enabled && this._buffer)) + return; + if (!this.needsUpdate && !force) + return; + const camera = this.components.camera.get(); + camera.updateMatrix(); + this.renderer.setSize(this.config.rtWidth, this.config.rtHeight); + this.renderer.setRenderTarget(this.renderTarget); + this.renderer.render(this._scene, camera); + const context = this.renderer.getContext(); + await readPixelsAsync(context, 0, 0, this.config.rtWidth, this.config.rtHeight, context.RGBA, context.UNSIGNED_BYTE, this._buffer); + this.renderer.setRenderTarget(null); + if (this.renderDebugFrame) { + this.renderer.render(this._scene, camera); } - function translateRequest(req) { - const index = req.query.index; - return index.isVirtual ? { - ...req, - query: { - index, - range: translateRange(req.query.range, index.keyTail) + this.worker.postMessage({ + buffer: this._buffer, + }); + this.needsUpdate = false; + }; + this.handleWorkerMessage = async (event) => { + const colors = event.data.colors; + this._recentlyHiddenMeshes = new Set(this._currentVisibleMeshes); + this._currentVisibleMeshes.clear(); + this._visibleMeshes = []; + // Make found meshes visible + for (const code of colors.values()) { + const mesh = this._meshColorMap.get(code); + if (mesh) { + this._visibleMeshes.push(mesh); + mesh.visible = true; + this._currentVisibleMeshes.add(mesh.uuid); + this._recentlyHiddenMeshes.delete(mesh.uuid); + if (mesh instanceof FragmentMesh) { + const highlighter = this.components.tools.get(FragmentHighlighter); + const { cullHighlightMeshes, selectName } = highlighter.config; + if (!cullHighlightMeshes) { + continue; + } + const fragments = mesh.fragment.fragments; + for (const name in fragments) { + if (name === selectName) { + continue; + } + const fragment = fragments[name]; + fragment.mesh.visible = true; + } } - } : req; + } } - const result = { - ...table, - schema: { - ...schema, - primaryKey, - indexes: allVirtualIndexes, - getIndexByKeyPath: findBestIndex - }, - count(req) { - return table.count(translateRequest(req)); - }, - query(req) { - return table.query(translateRequest(req)); - }, - openCursor(req) { - const { keyTail, isVirtual, keyLength } = req.query.index; - if (!isVirtual) - return table.openCursor(req); - function createVirtualCursor(cursor) { - function _continue(key) { - key != null ? - cursor.continue(pad(key, req.reverse ? down.MAX_KEY : down.MIN_KEY, keyTail)) : - req.unique ? - cursor.continue(cursor.key.slice(0, keyLength) - .concat(req.reverse - ? down.MIN_KEY - : down.MAX_KEY, keyTail)) : - cursor.continue(); + // Hide meshes that were visible before but not anymore + for (const uuid of this._recentlyHiddenMeshes) { + const mesh = this._meshes.get(uuid); + if (mesh === undefined) + continue; + mesh.visible = false; + if (mesh instanceof FragmentMesh) { + const highlighter = this.components.tools.get(FragmentHighlighter); + const { cullHighlightMeshes, selectName } = highlighter.config; + if (!cullHighlightMeshes) { + continue; + } + const fragments = mesh.fragment.fragments; + for (const name in fragments) { + if (name === selectName) { + continue; } - const virtualCursor = Object.create(cursor, { - continue: { value: _continue }, - continuePrimaryKey: { - value(key, primaryKey) { - cursor.continuePrimaryKey(pad(key, down.MAX_KEY, keyTail), primaryKey); - } - }, - primaryKey: { - get() { - return cursor.primaryKey; - } - }, - key: { - get() { - const key = cursor.key; - return keyLength === 1 ? - key[0] : - key.slice(0, keyLength); - } - }, - value: { - get() { - return cursor.value; - } - } - }); - return virtualCursor; + const fragment = fragments[name]; + fragment.mesh.visible = false; } - return table.openCursor(translateRequest(req)) - .then(cursor => cursor && createVirtualCursor(cursor)); } - }; - return result; + } + await this.onViewUpdated.trigger(); + }; + components.tools.add(ScreenCuller.uuid, this); + this.renderer = new THREE$1.WebGLRenderer(); + const planes = this.components.renderer.clippingPlanes; + this.renderer.clippingPlanes = planes; + this.materialCache = new Map(); + const code = ` + addEventListener("message", (event) => { + const { buffer } = event.data; + const colors = new Set(); + for (let i = 0; i < buffer.length; i += 4) { + const r = buffer[i]; + const g = buffer[i + 1]; + const b = buffer[i + 2]; + const code = "" + r + "-" + g + "-" + b; + colors.add(code); } - }; -} -const virtualIndexMiddleware = { - stack: "dbcore", - name: "VirtualIndexMiddleware", - level: 1, - create: createVirtualIndexMiddleware -}; - -function getObjectDiff(a, b, rv, prfx) { - rv = rv || {}; - prfx = prfx || ''; - keys(a).forEach((prop) => { - if (!hasOwn(b, prop)) { - rv[prfx + prop] = undefined; + postMessage({ colors }); + }); + `; + const blob = new Blob([code], { type: "application/javascript" }); + this.worker = new Worker(URL.createObjectURL(blob)); + this.worker.addEventListener("message", this.handleWorkerMessage); + } + async setup(config) { + this.config = { ...this.config, ...config }; + const { autoUpdate, updateInterval, rtHeight, rtWidth } = this.config; + this.renderTarget = new THREE$1.WebGLRenderTarget(rtWidth, rtHeight); + this.bufferSize = rtWidth * rtHeight * 4; + this._buffer = new Uint8Array(this.bufferSize); + if (autoUpdate) + window.setInterval(this.updateVisibility, updateInterval); + this.onSetup.trigger(this); + } + /** + * {@link Component.get}. + * @returns the map of internal meshes used to determine visibility. + */ + get() { + return this._colorMeshes; + } + /** {@link Disposable.dispose} */ + async dispose() { + var _a; + this.enabled = false; + this._currentVisibleMeshes.clear(); + this._recentlyHiddenMeshes.clear(); + this._scene.children.length = 0; + this.onViewUpdated.reset(); + this.onSetup.reset(); + this.worker.terminate(); + this.renderer.dispose(); + (_a = this.renderTarget) === null || _a === void 0 ? void 0 : _a.dispose(); + this._buffer = null; + this._transparentMat.dispose(); + this._meshColorMap.clear(); + this._visibleMeshes = []; + for (const id in this.materialCache) { + const material = this.materialCache.get(id); + if (material) { + material.dispose(); + } } - else { - var ap = a[prop], bp = b[prop]; - if (typeof ap === 'object' && typeof bp === 'object' && ap && bp) { - const apTypeName = toStringTag(ap); - const bpTypeName = toStringTag(bp); - if (apTypeName !== bpTypeName) { - rv[prfx + prop] = b[prop]; - } - else if (apTypeName === 'Object') { - getObjectDiff(ap, bp, rv, prfx + prop + '.'); + const disposer = this.components.tools.get(Disposer); + for (const id in this._colorMeshes) { + const mesh = this._colorMeshes.get(id); + if (mesh) { + disposer.destroy(mesh); + } + } + this._colorMeshes.clear(); + this._meshes.clear(); + await this.onDisposed.trigger(ScreenCuller.uuid); + this.onDisposed.reset(); + } + /** + * Adds a new mesh to be processed and managed by the culler. + * @mesh the mesh or instanced mesh to add. + */ + add(mesh) { + if (!this.enabled) + return; + const isInstanced = mesh instanceof THREE$1.InstancedMesh; + const { geometry, material } = mesh; + const { r, g, b, code } = this.getNextColor(); + const colorMaterial = this.getMaterial(r, g, b); + let newMaterial; + if (Array.isArray(material)) { + let transparentOnly = true; + const matArray = []; + for (const mat of material) { + if (this.isTransparent(mat)) { + matArray.push(this._transparentMat); } - else if (ap !== bp) { - rv[prfx + prop] = b[prop]; + else { + transparentOnly = false; + matArray.push(colorMaterial); } } - else if (ap !== bp) - rv[prfx + prop] = b[prop]; + // If we find that all the materials are transparent then we must remove this from analysis + if (transparentOnly) { + colorMaterial.dispose(); + return; + } + newMaterial = matArray; } - }); - keys(b).forEach((prop) => { - if (!hasOwn(a, prop)) { - rv[prfx + prop] = b[prop]; + else if (this.isTransparent(material)) { + // This material is transparent, so we must remove it from analysis + colorMaterial.dispose(); + return; } - }); - return rv; -} - -function getEffectiveKeys(primaryKey, req) { - if (req.type === 'delete') - return req.keys; - return req.keys || req.values.map(primaryKey.extractKey); -} - -const hooksMiddleware = { - stack: "dbcore", - name: "HooksMiddleware", - level: 2, - create: (downCore) => ({ - ...downCore, - table(tableName) { - const downTable = downCore.table(tableName); - const { primaryKey } = downTable.schema; - const tableMiddleware = { - ...downTable, - mutate(req) { - const dxTrans = PSD.trans; - const { deleting, creating, updating } = dxTrans.table(tableName).hook; - switch (req.type) { - case 'add': - if (creating.fire === nop) - break; - return dxTrans._promise('readwrite', () => addPutOrDelete(req), true); - case 'put': - if (creating.fire === nop && updating.fire === nop) - break; - return dxTrans._promise('readwrite', () => addPutOrDelete(req), true); - case 'delete': - if (deleting.fire === nop) - break; - return dxTrans._promise('readwrite', () => addPutOrDelete(req), true); - case 'deleteRange': - if (deleting.fire === nop) - break; - return dxTrans._promise('readwrite', () => deleteRange(req), true); - } - return downTable.mutate(req); - function addPutOrDelete(req) { - const dxTrans = PSD.trans; - const keys = req.keys || getEffectiveKeys(primaryKey, req); - if (!keys) - throw new Error("Keys missing"); - req = req.type === 'add' || req.type === 'put' ? - { ...req, keys } : - { ...req }; - if (req.type !== 'delete') - req.values = [...req.values]; - if (req.keys) - req.keys = [...req.keys]; - return getExistingValues(downTable, req, keys).then(existingValues => { - const contexts = keys.map((key, i) => { - const existingValue = existingValues[i]; - const ctx = { onerror: null, onsuccess: null }; - if (req.type === 'delete') { - deleting.fire.call(ctx, key, existingValue, dxTrans); - } - else if (req.type === 'add' || existingValue === undefined) { - const generatedPrimaryKey = creating.fire.call(ctx, key, req.values[i], dxTrans); - if (key == null && generatedPrimaryKey != null) { - key = generatedPrimaryKey; - req.keys[i] = key; - if (!primaryKey.outbound) { - setByKeyPath(req.values[i], primaryKey.keyPath, key); - } - } - } - else { - const objectDiff = getObjectDiff(existingValue, req.values[i]); - const additionalChanges = updating.fire.call(ctx, objectDiff, key, existingValue, dxTrans); - if (additionalChanges) { - const requestedValue = req.values[i]; - Object.keys(additionalChanges).forEach(keyPath => { - if (hasOwn(requestedValue, keyPath)) { - requestedValue[keyPath] = additionalChanges[keyPath]; - } - else { - setByKeyPath(requestedValue, keyPath, additionalChanges[keyPath]); - } - }); - } - } - return ctx; - }); - return downTable.mutate(req).then(({ failures, results, numFailures, lastResult }) => { - for (let i = 0; i < keys.length; ++i) { - const primKey = results ? results[i] : keys[i]; - const ctx = contexts[i]; - if (primKey == null) { - ctx.onerror && ctx.onerror(failures[i]); - } - else { - ctx.onsuccess && ctx.onsuccess(req.type === 'put' && existingValues[i] ? - req.values[i] : - primKey - ); - } - } - return { failures, results, numFailures, lastResult }; - }).catch(error => { - contexts.forEach(ctx => ctx.onerror && ctx.onerror(error)); - return Promise.reject(error); - }); - }); - } - function deleteRange(req) { - return deleteNextChunk(req.trans, req.range, 10000); - } - function deleteNextChunk(trans, range, limit) { - return downTable.query({ trans, values: false, query: { index: primaryKey, range }, limit }) - .then(({ result }) => { - return addPutOrDelete({ type: 'delete', keys: result, trans }).then(res => { - if (res.numFailures > 0) - return Promise.reject(res.failures[0]); - if (result.length < limit) { - return { failures: [], numFailures: 0, lastResult: undefined }; - } - else { - return deleteNextChunk(trans, { ...range, lower: result[result.length - 1], lowerOpen: true }, limit); - } - }); - }); - } + else { + newMaterial = colorMaterial; + } + this._meshColorMap.set(code, mesh); + const count = isInstanced ? mesh.count : 1; + const colorMesh = new THREE$1.InstancedMesh(geometry, newMaterial, count); + if (isInstanced) { + colorMesh.instanceMatrix = mesh.instanceMatrix; + } + else { + colorMesh.setMatrixAt(0, new THREE$1.Matrix4()); + } + mesh.visible = false; + colorMesh.applyMatrix4(mesh.matrix); + colorMesh.updateMatrix(); + if (mesh instanceof FragmentMesh) { + const fragment = mesh.fragment; + const parent = fragment.group; + if (parent) { + const manager = this.components.tools.get(FragmentManager); + const coordinationModel = manager.groups.find((model) => model.uuid === manager.baseCoordinationModel); + if (coordinationModel) { + colorMesh.applyMatrix4(parent.coordinationMatrix.clone().invert()); + colorMesh.applyMatrix4(coordinationModel.coordinationMatrix); } - }; - return tableMiddleware; - }, - }) -}; -function getExistingValues(table, req, effectiveKeys) { - return req.type === "add" - ? Promise.resolve([]) - : table.getMany({ trans: req.trans, keys: effectiveKeys, cache: "immutable" }); -} - -function getFromTransactionCache(keys, cache, clone) { - try { - if (!cache) - return null; - if (cache.keys.length < keys.length) - return null; - const result = []; - for (let i = 0, j = 0; i < cache.keys.length && j < keys.length; ++i) { - if (cmp(cache.keys[i], keys[j]) !== 0) - continue; - result.push(clone ? deepClone(cache.values[i]) : cache.values[i]); - ++j; + } } - return result.length === keys.length ? result : null; + this._scene.add(colorMesh); + this._colorMeshes.set(mesh.uuid, colorMesh); + this._meshes.set(mesh.uuid, mesh); } - catch (_a) { - return null; + getMaterial(r, g, b) { + const colorEnabled = THREE$1.ColorManagement.enabled; + THREE$1.ColorManagement.enabled = false; + const code = `rgb(${r}, ${g}, ${b})`; + const color = new THREE$1.Color(code); + let material = this.materialCache.get(code); + const clippingPlanes = this.components.renderer.clippingPlanes; + if (!material) { + material = new THREE$1.MeshBasicMaterial({ + color, + clippingPlanes, + side: THREE$1.DoubleSide, + }); + this.materialCache.set(code, material); + } + THREE$1.ColorManagement.enabled = colorEnabled; + return material; } -} -const cacheExistingValuesMiddleware = { - stack: "dbcore", - level: -1, - create: (core) => { - return { - table: (tableName) => { - const table = core.table(tableName); - return { - ...table, - getMany: (req) => { - if (!req.cache) { - return table.getMany(req); - } - const cachedResult = getFromTransactionCache(req.keys, req.trans["_cache"], req.cache === "clone"); - if (cachedResult) { - return DexiePromise.resolve(cachedResult); - } - return table.getMany(req).then((res) => { - req.trans["_cache"] = { - keys: req.keys, - values: req.cache === "clone" ? deepClone(res) : res, - }; - return res; - }); - }, - mutate: (req) => { - if (req.type !== "add") - req.trans["_cache"] = null; - return table.mutate(req); - }, - }; - }, + isTransparent(material) { + return material.transparent && material.opacity < 1; + } + getNextColor() { + if (this._colors.i === 0) { + this._colors.b++; + if (this._colors.b === 256) { + this._colors.b = 0; + this._colors.i = 1; + } + } + if (this._colors.i === 1) { + this._colors.g++; + this._colors.i = 0; + if (this._colors.g === 256) { + this._colors.g = 0; + this._colors.i = 2; + } + } + if (this._colors.i === 2) { + this._colors.r++; + this._colors.i = 1; + if (this._colors.r === 256) { + this._colors.r = 0; + this._colors.i = 0; + } + } + return { + r: this._colors.r, + g: this._colors.g, + b: this._colors.b, + code: `${this._colors.r}-${this._colors.g}-${this._colors.b}`, }; - }, -}; + } +} +ScreenCuller.uuid = "69f2a50d-c266-44fc-b1bd-fa4d34be89e6"; +ToolComponent.libraryUUIDs.add(ScreenCuller.uuid); -function isEmptyRange(node) { - return !("from" in node); +/* + * Dexie.js - a minimalistic wrapper for IndexedDB + * =============================================== + * + * By David Fahlander, david.fahlander@gmail.com + * + * Version 3.2.4, Tue May 30 2023 + * + * https://dexie.org + * + * Apache License Version 2.0, January 2004, http://www.apache.org/licenses/ + */ + +const _global = typeof globalThis !== 'undefined' ? globalThis : + typeof self !== 'undefined' ? self : + typeof window !== 'undefined' ? window : + global; + +const keys = Object.keys; +const isArray = Array.isArray; +if (typeof Promise !== 'undefined' && !_global.Promise) { + _global.Promise = Promise; } -const RangeSet = function (fromOrTree, to) { - if (this) { - extend(this, arguments.length ? { d: 1, from: fromOrTree, to: arguments.length > 1 ? to : fromOrTree } : { d: 0 }); +function extend(obj, extension) { + if (typeof extension !== 'object') + return obj; + keys(extension).forEach(function (key) { + obj[key] = extension[key]; + }); + return obj; +} +const getProto = Object.getPrototypeOf; +const _hasOwn = {}.hasOwnProperty; +function hasOwn(obj, prop) { + return _hasOwn.call(obj, prop); +} +function props(proto, extension) { + if (typeof extension === 'function') + extension = extension(getProto(proto)); + (typeof Reflect === "undefined" ? keys : Reflect.ownKeys)(extension).forEach(key => { + setProp(proto, key, extension[key]); + }); +} +const defineProperty = Object.defineProperty; +function setProp(obj, prop, functionOrGetSet, options) { + defineProperty(obj, prop, extend(functionOrGetSet && hasOwn(functionOrGetSet, "get") && typeof functionOrGetSet.get === 'function' ? + { get: functionOrGetSet.get, set: functionOrGetSet.set, configurable: true } : + { value: functionOrGetSet, configurable: true, writable: true }, options)); +} +function derive(Child) { + return { + from: function (Parent) { + Child.prototype = Object.create(Parent.prototype); + setProp(Child.prototype, "constructor", Child); + return { + extend: props.bind(null, Child.prototype) + }; + } + }; +} +const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; +function getPropertyDescriptor(obj, prop) { + const pd = getOwnPropertyDescriptor(obj, prop); + let proto; + return pd || (proto = getProto(obj)) && getPropertyDescriptor(proto, prop); +} +const _slice = [].slice; +function slice(args, start, end) { + return _slice.call(args, start, end); +} +function override(origFunc, overridedFactory) { + return overridedFactory(origFunc); +} +function assert(b) { + if (!b) + throw new Error("Assertion Failed"); +} +function asap$1(fn) { + if (_global.setImmediate) + setImmediate(fn); + else + setTimeout(fn, 0); +} +function arrayToObject(array, extractor) { + return array.reduce((result, item, i) => { + var nameAndValue = extractor(item, i); + if (nameAndValue) + result[nameAndValue[0]] = nameAndValue[1]; + return result; + }, {}); +} +function tryCatch(fn, onerror, args) { + try { + fn.apply(null, args); } - else { - const rv = new RangeSet(); - if (fromOrTree && ("d" in fromOrTree)) { - extend(rv, fromOrTree); + catch (ex) { + onerror && onerror(ex); + } +} +function getByKeyPath(obj, keyPath) { + if (hasOwn(obj, keyPath)) + return obj[keyPath]; + if (!keyPath) + return obj; + if (typeof keyPath !== 'string') { + var rv = []; + for (var i = 0, l = keyPath.length; i < l; ++i) { + var val = getByKeyPath(obj, keyPath[i]); + rv.push(val); } return rv; } -}; -props(RangeSet.prototype, { - add(rangeSet) { - mergeRanges(this, rangeSet); - return this; - }, - addKey(key) { - addRange(this, key, key); - return this; - }, - addKeys(keys) { - keys.forEach(key => addRange(this, key, key)); - return this; - }, - [iteratorSymbol]() { - return getRangeSetIterator(this); + var period = keyPath.indexOf('.'); + if (period !== -1) { + var innerObj = obj[keyPath.substr(0, period)]; + return innerObj === undefined ? undefined : getByKeyPath(innerObj, keyPath.substr(period + 1)); } -}); -function addRange(target, from, to) { - const diff = cmp(from, to); - if (isNaN(diff)) + return undefined; +} +function setByKeyPath(obj, keyPath, value) { + if (!obj || keyPath === undefined) return; - if (diff > 0) - throw RangeError(); - if (isEmptyRange(target)) - return extend(target, { from, to, d: 1 }); - const left = target.l; - const right = target.r; - if (cmp(to, target.from) < 0) { - left - ? addRange(left, from, to) - : (target.l = { from, to, d: 1, l: null, r: null }); - return rebalance(target); + if ('isFrozen' in Object && Object.isFrozen(obj)) + return; + if (typeof keyPath !== 'string' && 'length' in keyPath) { + assert(typeof value !== 'string' && 'length' in value); + for (var i = 0, l = keyPath.length; i < l; ++i) { + setByKeyPath(obj, keyPath[i], value[i]); + } } - if (cmp(from, target.to) > 0) { - right - ? addRange(right, from, to) - : (target.r = { from, to, d: 1, l: null, r: null }); - return rebalance(target); + else { + var period = keyPath.indexOf('.'); + if (period !== -1) { + var currentKeyPath = keyPath.substr(0, period); + var remainingKeyPath = keyPath.substr(period + 1); + if (remainingKeyPath === "") + if (value === undefined) { + if (isArray(obj) && !isNaN(parseInt(currentKeyPath))) + obj.splice(currentKeyPath, 1); + else + delete obj[currentKeyPath]; + } + else + obj[currentKeyPath] = value; + else { + var innerObj = obj[currentKeyPath]; + if (!innerObj || !hasOwn(obj, currentKeyPath)) + innerObj = (obj[currentKeyPath] = {}); + setByKeyPath(innerObj, remainingKeyPath, value); + } + } + else { + if (value === undefined) { + if (isArray(obj) && !isNaN(parseInt(keyPath))) + obj.splice(keyPath, 1); + else + delete obj[keyPath]; + } + else + obj[keyPath] = value; + } } - if (cmp(from, target.from) < 0) { - target.from = from; - target.l = null; - target.d = right ? right.d + 1 : 1; +} +function delByKeyPath(obj, keyPath) { + if (typeof keyPath === 'string') + setByKeyPath(obj, keyPath, undefined); + else if ('length' in keyPath) + [].map.call(keyPath, function (kp) { + setByKeyPath(obj, kp, undefined); + }); +} +function shallowClone(obj) { + var rv = {}; + for (var m in obj) { + if (hasOwn(obj, m)) + rv[m] = obj[m]; } - if (cmp(to, target.to) > 0) { - target.to = to; - target.r = null; - target.d = target.l ? target.l.d + 1 : 1; + return rv; +} +const concat = [].concat; +function flatten(a) { + return concat.apply([], a); +} +const intrinsicTypeNames = "Boolean,String,Date,RegExp,Blob,File,FileList,FileSystemFileHandle,ArrayBuffer,DataView,Uint8ClampedArray,ImageBitmap,ImageData,Map,Set,CryptoKey" + .split(',').concat(flatten([8, 16, 32, 64].map(num => ["Int", "Uint", "Float"].map(t => t + num + "Array")))).filter(t => _global[t]); +const intrinsicTypes = intrinsicTypeNames.map(t => _global[t]); +arrayToObject(intrinsicTypeNames, x => [x, true]); +let circularRefs = null; +function deepClone(any) { + circularRefs = typeof WeakMap !== 'undefined' && new WeakMap(); + const rv = innerDeepClone(any); + circularRefs = null; + return rv; +} +function innerDeepClone(any) { + if (!any || typeof any !== 'object') + return any; + let rv = circularRefs && circularRefs.get(any); + if (rv) + return rv; + if (isArray(any)) { + rv = []; + circularRefs && circularRefs.set(any, rv); + for (var i = 0, l = any.length; i < l; ++i) { + rv.push(innerDeepClone(any[i])); + } } - const rightWasCutOff = !target.r; - if (left && !target.l) { - mergeRanges(target, left); + else if (intrinsicTypes.indexOf(any.constructor) >= 0) { + rv = any; } - if (right && rightWasCutOff) { - mergeRanges(target, right); + else { + const proto = getProto(any); + rv = proto === Object.prototype ? {} : Object.create(proto); + circularRefs && circularRefs.set(any, rv); + for (var prop in any) { + if (hasOwn(any, prop)) { + rv[prop] = innerDeepClone(any[prop]); + } + } } + return rv; } -function mergeRanges(target, newSet) { - function _addRangeSet(target, { from, to, l, r }) { - addRange(target, from, to); - if (l) - _addRangeSet(target, l); - if (r) - _addRangeSet(target, r); - } - if (!isEmptyRange(newSet)) - _addRangeSet(target, newSet); +const { toString } = {}; +function toStringTag(o) { + return toString.call(o).slice(8, -1); } -function rangesOverlap(rangeSet1, rangeSet2) { - const i1 = getRangeSetIterator(rangeSet2); - let nextResult1 = i1.next(); - if (nextResult1.done) - return false; - let a = nextResult1.value; - const i2 = getRangeSetIterator(rangeSet1); - let nextResult2 = i2.next(a.from); - let b = nextResult2.value; - while (!nextResult1.done && !nextResult2.done) { - if (cmp(b.from, a.to) <= 0 && cmp(b.to, a.from) >= 0) - return true; - cmp(a.from, b.from) < 0 - ? (a = (nextResult1 = i1.next(b.from)).value) - : (b = (nextResult2 = i2.next(a.from)).value); +const iteratorSymbol = typeof Symbol !== 'undefined' ? + Symbol.iterator : + '@@iterator'; +const getIteratorOf = typeof iteratorSymbol === "symbol" ? function (x) { + var i; + return x != null && (i = x[iteratorSymbol]) && i.apply(x); +} : function () { return null; }; +const NO_CHAR_ARRAY = {}; +function getArrayOf(arrayLike) { + var i, a, x, it; + if (arguments.length === 1) { + if (isArray(arrayLike)) + return arrayLike.slice(); + if (this === NO_CHAR_ARRAY && typeof arrayLike === 'string') + return [arrayLike]; + if ((it = getIteratorOf(arrayLike))) { + a = []; + while ((x = it.next()), !x.done) + a.push(x.value); + return a; + } + if (arrayLike == null) + return [arrayLike]; + i = arrayLike.length; + if (typeof i === 'number') { + a = new Array(i); + while (i--) + a[i] = arrayLike[i]; + return a; + } + return [arrayLike]; } - return false; + i = arguments.length; + a = new Array(i); + while (i--) + a[i] = arguments[i]; + return a; } -function getRangeSetIterator(node) { - let state = isEmptyRange(node) ? null : { s: 0, n: node }; - return { - next(key) { - const keyProvided = arguments.length > 0; - while (state) { - switch (state.s) { - case 0: - state.s = 1; - if (keyProvided) { - while (state.n.l && cmp(key, state.n.from) < 0) - state = { up: state, n: state.n.l, s: 1 }; - } - else { - while (state.n.l) - state = { up: state, n: state.n.l, s: 1 }; - } - case 1: - state.s = 2; - if (!keyProvided || cmp(key, state.n.to) <= 0) - return { value: state.n, done: false }; - case 2: - if (state.n.r) { - state.s = 3; - state = { up: state, n: state.n.r, s: 0 }; - continue; - } - case 3: - state = state.up; - } - } - return { done: true }; - }, - }; +const isAsyncFunction = typeof Symbol !== 'undefined' + ? (fn) => fn[Symbol.toStringTag] === 'AsyncFunction' + : () => false; + +var debug = typeof location !== 'undefined' && + /^(http|https):\/\/(localhost|127\.0\.0\.1)/.test(location.href); +function setDebug(value, filter) { + debug = value; + libraryFilter = filter; } -function rebalance(target) { - var _a, _b; - const diff = (((_a = target.r) === null || _a === void 0 ? void 0 : _a.d) || 0) - (((_b = target.l) === null || _b === void 0 ? void 0 : _b.d) || 0); - const r = diff > 1 ? "r" : diff < -1 ? "l" : ""; - if (r) { - const l = r === "r" ? "l" : "r"; - const rootClone = { ...target }; - const oldRootRight = target[r]; - target.from = oldRootRight.from; - target.to = oldRootRight.to; - target[r] = oldRootRight[r]; - rootClone[r] = oldRootRight[l]; - target[l] = rootClone; - rootClone.d = computeDepth(rootClone); - } - target.d = computeDepth(target); +var libraryFilter = () => true; +const NEEDS_THROW_FOR_STACK = !new Error("").stack; +function getErrorWithStack() { + if (NEEDS_THROW_FOR_STACK) + try { + getErrorWithStack.arguments; + throw new Error(); + } + catch (e) { + return e; + } + return new Error(); } -function computeDepth({ r, l }) { - return (r ? (l ? Math.max(r.d, l.d) : r.d) : l ? l.d : 0) + 1; +function prettyStack(exception, numIgnoredFrames) { + var stack = exception.stack; + if (!stack) + return ""; + numIgnoredFrames = (numIgnoredFrames || 0); + if (stack.indexOf(exception.name) === 0) + numIgnoredFrames += (exception.name + exception.message).split('\n').length; + return stack.split('\n') + .slice(numIgnoredFrames) + .filter(libraryFilter) + .map(frame => "\n" + frame) + .join(''); } -const observabilityMiddleware = { - stack: "dbcore", - level: 0, - create: (core) => { - const dbName = core.schema.name; - const FULL_RANGE = new RangeSet(core.MIN_KEY, core.MAX_KEY); - return { - ...core, - table: (tableName) => { - const table = core.table(tableName); - const { schema } = table; - const { primaryKey } = schema; - const { extractKey, outbound } = primaryKey; - const tableClone = { - ...table, - mutate: (req) => { - const trans = req.trans; - const mutatedParts = trans.mutatedParts || (trans.mutatedParts = {}); - const getRangeSet = (indexName) => { - const part = `idb://${dbName}/${tableName}/${indexName}`; - return (mutatedParts[part] || - (mutatedParts[part] = new RangeSet())); - }; - const pkRangeSet = getRangeSet(""); - const delsRangeSet = getRangeSet(":dels"); - const { type } = req; - let [keys, newObjs] = req.type === "deleteRange" - ? [req.range] - : req.type === "delete" - ? [req.keys] - : req.values.length < 50 - ? [[], req.values] - : []; - const oldCache = req.trans["_cache"]; - return table.mutate(req).then((res) => { - if (isArray(keys)) { - if (type !== "delete") - keys = res.results; - pkRangeSet.addKeys(keys); - const oldObjs = getFromTransactionCache(keys, oldCache); - if (!oldObjs && type !== "add") { - delsRangeSet.addKeys(keys); - } - if (oldObjs || newObjs) { - trackAffectedIndexes(getRangeSet, schema, oldObjs, newObjs); - } - } - else if (keys) { - const range = { from: keys.lower, to: keys.upper }; - delsRangeSet.add(range); - pkRangeSet.add(range); - } - else { - pkRangeSet.add(FULL_RANGE); - delsRangeSet.add(FULL_RANGE); - schema.indexes.forEach(idx => getRangeSet(idx.name).add(FULL_RANGE)); - } - return res; - }); - }, - }; - const getRange = ({ query: { index, range }, }) => { - var _a, _b; - return [ - index, - new RangeSet((_a = range.lower) !== null && _a !== void 0 ? _a : core.MIN_KEY, (_b = range.upper) !== null && _b !== void 0 ? _b : core.MAX_KEY), - ]; - }; - const readSubscribers = { - get: (req) => [primaryKey, new RangeSet(req.key)], - getMany: (req) => [primaryKey, new RangeSet().addKeys(req.keys)], - count: getRange, - query: getRange, - openCursor: getRange, - }; - keys(readSubscribers).forEach(method => { - tableClone[method] = function (req) { - const { subscr } = PSD; - if (subscr) { - const getRangeSet = (indexName) => { - const part = `idb://${dbName}/${tableName}/${indexName}`; - return (subscr[part] || - (subscr[part] = new RangeSet())); - }; - const pkRangeSet = getRangeSet(""); - const delsRangeSet = getRangeSet(":dels"); - const [queriedIndex, queriedRanges] = readSubscribers[method](req); - getRangeSet(queriedIndex.name || "").add(queriedRanges); - if (!queriedIndex.isPrimaryKey) { - if (method === "count") { - delsRangeSet.add(FULL_RANGE); - } - else { - const keysPromise = method === "query" && - outbound && - req.values && - table.query({ - ...req, - values: false, - }); - return table[method].apply(this, arguments).then((res) => { - if (method === "query") { - if (outbound && req.values) { - return keysPromise.then(({ result: resultingKeys }) => { - pkRangeSet.addKeys(resultingKeys); - return res; - }); - } - const pKeys = req.values - ? res.result.map(extractKey) - : res.result; - if (req.values) { - pkRangeSet.addKeys(pKeys); - } - else { - delsRangeSet.addKeys(pKeys); - } - } - else if (method === "openCursor") { - const cursor = res; - const wantValues = req.values; - return (cursor && - Object.create(cursor, { - key: { - get() { - delsRangeSet.addKey(cursor.primaryKey); - return cursor.key; - }, - }, - primaryKey: { - get() { - const pkey = cursor.primaryKey; - delsRangeSet.addKey(pkey); - return pkey; - }, - }, - value: { - get() { - wantValues && pkRangeSet.addKey(cursor.primaryKey); - return cursor.value; - }, - }, - })); - } - return res; - }); - } - } - } - return table[method].apply(this, arguments); - }; - }); - return tableClone; - }, - }; - }, +var dexieErrorNames = [ + 'Modify', + 'Bulk', + 'OpenFailed', + 'VersionChange', + 'Schema', + 'Upgrade', + 'InvalidTable', + 'MissingAPI', + 'NoSuchDatabase', + 'InvalidArgument', + 'SubTransaction', + 'Unsupported', + 'Internal', + 'DatabaseClosed', + 'PrematureCommit', + 'ForeignAwait' +]; +var idbDomErrorNames = [ + 'Unknown', + 'Constraint', + 'Data', + 'TransactionInactive', + 'ReadOnly', + 'Version', + 'NotFound', + 'InvalidState', + 'InvalidAccess', + 'Abort', + 'Timeout', + 'QuotaExceeded', + 'Syntax', + 'DataClone' +]; +var errorList = dexieErrorNames.concat(idbDomErrorNames); +var defaultTexts = { + VersionChanged: "Database version changed by other database connection", + DatabaseClosed: "Database has been closed", + Abort: "Transaction aborted", + TransactionInactive: "Transaction has already completed or failed", + MissingAPI: "IndexedDB API missing. Please visit https://tinyurl.com/y2uuvskb" }; -function trackAffectedIndexes(getRangeSet, schema, oldObjs, newObjs) { - function addAffectedIndex(ix) { - const rangeSet = getRangeSet(ix.name || ""); - function extractKey(obj) { - return obj != null ? ix.extractKey(obj) : null; +function DexieError(name, msg) { + this._e = getErrorWithStack(); + this.name = name; + this.message = msg; +} +derive(DexieError).from(Error).extend({ + stack: { + get: function () { + return this._stack || + (this._stack = this.name + ": " + this.message + prettyStack(this._e, 2)); } - const addKeyOrKeys = (key) => ix.multiEntry && isArray(key) - ? key.forEach(key => rangeSet.addKey(key)) - : rangeSet.addKey(key); - (oldObjs || newObjs).forEach((_, i) => { - const oldKey = oldObjs && extractKey(oldObjs[i]); - const newKey = newObjs && extractKey(newObjs[i]); - if (cmp(oldKey, newKey) !== 0) { - if (oldKey != null) - addKeyOrKeys(oldKey); - if (newKey != null) - addKeyOrKeys(newKey); - } - }); - } - schema.indexes.forEach(addAffectedIndex); + }, + toString: function () { return this.name + ": " + this.message; } +}); +function getMultiErrorMessage(msg, failures) { + return msg + ". Errors: " + Object.keys(failures) + .map(key => failures[key].toString()) + .filter((v, i, s) => s.indexOf(v) === i) + .join('\n'); } - -class Dexie$1 { - constructor(name, options) { - this._middlewares = {}; - this.verno = 0; - const deps = Dexie$1.dependencies; - this._options = options = { - addons: Dexie$1.addons, - autoOpen: true, - indexedDB: deps.indexedDB, - IDBKeyRange: deps.IDBKeyRange, - ...options - }; - this._deps = { - indexedDB: options.indexedDB, - IDBKeyRange: options.IDBKeyRange - }; - const { addons, } = options; - this._dbSchema = {}; - this._versions = []; - this._storeNames = []; - this._allTables = {}; - this.idbdb = null; - this._novip = this; - const state = { - dbOpenError: null, - isBeingOpened: false, - onReadyBeingFired: null, - openComplete: false, - dbReadyResolve: nop, - dbReadyPromise: null, - cancelOpen: nop, - openCanceller: null, - autoSchema: true, - PR1398_maxLoop: 3 - }; - state.dbReadyPromise = new DexiePromise(resolve => { - state.dbReadyResolve = resolve; - }); - state.openCanceller = new DexiePromise((_, reject) => { - state.cancelOpen = reject; - }); - this._state = state; - this.name = name; - this.on = Events(this, "populate", "blocked", "versionchange", "close", { ready: [promisableChain, nop] }); - this.on.ready.subscribe = override(this.on.ready.subscribe, subscribe => { - return (subscriber, bSticky) => { - Dexie$1.vip(() => { - const state = this._state; - if (state.openComplete) { - if (!state.dbOpenError) - DexiePromise.resolve().then(subscriber); - if (bSticky) - subscribe(subscriber); - } - else if (state.onReadyBeingFired) { - state.onReadyBeingFired.push(subscriber); - if (bSticky) - subscribe(subscriber); - } - else { - subscribe(subscriber); - const db = this; - if (!bSticky) - subscribe(function unsubscribe() { - db.on.ready.unsubscribe(subscriber); - db.on.ready.unsubscribe(unsubscribe); - }); - } - }); - }; - }); - this.Collection = createCollectionConstructor(this); - this.Table = createTableConstructor(this); - this.Transaction = createTransactionConstructor(this); - this.Version = createVersionConstructor(this); - this.WhereClause = createWhereClauseConstructor(this); - this.on("versionchange", ev => { - if (ev.newVersion > 0) - console.warn(`Another connection wants to upgrade database '${this.name}'. Closing db now to resume the upgrade.`); - else - console.warn(`Another connection wants to delete database '${this.name}'. Closing db now to resume the delete request.`); - this.close(); - }); - this.on("blocked", ev => { - if (!ev.newVersion || ev.newVersion < ev.oldVersion) - console.warn(`Dexie.delete('${this.name}') was blocked`); - else - console.warn(`Upgrade '${this.name}' blocked by other connection holding version ${ev.oldVersion / 10}`); - }); - this._maxKey = getMaxKey(options.IDBKeyRange); - this._createTransaction = (mode, storeNames, dbschema, parentTransaction) => new this.Transaction(mode, storeNames, dbschema, this._options.chromeTransactionDurability, parentTransaction); - this._fireOnBlocked = ev => { - this.on("blocked").fire(ev); - connections - .filter(c => c.name === this.name && c !== this && !c._state.vcFired) - .map(c => c.on("versionchange").fire(ev)); - }; - this.use(virtualIndexMiddleware); - this.use(hooksMiddleware); - this.use(observabilityMiddleware); - this.use(cacheExistingValuesMiddleware); - this.vip = Object.create(this, { _vip: { value: true } }); - addons.forEach(addon => addon(this)); - } - version(versionNumber) { - if (isNaN(versionNumber) || versionNumber < 0.1) - throw new exceptions.Type(`Given version is not a positive number`); - versionNumber = Math.round(versionNumber * 10) / 10; - if (this.idbdb || this._state.isBeingOpened) - throw new exceptions.Schema("Cannot add version when database is open"); - this.verno = Math.max(this.verno, versionNumber); - const versions = this._versions; - var versionInstance = versions.filter(v => v._cfg.version === versionNumber)[0]; - if (versionInstance) - return versionInstance; - versionInstance = new this.Version(versionNumber); - versions.push(versionInstance); - versions.sort(lowerVersionFirst); - versionInstance.stores({}); - this._state.autoSchema = false; - return versionInstance; - } - _whenReady(fn) { - return (this.idbdb && (this._state.openComplete || PSD.letThrough || this._vip)) ? fn() : new DexiePromise((resolve, reject) => { - if (this._state.openComplete) { - return reject(new exceptions.DatabaseClosed(this._state.dbOpenError)); - } - if (!this._state.isBeingOpened) { - if (!this._options.autoOpen) { - reject(new exceptions.DatabaseClosed()); - return; - } - this.open().catch(nop); - } - this._state.dbReadyPromise.then(resolve, reject); - }).then(fn); - } - use({ stack, create, level, name }) { - if (name) - this.unuse({ stack, name }); - const middlewares = this._middlewares[stack] || (this._middlewares[stack] = []); - middlewares.push({ stack, create, level: level == null ? 10 : level, name }); - middlewares.sort((a, b) => a.level - b.level); - return this; - } - unuse({ stack, name, create }) { - if (stack && this._middlewares[stack]) { - this._middlewares[stack] = this._middlewares[stack].filter(mw => create ? mw.create !== create : - name ? mw.name !== name : - false); +function ModifyError(msg, failures, successCount, failedKeys) { + this._e = getErrorWithStack(); + this.failures = failures; + this.failedKeys = failedKeys; + this.successCount = successCount; + this.message = getMultiErrorMessage(msg, failures); +} +derive(ModifyError).from(DexieError); +function BulkError(msg, failures) { + this._e = getErrorWithStack(); + this.name = "BulkError"; + this.failures = Object.keys(failures).map(pos => failures[pos]); + this.failuresByPos = failures; + this.message = getMultiErrorMessage(msg, failures); +} +derive(BulkError).from(DexieError); +var errnames = errorList.reduce((obj, name) => (obj[name] = name + "Error", obj), {}); +const BaseException = DexieError; +var exceptions = errorList.reduce((obj, name) => { + var fullName = name + "Error"; + function DexieError(msgOrInner, inner) { + this._e = getErrorWithStack(); + this.name = fullName; + if (!msgOrInner) { + this.message = defaultTexts[name] || fullName; + this.inner = null; } - return this; - } - open() { - return dexieOpen(this); - } - _close() { - const state = this._state; - const idx = connections.indexOf(this); - if (idx >= 0) - connections.splice(idx, 1); - if (this.idbdb) { - try { - this.idbdb.close(); - } - catch (e) { } - this._novip.idbdb = null; + else if (typeof msgOrInner === 'string') { + this.message = `${msgOrInner}${!inner ? '' : '\n ' + inner}`; + this.inner = inner || null; + } + else if (typeof msgOrInner === 'object') { + this.message = `${msgOrInner.name} ${msgOrInner.message}`; + this.inner = msgOrInner; } - state.dbReadyPromise = new DexiePromise(resolve => { - state.dbReadyResolve = resolve; - }); - state.openCanceller = new DexiePromise((_, reject) => { - state.cancelOpen = reject; - }); - } - close() { - this._close(); - const state = this._state; - this._options.autoOpen = false; - state.dbOpenError = new exceptions.DatabaseClosed(); - if (state.isBeingOpened) - state.cancelOpen(state.dbOpenError); - } - delete() { - const hasArguments = arguments.length > 0; - const state = this._state; - return new DexiePromise((resolve, reject) => { - const doDelete = () => { - this.close(); - var req = this._deps.indexedDB.deleteDatabase(this.name); - req.onsuccess = wrap(() => { - _onDatabaseDeleted(this._deps, this.name); - resolve(); - }); - req.onerror = eventRejectHandler(reject); - req.onblocked = this._fireOnBlocked; - }; - if (hasArguments) - throw new exceptions.InvalidArgument("Arguments not allowed in db.delete()"); - if (state.isBeingOpened) { - state.dbReadyPromise.then(doDelete); - } - else { - doDelete(); - } - }); - } - backendDB() { - return this.idbdb; - } - isOpen() { - return this.idbdb !== null; - } - hasBeenClosed() { - const dbOpenError = this._state.dbOpenError; - return dbOpenError && (dbOpenError.name === 'DatabaseClosed'); - } - hasFailed() { - return this._state.dbOpenError !== null; - } - dynamicallyOpened() { - return this._state.autoSchema; - } - get tables() { - return keys(this._allTables).map(name => this._allTables[name]); } - transaction() { - const args = extractTransactionArgs.apply(this, arguments); - return this._transaction.apply(this, args); - } - _transaction(mode, tables, scopeFunc) { - let parentTransaction = PSD.trans; - if (!parentTransaction || parentTransaction.db !== this || mode.indexOf('!') !== -1) - parentTransaction = null; - const onlyIfCompatible = mode.indexOf('?') !== -1; - mode = mode.replace('!', '').replace('?', ''); - let idbMode, storeNames; - try { - storeNames = tables.map(table => { - var storeName = table instanceof this.Table ? table.name : table; - if (typeof storeName !== 'string') - throw new TypeError("Invalid table argument to Dexie.transaction(). Only Table or String are allowed"); - return storeName; - }); - if (mode == "r" || mode === READONLY) - idbMode = READONLY; - else if (mode == "rw" || mode == READWRITE) - idbMode = READWRITE; - else - throw new exceptions.InvalidArgument("Invalid transaction mode: " + mode); - if (parentTransaction) { - if (parentTransaction.mode === READONLY && idbMode === READWRITE) { - if (onlyIfCompatible) { - parentTransaction = null; - } - else - throw new exceptions.SubTransaction("Cannot enter a sub-transaction with READWRITE mode when parent transaction is READONLY"); - } - if (parentTransaction) { - storeNames.forEach(storeName => { - if (parentTransaction && parentTransaction.storeNames.indexOf(storeName) === -1) { - if (onlyIfCompatible) { - parentTransaction = null; - } - else - throw new exceptions.SubTransaction("Table " + storeName + - " not included in parent transaction."); - } - }); - } - if (onlyIfCompatible && parentTransaction && !parentTransaction.active) { - parentTransaction = null; - } - } - } - catch (e) { - return parentTransaction ? - parentTransaction._promise(null, (_, reject) => { reject(e); }) : - rejection(e); - } - const enterTransaction = enterTransactionScope.bind(null, this, idbMode, storeNames, parentTransaction, scopeFunc); - return (parentTransaction ? - parentTransaction._promise(idbMode, enterTransaction, "lock") : - PSD.trans ? - usePSD(PSD.transless, () => this._whenReady(enterTransaction)) : - this._whenReady(enterTransaction)); - } - table(tableName) { - if (!hasOwn(this._allTables, tableName)) { - throw new exceptions.InvalidTable(`Table ${tableName} does not exist`); - } - return this._allTables[tableName]; + derive(DexieError).from(BaseException); + obj[name] = DexieError; + return obj; +}, {}); +exceptions.Syntax = SyntaxError; +exceptions.Type = TypeError; +exceptions.Range = RangeError; +var exceptionMap = idbDomErrorNames.reduce((obj, name) => { + obj[name + "Error"] = exceptions[name]; + return obj; +}, {}); +function mapError(domError, message) { + if (!domError || domError instanceof DexieError || domError instanceof TypeError || domError instanceof SyntaxError || !domError.name || !exceptionMap[domError.name]) + return domError; + var rv = new exceptionMap[domError.name](message || domError.message, domError); + if ("stack" in domError) { + setProp(rv, "stack", { get: function () { + return this.inner.stack; + } }); } + return rv; } +var fullNameExceptions = errorList.reduce((obj, name) => { + if (["Syntax", "Type", "Range"].indexOf(name) === -1) + obj[name + "Error"] = exceptions[name]; + return obj; +}, {}); +fullNameExceptions.ModifyError = ModifyError; +fullNameExceptions.DexieError = DexieError; +fullNameExceptions.BulkError = BulkError; -const symbolObservable = typeof Symbol !== "undefined" && "observable" in Symbol - ? Symbol.observable - : "@@observable"; -class Observable { - constructor(subscribe) { - this._subscribe = subscribe; - } - subscribe(x, error, complete) { - return this._subscribe(!x || typeof x === "function" ? { next: x, error, complete } : x); - } - [symbolObservable]() { - return this; - } +function nop() { } +function mirror(val) { return val; } +function pureFunctionChain(f1, f2) { + if (f1 == null || f1 === mirror) + return f2; + return function (val) { + return f2(f1(val)); + }; } - -function extendObservabilitySet(target, newSet) { - keys(newSet).forEach(part => { - const rangeSet = target[part] || (target[part] = new RangeSet()); - mergeRanges(rangeSet, newSet[part]); - }); - return target; +function callBoth(on1, on2) { + return function () { + on1.apply(this, arguments); + on2.apply(this, arguments); + }; } - -function liveQuery(querier) { - let hasValue = false; - let currentValue = undefined; - const observable = new Observable((observer) => { - const scopeFuncIsAsync = isAsyncFunction(querier); - function execute(subscr) { - if (scopeFuncIsAsync) { - incrementExpectedAwaits(); - } - const exec = () => newScope(querier, { subscr, trans: null }); - const rv = PSD.trans - ? - usePSD(PSD.transless, exec) - : exec(); - if (scopeFuncIsAsync) { - rv.then(decrementExpectedAwaits, decrementExpectedAwaits); - } - return rv; - } - let closed = false; - let accumMuts = {}; - let currentObs = {}; - const subscription = { - get closed() { - return closed; - }, - unsubscribe: () => { - closed = true; - globalEvents.storagemutated.unsubscribe(mutationListener); - }, - }; - observer.start && observer.start(subscription); - let querying = false, startedListening = false; - function shouldNotify() { - return keys(currentObs).some((key) => accumMuts[key] && rangesOverlap(accumMuts[key], currentObs[key])); - } - const mutationListener = (parts) => { - extendObservabilitySet(accumMuts, parts); - if (shouldNotify()) { - doQuery(); - } - }; - const doQuery = () => { - if (querying || closed) - return; - accumMuts = {}; - const subscr = {}; - const ret = execute(subscr); - if (!startedListening) { - globalEvents(DEXIE_STORAGE_MUTATED_EVENT_NAME, mutationListener); - startedListening = true; - } - querying = true; - Promise.resolve(ret).then((result) => { - hasValue = true; - currentValue = result; - querying = false; - if (closed) - return; - if (shouldNotify()) { - doQuery(); - } - else { - accumMuts = {}; - currentObs = subscr; - observer.next && observer.next(result); - } - }, (err) => { - querying = false; - hasValue = false; - observer.error && observer.error(err); - subscription.unsubscribe(); - }); - }; - doQuery(); - return subscription; - }); - observable.hasValue = () => hasValue; - observable.getValue = () => currentValue; - return observable; +function hookCreatingChain(f1, f2) { + if (f1 === nop) + return f2; + return function () { + var res = f1.apply(this, arguments); + if (res !== undefined) + arguments[0] = res; + var onsuccess = this.onsuccess, + onerror = this.onerror; + this.onsuccess = null; + this.onerror = null; + var res2 = f2.apply(this, arguments); + if (onsuccess) + this.onsuccess = this.onsuccess ? callBoth(onsuccess, this.onsuccess) : onsuccess; + if (onerror) + this.onerror = this.onerror ? callBoth(onerror, this.onerror) : onerror; + return res2 !== undefined ? res2 : res; + }; } - -let domDeps; -try { - domDeps = { - indexedDB: _global.indexedDB || _global.mozIndexedDB || _global.webkitIndexedDB || _global.msIndexedDB, - IDBKeyRange: _global.IDBKeyRange || _global.webkitIDBKeyRange +function hookDeletingChain(f1, f2) { + if (f1 === nop) + return f2; + return function () { + f1.apply(this, arguments); + var onsuccess = this.onsuccess, + onerror = this.onerror; + this.onsuccess = this.onerror = null; + f2.apply(this, arguments); + if (onsuccess) + this.onsuccess = this.onsuccess ? callBoth(onsuccess, this.onsuccess) : onsuccess; + if (onerror) + this.onerror = this.onerror ? callBoth(onerror, this.onerror) : onerror; }; } -catch (e) { - domDeps = { indexedDB: null, IDBKeyRange: null }; +function hookUpdatingChain(f1, f2) { + if (f1 === nop) + return f2; + return function (modifications) { + var res = f1.apply(this, arguments); + extend(modifications, res); + var onsuccess = this.onsuccess, + onerror = this.onerror; + this.onsuccess = null; + this.onerror = null; + var res2 = f2.apply(this, arguments); + if (onsuccess) + this.onsuccess = this.onsuccess ? callBoth(onsuccess, this.onsuccess) : onsuccess; + if (onerror) + this.onerror = this.onerror ? callBoth(onerror, this.onerror) : onerror; + return res === undefined ? + (res2 === undefined ? undefined : res2) : + (extend(res, res2)); + }; } - -const Dexie = Dexie$1; -props(Dexie, { - ...fullNameExceptions, - delete(databaseName) { - const db = new Dexie(databaseName, { addons: [] }); - return db.delete(); - }, - exists(name) { - return new Dexie(name, { addons: [] }).open().then(db => { - db.close(); - return true; - }).catch('NoSuchDatabaseError', () => false); - }, - getDatabaseNames(cb) { - try { - return getDatabaseNames(Dexie.dependencies).then(cb); - } - catch (_a) { - return rejection(new exceptions.MissingAPI()); - } - }, - defineClass() { - function Class(content) { - extend(this, content); +function reverseStoppableEventChain(f1, f2) { + if (f1 === nop) + return f2; + return function () { + if (f2.apply(this, arguments) === false) + return false; + return f1.apply(this, arguments); + }; +} +function promisableChain(f1, f2) { + if (f1 === nop) + return f2; + return function () { + var res = f1.apply(this, arguments); + if (res && typeof res.then === 'function') { + var thiz = this, i = arguments.length, args = new Array(i); + while (i--) + args[i] = arguments[i]; + return res.then(function () { + return f2.apply(thiz, args); + }); } - return Class; - }, - ignoreTransaction(scopeFunc) { - return PSD.trans ? - usePSD(PSD.transless, scopeFunc) : - scopeFunc(); - }, - vip, - async: function (generatorFn) { - return function () { + return f2.apply(this, arguments); + }; +} + +var INTERNAL = {}; +const LONG_STACKS_CLIP_LIMIT = 100, +MAX_LONG_STACKS = 20, ZONE_ECHO_LIMIT = 100, [resolvedNativePromise, nativePromiseProto, resolvedGlobalPromise] = typeof Promise === 'undefined' ? + [] : + (() => { + let globalP = Promise.resolve(); + if (typeof crypto === 'undefined' || !crypto.subtle) + return [globalP, getProto(globalP), globalP]; + const nativeP = crypto.subtle.digest("SHA-512", new Uint8Array([0])); + return [ + nativeP, + getProto(nativeP), + globalP + ]; + })(), nativePromiseThen = nativePromiseProto && nativePromiseProto.then; +const NativePromise = resolvedNativePromise && resolvedNativePromise.constructor; +const patchGlobalPromise = !!resolvedGlobalPromise; +var stack_being_generated = false; +var schedulePhysicalTick = resolvedGlobalPromise ? + () => { resolvedGlobalPromise.then(physicalTick); } + : + _global.setImmediate ? + setImmediate.bind(null, physicalTick) : + _global.MutationObserver ? + () => { + var hiddenDiv = document.createElement("div"); + (new MutationObserver(() => { + physicalTick(); + hiddenDiv = null; + })).observe(hiddenDiv, { attributes: true }); + hiddenDiv.setAttribute('i', '1'); + } : + () => { setTimeout(physicalTick, 0); }; +var asap = function (callback, args) { + microtickQueue.push([callback, args]); + if (needsNewPhysicalTick) { + schedulePhysicalTick(); + needsNewPhysicalTick = false; + } +}; +var isOutsideMicroTick = true, +needsNewPhysicalTick = true, +unhandledErrors = [], +rejectingErrors = [], +currentFulfiller = null, rejectionMapper = mirror; +var globalPSD = { + id: 'global', + global: true, + ref: 0, + unhandleds: [], + onunhandled: globalError, + pgp: false, + env: {}, + finalize: function () { + this.unhandleds.forEach(uh => { try { - var rv = awaitIterator(generatorFn.apply(this, arguments)); - if (!rv || typeof rv.then !== 'function') - return DexiePromise.resolve(rv); - return rv; - } - catch (e) { - return rejection(e); + globalError(uh[0], uh[1]); } - }; - }, - spawn: function (generatorFn, args, thiz) { - try { - var rv = awaitIterator(generatorFn.apply(thiz, args || [])); - if (!rv || typeof rv.then !== 'function') - return DexiePromise.resolve(rv); + catch (e) { } + }); + } +}; +var PSD = globalPSD; +var microtickQueue = []; +var numScheduledCalls = 0; +var tickFinalizers = []; +function DexiePromise(fn) { + if (typeof this !== 'object') + throw new TypeError('Promises must be constructed via new'); + this._listeners = []; + this.onuncatched = nop; + this._lib = false; + var psd = (this._PSD = PSD); + if (debug) { + this._stackHolder = getErrorWithStack(); + this._prev = null; + this._numPrev = 0; + } + if (typeof fn !== 'function') { + if (fn !== INTERNAL) + throw new TypeError('Not a function'); + this._state = arguments[1]; + this._value = arguments[2]; + if (this._state === false) + handleRejection(this, this._value); + return; + } + this._state = null; + this._value = null; + ++psd.ref; + executePromiseTask(this, fn); +} +const thenProp = { + get: function () { + var psd = PSD, microTaskId = totalEchoes; + function then(onFulfilled, onRejected) { + var possibleAwait = !psd.global && (psd !== PSD || microTaskId !== totalEchoes); + const cleanup = possibleAwait && !decrementExpectedAwaits(); + var rv = new DexiePromise((resolve, reject) => { + propagateToListener(this, new Listener(nativeAwaitCompatibleWrap(onFulfilled, psd, possibleAwait, cleanup), nativeAwaitCompatibleWrap(onRejected, psd, possibleAwait, cleanup), resolve, reject, psd)); + }); + debug && linkToPreviousPromise(rv, this); return rv; } - catch (e) { - return rejection(e); - } + then.prototype = INTERNAL; + return then; }, - currentTransaction: { - get: () => PSD.trans || null + set: function (value) { + setProp(this, 'then', value && value.prototype === INTERNAL ? + thenProp : + { + get: function () { + return value; + }, + set: thenProp.set + }); + } +}; +props(DexiePromise.prototype, { + then: thenProp, + _then: function (onFulfilled, onRejected) { + propagateToListener(this, new Listener(null, null, onFulfilled, onRejected, PSD)); }, - waitFor: function (promiseOrFunction, optionalTimeout) { - const promise = DexiePromise.resolve(typeof promiseOrFunction === 'function' ? - Dexie.ignoreTransaction(promiseOrFunction) : - promiseOrFunction) - .timeout(optionalTimeout || 60000); - return PSD.trans ? - PSD.trans.waitFor(promise) : - promise; + catch: function (onRejected) { + if (arguments.length === 1) + return this.then(null, onRejected); + var type = arguments[0], handler = arguments[1]; + return typeof type === 'function' ? this.then(null, err => + err instanceof type ? handler(err) : PromiseReject(err)) + : this.then(null, err => + err && err.name === type ? handler(err) : PromiseReject(err)); }, - Promise: DexiePromise, - debug: { - get: () => debug, - set: value => { - setDebug(value, value === 'dexie' ? () => true : dexieStackFrameFilter); - } + finally: function (onFinally) { + return this.then(value => { + onFinally(); + return value; + }, err => { + onFinally(); + return PromiseReject(err); + }); }, - derive: derive, - extend: extend, - props: props, - override: override, - Events: Events, - on: globalEvents, - liveQuery, - extendObservabilitySet, - getByKeyPath: getByKeyPath, - setByKeyPath: setByKeyPath, - delByKeyPath: delByKeyPath, - shallowClone: shallowClone, - deepClone: deepClone, - getObjectDiff: getObjectDiff, - cmp, - asap: asap$1, - minKey: minKey, - addons: [], - connections: connections, - errnames: errnames, - dependencies: domDeps, - semVer: DEXIE_VERSION, - version: DEXIE_VERSION.split('.') - .map(n => parseInt(n)) - .reduce((p, c, i) => p + (c / Math.pow(10, i * 2))), -}); -Dexie.maxKey = getMaxKey(Dexie.dependencies.IDBKeyRange); - -if (typeof dispatchEvent !== 'undefined' && typeof addEventListener !== 'undefined') { - globalEvents(DEXIE_STORAGE_MUTATED_EVENT_NAME, updatedParts => { - if (!propagatingLocally) { - let event; - if (isIEOrEdge) { - event = document.createEvent('CustomEvent'); - event.initCustomEvent(STORAGE_MUTATED_DOM_EVENT_NAME, true, true, updatedParts); + stack: { + get: function () { + if (this._stack) + return this._stack; + try { + stack_being_generated = true; + var stacks = getStack(this, [], MAX_LONG_STACKS); + var stack = stacks.join("\nFrom previous: "); + if (this._state !== null) + this._stack = stack; + return stack; } - else { - event = new CustomEvent(STORAGE_MUTATED_DOM_EVENT_NAME, { - detail: updatedParts - }); + finally { + stack_being_generated = false; } - propagatingLocally = true; - dispatchEvent(event); - propagatingLocally = false; - } - }); - addEventListener(STORAGE_MUTATED_DOM_EVENT_NAME, ({ detail }) => { - if (!propagatingLocally) { - propagateLocally(detail); } - }); -} -function propagateLocally(updateParts) { - let wasMe = propagatingLocally; - try { - propagatingLocally = true; - globalEvents.storagemutated.fire(updateParts); - } - finally { - propagatingLocally = wasMe; + }, + timeout: function (ms, msg) { + return ms < Infinity ? + new DexiePromise((resolve, reject) => { + var handle = setTimeout(() => reject(new exceptions.Timeout(msg)), ms); + this.then(resolve, reject).finally(clearTimeout.bind(null, handle)); + }) : this; } +}); +if (typeof Symbol !== 'undefined' && Symbol.toStringTag) + setProp(DexiePromise.prototype, Symbol.toStringTag, 'Dexie.Promise'); +globalPSD.env = snapShot(); +function Listener(onFulfilled, onRejected, resolve, reject, zone) { + this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; + this.onRejected = typeof onRejected === 'function' ? onRejected : null; + this.resolve = resolve; + this.reject = reject; + this.psd = zone; } -let propagatingLocally = false; - -if (typeof BroadcastChannel !== 'undefined') { - const bc = new BroadcastChannel(STORAGE_MUTATED_DOM_EVENT_NAME); - if (typeof bc.unref === 'function') { - bc.unref(); +props(DexiePromise, { + all: function () { + var values = getArrayOf.apply(null, arguments) + .map(onPossibleParallellAsync); + return new DexiePromise(function (resolve, reject) { + if (values.length === 0) + resolve([]); + var remaining = values.length; + values.forEach((a, i) => DexiePromise.resolve(a).then(x => { + values[i] = x; + if (!--remaining) + resolve(values); + }, reject)); + }); + }, + resolve: value => { + if (value instanceof DexiePromise) + return value; + if (value && typeof value.then === 'function') + return new DexiePromise((resolve, reject) => { + value.then(resolve, reject); + }); + var rv = new DexiePromise(INTERNAL, true, value); + linkToPreviousPromise(rv, currentFulfiller); + return rv; + }, + reject: PromiseReject, + race: function () { + var values = getArrayOf.apply(null, arguments).map(onPossibleParallellAsync); + return new DexiePromise((resolve, reject) => { + values.map(value => DexiePromise.resolve(value).then(resolve, reject)); + }); + }, + PSD: { + get: () => PSD, + set: value => PSD = value + }, + totalEchoes: { get: () => totalEchoes }, + newPSD: newScope, + usePSD: usePSD, + scheduler: { + get: () => asap, + set: value => { asap = value; } + }, + rejectionMapper: { + get: () => rejectionMapper, + set: value => { rejectionMapper = value; } + }, + follow: (fn, zoneProps) => { + return new DexiePromise((resolve, reject) => { + return newScope((resolve, reject) => { + var psd = PSD; + psd.unhandleds = []; + psd.onunhandled = reject; + psd.finalize = callBoth(function () { + run_at_end_of_this_or_next_physical_tick(() => { + this.unhandleds.length === 0 ? resolve() : reject(this.unhandleds[0]); + }); + }, psd.finalize); + fn(); + }, zoneProps, resolve, reject); + }); } - globalEvents(DEXIE_STORAGE_MUTATED_EVENT_NAME, (changedParts) => { - if (!propagatingLocally) { - bc.postMessage(changedParts); - } - }); - bc.onmessage = (ev) => { - if (ev.data) - propagateLocally(ev.data); - }; +}); +if (NativePromise) { + if (NativePromise.allSettled) + setProp(DexiePromise, "allSettled", function () { + const possiblePromises = getArrayOf.apply(null, arguments).map(onPossibleParallellAsync); + return new DexiePromise(resolve => { + if (possiblePromises.length === 0) + resolve([]); + let remaining = possiblePromises.length; + const results = new Array(remaining); + possiblePromises.forEach((p, i) => DexiePromise.resolve(p).then(value => results[i] = { status: "fulfilled", value }, reason => results[i] = { status: "rejected", reason }) + .then(() => --remaining || resolve(results))); + }); + }); + if (NativePromise.any && typeof AggregateError !== 'undefined') + setProp(DexiePromise, "any", function () { + const possiblePromises = getArrayOf.apply(null, arguments).map(onPossibleParallellAsync); + return new DexiePromise((resolve, reject) => { + if (possiblePromises.length === 0) + reject(new AggregateError([])); + let remaining = possiblePromises.length; + const failures = new Array(remaining); + possiblePromises.forEach((p, i) => DexiePromise.resolve(p).then(value => resolve(value), failure => { + failures[i] = failure; + if (!--remaining) + reject(new AggregateError(failures)); + })); + }); + }); } -else if (typeof self !== 'undefined' && typeof navigator !== 'undefined') { - globalEvents(DEXIE_STORAGE_MUTATED_EVENT_NAME, (changedParts) => { - try { - if (!propagatingLocally) { - if (typeof localStorage !== 'undefined') { - localStorage.setItem(STORAGE_MUTATED_DOM_EVENT_NAME, JSON.stringify({ - trig: Math.random(), - changedParts, - })); - } - if (typeof self['clients'] === 'object') { - [...self['clients'].matchAll({ includeUncontrolled: true })].forEach((client) => client.postMessage({ - type: STORAGE_MUTATED_DOM_EVENT_NAME, - changedParts, - })); - } +function executePromiseTask(promise, fn) { + try { + fn(value => { + if (promise._state !== null) + return; + if (value === promise) + throw new TypeError('A promise cannot be resolved with itself.'); + var shouldExecuteTick = promise._lib && beginMicroTickScope(); + if (value && typeof value.then === 'function') { + executePromiseTask(promise, (resolve, reject) => { + value instanceof DexiePromise ? + value._then(resolve, reject) : + value.then(resolve, reject); + }); } - } - catch (_a) { } - }); - if (typeof addEventListener !== 'undefined') { - addEventListener('storage', (ev) => { - if (ev.key === STORAGE_MUTATED_DOM_EVENT_NAME) { - const data = JSON.parse(ev.newValue); - if (data) - propagateLocally(data.changedParts); + else { + promise._state = true; + promise._value = value; + propagateAllListeners(promise); } - }); - } - const swContainer = self.document && navigator.serviceWorker; - if (swContainer) { - swContainer.addEventListener('message', propagateMessageLocally); + if (shouldExecuteTick) + endMicroTickScope(); + }, handleRejection.bind(null, promise)); } -} -function propagateMessageLocally({ data }) { - if (data && data.type === STORAGE_MUTATED_DOM_EVENT_NAME) { - propagateLocally(data.changedParts); + catch (ex) { + handleRejection(promise, ex); } } - -DexiePromise.rejectionMapper = mapError; -setDebug(debug, dexieStackFrameFilter); - -class ModelDatabase extends Dexie$1 { - constructor() { - super("ModelDatabase"); - this.version(2).stores({ - models: "id, file", +function handleRejection(promise, reason) { + rejectingErrors.push(reason); + if (promise._state !== null) + return; + var shouldExecuteTick = promise._lib && beginMicroTickScope(); + reason = rejectionMapper(reason); + promise._state = false; + promise._value = reason; + debug && reason !== null && typeof reason === 'object' && !reason._promise && tryCatch(() => { + var origProp = getPropertyDescriptor(reason, "stack"); + reason._promise = promise; + setProp(reason, "stack", { + get: () => stack_being_generated ? + origProp && (origProp.get ? + origProp.get.apply(reason) : + origProp.value) : + promise.stack }); - } + }); + addPossiblyUnhandledError(promise); + propagateAllListeners(promise); + if (shouldExecuteTick) + endMicroTickScope(); } - -// TODO: Implement UI elements (this is probably just for 3d scans) -/** - * A tool to cache files using the browser's IndexedDB API. This might - * save loading time and infrastructure costs for files that need to be - * fetched from the cloud. - */ -class LocalCacher extends Component { - /** The IDs of all the stored files. */ - get ids() { - const serialized = localStorage.getItem(this._storedModels) || "[]"; - return JSON.parse(serialized); - } - constructor(components) { - super(components); - /** Fires when a file has been loaded from cache. */ - this.onFileLoaded = new Event(); - /** Fires when a file has been saved into cache. */ - this.onItemSaved = new Event(); - /** {@link Disposable.onDisposed} */ - this.onDisposed = new Event(); - /** {@link Component.enabled} */ - this.enabled = true; - /** {@link UI.uiElement} */ - this.uiElement = new UIElement(); - this.cards = []; - this._storedModels = "open-bim-components-stored-files"; - components.tools.add(LocalCacher.uuid, this); - this._db = new ModelDatabase(); - if (components.uiEnabled) { - this.setUI(components); - } - } - /** - * {@link Component.get}. - * @param id the ID of the file to fetch. - */ - async get(id) { - if (this.exists(id)) { - await this._db.open(); - const result = await this.getModelFromLocalCache(id); - this._db.close(); - return result; - } - return null; - } - /** - * Saves the file with the given ID. - * @param id the ID to assign to the file. - * @param url the URL where the file is located. - */ - async save(id, url) { - this.addStoredID(id); - const rawData = await fetch(url); - const file = await rawData.blob(); - await this._db.open(); - await this._db.models.add({ - id, - file, - }); - this._db.close(); +function propagateAllListeners(promise) { + var listeners = promise._listeners; + promise._listeners = []; + for (var i = 0, len = listeners.length; i < len; ++i) { + propagateToListener(promise, listeners[i]); } - /** - * Checks if there's a file stored with the given ID. - * @param id to check. - */ - exists(id) { - const stored = localStorage.getItem(id); - return stored !== null; + var psd = promise._PSD; + --psd.ref || psd.finalize(); + if (numScheduledCalls === 0) { + ++numScheduledCalls; + asap(() => { + if (--numScheduledCalls === 0) + finalizePhysicalTick(); + }, []); } - /** - * Deletes the files stored in the given ids. - * @param ids the identifiers of the files to delete. - */ - async delete(ids) { - await this._db.open(); - for (const id of ids) { - if (this.exists(id)) { - this.removeStoredID(id); - await this._db.models.where("id").equals(id).delete(); - } - } - this._db.close(); +} +function propagateToListener(promise, listener) { + if (promise._state === null) { + promise._listeners.push(listener); + return; } - /** Deletes all the stored files. */ - async deleteAll() { - await this._db.open(); - this.clearStoredIDs(); - await this._db.delete(); - this._db = new ModelDatabase(); - this._db.close(); + var cb = promise._state ? listener.onFulfilled : listener.onRejected; + if (cb === null) { + return (promise._state ? listener.resolve : listener.reject)(promise._value); } - /** {@link Disposable.dispose} */ - async dispose() { - this.onFileLoaded.reset(); - this.onItemSaved.reset(); - for (const card of this.cards) { - await card.dispose(); + ++listener.psd.ref; + ++numScheduledCalls; + asap(callListener, [cb, promise, listener]); +} +function callListener(cb, promise, listener) { + try { + currentFulfiller = promise; + var ret, value = promise._value; + if (promise._state) { + ret = cb(value); } - this.cards = []; - await this.uiElement.dispose(); - this._db = null; - await this.onDisposed.trigger(LocalCacher.uuid); - this.onDisposed.reset(); - } - setUI(components) { - const main = new Button(components); - main.materialIcon = "storage"; - main.tooltip = "Local cacher"; - const saveButton = new Button(components); - saveButton.label = "Save"; - saveButton.materialIcon = "save"; - const loadButton = new Button(components); - loadButton.label = "Download"; - loadButton.materialIcon = "download"; - main.addChild(saveButton, loadButton); - const floatingMenu = new FloatingWindow(components, "file-list-menu"); - this.uiElement.set({ main, loadButton, saveButton, floatingMenu }); - floatingMenu.title = "Saved Files"; - floatingMenu.visible = false; - const savedFilesMenuHTML = floatingMenu.get(); - savedFilesMenuHTML.style.left = "70px"; - savedFilesMenuHTML.style.top = "100px"; - savedFilesMenuHTML.style.width = "340px"; - savedFilesMenuHTML.style.height = "400px"; - const renderer = this.components.renderer.get(); - const viewerContainer = renderer.domElement.parentElement; - viewerContainer.appendChild(floatingMenu.get()); - } - async getModelFromLocalCache(id) { - const found = await this._db.models.where("id").equals(id).toArray(); - return found[0].file; - } - clearStoredIDs() { - const ids = this.ids; - for (const id of ids) { - this.removeStoredID(id); + else { + if (rejectingErrors.length) + rejectingErrors = []; + ret = cb(value); + if (rejectingErrors.indexOf(value) === -1) + markErrorAsHandled(promise); } + listener.resolve(ret); } - removeStoredID(id) { - localStorage.removeItem(id); - const allIDs = this.ids; - const ids = allIDs.filter((savedId) => savedId !== id); - this.setStoredIDs(ids); - } - addStoredID(id) { - const time = performance.now().toString(); - localStorage.setItem(id, time); - const ids = this.ids; - ids.push(id); - this.setStoredIDs(ids); + catch (e) { + listener.reject(e); } - setStoredIDs(ids) { - localStorage.setItem(this._storedModels, JSON.stringify(ids)); + finally { + currentFulfiller = null; + if (--numScheduledCalls === 0) + finalizePhysicalTick(); + --listener.psd.ref || listener.psd.finalize(); } } -LocalCacher.uuid = "22ae591a-3a67-4988-86c6-68d7b83febf2"; -ToolComponent.libraryUUIDs.add(LocalCacher.uuid); - -class SimpleSVGViewport extends Component { - get enabled() { - return this._enabled; - } - set enabled(value) { - this._enabled = value; - this.resize(); - this._undoList = []; - this.uiElement.get("toolbar").visible = value; - if (value) { - this._viewport.classList.remove("pointer-events-none"); +function getStack(promise, stacks, limit) { + if (stacks.length === limit) + return stacks; + var stack = ""; + if (promise._state === false) { + var failure = promise._value, errorName, message; + if (failure != null) { + errorName = failure.name || "Error"; + message = failure.message || failure; + stack = prettyStack(failure, 0); } else { - this.clear(); - this.uiElement.get("settingsWindow").visible = false; - this._viewport.classList.add("pointer-events-none"); + errorName = failure; + message = ""; } + stacks.push(errorName + (message ? ": " + message : "") + stack); } - set config(value) { - this._config = { ...this._config, ...value }; - } - get config() { - return this._config; - } - constructor(components, config) { - super(components); - this.uiElement = new UIElement(); - this.id = generateUUID().toLowerCase(); - this._enabled = false; - /** {@link Disposable.onDisposed} */ - this.onDisposed = new Event(); - this._viewport = document.createElementNS("http://www.w3.org/2000/svg", "svg"); - this._size = new Vector2$1(); - this._undoList = []; - this.onResize = () => { - this.resize(); - }; - const defaultConfig = { - fillColor: "transparent", - strokeColor: "#ff0000", - strokeWidth: 4, - }; - this.config = { ...defaultConfig, ...(config !== null && config !== void 0 ? config : {}) }; - this._viewport.classList.add("absolute", "top-0", "right-0"); - // this._viewport.setAttribute("preserveAspectRatio", "xMidYMid") - this._viewport.setAttribute("width", "100%"); - this._viewport.setAttribute("height", "100%"); - // const renderer = this._components.renderer; - // const rendererSize = renderer.getSize(); - // const width = rendererSize.x - // const height = rendererSize.y - // this._viewport.setAttribute("viewBox", `0 0 ${width} ${height}`); - this.setUI(); - this.enabled = false; - this.components.ui.viewerContainer.append(this._viewport); - this.setupEvents(true); - } - async dispose() { - this._undoList = []; - this.uiElement.dispose(); - await this.onDisposed.trigger(); - this.onDisposed.reset(); + if (debug) { + stack = prettyStack(promise._stackHolder, 2); + if (stack && stacks.indexOf(stack) === -1) + stacks.push(stack); + if (promise._prev) + getStack(promise._prev, stacks, limit); } - get() { - return this._viewport; + return stacks; +} +function linkToPreviousPromise(promise, prev) { + var numPrev = prev ? prev._numPrev + 1 : 0; + if (numPrev < LONG_STACKS_CLIP_LIMIT) { + promise._prev = prev; + promise._numPrev = numPrev; } - clear() { - const viewport = this.get(); - this._undoList = []; - while (viewport.firstChild) { - viewport.removeChild(viewport.firstChild); +} +function physicalTick() { + beginMicroTickScope() && endMicroTickScope(); +} +function beginMicroTickScope() { + var wasRootExec = isOutsideMicroTick; + isOutsideMicroTick = false; + needsNewPhysicalTick = false; + return wasRootExec; +} +function endMicroTickScope() { + var callbacks, i, l; + do { + while (microtickQueue.length > 0) { + callbacks = microtickQueue; + microtickQueue = []; + l = callbacks.length; + for (i = 0; i < l; ++i) { + var item = callbacks[i]; + item[0].apply(null, item[1]); + } } + } while (microtickQueue.length > 0); + isOutsideMicroTick = true; + needsNewPhysicalTick = true; +} +function finalizePhysicalTick() { + var unhandledErrs = unhandledErrors; + unhandledErrors = []; + unhandledErrs.forEach(p => { + p._PSD.onunhandled.call(null, p._value, p); + }); + var finalizers = tickFinalizers.slice(0); + var i = finalizers.length; + while (i) + finalizers[--i](); +} +function run_at_end_of_this_or_next_physical_tick(fn) { + function finalizer() { + fn(); + tickFinalizers.splice(tickFinalizers.indexOf(finalizer), 1); } - getDrawing() { - return this.get().childNodes; - } - // setDrawing() { - // if (!this.enabled) { } - // } - /** {@link Resizeable.resize}. */ - resize() { - const renderer = this.components.renderer; - const rendererSize = renderer.getSize(); - const width = this.enabled ? rendererSize.x : 0; - const height = this.enabled ? rendererSize.y : 0; - this._size.set(width, height); - // this._viewport.setAttribute("viewBox", `0 0 ${this._size.x} ${this._size.y}`); - } - /** {@link Resizeable.getSize}. */ - getSize() { - return this._size; - } - setupEvents(active) { - if (active) { - window.addEventListener("resize", this.onResize); + tickFinalizers.push(finalizer); + ++numScheduledCalls; + asap(() => { + if (--numScheduledCalls === 0) + finalizePhysicalTick(); + }, []); +} +function addPossiblyUnhandledError(promise) { + if (!unhandledErrors.some(p => p._value === promise._value)) + unhandledErrors.push(promise); +} +function markErrorAsHandled(promise) { + var i = unhandledErrors.length; + while (i) + if (unhandledErrors[--i]._value === promise._value) { + unhandledErrors.splice(i, 1); + return; } - else { - window.removeEventListener("resize", this.onResize); +} +function PromiseReject(reason) { + return new DexiePromise(INTERNAL, false, reason); +} +function wrap(fn, errorCatcher) { + var psd = PSD; + return function () { + var wasRootExec = beginMicroTickScope(), outerScope = PSD; + try { + switchToZone(psd, true); + return fn.apply(this, arguments); } - } - setUI() { - var _a, _b; - const undoDrawingBtn = new Button(this.components, { - materialIconName: "undo", - }); - undoDrawingBtn.onClick.add(() => { - if (this._viewport.lastChild) { - this._undoList.push(this._viewport.lastChild); - this._viewport.lastChild.remove(); - } - }); - const redoDrawingBtn = new Button(this.components, { - materialIconName: "redo", - }); - redoDrawingBtn.onClick.add(() => { - const childNode = this._undoList[this._undoList.length - 1]; - if (childNode) { - this._undoList.pop(); - this._viewport.append(childNode); - } - }); - const clearDrawingBtn = new Button(this.components, { - materialIconName: "delete", - }); - clearDrawingBtn.onClick.add(() => this.clear()); - // #region Settings window - const settingsWindow = new FloatingWindow(this.components, this.id); - settingsWindow.title = "Drawing Settings"; - settingsWindow.visible = false; - const viewerContainer = this.components.renderer.get().domElement - .parentElement; - viewerContainer.append(settingsWindow.get()); - const strokeWidth = new RangeInput(this.components); - strokeWidth.label = "Stroke Width"; - strokeWidth.min = 2; - strokeWidth.max = 6; - strokeWidth.value = 4; - // strokeWidth.id = this.id; - strokeWidth.onChange.add((value) => { - // @ts-ignore - this.config = { strokeWidth: value }; - }); - const strokeColorInput = new ColorInput(this.components); - strokeColorInput.label = "Stroke Color"; - strokeColorInput.value = (_a = this.config.strokeColor) !== null && _a !== void 0 ? _a : "#BCF124"; - // strokeColorInput.name = "stroke-color"; - // strokeColorInput.id = this.id; - strokeColorInput.onChange.add((value) => { - this.config = { strokeColor: value }; - }); - const fillColorInput = new ColorInput(this.components); - strokeColorInput.label = "Fill Color"; - strokeColorInput.value = (_b = this.config.fillColor) !== null && _b !== void 0 ? _b : "#BCF124"; - // strokeColorInput.name = "fill-color"; - // strokeColorInput.id = this.id; - fillColorInput.onChange.add((value) => { - this.config = { fillColor: value }; - }); - settingsWindow.addChild(strokeColorInput, fillColorInput, strokeWidth); - const settingsBtn = new Button(this.components, { - materialIconName: "settings", - }); - settingsBtn.onClick.add(() => { - settingsWindow.visible = !settingsWindow.visible; - settingsBtn.active = settingsWindow.visible; + catch (e) { + errorCatcher && errorCatcher(e); + } + finally { + switchToZone(outerScope, false); + if (wasRootExec) + endMicroTickScope(); + } + }; +} +const task = { awaits: 0, echoes: 0, id: 0 }; +var taskCounter = 0; +var zoneStack = []; +var zoneEchoes = 0; +var totalEchoes = 0; +var zone_id_counter = 0; +function newScope(fn, props, a1, a2) { + var parent = PSD, psd = Object.create(parent); + psd.parent = parent; + psd.ref = 0; + psd.global = false; + psd.id = ++zone_id_counter; + var globalEnv = globalPSD.env; + psd.env = patchGlobalPromise ? { + Promise: DexiePromise, + PromiseProp: { value: DexiePromise, configurable: true, writable: true }, + all: DexiePromise.all, + race: DexiePromise.race, + allSettled: DexiePromise.allSettled, + any: DexiePromise.any, + resolve: DexiePromise.resolve, + reject: DexiePromise.reject, + nthen: getPatchedPromiseThen(globalEnv.nthen, psd), + gthen: getPatchedPromiseThen(globalEnv.gthen, psd) + } : {}; + if (props) + extend(psd, props); + ++parent.ref; + psd.finalize = function () { + --this.parent.ref || this.parent.finalize(); + }; + var rv = usePSD(psd, fn, a1, a2); + if (psd.ref === 0) + psd.finalize(); + return rv; +} +function incrementExpectedAwaits() { + if (!task.id) + task.id = ++taskCounter; + ++task.awaits; + task.echoes += ZONE_ECHO_LIMIT; + return task.id; +} +function decrementExpectedAwaits() { + if (!task.awaits) + return false; + if (--task.awaits === 0) + task.id = 0; + task.echoes = task.awaits * ZONE_ECHO_LIMIT; + return true; +} +if (('' + nativePromiseThen).indexOf('[native code]') === -1) { + incrementExpectedAwaits = decrementExpectedAwaits = nop; +} +function onPossibleParallellAsync(possiblePromise) { + if (task.echoes && possiblePromise && possiblePromise.constructor === NativePromise) { + incrementExpectedAwaits(); + return possiblePromise.then(x => { + decrementExpectedAwaits(); + return x; + }, e => { + decrementExpectedAwaits(); + return rejection(e); }); - settingsWindow.onHidden.add(() => (settingsBtn.active = false)); - const toolbar = new Toolbar(this.components, { position: "right" }); - toolbar.addChild(settingsBtn, undoDrawingBtn, redoDrawingBtn, clearDrawingBtn); - this.uiElement.set({ toolbar, settingsWindow }); } + return possiblePromise; } - -// TODO: Clean up and document -// TODO: Disable / enable instance color for instance meshes -/** - * A tool to easily handle the materials of massive amounts of - * objects and scene background easily. - */ -class MaterialManager extends Component { - constructor(components) { - super(components); - /** {@link Component.enabled} */ - this.enabled = true; - this._originalBackground = null; - /** {@link Disposable.onDisposed} */ - this.onDisposed = new Event(); - this._originals = {}; - this._list = {}; - this.components.tools.add(MaterialManager.uuid, this); +function zoneEnterEcho(targetZone) { + ++totalEchoes; + if (!task.echoes || --task.echoes === 0) { + task.echoes = task.id = 0; } - /** - * {@link Component.get}. - * @return list of created materials. - */ - get() { - return Object.keys(this._list); + zoneStack.push(PSD); + switchToZone(targetZone, true); +} +function zoneLeaveEcho() { + var zone = zoneStack[zoneStack.length - 1]; + zoneStack.pop(); + switchToZone(zone, false); +} +function switchToZone(targetZone, bEnteringZone) { + var currentZone = PSD; + if (bEnteringZone ? task.echoes && (!zoneEchoes++ || targetZone !== PSD) : zoneEchoes && (!--zoneEchoes || targetZone !== PSD)) { + enqueueNativeMicroTask(bEnteringZone ? zoneEnterEcho.bind(null, targetZone) : zoneLeaveEcho); } - /** - * Turns the specified material styles on or off. - * - * @param active whether to turn it on or off. - * @param ids the ids of the style to turn on or off. - */ - set(active, ids = Object.keys(this._list)) { - for (const id of ids) { - const { material, meshes } = this._list[id]; - for (const mesh of meshes) { - if (active) { - if (!this._originals[mesh.uuid]) { - this._originals[mesh.uuid] = { material: mesh.material }; - } - if (mesh instanceof THREE$1.InstancedMesh && mesh.instanceColor) { - this._originals[mesh.uuid].instances = mesh.instanceColor; - mesh.instanceColor = null; - } - mesh.material = material; - } - else { - if (!this._originals[mesh.uuid]) - continue; - mesh.material = this._originals[mesh.uuid].material; - const instances = this._originals[mesh.uuid].instances; - if (mesh instanceof THREE$1.InstancedMesh && instances) { - mesh.instanceColor = instances; - } - } - } + if (targetZone === PSD) + return; + PSD = targetZone; + if (currentZone === globalPSD) + globalPSD.env = snapShot(); + if (patchGlobalPromise) { + var GlobalPromise = globalPSD.env.Promise; + var targetEnv = targetZone.env; + nativePromiseProto.then = targetEnv.nthen; + GlobalPromise.prototype.then = targetEnv.gthen; + if (currentZone.global || targetZone.global) { + Object.defineProperty(_global, 'Promise', targetEnv.PromiseProp); + GlobalPromise.all = targetEnv.all; + GlobalPromise.race = targetEnv.race; + GlobalPromise.resolve = targetEnv.resolve; + GlobalPromise.reject = targetEnv.reject; + if (targetEnv.allSettled) + GlobalPromise.allSettled = targetEnv.allSettled; + if (targetEnv.any) + GlobalPromise.any = targetEnv.any; } } - /** {@link Disposable.dispose} */ - async dispose() { - for (const id in this._list) { - const { material } = this._list[id]; - material.dispose(); - } - this._list = {}; - this._originals = {}; - await this.onDisposed.trigger(MaterialManager.uuid); - this.onDisposed.reset(); +} +function snapShot() { + var GlobalPromise = _global.Promise; + return patchGlobalPromise ? { + Promise: GlobalPromise, + PromiseProp: Object.getOwnPropertyDescriptor(_global, "Promise"), + all: GlobalPromise.all, + race: GlobalPromise.race, + allSettled: GlobalPromise.allSettled, + any: GlobalPromise.any, + resolve: GlobalPromise.resolve, + reject: GlobalPromise.reject, + nthen: nativePromiseProto.then, + gthen: GlobalPromise.prototype.then + } : {}; +} +function usePSD(psd, fn, a1, a2, a3) { + var outerScope = PSD; + try { + switchToZone(psd, true); + return fn(a1, a2, a3); } - /** - * Sets the color of the background of the scene. - * - * @param color: the color to apply. - */ - setBackgroundColor(color) { - const scene = this.components.scene.get(); - if (!this._originalBackground) { - this._originalBackground = scene.background; + finally { + switchToZone(outerScope, false); + } +} +function enqueueNativeMicroTask(job) { + nativePromiseThen.call(resolvedNativePromise, job); +} +function nativeAwaitCompatibleWrap(fn, zone, possibleAwait, cleanup) { + return typeof fn !== 'function' ? fn : function () { + var outerZone = PSD; + if (possibleAwait) + incrementExpectedAwaits(); + switchToZone(zone, true); + try { + return fn.apply(this, arguments); } - if (this._originalBackground) { - scene.background = color; + finally { + switchToZone(outerZone, false); + if (cleanup) + enqueueNativeMicroTask(decrementExpectedAwaits); } + }; +} +function getPatchedPromiseThen(origThen, zone) { + return function (onResolved, onRejected) { + return origThen.call(this, nativeAwaitCompatibleWrap(onResolved, zone), nativeAwaitCompatibleWrap(onRejected, zone)); + }; +} +const UNHANDLEDREJECTION = "unhandledrejection"; +function globalError(err, promise) { + var rv; + try { + rv = promise.onuncatched(err); } - /** - * Resets the scene background to the color that was being used - * before applying the material manager. - */ - resetBackgroundColor() { - const scene = this.components.scene.get(); - if (this._originalBackground) { - scene.background = this._originalBackground; + catch (e) { } + if (rv !== false) + try { + var event, eventData = { promise: promise, reason: err }; + if (_global.document && document.createEvent) { + event = document.createEvent('Event'); + event.initEvent(UNHANDLEDREJECTION, true, true); + extend(event, eventData); + } + else if (_global.CustomEvent) { + event = new CustomEvent(UNHANDLEDREJECTION, { detail: eventData }); + extend(event, eventData); + } + if (event && _global.dispatchEvent) { + dispatchEvent(event); + if (!_global.PromiseRejectionEvent && _global.onunhandledrejection) + try { + _global.onunhandledrejection(event); + } + catch (_) { } + } + if (debug && event && !event.defaultPrevented) { + console.warn(`Unhandled rejection: ${err.stack || err}`); + } } - } - /** - * Creates a new material style. - * @param id the identifier of the style to create. - * @param material the material of the style. - */ - addMaterial(id, material) { - if (this._list[id]) { - throw new Error("This ID already exists!"); + catch (e) { } +} +var rejection = DexiePromise.reject; + +function tempTransaction(db, mode, storeNames, fn) { + if (!db.idbdb || (!db._state.openComplete && (!PSD.letThrough && !db._vip))) { + if (db._state.openComplete) { + return rejection(new exceptions.DatabaseClosed(db._state.dbOpenError)); } - this._list[id] = { material, meshes: new Set() }; + if (!db._state.isBeingOpened) { + if (!db._options.autoOpen) + return rejection(new exceptions.DatabaseClosed()); + db.open().catch(nop); + } + return db._state.dbReadyPromise.then(() => tempTransaction(db, mode, storeNames, fn)); } - /** - * Assign meshes to a certain style. - * @param id the identifier of the style. - * @param meshes the meshes to assign to the style. - */ - addMeshes(id, meshes) { - if (!this._list[id]) { - throw new Error("This ID doesn't exists!"); + else { + var trans = db._createTransaction(mode, storeNames, db._dbSchema); + try { + trans.create(); + db._state.PR1398_maxLoop = 3; } - for (const mesh of meshes) { - this._list[id].meshes.add(mesh); + catch (ex) { + if (ex.name === errnames.InvalidState && db.isOpen() && --db._state.PR1398_maxLoop > 0) { + console.warn('Dexie: Need to reopen db'); + db._close(); + return db.open().then(() => tempTransaction(db, mode, storeNames, fn)); + } + return rejection(ex); } + return trans._promise(mode, (resolve, reject) => { + return newScope(() => { + PSD.trans = trans; + return fn(resolve, reject, trans); + }); + }).then(result => { + return trans._completion.then(() => result); + }); } } -MaterialManager.uuid = "24989d27-fa2f-4797-8b08-35918f74e502"; -ToolComponent.libraryUUIDs.add(MaterialManager.uuid); - -// OrbitControls performs orbiting, dollying (zooming), and panning. -// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). -// -// Orbit - left mouse / touch: one-finger move -// Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish -// Pan - right mouse, or left mouse + ctrl/meta/shiftKey, or arrow keys / touch: two-finger move -const _changeEvent = { type: 'change' }; -const _startEvent = { type: 'start' }; -const _endEvent = { type: 'end' }; - -class OrbitControls extends EventDispatcher$1 { - - constructor( object, domElement ) { +const DEXIE_VERSION = '3.2.4'; +const maxString = String.fromCharCode(65535); +const minKey = -Infinity; +const INVALID_KEY_ARGUMENT = "Invalid key provided. Keys must be of type string, number, Date or Array."; +const STRING_EXPECTED = "String expected."; +const connections = []; +const isIEOrEdge = typeof navigator !== 'undefined' && /(MSIE|Trident|Edge)/.test(navigator.userAgent); +const hasIEDeleteObjectStoreBug = isIEOrEdge; +const hangsOnDeleteLargeKeyRange = isIEOrEdge; +const dexieStackFrameFilter = frame => !/(dexie\.js|dexie\.min\.js)/.test(frame); +const DBNAMES_DB = '__dbnames'; +const READONLY = 'readonly'; +const READWRITE = 'readwrite'; - super(); +function combine(filter1, filter2) { + return filter1 ? + filter2 ? + function () { return filter1.apply(this, arguments) && filter2.apply(this, arguments); } : + filter1 : + filter2; +} - this.object = object; - this.domElement = domElement; - this.domElement.style.touchAction = 'none'; // disable touch scroll +const AnyRange = { + type: 3 , + lower: -Infinity, + lowerOpen: false, + upper: [[]], + upperOpen: false +}; - // Set to false to disable this control - this.enabled = true; - - // "target" sets the location of focus, where the object orbits around - this.target = new Vector3$1(); - - // How far you can dolly in and out ( PerspectiveCamera only ) - this.minDistance = 0; - this.maxDistance = Infinity; - - // How far you can zoom in and out ( OrthographicCamera only ) - this.minZoom = 0; - this.maxZoom = Infinity; - - // How far you can orbit vertically, upper and lower limits. - // Range is 0 to Math.PI radians. - this.minPolarAngle = 0; // radians - this.maxPolarAngle = Math.PI; // radians - - // How far you can orbit horizontally, upper and lower limits. - // If set, the interval [ min, max ] must be a sub-interval of [ - 2 PI, 2 PI ], with ( max - min < 2 PI ) - this.minAzimuthAngle = - Infinity; // radians - this.maxAzimuthAngle = Infinity; // radians - - // Set to true to enable damping (inertia) - // If damping is enabled, you must call controls.update() in your animation loop - this.enableDamping = false; - this.dampingFactor = 0.05; - - // This option actually enables dollying in and out; left as "zoom" for backwards compatibility. - // Set to false to disable zooming - this.enableZoom = true; - this.zoomSpeed = 1.0; - - // Set to false to disable rotating - this.enableRotate = true; - this.rotateSpeed = 1.0; - - // Set to false to disable panning - this.enablePan = true; - this.panSpeed = 1.0; - this.screenSpacePanning = true; // if false, pan orthogonal to world-space direction camera.up - this.keyPanSpeed = 7.0; // pixels moved per arrow key push - - // Set to true to automatically rotate around the target - // If auto-rotate is enabled, you must call controls.update() in your animation loop - this.autoRotate = false; - this.autoRotateSpeed = 2.0; // 30 seconds per orbit when fps is 60 - - // The four arrow keys - this.keys = { LEFT: 'ArrowLeft', UP: 'ArrowUp', RIGHT: 'ArrowRight', BOTTOM: 'ArrowDown' }; - - // Mouse buttons - this.mouseButtons = { LEFT: MOUSE.ROTATE, MIDDLE: MOUSE.DOLLY, RIGHT: MOUSE.PAN }; - - // Touch fingers - this.touches = { ONE: TOUCH.ROTATE, TWO: TOUCH.DOLLY_PAN }; - - // for reset - this.target0 = this.target.clone(); - this.position0 = this.object.position.clone(); - this.zoom0 = this.object.zoom; - - // the target DOM element for key events - this._domElementKeyEvents = null; - - // - // public methods - // +function workaroundForUndefinedPrimKey(keyPath) { + return typeof keyPath === "string" && !/\./.test(keyPath) + ? (obj) => { + if (obj[keyPath] === undefined && (keyPath in obj)) { + obj = deepClone(obj); + delete obj[keyPath]; + } + return obj; + } + : (obj) => obj; +} - this.getPolarAngle = function () { +let Table$3 = class Table { + _trans(mode, fn, writeLocked) { + const trans = this._tx || PSD.trans; + const tableName = this.name; + function checkTableInTransaction(resolve, reject, trans) { + if (!trans.schema[tableName]) + throw new exceptions.NotFound("Table " + tableName + " not part of transaction"); + return fn(trans.idbtrans, trans); + } + const wasRootExec = beginMicroTickScope(); + try { + return trans && trans.db === this.db ? + trans === PSD.trans ? + trans._promise(mode, checkTableInTransaction, writeLocked) : + newScope(() => trans._promise(mode, checkTableInTransaction, writeLocked), { trans: trans, transless: PSD.transless || PSD }) : + tempTransaction(this.db, mode, [this.name], checkTableInTransaction); + } + finally { + if (wasRootExec) + endMicroTickScope(); + } + } + get(keyOrCrit, cb) { + if (keyOrCrit && keyOrCrit.constructor === Object) + return this.where(keyOrCrit).first(cb); + return this._trans('readonly', (trans) => { + return this.core.get({ trans, key: keyOrCrit }) + .then(res => this.hook.reading.fire(res)); + }).then(cb); + } + where(indexOrCrit) { + if (typeof indexOrCrit === 'string') + return new this.db.WhereClause(this, indexOrCrit); + if (isArray(indexOrCrit)) + return new this.db.WhereClause(this, `[${indexOrCrit.join('+')}]`); + const keyPaths = keys(indexOrCrit); + if (keyPaths.length === 1) + return this + .where(keyPaths[0]) + .equals(indexOrCrit[keyPaths[0]]); + const compoundIndex = this.schema.indexes.concat(this.schema.primKey).filter(ix => ix.compound && + keyPaths.every(keyPath => ix.keyPath.indexOf(keyPath) >= 0) && + ix.keyPath.every(keyPath => keyPaths.indexOf(keyPath) >= 0))[0]; + if (compoundIndex && this.db._maxKey !== maxString) + return this + .where(compoundIndex.name) + .equals(compoundIndex.keyPath.map(kp => indexOrCrit[kp])); + if (!compoundIndex && debug) + console.warn(`The query ${JSON.stringify(indexOrCrit)} on ${this.name} would benefit of a ` + + `compound index [${keyPaths.join('+')}]`); + const { idxByName } = this.schema; + const idb = this.db._deps.indexedDB; + function equals(a, b) { + try { + return idb.cmp(a, b) === 0; + } + catch (e) { + return false; + } + } + const [idx, filterFunction] = keyPaths.reduce(([prevIndex, prevFilterFn], keyPath) => { + const index = idxByName[keyPath]; + const value = indexOrCrit[keyPath]; + return [ + prevIndex || index, + prevIndex || !index ? + combine(prevFilterFn, index && index.multi ? + x => { + const prop = getByKeyPath(x, keyPath); + return isArray(prop) && prop.some(item => equals(value, item)); + } : x => equals(value, getByKeyPath(x, keyPath))) + : prevFilterFn + ]; + }, [null, null]); + return idx ? + this.where(idx.name).equals(indexOrCrit[idx.keyPath]) + .filter(filterFunction) : + compoundIndex ? + this.filter(filterFunction) : + this.where(keyPaths).equals(''); + } + filter(filterFunction) { + return this.toCollection().and(filterFunction); + } + count(thenShortcut) { + return this.toCollection().count(thenShortcut); + } + offset(offset) { + return this.toCollection().offset(offset); + } + limit(numRows) { + return this.toCollection().limit(numRows); + } + each(callback) { + return this.toCollection().each(callback); + } + toArray(thenShortcut) { + return this.toCollection().toArray(thenShortcut); + } + toCollection() { + return new this.db.Collection(new this.db.WhereClause(this)); + } + orderBy(index) { + return new this.db.Collection(new this.db.WhereClause(this, isArray(index) ? + `[${index.join('+')}]` : + index)); + } + reverse() { + return this.toCollection().reverse(); + } + mapToClass(constructor) { + this.schema.mappedClass = constructor; + const readHook = obj => { + if (!obj) + return obj; + const res = Object.create(constructor.prototype); + for (var m in obj) + if (hasOwn(obj, m)) + try { + res[m] = obj[m]; + } + catch (_) { } + return res; + }; + if (this.schema.readHook) { + this.hook.reading.unsubscribe(this.schema.readHook); + } + this.schema.readHook = readHook; + this.hook("reading", readHook); + return constructor; + } + defineClass() { + function Class(content) { + extend(this, content); + } + return this.mapToClass(Class); + } + add(obj, key) { + const { auto, keyPath } = this.schema.primKey; + let objToAdd = obj; + if (keyPath && auto) { + objToAdd = workaroundForUndefinedPrimKey(keyPath)(obj); + } + return this._trans('readwrite', trans => { + return this.core.mutate({ trans, type: 'add', keys: key != null ? [key] : null, values: [objToAdd] }); + }).then(res => res.numFailures ? DexiePromise.reject(res.failures[0]) : res.lastResult) + .then(lastResult => { + if (keyPath) { + try { + setByKeyPath(obj, keyPath, lastResult); + } + catch (_) { } + } + return lastResult; + }); + } + update(keyOrObject, modifications) { + if (typeof keyOrObject === 'object' && !isArray(keyOrObject)) { + const key = getByKeyPath(keyOrObject, this.schema.primKey.keyPath); + if (key === undefined) + return rejection(new exceptions.InvalidArgument("Given object does not contain its primary key")); + try { + if (typeof modifications !== "function") { + keys(modifications).forEach(keyPath => { + setByKeyPath(keyOrObject, keyPath, modifications[keyPath]); + }); + } + else { + modifications(keyOrObject, { value: keyOrObject, primKey: key }); + } + } + catch (_a) { + } + return this.where(":id").equals(key).modify(modifications); + } + else { + return this.where(":id").equals(keyOrObject).modify(modifications); + } + } + put(obj, key) { + const { auto, keyPath } = this.schema.primKey; + let objToAdd = obj; + if (keyPath && auto) { + objToAdd = workaroundForUndefinedPrimKey(keyPath)(obj); + } + return this._trans('readwrite', trans => this.core.mutate({ trans, type: 'put', values: [objToAdd], keys: key != null ? [key] : null })) + .then(res => res.numFailures ? DexiePromise.reject(res.failures[0]) : res.lastResult) + .then(lastResult => { + if (keyPath) { + try { + setByKeyPath(obj, keyPath, lastResult); + } + catch (_) { } + } + return lastResult; + }); + } + delete(key) { + return this._trans('readwrite', trans => this.core.mutate({ trans, type: 'delete', keys: [key] })) + .then(res => res.numFailures ? DexiePromise.reject(res.failures[0]) : undefined); + } + clear() { + return this._trans('readwrite', trans => this.core.mutate({ trans, type: 'deleteRange', range: AnyRange })) + .then(res => res.numFailures ? DexiePromise.reject(res.failures[0]) : undefined); + } + bulkGet(keys) { + return this._trans('readonly', trans => { + return this.core.getMany({ + keys, + trans + }).then(result => result.map(res => this.hook.reading.fire(res))); + }); + } + bulkAdd(objects, keysOrOptions, options) { + const keys = Array.isArray(keysOrOptions) ? keysOrOptions : undefined; + options = options || (keys ? undefined : keysOrOptions); + const wantResults = options ? options.allKeys : undefined; + return this._trans('readwrite', trans => { + const { auto, keyPath } = this.schema.primKey; + if (keyPath && keys) + throw new exceptions.InvalidArgument("bulkAdd(): keys argument invalid on tables with inbound keys"); + if (keys && keys.length !== objects.length) + throw new exceptions.InvalidArgument("Arguments objects and keys must have the same length"); + const numObjects = objects.length; + let objectsToAdd = keyPath && auto ? + objects.map(workaroundForUndefinedPrimKey(keyPath)) : + objects; + return this.core.mutate({ trans, type: 'add', keys: keys, values: objectsToAdd, wantResults }) + .then(({ numFailures, results, lastResult, failures }) => { + const result = wantResults ? results : lastResult; + if (numFailures === 0) + return result; + throw new BulkError(`${this.name}.bulkAdd(): ${numFailures} of ${numObjects} operations failed`, failures); + }); + }); + } + bulkPut(objects, keysOrOptions, options) { + const keys = Array.isArray(keysOrOptions) ? keysOrOptions : undefined; + options = options || (keys ? undefined : keysOrOptions); + const wantResults = options ? options.allKeys : undefined; + return this._trans('readwrite', trans => { + const { auto, keyPath } = this.schema.primKey; + if (keyPath && keys) + throw new exceptions.InvalidArgument("bulkPut(): keys argument invalid on tables with inbound keys"); + if (keys && keys.length !== objects.length) + throw new exceptions.InvalidArgument("Arguments objects and keys must have the same length"); + const numObjects = objects.length; + let objectsToPut = keyPath && auto ? + objects.map(workaroundForUndefinedPrimKey(keyPath)) : + objects; + return this.core.mutate({ trans, type: 'put', keys: keys, values: objectsToPut, wantResults }) + .then(({ numFailures, results, lastResult, failures }) => { + const result = wantResults ? results : lastResult; + if (numFailures === 0) + return result; + throw new BulkError(`${this.name}.bulkPut(): ${numFailures} of ${numObjects} operations failed`, failures); + }); + }); + } + bulkDelete(keys) { + const numKeys = keys.length; + return this._trans('readwrite', trans => { + return this.core.mutate({ trans, type: 'delete', keys: keys }); + }).then(({ numFailures, lastResult, failures }) => { + if (numFailures === 0) + return lastResult; + throw new BulkError(`${this.name}.bulkDelete(): ${numFailures} of ${numKeys} operations failed`, failures); + }); + } +}; - return spherical.phi; +function Events(ctx) { + var evs = {}; + var rv = function (eventName, subscriber) { + if (subscriber) { + var i = arguments.length, args = new Array(i - 1); + while (--i) + args[i - 1] = arguments[i]; + evs[eventName].subscribe.apply(null, args); + return ctx; + } + else if (typeof (eventName) === 'string') { + return evs[eventName]; + } + }; + rv.addEventType = add; + for (var i = 1, l = arguments.length; i < l; ++i) { + add(arguments[i]); + } + return rv; + function add(eventName, chainFunction, defaultFunction) { + if (typeof eventName === 'object') + return addConfiguredEvents(eventName); + if (!chainFunction) + chainFunction = reverseStoppableEventChain; + if (!defaultFunction) + defaultFunction = nop; + var context = { + subscribers: [], + fire: defaultFunction, + subscribe: function (cb) { + if (context.subscribers.indexOf(cb) === -1) { + context.subscribers.push(cb); + context.fire = chainFunction(context.fire, cb); + } + }, + unsubscribe: function (cb) { + context.subscribers = context.subscribers.filter(function (fn) { return fn !== cb; }); + context.fire = context.subscribers.reduce(chainFunction, defaultFunction); + } + }; + evs[eventName] = rv[eventName] = context; + return context; + } + function addConfiguredEvents(cfg) { + keys(cfg).forEach(function (eventName) { + var args = cfg[eventName]; + if (isArray(args)) { + add(eventName, cfg[eventName][0], cfg[eventName][1]); + } + else if (args === 'asap') { + var context = add(eventName, mirror, function fire() { + var i = arguments.length, args = new Array(i); + while (i--) + args[i] = arguments[i]; + context.subscribers.forEach(function (fn) { + asap$1(function fireEvent() { + fn.apply(null, args); + }); + }); + }); + } + else + throw new exceptions.InvalidArgument("Invalid event config"); + }); + } +} - }; +function makeClassConstructor(prototype, constructor) { + derive(constructor).from({ prototype }); + return constructor; +} - this.getAzimuthalAngle = function () { +function createTableConstructor(db) { + return makeClassConstructor(Table$3.prototype, function Table(name, tableSchema, trans) { + this.db = db; + this._tx = trans; + this.name = name; + this.schema = tableSchema; + this.hook = db._allTables[name] ? db._allTables[name].hook : Events(null, { + "creating": [hookCreatingChain, nop], + "reading": [pureFunctionChain, mirror], + "updating": [hookUpdatingChain, nop], + "deleting": [hookDeletingChain, nop] + }); + }); +} - return spherical.theta; +function isPlainKeyRange(ctx, ignoreLimitFilter) { + return !(ctx.filter || ctx.algorithm || ctx.or) && + (ignoreLimitFilter ? ctx.justLimit : !ctx.replayFilter); +} +function addFilter(ctx, fn) { + ctx.filter = combine(ctx.filter, fn); +} +function addReplayFilter(ctx, factory, isLimitFilter) { + var curr = ctx.replayFilter; + ctx.replayFilter = curr ? () => combine(curr(), factory()) : factory; + ctx.justLimit = isLimitFilter && !curr; +} +function addMatchFilter(ctx, fn) { + ctx.isMatch = combine(ctx.isMatch, fn); +} +function getIndexOrStore(ctx, coreSchema) { + if (ctx.isPrimKey) + return coreSchema.primaryKey; + const index = coreSchema.getIndexByKeyPath(ctx.index); + if (!index) + throw new exceptions.Schema("KeyPath " + ctx.index + " on object store " + coreSchema.name + " is not indexed"); + return index; +} +function openCursor(ctx, coreTable, trans) { + const index = getIndexOrStore(ctx, coreTable.schema); + return coreTable.openCursor({ + trans, + values: !ctx.keysOnly, + reverse: ctx.dir === 'prev', + unique: !!ctx.unique, + query: { + index, + range: ctx.range + } + }); +} +function iter(ctx, fn, coreTrans, coreTable) { + const filter = ctx.replayFilter ? combine(ctx.filter, ctx.replayFilter()) : ctx.filter; + if (!ctx.or) { + return iterate(openCursor(ctx, coreTable, coreTrans), combine(ctx.algorithm, filter), fn, !ctx.keysOnly && ctx.valueMapper); + } + else { + const set = {}; + const union = (item, cursor, advance) => { + if (!filter || filter(cursor, advance, result => cursor.stop(result), err => cursor.fail(err))) { + var primaryKey = cursor.primaryKey; + var key = '' + primaryKey; + if (key === '[object ArrayBuffer]') + key = '' + new Uint8Array(primaryKey); + if (!hasOwn(set, key)) { + set[key] = true; + fn(item, cursor, advance); + } + } + }; + return Promise.all([ + ctx.or._iterate(union, coreTrans), + iterate(openCursor(ctx, coreTable, coreTrans), ctx.algorithm, union, !ctx.keysOnly && ctx.valueMapper) + ]); + } +} +function iterate(cursorPromise, filter, fn, valueMapper) { + var mappedFn = valueMapper ? (x, c, a) => fn(valueMapper(x), c, a) : fn; + var wrappedFn = wrap(mappedFn); + return cursorPromise.then(cursor => { + if (cursor) { + return cursor.start(() => { + var c = () => cursor.continue(); + if (!filter || filter(cursor, advancer => c = advancer, val => { cursor.stop(val); c = nop; }, e => { cursor.fail(e); c = nop; })) + wrappedFn(cursor.value, cursor, advancer => c = advancer); + c(); + }); + } + }); +} - }; - - this.getDistance = function () { - - return this.object.position.distanceTo( this.target ); - - }; - - this.listenToKeyEvents = function ( domElement ) { - - domElement.addEventListener( 'keydown', onKeyDown ); - this._domElementKeyEvents = domElement; - - }; +function cmp(a, b) { + try { + const ta = type(a); + const tb = type(b); + if (ta !== tb) { + if (ta === 'Array') + return 1; + if (tb === 'Array') + return -1; + if (ta === 'binary') + return 1; + if (tb === 'binary') + return -1; + if (ta === 'string') + return 1; + if (tb === 'string') + return -1; + if (ta === 'Date') + return 1; + if (tb !== 'Date') + return NaN; + return -1; + } + switch (ta) { + case 'number': + case 'Date': + case 'string': + return a > b ? 1 : a < b ? -1 : 0; + case 'binary': { + return compareUint8Arrays(getUint8Array(a), getUint8Array(b)); + } + case 'Array': + return compareArrays(a, b); + } + } + catch (_a) { } + return NaN; +} +function compareArrays(a, b) { + const al = a.length; + const bl = b.length; + const l = al < bl ? al : bl; + for (let i = 0; i < l; ++i) { + const res = cmp(a[i], b[i]); + if (res !== 0) + return res; + } + return al === bl ? 0 : al < bl ? -1 : 1; +} +function compareUint8Arrays(a, b) { + const al = a.length; + const bl = b.length; + const l = al < bl ? al : bl; + for (let i = 0; i < l; ++i) { + if (a[i] !== b[i]) + return a[i] < b[i] ? -1 : 1; + } + return al === bl ? 0 : al < bl ? -1 : 1; +} +function type(x) { + const t = typeof x; + if (t !== 'object') + return t; + if (ArrayBuffer.isView(x)) + return 'binary'; + const tsTag = toStringTag(x); + return tsTag === 'ArrayBuffer' ? 'binary' : tsTag; +} +function getUint8Array(a) { + if (a instanceof Uint8Array) + return a; + if (ArrayBuffer.isView(a)) + return new Uint8Array(a.buffer, a.byteOffset, a.byteLength); + return new Uint8Array(a); +} - this.stopListenToKeyEvents = function () { +class Collection { + _read(fn, cb) { + var ctx = this._ctx; + return ctx.error ? + ctx.table._trans(null, rejection.bind(null, ctx.error)) : + ctx.table._trans('readonly', fn).then(cb); + } + _write(fn) { + var ctx = this._ctx; + return ctx.error ? + ctx.table._trans(null, rejection.bind(null, ctx.error)) : + ctx.table._trans('readwrite', fn, "locked"); + } + _addAlgorithm(fn) { + var ctx = this._ctx; + ctx.algorithm = combine(ctx.algorithm, fn); + } + _iterate(fn, coreTrans) { + return iter(this._ctx, fn, coreTrans, this._ctx.table.core); + } + clone(props) { + var rv = Object.create(this.constructor.prototype), ctx = Object.create(this._ctx); + if (props) + extend(ctx, props); + rv._ctx = ctx; + return rv; + } + raw() { + this._ctx.valueMapper = null; + return this; + } + each(fn) { + var ctx = this._ctx; + return this._read(trans => iter(ctx, fn, trans, ctx.table.core)); + } + count(cb) { + return this._read(trans => { + const ctx = this._ctx; + const coreTable = ctx.table.core; + if (isPlainKeyRange(ctx, true)) { + return coreTable.count({ + trans, + query: { + index: getIndexOrStore(ctx, coreTable.schema), + range: ctx.range + } + }).then(count => Math.min(count, ctx.limit)); + } + else { + var count = 0; + return iter(ctx, () => { ++count; return false; }, trans, coreTable) + .then(() => count); + } + }).then(cb); + } + sortBy(keyPath, cb) { + const parts = keyPath.split('.').reverse(), lastPart = parts[0], lastIndex = parts.length - 1; + function getval(obj, i) { + if (i) + return getval(obj[parts[i]], i - 1); + return obj[lastPart]; + } + var order = this._ctx.dir === "next" ? 1 : -1; + function sorter(a, b) { + var aVal = getval(a, lastIndex), bVal = getval(b, lastIndex); + return aVal < bVal ? -order : aVal > bVal ? order : 0; + } + return this.toArray(function (a) { + return a.sort(sorter); + }).then(cb); + } + toArray(cb) { + return this._read(trans => { + var ctx = this._ctx; + if (ctx.dir === 'next' && isPlainKeyRange(ctx, true) && ctx.limit > 0) { + const { valueMapper } = ctx; + const index = getIndexOrStore(ctx, ctx.table.core.schema); + return ctx.table.core.query({ + trans, + limit: ctx.limit, + values: true, + query: { + index, + range: ctx.range + } + }).then(({ result }) => valueMapper ? result.map(valueMapper) : result); + } + else { + const a = []; + return iter(ctx, item => a.push(item), trans, ctx.table.core).then(() => a); + } + }, cb); + } + offset(offset) { + var ctx = this._ctx; + if (offset <= 0) + return this; + ctx.offset += offset; + if (isPlainKeyRange(ctx)) { + addReplayFilter(ctx, () => { + var offsetLeft = offset; + return (cursor, advance) => { + if (offsetLeft === 0) + return true; + if (offsetLeft === 1) { + --offsetLeft; + return false; + } + advance(() => { + cursor.advance(offsetLeft); + offsetLeft = 0; + }); + return false; + }; + }); + } + else { + addReplayFilter(ctx, () => { + var offsetLeft = offset; + return () => (--offsetLeft < 0); + }); + } + return this; + } + limit(numRows) { + this._ctx.limit = Math.min(this._ctx.limit, numRows); + addReplayFilter(this._ctx, () => { + var rowsLeft = numRows; + return function (cursor, advance, resolve) { + if (--rowsLeft <= 0) + advance(resolve); + return rowsLeft >= 0; + }; + }, true); + return this; + } + until(filterFunction, bIncludeStopEntry) { + addFilter(this._ctx, function (cursor, advance, resolve) { + if (filterFunction(cursor.value)) { + advance(resolve); + return bIncludeStopEntry; + } + else { + return true; + } + }); + return this; + } + first(cb) { + return this.limit(1).toArray(function (a) { return a[0]; }).then(cb); + } + last(cb) { + return this.reverse().first(cb); + } + filter(filterFunction) { + addFilter(this._ctx, function (cursor) { + return filterFunction(cursor.value); + }); + addMatchFilter(this._ctx, filterFunction); + return this; + } + and(filter) { + return this.filter(filter); + } + or(indexName) { + return new this.db.WhereClause(this._ctx.table, indexName, this); + } + reverse() { + this._ctx.dir = (this._ctx.dir === "prev" ? "next" : "prev"); + if (this._ondirectionchange) + this._ondirectionchange(this._ctx.dir); + return this; + } + desc() { + return this.reverse(); + } + eachKey(cb) { + var ctx = this._ctx; + ctx.keysOnly = !ctx.isMatch; + return this.each(function (val, cursor) { cb(cursor.key, cursor); }); + } + eachUniqueKey(cb) { + this._ctx.unique = "unique"; + return this.eachKey(cb); + } + eachPrimaryKey(cb) { + var ctx = this._ctx; + ctx.keysOnly = !ctx.isMatch; + return this.each(function (val, cursor) { cb(cursor.primaryKey, cursor); }); + } + keys(cb) { + var ctx = this._ctx; + ctx.keysOnly = !ctx.isMatch; + var a = []; + return this.each(function (item, cursor) { + a.push(cursor.key); + }).then(function () { + return a; + }).then(cb); + } + primaryKeys(cb) { + var ctx = this._ctx; + if (ctx.dir === 'next' && isPlainKeyRange(ctx, true) && ctx.limit > 0) { + return this._read(trans => { + var index = getIndexOrStore(ctx, ctx.table.core.schema); + return ctx.table.core.query({ + trans, + values: false, + limit: ctx.limit, + query: { + index, + range: ctx.range + } + }); + }).then(({ result }) => result).then(cb); + } + ctx.keysOnly = !ctx.isMatch; + var a = []; + return this.each(function (item, cursor) { + a.push(cursor.primaryKey); + }).then(function () { + return a; + }).then(cb); + } + uniqueKeys(cb) { + this._ctx.unique = "unique"; + return this.keys(cb); + } + firstKey(cb) { + return this.limit(1).keys(function (a) { return a[0]; }).then(cb); + } + lastKey(cb) { + return this.reverse().firstKey(cb); + } + distinct() { + var ctx = this._ctx, idx = ctx.index && ctx.table.schema.idxByName[ctx.index]; + if (!idx || !idx.multi) + return this; + var set = {}; + addFilter(this._ctx, function (cursor) { + var strKey = cursor.primaryKey.toString(); + var found = hasOwn(set, strKey); + set[strKey] = true; + return !found; + }); + return this; + } + modify(changes) { + var ctx = this._ctx; + return this._write(trans => { + var modifyer; + if (typeof changes === 'function') { + modifyer = changes; + } + else { + var keyPaths = keys(changes); + var numKeys = keyPaths.length; + modifyer = function (item) { + var anythingModified = false; + for (var i = 0; i < numKeys; ++i) { + var keyPath = keyPaths[i], val = changes[keyPath]; + if (getByKeyPath(item, keyPath) !== val) { + setByKeyPath(item, keyPath, val); + anythingModified = true; + } + } + return anythingModified; + }; + } + const coreTable = ctx.table.core; + const { outbound, extractKey } = coreTable.schema.primaryKey; + const limit = this.db._options.modifyChunkSize || 200; + const totalFailures = []; + let successCount = 0; + const failedKeys = []; + const applyMutateResult = (expectedCount, res) => { + const { failures, numFailures } = res; + successCount += expectedCount - numFailures; + for (let pos of keys(failures)) { + totalFailures.push(failures[pos]); + } + }; + return this.clone().primaryKeys().then(keys => { + const nextChunk = (offset) => { + const count = Math.min(limit, keys.length - offset); + return coreTable.getMany({ + trans, + keys: keys.slice(offset, offset + count), + cache: "immutable" + }).then(values => { + const addValues = []; + const putValues = []; + const putKeys = outbound ? [] : null; + const deleteKeys = []; + for (let i = 0; i < count; ++i) { + const origValue = values[i]; + const ctx = { + value: deepClone(origValue), + primKey: keys[offset + i] + }; + if (modifyer.call(ctx, ctx.value, ctx) !== false) { + if (ctx.value == null) { + deleteKeys.push(keys[offset + i]); + } + else if (!outbound && cmp(extractKey(origValue), extractKey(ctx.value)) !== 0) { + deleteKeys.push(keys[offset + i]); + addValues.push(ctx.value); + } + else { + putValues.push(ctx.value); + if (outbound) + putKeys.push(keys[offset + i]); + } + } + } + const criteria = isPlainKeyRange(ctx) && + ctx.limit === Infinity && + (typeof changes !== 'function' || changes === deleteCallback) && { + index: ctx.index, + range: ctx.range + }; + return Promise.resolve(addValues.length > 0 && + coreTable.mutate({ trans, type: 'add', values: addValues }) + .then(res => { + for (let pos in res.failures) { + deleteKeys.splice(parseInt(pos), 1); + } + applyMutateResult(addValues.length, res); + })).then(() => (putValues.length > 0 || (criteria && typeof changes === 'object')) && + coreTable.mutate({ + trans, + type: 'put', + keys: putKeys, + values: putValues, + criteria, + changeSpec: typeof changes !== 'function' + && changes + }).then(res => applyMutateResult(putValues.length, res))).then(() => (deleteKeys.length > 0 || (criteria && changes === deleteCallback)) && + coreTable.mutate({ + trans, + type: 'delete', + keys: deleteKeys, + criteria + }).then(res => applyMutateResult(deleteKeys.length, res))).then(() => { + return keys.length > offset + count && nextChunk(offset + limit); + }); + }); + }; + return nextChunk(0).then(() => { + if (totalFailures.length > 0) + throw new ModifyError("Error modifying one or more objects", totalFailures, successCount, failedKeys); + return keys.length; + }); + }); + }); + } + delete() { + var ctx = this._ctx, range = ctx.range; + if (isPlainKeyRange(ctx) && + ((ctx.isPrimKey && !hangsOnDeleteLargeKeyRange) || range.type === 3 )) + { + return this._write(trans => { + const { primaryKey } = ctx.table.core.schema; + const coreRange = range; + return ctx.table.core.count({ trans, query: { index: primaryKey, range: coreRange } }).then(count => { + return ctx.table.core.mutate({ trans, type: 'deleteRange', range: coreRange }) + .then(({ failures, lastResult, results, numFailures }) => { + if (numFailures) + throw new ModifyError("Could not delete some values", Object.keys(failures).map(pos => failures[pos]), count - numFailures); + return count - numFailures; + }); + }); + }); + } + return this.modify(deleteCallback); + } +} +const deleteCallback = (value, ctx) => ctx.value = null; - this._domElementKeyEvents.removeEventListener( 'keydown', onKeyDown ); - this._domElementKeyEvents = null; +function createCollectionConstructor(db) { + return makeClassConstructor(Collection.prototype, function Collection(whereClause, keyRangeGenerator) { + this.db = db; + let keyRange = AnyRange, error = null; + if (keyRangeGenerator) + try { + keyRange = keyRangeGenerator(); + } + catch (ex) { + error = ex; + } + const whereCtx = whereClause._ctx; + const table = whereCtx.table; + const readingHook = table.hook.reading.fire; + this._ctx = { + table: table, + index: whereCtx.index, + isPrimKey: (!whereCtx.index || (table.schema.primKey.keyPath && whereCtx.index === table.schema.primKey.name)), + range: keyRange, + keysOnly: false, + dir: "next", + unique: "", + algorithm: null, + filter: null, + replayFilter: null, + justLimit: true, + isMatch: null, + offset: 0, + limit: Infinity, + error: error, + or: whereCtx.or, + valueMapper: readingHook !== mirror ? readingHook : null + }; + }); +} - }; +function simpleCompare(a, b) { + return a < b ? -1 : a === b ? 0 : 1; +} +function simpleCompareReverse(a, b) { + return a > b ? -1 : a === b ? 0 : 1; +} - this.saveState = function () { - - scope.target0.copy( scope.target ); - scope.position0.copy( scope.object.position ); - scope.zoom0 = scope.object.zoom; - - }; - - this.reset = function () { - - scope.target.copy( scope.target0 ); - scope.object.position.copy( scope.position0 ); - scope.object.zoom = scope.zoom0; - - scope.object.updateProjectionMatrix(); - scope.dispatchEvent( _changeEvent ); - - scope.update(); - - state = STATE.NONE; - - }; - - // this method is exposed, but perhaps it would be better if we can make it private... - this.update = function () { - - const offset = new Vector3$1(); - - // so camera.up is the orbit axis - const quat = new Quaternion$1().setFromUnitVectors( object.up, new Vector3$1( 0, 1, 0 ) ); - const quatInverse = quat.clone().invert(); - - const lastPosition = new Vector3$1(); - const lastQuaternion = new Quaternion$1(); - - const twoPI = 2 * Math.PI; - - return function update() { - - const position = scope.object.position; - - offset.copy( position ).sub( scope.target ); - - // rotate offset to "y-axis-is-up" space - offset.applyQuaternion( quat ); - - // angle from z-axis around y-axis - spherical.setFromVector3( offset ); - - if ( scope.autoRotate && state === STATE.NONE ) { - - rotateLeft( getAutoRotationAngle() ); - - } - - if ( scope.enableDamping ) { - - spherical.theta += sphericalDelta.theta * scope.dampingFactor; - spherical.phi += sphericalDelta.phi * scope.dampingFactor; - - } else { - - spherical.theta += sphericalDelta.theta; - spherical.phi += sphericalDelta.phi; - - } - - // restrict theta to be between desired limits - - let min = scope.minAzimuthAngle; - let max = scope.maxAzimuthAngle; - - if ( isFinite( min ) && isFinite( max ) ) { - - if ( min < - Math.PI ) min += twoPI; else if ( min > Math.PI ) min -= twoPI; - - if ( max < - Math.PI ) max += twoPI; else if ( max > Math.PI ) max -= twoPI; - - if ( min <= max ) { - - spherical.theta = Math.max( min, Math.min( max, spherical.theta ) ); - - } else { - - spherical.theta = ( spherical.theta > ( min + max ) / 2 ) ? - Math.max( min, spherical.theta ) : - Math.min( max, spherical.theta ); - - } - - } - - // restrict phi to be between desired limits - spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) ); - - spherical.makeSafe(); - - - spherical.radius *= scale; - - // restrict radius to be between desired limits - spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) ); - - // move target to panned location - - if ( scope.enableDamping === true ) { - - scope.target.addScaledVector( panOffset, scope.dampingFactor ); - - } else { - - scope.target.add( panOffset ); - - } - - offset.setFromSpherical( spherical ); - - // rotate offset back to "camera-up-vector-is-up" space - offset.applyQuaternion( quatInverse ); - - position.copy( scope.target ).add( offset ); - - scope.object.lookAt( scope.target ); - - if ( scope.enableDamping === true ) { - - sphericalDelta.theta *= ( 1 - scope.dampingFactor ); - sphericalDelta.phi *= ( 1 - scope.dampingFactor ); - - panOffset.multiplyScalar( 1 - scope.dampingFactor ); - - } else { - - sphericalDelta.set( 0, 0, 0 ); - - panOffset.set( 0, 0, 0 ); - - } +function fail(collectionOrWhereClause, err, T) { + var collection = collectionOrWhereClause instanceof WhereClause ? + new collectionOrWhereClause.Collection(collectionOrWhereClause) : + collectionOrWhereClause; + collection._ctx.error = T ? new T(err) : new TypeError(err); + return collection; +} +function emptyCollection(whereClause) { + return new whereClause.Collection(whereClause, () => rangeEqual("")).limit(0); +} +function upperFactory(dir) { + return dir === "next" ? + (s) => s.toUpperCase() : + (s) => s.toLowerCase(); +} +function lowerFactory(dir) { + return dir === "next" ? + (s) => s.toLowerCase() : + (s) => s.toUpperCase(); +} +function nextCasing(key, lowerKey, upperNeedle, lowerNeedle, cmp, dir) { + var length = Math.min(key.length, lowerNeedle.length); + var llp = -1; + for (var i = 0; i < length; ++i) { + var lwrKeyChar = lowerKey[i]; + if (lwrKeyChar !== lowerNeedle[i]) { + if (cmp(key[i], upperNeedle[i]) < 0) + return key.substr(0, i) + upperNeedle[i] + upperNeedle.substr(i + 1); + if (cmp(key[i], lowerNeedle[i]) < 0) + return key.substr(0, i) + lowerNeedle[i] + upperNeedle.substr(i + 1); + if (llp >= 0) + return key.substr(0, llp) + lowerKey[llp] + upperNeedle.substr(llp + 1); + return null; + } + if (cmp(key[i], lwrKeyChar) < 0) + llp = i; + } + if (length < lowerNeedle.length && dir === "next") + return key + upperNeedle.substr(key.length); + if (length < key.length && dir === "prev") + return key.substr(0, upperNeedle.length); + return (llp < 0 ? null : key.substr(0, llp) + lowerNeedle[llp] + upperNeedle.substr(llp + 1)); +} +function addIgnoreCaseAlgorithm(whereClause, match, needles, suffix) { + var upper, lower, compare, upperNeedles, lowerNeedles, direction, nextKeySuffix, needlesLen = needles.length; + if (!needles.every(s => typeof s === 'string')) { + return fail(whereClause, STRING_EXPECTED); + } + function initDirection(dir) { + upper = upperFactory(dir); + lower = lowerFactory(dir); + compare = (dir === "next" ? simpleCompare : simpleCompareReverse); + var needleBounds = needles.map(function (needle) { + return { lower: lower(needle), upper: upper(needle) }; + }).sort(function (a, b) { + return compare(a.lower, b.lower); + }); + upperNeedles = needleBounds.map(function (nb) { return nb.upper; }); + lowerNeedles = needleBounds.map(function (nb) { return nb.lower; }); + direction = dir; + nextKeySuffix = (dir === "next" ? "" : suffix); + } + initDirection("next"); + var c = new whereClause.Collection(whereClause, () => createRange(upperNeedles[0], lowerNeedles[needlesLen - 1] + suffix)); + c._ondirectionchange = function (direction) { + initDirection(direction); + }; + var firstPossibleNeedle = 0; + c._addAlgorithm(function (cursor, advance, resolve) { + var key = cursor.key; + if (typeof key !== 'string') + return false; + var lowerKey = lower(key); + if (match(lowerKey, lowerNeedles, firstPossibleNeedle)) { + return true; + } + else { + var lowestPossibleCasing = null; + for (var i = firstPossibleNeedle; i < needlesLen; ++i) { + var casing = nextCasing(key, lowerKey, upperNeedles[i], lowerNeedles[i], compare, direction); + if (casing === null && lowestPossibleCasing === null) + firstPossibleNeedle = i + 1; + else if (lowestPossibleCasing === null || compare(lowestPossibleCasing, casing) > 0) { + lowestPossibleCasing = casing; + } + } + if (lowestPossibleCasing !== null) { + advance(function () { cursor.continue(lowestPossibleCasing + nextKeySuffix); }); + } + else { + advance(resolve); + } + return false; + } + }); + return c; +} +function createRange(lower, upper, lowerOpen, upperOpen) { + return { + type: 2 , + lower, + upper, + lowerOpen, + upperOpen + }; +} +function rangeEqual(value) { + return { + type: 1 , + lower: value, + upper: value + }; +} - scale = 1; +class WhereClause { + get Collection() { + return this._ctx.table.db.Collection; + } + between(lower, upper, includeLower, includeUpper) { + includeLower = includeLower !== false; + includeUpper = includeUpper === true; + try { + if ((this._cmp(lower, upper) > 0) || + (this._cmp(lower, upper) === 0 && (includeLower || includeUpper) && !(includeLower && includeUpper))) + return emptyCollection(this); + return new this.Collection(this, () => createRange(lower, upper, !includeLower, !includeUpper)); + } + catch (e) { + return fail(this, INVALID_KEY_ARGUMENT); + } + } + equals(value) { + if (value == null) + return fail(this, INVALID_KEY_ARGUMENT); + return new this.Collection(this, () => rangeEqual(value)); + } + above(value) { + if (value == null) + return fail(this, INVALID_KEY_ARGUMENT); + return new this.Collection(this, () => createRange(value, undefined, true)); + } + aboveOrEqual(value) { + if (value == null) + return fail(this, INVALID_KEY_ARGUMENT); + return new this.Collection(this, () => createRange(value, undefined, false)); + } + below(value) { + if (value == null) + return fail(this, INVALID_KEY_ARGUMENT); + return new this.Collection(this, () => createRange(undefined, value, false, true)); + } + belowOrEqual(value) { + if (value == null) + return fail(this, INVALID_KEY_ARGUMENT); + return new this.Collection(this, () => createRange(undefined, value)); + } + startsWith(str) { + if (typeof str !== 'string') + return fail(this, STRING_EXPECTED); + return this.between(str, str + maxString, true, true); + } + startsWithIgnoreCase(str) { + if (str === "") + return this.startsWith(str); + return addIgnoreCaseAlgorithm(this, (x, a) => x.indexOf(a[0]) === 0, [str], maxString); + } + equalsIgnoreCase(str) { + return addIgnoreCaseAlgorithm(this, (x, a) => x === a[0], [str], ""); + } + anyOfIgnoreCase() { + var set = getArrayOf.apply(NO_CHAR_ARRAY, arguments); + if (set.length === 0) + return emptyCollection(this); + return addIgnoreCaseAlgorithm(this, (x, a) => a.indexOf(x) !== -1, set, ""); + } + startsWithAnyOfIgnoreCase() { + var set = getArrayOf.apply(NO_CHAR_ARRAY, arguments); + if (set.length === 0) + return emptyCollection(this); + return addIgnoreCaseAlgorithm(this, (x, a) => a.some(n => x.indexOf(n) === 0), set, maxString); + } + anyOf() { + const set = getArrayOf.apply(NO_CHAR_ARRAY, arguments); + let compare = this._cmp; + try { + set.sort(compare); + } + catch (e) { + return fail(this, INVALID_KEY_ARGUMENT); + } + if (set.length === 0) + return emptyCollection(this); + const c = new this.Collection(this, () => createRange(set[0], set[set.length - 1])); + c._ondirectionchange = direction => { + compare = (direction === "next" ? + this._ascending : + this._descending); + set.sort(compare); + }; + let i = 0; + c._addAlgorithm((cursor, advance, resolve) => { + const key = cursor.key; + while (compare(key, set[i]) > 0) { + ++i; + if (i === set.length) { + advance(resolve); + return false; + } + } + if (compare(key, set[i]) === 0) { + return true; + } + else { + advance(() => { cursor.continue(set[i]); }); + return false; + } + }); + return c; + } + notEqual(value) { + return this.inAnyRange([[minKey, value], [value, this.db._maxKey]], { includeLowers: false, includeUppers: false }); + } + noneOf() { + const set = getArrayOf.apply(NO_CHAR_ARRAY, arguments); + if (set.length === 0) + return new this.Collection(this); + try { + set.sort(this._ascending); + } + catch (e) { + return fail(this, INVALID_KEY_ARGUMENT); + } + const ranges = set.reduce((res, val) => res ? + res.concat([[res[res.length - 1][1], val]]) : + [[minKey, val]], null); + ranges.push([set[set.length - 1], this.db._maxKey]); + return this.inAnyRange(ranges, { includeLowers: false, includeUppers: false }); + } + inAnyRange(ranges, options) { + const cmp = this._cmp, ascending = this._ascending, descending = this._descending, min = this._min, max = this._max; + if (ranges.length === 0) + return emptyCollection(this); + if (!ranges.every(range => range[0] !== undefined && + range[1] !== undefined && + ascending(range[0], range[1]) <= 0)) { + return fail(this, "First argument to inAnyRange() must be an Array of two-value Arrays [lower,upper] where upper must not be lower than lower", exceptions.InvalidArgument); + } + const includeLowers = !options || options.includeLowers !== false; + const includeUppers = options && options.includeUppers === true; + function addRange(ranges, newRange) { + let i = 0, l = ranges.length; + for (; i < l; ++i) { + const range = ranges[i]; + if (cmp(newRange[0], range[1]) < 0 && cmp(newRange[1], range[0]) > 0) { + range[0] = min(range[0], newRange[0]); + range[1] = max(range[1], newRange[1]); + break; + } + } + if (i === l) + ranges.push(newRange); + return ranges; + } + let sortDirection = ascending; + function rangeSorter(a, b) { return sortDirection(a[0], b[0]); } + let set; + try { + set = ranges.reduce(addRange, []); + set.sort(rangeSorter); + } + catch (ex) { + return fail(this, INVALID_KEY_ARGUMENT); + } + let rangePos = 0; + const keyIsBeyondCurrentEntry = includeUppers ? + key => ascending(key, set[rangePos][1]) > 0 : + key => ascending(key, set[rangePos][1]) >= 0; + const keyIsBeforeCurrentEntry = includeLowers ? + key => descending(key, set[rangePos][0]) > 0 : + key => descending(key, set[rangePos][0]) >= 0; + function keyWithinCurrentRange(key) { + return !keyIsBeyondCurrentEntry(key) && !keyIsBeforeCurrentEntry(key); + } + let checkKey = keyIsBeyondCurrentEntry; + const c = new this.Collection(this, () => createRange(set[0][0], set[set.length - 1][1], !includeLowers, !includeUppers)); + c._ondirectionchange = direction => { + if (direction === "next") { + checkKey = keyIsBeyondCurrentEntry; + sortDirection = ascending; + } + else { + checkKey = keyIsBeforeCurrentEntry; + sortDirection = descending; + } + set.sort(rangeSorter); + }; + c._addAlgorithm((cursor, advance, resolve) => { + var key = cursor.key; + while (checkKey(key)) { + ++rangePos; + if (rangePos === set.length) { + advance(resolve); + return false; + } + } + if (keyWithinCurrentRange(key)) { + return true; + } + else if (this._cmp(key, set[rangePos][1]) === 0 || this._cmp(key, set[rangePos][0]) === 0) { + return false; + } + else { + advance(() => { + if (sortDirection === ascending) + cursor.continue(set[rangePos][0]); + else + cursor.continue(set[rangePos][1]); + }); + return false; + } + }); + return c; + } + startsWithAnyOf() { + const set = getArrayOf.apply(NO_CHAR_ARRAY, arguments); + if (!set.every(s => typeof s === 'string')) { + return fail(this, "startsWithAnyOf() only works with strings"); + } + if (set.length === 0) + return emptyCollection(this); + return this.inAnyRange(set.map((str) => [str, str + maxString])); + } +} - // update condition is: - // min(camera displacement, camera rotation in radians)^2 > EPS - // using small-angle approximation cos(x/2) = 1 - x^2 / 8 +function createWhereClauseConstructor(db) { + return makeClassConstructor(WhereClause.prototype, function WhereClause(table, index, orCollection) { + this.db = db; + this._ctx = { + table: table, + index: index === ":id" ? null : index, + or: orCollection + }; + const indexedDB = db._deps.indexedDB; + if (!indexedDB) + throw new exceptions.MissingAPI(); + this._cmp = this._ascending = indexedDB.cmp.bind(indexedDB); + this._descending = (a, b) => indexedDB.cmp(b, a); + this._max = (a, b) => indexedDB.cmp(a, b) > 0 ? a : b; + this._min = (a, b) => indexedDB.cmp(a, b) < 0 ? a : b; + this._IDBKeyRange = db._deps.IDBKeyRange; + }); +} - if ( zoomChanged || - lastPosition.distanceToSquared( scope.object.position ) > EPS || - 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) { +function eventRejectHandler(reject) { + return wrap(function (event) { + preventDefault(event); + reject(event.target.error); + return false; + }); +} +function preventDefault(event) { + if (event.stopPropagation) + event.stopPropagation(); + if (event.preventDefault) + event.preventDefault(); +} - scope.dispatchEvent( _changeEvent ); +const DEXIE_STORAGE_MUTATED_EVENT_NAME = 'storagemutated'; +const STORAGE_MUTATED_DOM_EVENT_NAME = 'x-storagemutated-1'; +const globalEvents = Events(null, DEXIE_STORAGE_MUTATED_EVENT_NAME); - lastPosition.copy( scope.object.position ); - lastQuaternion.copy( scope.object.quaternion ); - zoomChanged = false; - - return true; - - } - - return false; - - }; - - }(); - - this.dispose = function () { - - scope.domElement.removeEventListener( 'contextmenu', onContextMenu ); - - scope.domElement.removeEventListener( 'pointerdown', onPointerDown ); - scope.domElement.removeEventListener( 'pointercancel', onPointerUp ); - scope.domElement.removeEventListener( 'wheel', onMouseWheel ); - - scope.domElement.removeEventListener( 'pointermove', onPointerMove ); - scope.domElement.removeEventListener( 'pointerup', onPointerUp ); - - - if ( scope._domElementKeyEvents !== null ) { - - scope._domElementKeyEvents.removeEventListener( 'keydown', onKeyDown ); - scope._domElementKeyEvents = null; - - } - - //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here? - - }; - - // - // internals - // - - const scope = this; - - const STATE = { - NONE: - 1, - ROTATE: 0, - DOLLY: 1, - PAN: 2, - TOUCH_ROTATE: 3, - TOUCH_PAN: 4, - TOUCH_DOLLY_PAN: 5, - TOUCH_DOLLY_ROTATE: 6 - }; - - let state = STATE.NONE; - - const EPS = 0.000001; - - // current position in spherical coordinates - const spherical = new Spherical(); - const sphericalDelta = new Spherical(); - - let scale = 1; - const panOffset = new Vector3$1(); - let zoomChanged = false; - - const rotateStart = new Vector2$1(); - const rotateEnd = new Vector2$1(); - const rotateDelta = new Vector2$1(); - - const panStart = new Vector2$1(); - const panEnd = new Vector2$1(); - const panDelta = new Vector2$1(); - - const dollyStart = new Vector2$1(); - const dollyEnd = new Vector2$1(); - const dollyDelta = new Vector2$1(); - - const pointers = []; - const pointerPositions = {}; - - function getAutoRotationAngle() { - - return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; - - } - - function getZoomScale() { - - return Math.pow( 0.95, scope.zoomSpeed ); - - } - - function rotateLeft( angle ) { - - sphericalDelta.theta -= angle; - - } - - function rotateUp( angle ) { - - sphericalDelta.phi -= angle; - - } - - const panLeft = function () { - - const v = new Vector3$1(); - - return function panLeft( distance, objectMatrix ) { - - v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix - v.multiplyScalar( - distance ); - - panOffset.add( v ); - - }; - - }(); - - const panUp = function () { - - const v = new Vector3$1(); - - return function panUp( distance, objectMatrix ) { - - if ( scope.screenSpacePanning === true ) { - - v.setFromMatrixColumn( objectMatrix, 1 ); - - } else { - - v.setFromMatrixColumn( objectMatrix, 0 ); - v.crossVectors( scope.object.up, v ); - - } - - v.multiplyScalar( distance ); - - panOffset.add( v ); - - }; - - }(); - - // deltaX and deltaY are in pixels; right and down are positive - const pan = function () { - - const offset = new Vector3$1(); - - return function pan( deltaX, deltaY ) { - - const element = scope.domElement; - - if ( scope.object.isPerspectiveCamera ) { - - // perspective - const position = scope.object.position; - offset.copy( position ).sub( scope.target ); - let targetDistance = offset.length(); - - // half of the fov is center to top of screen - targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 ); - - // we use only clientHeight here so aspect ratio does not distort speed - panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix ); - panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix ); - - } else if ( scope.object.isOrthographicCamera ) { - - // orthographic - panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix ); - panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix ); - - } else { - - // camera neither orthographic nor perspective - console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); - scope.enablePan = false; - - } - - }; - - }(); - - function dollyOut( dollyScale ) { - - if ( scope.object.isPerspectiveCamera ) { - - scale /= dollyScale; - - } else if ( scope.object.isOrthographicCamera ) { - - scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) ); - scope.object.updateProjectionMatrix(); - zoomChanged = true; - - } else { - - console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); - scope.enableZoom = false; - - } - - } - - function dollyIn( dollyScale ) { - - if ( scope.object.isPerspectiveCamera ) { - - scale *= dollyScale; - - } else if ( scope.object.isOrthographicCamera ) { - - scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) ); - scope.object.updateProjectionMatrix(); - zoomChanged = true; - - } else { - - console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); - scope.enableZoom = false; - - } - - } - - // - // event callbacks - update the object state - // - - function handleMouseDownRotate( event ) { - - rotateStart.set( event.clientX, event.clientY ); - - } - - function handleMouseDownDolly( event ) { - - dollyStart.set( event.clientX, event.clientY ); - - } - - function handleMouseDownPan( event ) { - - panStart.set( event.clientX, event.clientY ); - - } - - function handleMouseMoveRotate( event ) { - - rotateEnd.set( event.clientX, event.clientY ); - - rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed ); - - const element = scope.domElement; +class Transaction { + _lock() { + assert(!PSD.global); + ++this._reculock; + if (this._reculock === 1 && !PSD.global) + PSD.lockOwnerFor = this; + return this; + } + _unlock() { + assert(!PSD.global); + if (--this._reculock === 0) { + if (!PSD.global) + PSD.lockOwnerFor = null; + while (this._blockedFuncs.length > 0 && !this._locked()) { + var fnAndPSD = this._blockedFuncs.shift(); + try { + usePSD(fnAndPSD[1], fnAndPSD[0]); + } + catch (e) { } + } + } + return this; + } + _locked() { + return this._reculock && PSD.lockOwnerFor !== this; + } + create(idbtrans) { + if (!this.mode) + return this; + const idbdb = this.db.idbdb; + const dbOpenError = this.db._state.dbOpenError; + assert(!this.idbtrans); + if (!idbtrans && !idbdb) { + switch (dbOpenError && dbOpenError.name) { + case "DatabaseClosedError": + throw new exceptions.DatabaseClosed(dbOpenError); + case "MissingAPIError": + throw new exceptions.MissingAPI(dbOpenError.message, dbOpenError); + default: + throw new exceptions.OpenFailed(dbOpenError); + } + } + if (!this.active) + throw new exceptions.TransactionInactive(); + assert(this._completion._state === null); + idbtrans = this.idbtrans = idbtrans || + (this.db.core + ? this.db.core.transaction(this.storeNames, this.mode, { durability: this.chromeTransactionDurability }) + : idbdb.transaction(this.storeNames, this.mode, { durability: this.chromeTransactionDurability })); + idbtrans.onerror = wrap(ev => { + preventDefault(ev); + this._reject(idbtrans.error); + }); + idbtrans.onabort = wrap(ev => { + preventDefault(ev); + this.active && this._reject(new exceptions.Abort(idbtrans.error)); + this.active = false; + this.on("abort").fire(ev); + }); + idbtrans.oncomplete = wrap(() => { + this.active = false; + this._resolve(); + if ('mutatedParts' in idbtrans) { + globalEvents.storagemutated.fire(idbtrans["mutatedParts"]); + } + }); + return this; + } + _promise(mode, fn, bWriteLock) { + if (mode === 'readwrite' && this.mode !== 'readwrite') + return rejection(new exceptions.ReadOnly("Transaction is readonly")); + if (!this.active) + return rejection(new exceptions.TransactionInactive()); + if (this._locked()) { + return new DexiePromise((resolve, reject) => { + this._blockedFuncs.push([() => { + this._promise(mode, fn, bWriteLock).then(resolve, reject); + }, PSD]); + }); + } + else if (bWriteLock) { + return newScope(() => { + var p = new DexiePromise((resolve, reject) => { + this._lock(); + const rv = fn(resolve, reject, this); + if (rv && rv.then) + rv.then(resolve, reject); + }); + p.finally(() => this._unlock()); + p._lib = true; + return p; + }); + } + else { + var p = new DexiePromise((resolve, reject) => { + var rv = fn(resolve, reject, this); + if (rv && rv.then) + rv.then(resolve, reject); + }); + p._lib = true; + return p; + } + } + _root() { + return this.parent ? this.parent._root() : this; + } + waitFor(promiseLike) { + var root = this._root(); + const promise = DexiePromise.resolve(promiseLike); + if (root._waitingFor) { + root._waitingFor = root._waitingFor.then(() => promise); + } + else { + root._waitingFor = promise; + root._waitingQueue = []; + var store = root.idbtrans.objectStore(root.storeNames[0]); + (function spin() { + ++root._spinCount; + while (root._waitingQueue.length) + (root._waitingQueue.shift())(); + if (root._waitingFor) + store.get(-Infinity).onsuccess = spin; + }()); + } + var currentWaitPromise = root._waitingFor; + return new DexiePromise((resolve, reject) => { + promise.then(res => root._waitingQueue.push(wrap(resolve.bind(null, res))), err => root._waitingQueue.push(wrap(reject.bind(null, err)))).finally(() => { + if (root._waitingFor === currentWaitPromise) { + root._waitingFor = null; + } + }); + }); + } + abort() { + if (this.active) { + this.active = false; + if (this.idbtrans) + this.idbtrans.abort(); + this._reject(new exceptions.Abort()); + } + } + table(tableName) { + const memoizedTables = (this._memoizedTables || (this._memoizedTables = {})); + if (hasOwn(memoizedTables, tableName)) + return memoizedTables[tableName]; + const tableSchema = this.schema[tableName]; + if (!tableSchema) { + throw new exceptions.NotFound("Table " + tableName + " not part of transaction"); + } + const transactionBoundTable = new this.db.Table(tableName, tableSchema, this); + transactionBoundTable.core = this.db.core.table(tableName); + memoizedTables[tableName] = transactionBoundTable; + return transactionBoundTable; + } +} - rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height +function createTransactionConstructor(db) { + return makeClassConstructor(Transaction.prototype, function Transaction(mode, storeNames, dbschema, chromeTransactionDurability, parent) { + this.db = db; + this.mode = mode; + this.storeNames = storeNames; + this.schema = dbschema; + this.chromeTransactionDurability = chromeTransactionDurability; + this.idbtrans = null; + this.on = Events(this, "complete", "error", "abort"); + this.parent = parent || null; + this.active = true; + this._reculock = 0; + this._blockedFuncs = []; + this._resolve = null; + this._reject = null; + this._waitingFor = null; + this._waitingQueue = null; + this._spinCount = 0; + this._completion = new DexiePromise((resolve, reject) => { + this._resolve = resolve; + this._reject = reject; + }); + this._completion.then(() => { + this.active = false; + this.on.complete.fire(); + }, e => { + var wasActive = this.active; + this.active = false; + this.on.error.fire(e); + this.parent ? + this.parent._reject(e) : + wasActive && this.idbtrans && this.idbtrans.abort(); + return rejection(e); + }); + }); +} - rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight ); +function createIndexSpec(name, keyPath, unique, multi, auto, compound, isPrimKey) { + return { + name, + keyPath, + unique, + multi, + auto, + compound, + src: (unique && !isPrimKey ? '&' : '') + (multi ? '*' : '') + (auto ? "++" : "") + nameFromKeyPath(keyPath) + }; +} +function nameFromKeyPath(keyPath) { + return typeof keyPath === 'string' ? + keyPath : + keyPath ? ('[' + [].join.call(keyPath, '+') + ']') : ""; +} - rotateStart.copy( rotateEnd ); +function createTableSchema(name, primKey, indexes) { + return { + name, + primKey, + indexes, + mappedClass: null, + idxByName: arrayToObject(indexes, index => [index.name, index]) + }; +} - scope.update(); +function safariMultiStoreFix(storeNames) { + return storeNames.length === 1 ? storeNames[0] : storeNames; +} +let getMaxKey = (IdbKeyRange) => { + try { + IdbKeyRange.only([[]]); + getMaxKey = () => [[]]; + return [[]]; + } + catch (e) { + getMaxKey = () => maxString; + return maxString; + } +}; - } +function getKeyExtractor(keyPath) { + if (keyPath == null) { + return () => undefined; + } + else if (typeof keyPath === 'string') { + return getSinglePathKeyExtractor(keyPath); + } + else { + return obj => getByKeyPath(obj, keyPath); + } +} +function getSinglePathKeyExtractor(keyPath) { + const split = keyPath.split('.'); + if (split.length === 1) { + return obj => obj[keyPath]; + } + else { + return obj => getByKeyPath(obj, keyPath); + } +} - function handleMouseMoveDolly( event ) { - - dollyEnd.set( event.clientX, event.clientY ); - - dollyDelta.subVectors( dollyEnd, dollyStart ); - - if ( dollyDelta.y > 0 ) { - - dollyOut( getZoomScale() ); - - } else if ( dollyDelta.y < 0 ) { - - dollyIn( getZoomScale() ); - - } - - dollyStart.copy( dollyEnd ); - - scope.update(); - - } - - function handleMouseMovePan( event ) { - - panEnd.set( event.clientX, event.clientY ); - - panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed ); - - pan( panDelta.x, panDelta.y ); - - panStart.copy( panEnd ); - - scope.update(); - - } - - function handleMouseWheel( event ) { - - if ( event.deltaY < 0 ) { - - dollyIn( getZoomScale() ); - - } else if ( event.deltaY > 0 ) { - - dollyOut( getZoomScale() ); - - } - - scope.update(); - - } - - function handleKeyDown( event ) { - - let needsUpdate = false; - - switch ( event.code ) { - - case scope.keys.UP: - - if ( event.ctrlKey || event.metaKey || event.shiftKey ) { - - rotateUp( 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight ); - - } else { - - pan( 0, scope.keyPanSpeed ); - - } - - needsUpdate = true; - break; - - case scope.keys.BOTTOM: - - if ( event.ctrlKey || event.metaKey || event.shiftKey ) { - - rotateUp( - 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight ); - - } else { - - pan( 0, - scope.keyPanSpeed ); - - } - - needsUpdate = true; - break; - - case scope.keys.LEFT: - - if ( event.ctrlKey || event.metaKey || event.shiftKey ) { - - rotateLeft( 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight ); - - } else { - - pan( scope.keyPanSpeed, 0 ); - - } - - needsUpdate = true; - break; - - case scope.keys.RIGHT: - - if ( event.ctrlKey || event.metaKey || event.shiftKey ) { - - rotateLeft( - 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight ); - - } else { - - pan( - scope.keyPanSpeed, 0 ); - - } - - needsUpdate = true; - break; - - } - - if ( needsUpdate ) { - - // prevent the browser from scrolling on cursor keys - event.preventDefault(); - - scope.update(); - - } - - - } - - function handleTouchStartRotate() { - - if ( pointers.length === 1 ) { - - rotateStart.set( pointers[ 0 ].pageX, pointers[ 0 ].pageY ); - - } else { - - const x = 0.5 * ( pointers[ 0 ].pageX + pointers[ 1 ].pageX ); - const y = 0.5 * ( pointers[ 0 ].pageY + pointers[ 1 ].pageY ); +function arrayify(arrayLike) { + return [].slice.call(arrayLike); +} +let _id_counter = 0; +function getKeyPathAlias(keyPath) { + return keyPath == null ? + ":id" : + typeof keyPath === 'string' ? + keyPath : + `[${keyPath.join('+')}]`; +} +function createDBCore(db, IdbKeyRange, tmpTrans) { + function extractSchema(db, trans) { + const tables = arrayify(db.objectStoreNames); + return { + schema: { + name: db.name, + tables: tables.map(table => trans.objectStore(table)).map(store => { + const { keyPath, autoIncrement } = store; + const compound = isArray(keyPath); + const outbound = keyPath == null; + const indexByKeyPath = {}; + const result = { + name: store.name, + primaryKey: { + name: null, + isPrimaryKey: true, + outbound, + compound, + keyPath, + autoIncrement, + unique: true, + extractKey: getKeyExtractor(keyPath) + }, + indexes: arrayify(store.indexNames).map(indexName => store.index(indexName)) + .map(index => { + const { name, unique, multiEntry, keyPath } = index; + const compound = isArray(keyPath); + const result = { + name, + compound, + keyPath, + unique, + multiEntry, + extractKey: getKeyExtractor(keyPath) + }; + indexByKeyPath[getKeyPathAlias(keyPath)] = result; + return result; + }), + getIndexByKeyPath: (keyPath) => indexByKeyPath[getKeyPathAlias(keyPath)] + }; + indexByKeyPath[":id"] = result.primaryKey; + if (keyPath != null) { + indexByKeyPath[getKeyPathAlias(keyPath)] = result.primaryKey; + } + return result; + }) + }, + hasGetAll: tables.length > 0 && ('getAll' in trans.objectStore(tables[0])) && + !(typeof navigator !== 'undefined' && /Safari/.test(navigator.userAgent) && + !/(Chrome\/|Edge\/)/.test(navigator.userAgent) && + [].concat(navigator.userAgent.match(/Safari\/(\d*)/))[1] < 604) + }; + } + function makeIDBKeyRange(range) { + if (range.type === 3 ) + return null; + if (range.type === 4 ) + throw new Error("Cannot convert never type to IDBKeyRange"); + const { lower, upper, lowerOpen, upperOpen } = range; + const idbRange = lower === undefined ? + upper === undefined ? + null : + IdbKeyRange.upperBound(upper, !!upperOpen) : + upper === undefined ? + IdbKeyRange.lowerBound(lower, !!lowerOpen) : + IdbKeyRange.bound(lower, upper, !!lowerOpen, !!upperOpen); + return idbRange; + } + function createDbCoreTable(tableSchema) { + const tableName = tableSchema.name; + function mutate({ trans, type, keys, values, range }) { + return new Promise((resolve, reject) => { + resolve = wrap(resolve); + const store = trans.objectStore(tableName); + const outbound = store.keyPath == null; + const isAddOrPut = type === "put" || type === "add"; + if (!isAddOrPut && type !== 'delete' && type !== 'deleteRange') + throw new Error("Invalid operation type: " + type); + const { length } = keys || values || { length: 1 }; + if (keys && values && keys.length !== values.length) { + throw new Error("Given keys array must have same length as given values array."); + } + if (length === 0) + return resolve({ numFailures: 0, failures: {}, results: [], lastResult: undefined }); + let req; + const reqs = []; + const failures = []; + let numFailures = 0; + const errorHandler = event => { + ++numFailures; + preventDefault(event); + }; + if (type === 'deleteRange') { + if (range.type === 4 ) + return resolve({ numFailures, failures, results: [], lastResult: undefined }); + if (range.type === 3 ) + reqs.push(req = store.clear()); + else + reqs.push(req = store.delete(makeIDBKeyRange(range))); + } + else { + const [args1, args2] = isAddOrPut ? + outbound ? + [values, keys] : + [values, null] : + [keys, null]; + if (isAddOrPut) { + for (let i = 0; i < length; ++i) { + reqs.push(req = (args2 && args2[i] !== undefined ? + store[type](args1[i], args2[i]) : + store[type](args1[i]))); + req.onerror = errorHandler; + } + } + else { + for (let i = 0; i < length; ++i) { + reqs.push(req = store[type](args1[i])); + req.onerror = errorHandler; + } + } + } + const done = event => { + const lastResult = event.target.result; + reqs.forEach((req, i) => req.error != null && (failures[i] = req.error)); + resolve({ + numFailures, + failures, + results: type === "delete" ? keys : reqs.map(req => req.result), + lastResult + }); + }; + req.onerror = event => { + errorHandler(event); + done(event); + }; + req.onsuccess = done; + }); + } + function openCursor({ trans, values, query, reverse, unique }) { + return new Promise((resolve, reject) => { + resolve = wrap(resolve); + const { index, range } = query; + const store = trans.objectStore(tableName); + const source = index.isPrimaryKey ? + store : + store.index(index.name); + const direction = reverse ? + unique ? + "prevunique" : + "prev" : + unique ? + "nextunique" : + "next"; + const req = values || !('openKeyCursor' in source) ? + source.openCursor(makeIDBKeyRange(range), direction) : + source.openKeyCursor(makeIDBKeyRange(range), direction); + req.onerror = eventRejectHandler(reject); + req.onsuccess = wrap(ev => { + const cursor = req.result; + if (!cursor) { + resolve(null); + return; + } + cursor.___id = ++_id_counter; + cursor.done = false; + const _cursorContinue = cursor.continue.bind(cursor); + let _cursorContinuePrimaryKey = cursor.continuePrimaryKey; + if (_cursorContinuePrimaryKey) + _cursorContinuePrimaryKey = _cursorContinuePrimaryKey.bind(cursor); + const _cursorAdvance = cursor.advance.bind(cursor); + const doThrowCursorIsNotStarted = () => { throw new Error("Cursor not started"); }; + const doThrowCursorIsStopped = () => { throw new Error("Cursor not stopped"); }; + cursor.trans = trans; + cursor.stop = cursor.continue = cursor.continuePrimaryKey = cursor.advance = doThrowCursorIsNotStarted; + cursor.fail = wrap(reject); + cursor.next = function () { + let gotOne = 1; + return this.start(() => gotOne-- ? this.continue() : this.stop()).then(() => this); + }; + cursor.start = (callback) => { + const iterationPromise = new Promise((resolveIteration, rejectIteration) => { + resolveIteration = wrap(resolveIteration); + req.onerror = eventRejectHandler(rejectIteration); + cursor.fail = rejectIteration; + cursor.stop = value => { + cursor.stop = cursor.continue = cursor.continuePrimaryKey = cursor.advance = doThrowCursorIsStopped; + resolveIteration(value); + }; + }); + const guardedCallback = () => { + if (req.result) { + try { + callback(); + } + catch (err) { + cursor.fail(err); + } + } + else { + cursor.done = true; + cursor.start = () => { throw new Error("Cursor behind last entry"); }; + cursor.stop(); + } + }; + req.onsuccess = wrap(ev => { + req.onsuccess = guardedCallback; + guardedCallback(); + }); + cursor.continue = _cursorContinue; + cursor.continuePrimaryKey = _cursorContinuePrimaryKey; + cursor.advance = _cursorAdvance; + guardedCallback(); + return iterationPromise; + }; + resolve(cursor); + }, reject); + }); + } + function query(hasGetAll) { + return (request) => { + return new Promise((resolve, reject) => { + resolve = wrap(resolve); + const { trans, values, limit, query } = request; + const nonInfinitLimit = limit === Infinity ? undefined : limit; + const { index, range } = query; + const store = trans.objectStore(tableName); + const source = index.isPrimaryKey ? store : store.index(index.name); + const idbKeyRange = makeIDBKeyRange(range); + if (limit === 0) + return resolve({ result: [] }); + if (hasGetAll) { + const req = values ? + source.getAll(idbKeyRange, nonInfinitLimit) : + source.getAllKeys(idbKeyRange, nonInfinitLimit); + req.onsuccess = event => resolve({ result: event.target.result }); + req.onerror = eventRejectHandler(reject); + } + else { + let count = 0; + const req = values || !('openKeyCursor' in source) ? + source.openCursor(idbKeyRange) : + source.openKeyCursor(idbKeyRange); + const result = []; + req.onsuccess = event => { + const cursor = req.result; + if (!cursor) + return resolve({ result }); + result.push(values ? cursor.value : cursor.primaryKey); + if (++count === limit) + return resolve({ result }); + cursor.continue(); + }; + req.onerror = eventRejectHandler(reject); + } + }); + }; + } + return { + name: tableName, + schema: tableSchema, + mutate, + getMany({ trans, keys }) { + return new Promise((resolve, reject) => { + resolve = wrap(resolve); + const store = trans.objectStore(tableName); + const length = keys.length; + const result = new Array(length); + let keyCount = 0; + let callbackCount = 0; + let req; + const successHandler = event => { + const req = event.target; + if ((result[req._pos] = req.result) != null) + ; + if (++callbackCount === keyCount) + resolve(result); + }; + const errorHandler = eventRejectHandler(reject); + for (let i = 0; i < length; ++i) { + const key = keys[i]; + if (key != null) { + req = store.get(keys[i]); + req._pos = i; + req.onsuccess = successHandler; + req.onerror = errorHandler; + ++keyCount; + } + } + if (keyCount === 0) + resolve(result); + }); + }, + get({ trans, key }) { + return new Promise((resolve, reject) => { + resolve = wrap(resolve); + const store = trans.objectStore(tableName); + const req = store.get(key); + req.onsuccess = event => resolve(event.target.result); + req.onerror = eventRejectHandler(reject); + }); + }, + query: query(hasGetAll), + openCursor, + count({ query, trans }) { + const { index, range } = query; + return new Promise((resolve, reject) => { + const store = trans.objectStore(tableName); + const source = index.isPrimaryKey ? store : store.index(index.name); + const idbKeyRange = makeIDBKeyRange(range); + const req = idbKeyRange ? source.count(idbKeyRange) : source.count(); + req.onsuccess = wrap(ev => resolve(ev.target.result)); + req.onerror = eventRejectHandler(reject); + }); + } + }; + } + const { schema, hasGetAll } = extractSchema(db, tmpTrans); + const tables = schema.tables.map(tableSchema => createDbCoreTable(tableSchema)); + const tableMap = {}; + tables.forEach(table => tableMap[table.name] = table); + return { + stack: "dbcore", + transaction: db.transaction.bind(db), + table(name) { + const result = tableMap[name]; + if (!result) + throw new Error(`Table '${name}' not found`); + return tableMap[name]; + }, + MIN_KEY: -Infinity, + MAX_KEY: getMaxKey(IdbKeyRange), + schema + }; +} - rotateStart.set( x, y ); +function createMiddlewareStack(stackImpl, middlewares) { + return middlewares.reduce((down, { create }) => ({ ...down, ...create(down) }), stackImpl); +} +function createMiddlewareStacks(middlewares, idbdb, { IDBKeyRange, indexedDB }, tmpTrans) { + const dbcore = createMiddlewareStack(createDBCore(idbdb, IDBKeyRange, tmpTrans), middlewares.dbcore); + return { + dbcore + }; +} +function generateMiddlewareStacks({ _novip: db }, tmpTrans) { + const idbdb = tmpTrans.db; + const stacks = createMiddlewareStacks(db._middlewares, idbdb, db._deps, tmpTrans); + db.core = stacks.dbcore; + db.tables.forEach(table => { + const tableName = table.name; + if (db.core.schema.tables.some(tbl => tbl.name === tableName)) { + table.core = db.core.table(tableName); + if (db[tableName] instanceof db.Table) { + db[tableName].core = table.core; + } + } + }); +} - } - - } - - function handleTouchStartPan() { +function setApiOnPlace({ _novip: db }, objs, tableNames, dbschema) { + tableNames.forEach(tableName => { + const schema = dbschema[tableName]; + objs.forEach(obj => { + const propDesc = getPropertyDescriptor(obj, tableName); + if (!propDesc || ("value" in propDesc && propDesc.value === undefined)) { + if (obj === db.Transaction.prototype || obj instanceof db.Transaction) { + setProp(obj, tableName, { + get() { return this.table(tableName); }, + set(value) { + defineProperty(this, tableName, { value, writable: true, configurable: true, enumerable: true }); + } + }); + } + else { + obj[tableName] = new db.Table(tableName, schema); + } + } + }); + }); +} +function removeTablesApi({ _novip: db }, objs) { + objs.forEach(obj => { + for (let key in obj) { + if (obj[key] instanceof db.Table) + delete obj[key]; + } + }); +} +function lowerVersionFirst(a, b) { + return a._cfg.version - b._cfg.version; +} +function runUpgraders(db, oldVersion, idbUpgradeTrans, reject) { + const globalSchema = db._dbSchema; + const trans = db._createTransaction('readwrite', db._storeNames, globalSchema); + trans.create(idbUpgradeTrans); + trans._completion.catch(reject); + const rejectTransaction = trans._reject.bind(trans); + const transless = PSD.transless || PSD; + newScope(() => { + PSD.trans = trans; + PSD.transless = transless; + if (oldVersion === 0) { + keys(globalSchema).forEach(tableName => { + createTable(idbUpgradeTrans, tableName, globalSchema[tableName].primKey, globalSchema[tableName].indexes); + }); + generateMiddlewareStacks(db, idbUpgradeTrans); + DexiePromise.follow(() => db.on.populate.fire(trans)).catch(rejectTransaction); + } + else + updateTablesAndIndexes(db, oldVersion, trans, idbUpgradeTrans).catch(rejectTransaction); + }); +} +function updateTablesAndIndexes({ _novip: db }, oldVersion, trans, idbUpgradeTrans) { + const queue = []; + const versions = db._versions; + let globalSchema = db._dbSchema = buildGlobalSchema(db, db.idbdb, idbUpgradeTrans); + let anyContentUpgraderHasRun = false; + const versToRun = versions.filter(v => v._cfg.version >= oldVersion); + versToRun.forEach(version => { + queue.push(() => { + const oldSchema = globalSchema; + const newSchema = version._cfg.dbschema; + adjustToExistingIndexNames(db, oldSchema, idbUpgradeTrans); + adjustToExistingIndexNames(db, newSchema, idbUpgradeTrans); + globalSchema = db._dbSchema = newSchema; + const diff = getSchemaDiff(oldSchema, newSchema); + diff.add.forEach(tuple => { + createTable(idbUpgradeTrans, tuple[0], tuple[1].primKey, tuple[1].indexes); + }); + diff.change.forEach(change => { + if (change.recreate) { + throw new exceptions.Upgrade("Not yet support for changing primary key"); + } + else { + const store = idbUpgradeTrans.objectStore(change.name); + change.add.forEach(idx => addIndex(store, idx)); + change.change.forEach(idx => { + store.deleteIndex(idx.name); + addIndex(store, idx); + }); + change.del.forEach(idxName => store.deleteIndex(idxName)); + } + }); + const contentUpgrade = version._cfg.contentUpgrade; + if (contentUpgrade && version._cfg.version > oldVersion) { + generateMiddlewareStacks(db, idbUpgradeTrans); + trans._memoizedTables = {}; + anyContentUpgraderHasRun = true; + let upgradeSchema = shallowClone(newSchema); + diff.del.forEach(table => { + upgradeSchema[table] = oldSchema[table]; + }); + removeTablesApi(db, [db.Transaction.prototype]); + setApiOnPlace(db, [db.Transaction.prototype], keys(upgradeSchema), upgradeSchema); + trans.schema = upgradeSchema; + const contentUpgradeIsAsync = isAsyncFunction(contentUpgrade); + if (contentUpgradeIsAsync) { + incrementExpectedAwaits(); + } + let returnValue; + const promiseFollowed = DexiePromise.follow(() => { + returnValue = contentUpgrade(trans); + if (returnValue) { + if (contentUpgradeIsAsync) { + var decrementor = decrementExpectedAwaits.bind(null, null); + returnValue.then(decrementor, decrementor); + } + } + }); + return (returnValue && typeof returnValue.then === 'function' ? + DexiePromise.resolve(returnValue) : promiseFollowed.then(() => returnValue)); + } + }); + queue.push(idbtrans => { + if (!anyContentUpgraderHasRun || !hasIEDeleteObjectStoreBug) { + const newSchema = version._cfg.dbschema; + deleteRemovedTables(newSchema, idbtrans); + } + removeTablesApi(db, [db.Transaction.prototype]); + setApiOnPlace(db, [db.Transaction.prototype], db._storeNames, db._dbSchema); + trans.schema = db._dbSchema; + }); + }); + function runQueue() { + return queue.length ? DexiePromise.resolve(queue.shift()(trans.idbtrans)).then(runQueue) : + DexiePromise.resolve(); + } + return runQueue().then(() => { + createMissingTables(globalSchema, idbUpgradeTrans); + }); +} +function getSchemaDiff(oldSchema, newSchema) { + const diff = { + del: [], + add: [], + change: [] + }; + let table; + for (table in oldSchema) { + if (!newSchema[table]) + diff.del.push(table); + } + for (table in newSchema) { + const oldDef = oldSchema[table], newDef = newSchema[table]; + if (!oldDef) { + diff.add.push([table, newDef]); + } + else { + const change = { + name: table, + def: newDef, + recreate: false, + del: [], + add: [], + change: [] + }; + if (( + '' + (oldDef.primKey.keyPath || '')) !== ('' + (newDef.primKey.keyPath || '')) || + (oldDef.primKey.auto !== newDef.primKey.auto && !isIEOrEdge)) + { + change.recreate = true; + diff.change.push(change); + } + else { + const oldIndexes = oldDef.idxByName; + const newIndexes = newDef.idxByName; + let idxName; + for (idxName in oldIndexes) { + if (!newIndexes[idxName]) + change.del.push(idxName); + } + for (idxName in newIndexes) { + const oldIdx = oldIndexes[idxName], newIdx = newIndexes[idxName]; + if (!oldIdx) + change.add.push(newIdx); + else if (oldIdx.src !== newIdx.src) + change.change.push(newIdx); + } + if (change.del.length > 0 || change.add.length > 0 || change.change.length > 0) { + diff.change.push(change); + } + } + } + } + return diff; +} +function createTable(idbtrans, tableName, primKey, indexes) { + const store = idbtrans.db.createObjectStore(tableName, primKey.keyPath ? + { keyPath: primKey.keyPath, autoIncrement: primKey.auto } : + { autoIncrement: primKey.auto }); + indexes.forEach(idx => addIndex(store, idx)); + return store; +} +function createMissingTables(newSchema, idbtrans) { + keys(newSchema).forEach(tableName => { + if (!idbtrans.db.objectStoreNames.contains(tableName)) { + createTable(idbtrans, tableName, newSchema[tableName].primKey, newSchema[tableName].indexes); + } + }); +} +function deleteRemovedTables(newSchema, idbtrans) { + [].slice.call(idbtrans.db.objectStoreNames).forEach(storeName => newSchema[storeName] == null && idbtrans.db.deleteObjectStore(storeName)); +} +function addIndex(store, idx) { + store.createIndex(idx.name, idx.keyPath, { unique: idx.unique, multiEntry: idx.multi }); +} +function buildGlobalSchema(db, idbdb, tmpTrans) { + const globalSchema = {}; + const dbStoreNames = slice(idbdb.objectStoreNames, 0); + dbStoreNames.forEach(storeName => { + const store = tmpTrans.objectStore(storeName); + let keyPath = store.keyPath; + const primKey = createIndexSpec(nameFromKeyPath(keyPath), keyPath || "", false, false, !!store.autoIncrement, keyPath && typeof keyPath !== "string", true); + const indexes = []; + for (let j = 0; j < store.indexNames.length; ++j) { + const idbindex = store.index(store.indexNames[j]); + keyPath = idbindex.keyPath; + var index = createIndexSpec(idbindex.name, keyPath, !!idbindex.unique, !!idbindex.multiEntry, false, keyPath && typeof keyPath !== "string", false); + indexes.push(index); + } + globalSchema[storeName] = createTableSchema(storeName, primKey, indexes); + }); + return globalSchema; +} +function readGlobalSchema({ _novip: db }, idbdb, tmpTrans) { + db.verno = idbdb.version / 10; + const globalSchema = db._dbSchema = buildGlobalSchema(db, idbdb, tmpTrans); + db._storeNames = slice(idbdb.objectStoreNames, 0); + setApiOnPlace(db, [db._allTables], keys(globalSchema), globalSchema); +} +function verifyInstalledSchema(db, tmpTrans) { + const installedSchema = buildGlobalSchema(db, db.idbdb, tmpTrans); + const diff = getSchemaDiff(installedSchema, db._dbSchema); + return !(diff.add.length || diff.change.some(ch => ch.add.length || ch.change.length)); +} +function adjustToExistingIndexNames({ _novip: db }, schema, idbtrans) { + const storeNames = idbtrans.db.objectStoreNames; + for (let i = 0; i < storeNames.length; ++i) { + const storeName = storeNames[i]; + const store = idbtrans.objectStore(storeName); + db._hasGetAll = 'getAll' in store; + for (let j = 0; j < store.indexNames.length; ++j) { + const indexName = store.indexNames[j]; + const keyPath = store.index(indexName).keyPath; + const dexieName = typeof keyPath === 'string' ? keyPath : "[" + slice(keyPath).join('+') + "]"; + if (schema[storeName]) { + const indexSpec = schema[storeName].idxByName[dexieName]; + if (indexSpec) { + indexSpec.name = indexName; + delete schema[storeName].idxByName[dexieName]; + schema[storeName].idxByName[indexName] = indexSpec; + } + } + } + } + if (typeof navigator !== 'undefined' && /Safari/.test(navigator.userAgent) && + !/(Chrome\/|Edge\/)/.test(navigator.userAgent) && + _global.WorkerGlobalScope && _global instanceof _global.WorkerGlobalScope && + [].concat(navigator.userAgent.match(/Safari\/(\d*)/))[1] < 604) { + db._hasGetAll = false; + } +} +function parseIndexSyntax(primKeyAndIndexes) { + return primKeyAndIndexes.split(',').map((index, indexNum) => { + index = index.trim(); + const name = index.replace(/([&*]|\+\+)/g, ""); + const keyPath = /^\[/.test(name) ? name.match(/^\[(.*)\]$/)[1].split('+') : name; + return createIndexSpec(name, keyPath || null, /\&/.test(index), /\*/.test(index), /\+\+/.test(index), isArray(keyPath), indexNum === 0); + }); +} - if ( pointers.length === 1 ) { +class Version { + _parseStoresSpec(stores, outSchema) { + keys(stores).forEach(tableName => { + if (stores[tableName] !== null) { + var indexes = parseIndexSyntax(stores[tableName]); + var primKey = indexes.shift(); + if (primKey.multi) + throw new exceptions.Schema("Primary key cannot be multi-valued"); + indexes.forEach(idx => { + if (idx.auto) + throw new exceptions.Schema("Only primary key can be marked as autoIncrement (++)"); + if (!idx.keyPath) + throw new exceptions.Schema("Index must have a name and cannot be an empty string"); + }); + outSchema[tableName] = createTableSchema(tableName, primKey, indexes); + } + }); + } + stores(stores) { + const db = this.db; + this._cfg.storesSource = this._cfg.storesSource ? + extend(this._cfg.storesSource, stores) : + stores; + const versions = db._versions; + const storesSpec = {}; + let dbschema = {}; + versions.forEach(version => { + extend(storesSpec, version._cfg.storesSource); + dbschema = (version._cfg.dbschema = {}); + version._parseStoresSpec(storesSpec, dbschema); + }); + db._dbSchema = dbschema; + removeTablesApi(db, [db._allTables, db, db.Transaction.prototype]); + setApiOnPlace(db, [db._allTables, db, db.Transaction.prototype, this._cfg.tables], keys(dbschema), dbschema); + db._storeNames = keys(dbschema); + return this; + } + upgrade(upgradeFunction) { + this._cfg.contentUpgrade = promisableChain(this._cfg.contentUpgrade || nop, upgradeFunction); + return this; + } +} - panStart.set( pointers[ 0 ].pageX, pointers[ 0 ].pageY ); +function createVersionConstructor(db) { + return makeClassConstructor(Version.prototype, function Version(versionNumber) { + this.db = db; + this._cfg = { + version: versionNumber, + storesSource: null, + dbschema: {}, + tables: {}, + contentUpgrade: null + }; + }); +} - } else { +function getDbNamesTable(indexedDB, IDBKeyRange) { + let dbNamesDB = indexedDB["_dbNamesDB"]; + if (!dbNamesDB) { + dbNamesDB = indexedDB["_dbNamesDB"] = new Dexie$1(DBNAMES_DB, { + addons: [], + indexedDB, + IDBKeyRange, + }); + dbNamesDB.version(1).stores({ dbnames: "name" }); + } + return dbNamesDB.table("dbnames"); +} +function hasDatabasesNative(indexedDB) { + return indexedDB && typeof indexedDB.databases === "function"; +} +function getDatabaseNames({ indexedDB, IDBKeyRange, }) { + return hasDatabasesNative(indexedDB) + ? Promise.resolve(indexedDB.databases()).then((infos) => infos + .map((info) => info.name) + .filter((name) => name !== DBNAMES_DB)) + : getDbNamesTable(indexedDB, IDBKeyRange).toCollection().primaryKeys(); +} +function _onDatabaseCreated({ indexedDB, IDBKeyRange }, name) { + !hasDatabasesNative(indexedDB) && + name !== DBNAMES_DB && + getDbNamesTable(indexedDB, IDBKeyRange).put({ name }).catch(nop); +} +function _onDatabaseDeleted({ indexedDB, IDBKeyRange }, name) { + !hasDatabasesNative(indexedDB) && + name !== DBNAMES_DB && + getDbNamesTable(indexedDB, IDBKeyRange).delete(name).catch(nop); +} - const x = 0.5 * ( pointers[ 0 ].pageX + pointers[ 1 ].pageX ); - const y = 0.5 * ( pointers[ 0 ].pageY + pointers[ 1 ].pageY ); +function vip(fn) { + return newScope(function () { + PSD.letThrough = true; + return fn(); + }); +} - panStart.set( x, y ); +function idbReady() { + var isSafari = !navigator.userAgentData && + /Safari\//.test(navigator.userAgent) && + !/Chrom(e|ium)\//.test(navigator.userAgent); + if (!isSafari || !indexedDB.databases) + return Promise.resolve(); + var intervalId; + return new Promise(function (resolve) { + var tryIdb = function () { return indexedDB.databases().finally(resolve); }; + intervalId = setInterval(tryIdb, 100); + tryIdb(); + }).finally(function () { return clearInterval(intervalId); }); +} - } +function dexieOpen(db) { + const state = db._state; + const { indexedDB } = db._deps; + if (state.isBeingOpened || db.idbdb) + return state.dbReadyPromise.then(() => state.dbOpenError ? + rejection(state.dbOpenError) : + db); + debug && (state.openCanceller._stackHolder = getErrorWithStack()); + state.isBeingOpened = true; + state.dbOpenError = null; + state.openComplete = false; + const openCanceller = state.openCanceller; + function throwIfCancelled() { + if (state.openCanceller !== openCanceller) + throw new exceptions.DatabaseClosed('db.open() was cancelled'); + } + let resolveDbReady = state.dbReadyResolve, + upgradeTransaction = null, wasCreated = false; + return DexiePromise.race([openCanceller, (typeof navigator === 'undefined' ? DexiePromise.resolve() : idbReady()).then(() => new DexiePromise((resolve, reject) => { + throwIfCancelled(); + if (!indexedDB) + throw new exceptions.MissingAPI(); + const dbName = db.name; + const req = state.autoSchema ? + indexedDB.open(dbName) : + indexedDB.open(dbName, Math.round(db.verno * 10)); + if (!req) + throw new exceptions.MissingAPI(); + req.onerror = eventRejectHandler(reject); + req.onblocked = wrap(db._fireOnBlocked); + req.onupgradeneeded = wrap(e => { + upgradeTransaction = req.transaction; + if (state.autoSchema && !db._options.allowEmptyDB) { + req.onerror = preventDefault; + upgradeTransaction.abort(); + req.result.close(); + const delreq = indexedDB.deleteDatabase(dbName); + delreq.onsuccess = delreq.onerror = wrap(() => { + reject(new exceptions.NoSuchDatabase(`Database ${dbName} doesnt exist`)); + }); + } + else { + upgradeTransaction.onerror = eventRejectHandler(reject); + var oldVer = e.oldVersion > Math.pow(2, 62) ? 0 : e.oldVersion; + wasCreated = oldVer < 1; + db._novip.idbdb = req.result; + runUpgraders(db, oldVer / 10, upgradeTransaction, reject); + } + }, reject); + req.onsuccess = wrap(() => { + upgradeTransaction = null; + const idbdb = db._novip.idbdb = req.result; + const objectStoreNames = slice(idbdb.objectStoreNames); + if (objectStoreNames.length > 0) + try { + const tmpTrans = idbdb.transaction(safariMultiStoreFix(objectStoreNames), 'readonly'); + if (state.autoSchema) + readGlobalSchema(db, idbdb, tmpTrans); + else { + adjustToExistingIndexNames(db, db._dbSchema, tmpTrans); + if (!verifyInstalledSchema(db, tmpTrans)) { + console.warn(`Dexie SchemaDiff: Schema was extended without increasing the number passed to db.version(). Some queries may fail.`); + } + } + generateMiddlewareStacks(db, tmpTrans); + } + catch (e) { + } + connections.push(db); + idbdb.onversionchange = wrap(ev => { + state.vcFired = true; + db.on("versionchange").fire(ev); + }); + idbdb.onclose = wrap(ev => { + db.on("close").fire(ev); + }); + if (wasCreated) + _onDatabaseCreated(db._deps, dbName); + resolve(); + }, reject); + }))]).then(() => { + throwIfCancelled(); + state.onReadyBeingFired = []; + return DexiePromise.resolve(vip(() => db.on.ready.fire(db.vip))).then(function fireRemainders() { + if (state.onReadyBeingFired.length > 0) { + let remainders = state.onReadyBeingFired.reduce(promisableChain, nop); + state.onReadyBeingFired = []; + return DexiePromise.resolve(vip(() => remainders(db.vip))).then(fireRemainders); + } + }); + }).finally(() => { + state.onReadyBeingFired = null; + state.isBeingOpened = false; + }).then(() => { + return db; + }).catch(err => { + state.dbOpenError = err; + try { + upgradeTransaction && upgradeTransaction.abort(); + } + catch (_a) { } + if (openCanceller === state.openCanceller) { + db._close(); + } + return rejection(err); + }).finally(() => { + state.openComplete = true; + resolveDbReady(); + }); +} - } +function awaitIterator(iterator) { + var callNext = result => iterator.next(result), doThrow = error => iterator.throw(error), onSuccess = step(callNext), onError = step(doThrow); + function step(getNext) { + return (val) => { + var next = getNext(val), value = next.value; + return next.done ? value : + (!value || typeof value.then !== 'function' ? + isArray(value) ? Promise.all(value).then(onSuccess, onError) : onSuccess(value) : + value.then(onSuccess, onError)); + }; + } + return step(callNext)(); +} - function handleTouchStartDolly() { - - const dx = pointers[ 0 ].pageX - pointers[ 1 ].pageX; - const dy = pointers[ 0 ].pageY - pointers[ 1 ].pageY; - - const distance = Math.sqrt( dx * dx + dy * dy ); - - dollyStart.set( 0, distance ); - - } - - function handleTouchStartDollyPan() { - - if ( scope.enableZoom ) handleTouchStartDolly(); - - if ( scope.enablePan ) handleTouchStartPan(); - - } - - function handleTouchStartDollyRotate() { - - if ( scope.enableZoom ) handleTouchStartDolly(); - - if ( scope.enableRotate ) handleTouchStartRotate(); - - } - - function handleTouchMoveRotate( event ) { - - if ( pointers.length == 1 ) { - - rotateEnd.set( event.pageX, event.pageY ); - - } else { - - const position = getSecondPointerPosition( event ); - - const x = 0.5 * ( event.pageX + position.x ); - const y = 0.5 * ( event.pageY + position.y ); - - rotateEnd.set( x, y ); - - } - - rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed ); - - const element = scope.domElement; - - rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height - - rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight ); - - rotateStart.copy( rotateEnd ); - - } - - function handleTouchMovePan( event ) { - - if ( pointers.length === 1 ) { - - panEnd.set( event.pageX, event.pageY ); - - } else { - - const position = getSecondPointerPosition( event ); - - const x = 0.5 * ( event.pageX + position.x ); - const y = 0.5 * ( event.pageY + position.y ); - - panEnd.set( x, y ); - - } - - panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed ); - - pan( panDelta.x, panDelta.y ); - - panStart.copy( panEnd ); +function extractTransactionArgs(mode, _tableArgs_, scopeFunc) { + var i = arguments.length; + if (i < 2) + throw new exceptions.InvalidArgument("Too few arguments"); + var args = new Array(i - 1); + while (--i) + args[i - 1] = arguments[i]; + scopeFunc = args.pop(); + var tables = flatten(args); + return [mode, tables, scopeFunc]; +} +function enterTransactionScope(db, mode, storeNames, parentTransaction, scopeFunc) { + return DexiePromise.resolve().then(() => { + const transless = PSD.transless || PSD; + const trans = db._createTransaction(mode, storeNames, db._dbSchema, parentTransaction); + const zoneProps = { + trans: trans, + transless: transless + }; + if (parentTransaction) { + trans.idbtrans = parentTransaction.idbtrans; + } + else { + try { + trans.create(); + db._state.PR1398_maxLoop = 3; + } + catch (ex) { + if (ex.name === errnames.InvalidState && db.isOpen() && --db._state.PR1398_maxLoop > 0) { + console.warn('Dexie: Need to reopen db'); + db._close(); + return db.open().then(() => enterTransactionScope(db, mode, storeNames, null, scopeFunc)); + } + return rejection(ex); + } + } + const scopeFuncIsAsync = isAsyncFunction(scopeFunc); + if (scopeFuncIsAsync) { + incrementExpectedAwaits(); + } + let returnValue; + const promiseFollowed = DexiePromise.follow(() => { + returnValue = scopeFunc.call(trans, trans); + if (returnValue) { + if (scopeFuncIsAsync) { + var decrementor = decrementExpectedAwaits.bind(null, null); + returnValue.then(decrementor, decrementor); + } + else if (typeof returnValue.next === 'function' && typeof returnValue.throw === 'function') { + returnValue = awaitIterator(returnValue); + } + } + }, zoneProps); + return (returnValue && typeof returnValue.then === 'function' ? + DexiePromise.resolve(returnValue).then(x => trans.active ? + x + : rejection(new exceptions.PrematureCommit("Transaction committed too early. See http://bit.ly/2kdckMn"))) + : promiseFollowed.then(() => returnValue)).then(x => { + if (parentTransaction) + trans._resolve(); + return trans._completion.then(() => x); + }).catch(e => { + trans._reject(e); + return rejection(e); + }); + }); +} - } +function pad(a, value, count) { + const result = isArray(a) ? a.slice() : [a]; + for (let i = 0; i < count; ++i) + result.push(value); + return result; +} +function createVirtualIndexMiddleware(down) { + return { + ...down, + table(tableName) { + const table = down.table(tableName); + const { schema } = table; + const indexLookup = {}; + const allVirtualIndexes = []; + function addVirtualIndexes(keyPath, keyTail, lowLevelIndex) { + const keyPathAlias = getKeyPathAlias(keyPath); + const indexList = (indexLookup[keyPathAlias] = indexLookup[keyPathAlias] || []); + const keyLength = keyPath == null ? 0 : typeof keyPath === 'string' ? 1 : keyPath.length; + const isVirtual = keyTail > 0; + const virtualIndex = { + ...lowLevelIndex, + isVirtual, + keyTail, + keyLength, + extractKey: getKeyExtractor(keyPath), + unique: !isVirtual && lowLevelIndex.unique + }; + indexList.push(virtualIndex); + if (!virtualIndex.isPrimaryKey) { + allVirtualIndexes.push(virtualIndex); + } + if (keyLength > 1) { + const virtualKeyPath = keyLength === 2 ? + keyPath[0] : + keyPath.slice(0, keyLength - 1); + addVirtualIndexes(virtualKeyPath, keyTail + 1, lowLevelIndex); + } + indexList.sort((a, b) => a.keyTail - b.keyTail); + return virtualIndex; + } + const primaryKey = addVirtualIndexes(schema.primaryKey.keyPath, 0, schema.primaryKey); + indexLookup[":id"] = [primaryKey]; + for (const index of schema.indexes) { + addVirtualIndexes(index.keyPath, 0, index); + } + function findBestIndex(keyPath) { + const result = indexLookup[getKeyPathAlias(keyPath)]; + return result && result[0]; + } + function translateRange(range, keyTail) { + return { + type: range.type === 1 ? + 2 : + range.type, + lower: pad(range.lower, range.lowerOpen ? down.MAX_KEY : down.MIN_KEY, keyTail), + lowerOpen: true, + upper: pad(range.upper, range.upperOpen ? down.MIN_KEY : down.MAX_KEY, keyTail), + upperOpen: true + }; + } + function translateRequest(req) { + const index = req.query.index; + return index.isVirtual ? { + ...req, + query: { + index, + range: translateRange(req.query.range, index.keyTail) + } + } : req; + } + const result = { + ...table, + schema: { + ...schema, + primaryKey, + indexes: allVirtualIndexes, + getIndexByKeyPath: findBestIndex + }, + count(req) { + return table.count(translateRequest(req)); + }, + query(req) { + return table.query(translateRequest(req)); + }, + openCursor(req) { + const { keyTail, isVirtual, keyLength } = req.query.index; + if (!isVirtual) + return table.openCursor(req); + function createVirtualCursor(cursor) { + function _continue(key) { + key != null ? + cursor.continue(pad(key, req.reverse ? down.MAX_KEY : down.MIN_KEY, keyTail)) : + req.unique ? + cursor.continue(cursor.key.slice(0, keyLength) + .concat(req.reverse + ? down.MIN_KEY + : down.MAX_KEY, keyTail)) : + cursor.continue(); + } + const virtualCursor = Object.create(cursor, { + continue: { value: _continue }, + continuePrimaryKey: { + value(key, primaryKey) { + cursor.continuePrimaryKey(pad(key, down.MAX_KEY, keyTail), primaryKey); + } + }, + primaryKey: { + get() { + return cursor.primaryKey; + } + }, + key: { + get() { + const key = cursor.key; + return keyLength === 1 ? + key[0] : + key.slice(0, keyLength); + } + }, + value: { + get() { + return cursor.value; + } + } + }); + return virtualCursor; + } + return table.openCursor(translateRequest(req)) + .then(cursor => cursor && createVirtualCursor(cursor)); + } + }; + return result; + } + }; +} +const virtualIndexMiddleware = { + stack: "dbcore", + name: "VirtualIndexMiddleware", + level: 1, + create: createVirtualIndexMiddleware +}; - function handleTouchMoveDolly( event ) { +function getObjectDiff(a, b, rv, prfx) { + rv = rv || {}; + prfx = prfx || ''; + keys(a).forEach((prop) => { + if (!hasOwn(b, prop)) { + rv[prfx + prop] = undefined; + } + else { + var ap = a[prop], bp = b[prop]; + if (typeof ap === 'object' && typeof bp === 'object' && ap && bp) { + const apTypeName = toStringTag(ap); + const bpTypeName = toStringTag(bp); + if (apTypeName !== bpTypeName) { + rv[prfx + prop] = b[prop]; + } + else if (apTypeName === 'Object') { + getObjectDiff(ap, bp, rv, prfx + prop + '.'); + } + else if (ap !== bp) { + rv[prfx + prop] = b[prop]; + } + } + else if (ap !== bp) + rv[prfx + prop] = b[prop]; + } + }); + keys(b).forEach((prop) => { + if (!hasOwn(a, prop)) { + rv[prfx + prop] = b[prop]; + } + }); + return rv; +} - const position = getSecondPointerPosition( event ); +function getEffectiveKeys(primaryKey, req) { + if (req.type === 'delete') + return req.keys; + return req.keys || req.values.map(primaryKey.extractKey); +} - const dx = event.pageX - position.x; - const dy = event.pageY - position.y; +const hooksMiddleware = { + stack: "dbcore", + name: "HooksMiddleware", + level: 2, + create: (downCore) => ({ + ...downCore, + table(tableName) { + const downTable = downCore.table(tableName); + const { primaryKey } = downTable.schema; + const tableMiddleware = { + ...downTable, + mutate(req) { + const dxTrans = PSD.trans; + const { deleting, creating, updating } = dxTrans.table(tableName).hook; + switch (req.type) { + case 'add': + if (creating.fire === nop) + break; + return dxTrans._promise('readwrite', () => addPutOrDelete(req), true); + case 'put': + if (creating.fire === nop && updating.fire === nop) + break; + return dxTrans._promise('readwrite', () => addPutOrDelete(req), true); + case 'delete': + if (deleting.fire === nop) + break; + return dxTrans._promise('readwrite', () => addPutOrDelete(req), true); + case 'deleteRange': + if (deleting.fire === nop) + break; + return dxTrans._promise('readwrite', () => deleteRange(req), true); + } + return downTable.mutate(req); + function addPutOrDelete(req) { + const dxTrans = PSD.trans; + const keys = req.keys || getEffectiveKeys(primaryKey, req); + if (!keys) + throw new Error("Keys missing"); + req = req.type === 'add' || req.type === 'put' ? + { ...req, keys } : + { ...req }; + if (req.type !== 'delete') + req.values = [...req.values]; + if (req.keys) + req.keys = [...req.keys]; + return getExistingValues(downTable, req, keys).then(existingValues => { + const contexts = keys.map((key, i) => { + const existingValue = existingValues[i]; + const ctx = { onerror: null, onsuccess: null }; + if (req.type === 'delete') { + deleting.fire.call(ctx, key, existingValue, dxTrans); + } + else if (req.type === 'add' || existingValue === undefined) { + const generatedPrimaryKey = creating.fire.call(ctx, key, req.values[i], dxTrans); + if (key == null && generatedPrimaryKey != null) { + key = generatedPrimaryKey; + req.keys[i] = key; + if (!primaryKey.outbound) { + setByKeyPath(req.values[i], primaryKey.keyPath, key); + } + } + } + else { + const objectDiff = getObjectDiff(existingValue, req.values[i]); + const additionalChanges = updating.fire.call(ctx, objectDiff, key, existingValue, dxTrans); + if (additionalChanges) { + const requestedValue = req.values[i]; + Object.keys(additionalChanges).forEach(keyPath => { + if (hasOwn(requestedValue, keyPath)) { + requestedValue[keyPath] = additionalChanges[keyPath]; + } + else { + setByKeyPath(requestedValue, keyPath, additionalChanges[keyPath]); + } + }); + } + } + return ctx; + }); + return downTable.mutate(req).then(({ failures, results, numFailures, lastResult }) => { + for (let i = 0; i < keys.length; ++i) { + const primKey = results ? results[i] : keys[i]; + const ctx = contexts[i]; + if (primKey == null) { + ctx.onerror && ctx.onerror(failures[i]); + } + else { + ctx.onsuccess && ctx.onsuccess(req.type === 'put' && existingValues[i] ? + req.values[i] : + primKey + ); + } + } + return { failures, results, numFailures, lastResult }; + }).catch(error => { + contexts.forEach(ctx => ctx.onerror && ctx.onerror(error)); + return Promise.reject(error); + }); + }); + } + function deleteRange(req) { + return deleteNextChunk(req.trans, req.range, 10000); + } + function deleteNextChunk(trans, range, limit) { + return downTable.query({ trans, values: false, query: { index: primaryKey, range }, limit }) + .then(({ result }) => { + return addPutOrDelete({ type: 'delete', keys: result, trans }).then(res => { + if (res.numFailures > 0) + return Promise.reject(res.failures[0]); + if (result.length < limit) { + return { failures: [], numFailures: 0, lastResult: undefined }; + } + else { + return deleteNextChunk(trans, { ...range, lower: result[result.length - 1], lowerOpen: true }, limit); + } + }); + }); + } + } + }; + return tableMiddleware; + }, + }) +}; +function getExistingValues(table, req, effectiveKeys) { + return req.type === "add" + ? Promise.resolve([]) + : table.getMany({ trans: req.trans, keys: effectiveKeys, cache: "immutable" }); +} - const distance = Math.sqrt( dx * dx + dy * dy ); - - dollyEnd.set( 0, distance ); - - dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) ); - - dollyOut( dollyDelta.y ); - - dollyStart.copy( dollyEnd ); - - } - - function handleTouchMoveDollyPan( event ) { - - if ( scope.enableZoom ) handleTouchMoveDolly( event ); - - if ( scope.enablePan ) handleTouchMovePan( event ); - - } - - function handleTouchMoveDollyRotate( event ) { - - if ( scope.enableZoom ) handleTouchMoveDolly( event ); - - if ( scope.enableRotate ) handleTouchMoveRotate( event ); - - } - - // - // event handlers - FSM: listen for events and reset state - // - - function onPointerDown( event ) { - - if ( scope.enabled === false ) return; - - if ( pointers.length === 0 ) { - - scope.domElement.setPointerCapture( event.pointerId ); - - scope.domElement.addEventListener( 'pointermove', onPointerMove ); - scope.domElement.addEventListener( 'pointerup', onPointerUp ); - - } - - // - - addPointer( event ); - - if ( event.pointerType === 'touch' ) { - - onTouchStart( event ); - - } else { - - onMouseDown( event ); - - } - - } - - function onPointerMove( event ) { - - if ( scope.enabled === false ) return; - - if ( event.pointerType === 'touch' ) { - - onTouchMove( event ); - - } else { - - onMouseMove( event ); - - } - - } - - function onPointerUp( event ) { - - removePointer( event ); - - if ( pointers.length === 0 ) { - - scope.domElement.releasePointerCapture( event.pointerId ); - - scope.domElement.removeEventListener( 'pointermove', onPointerMove ); - scope.domElement.removeEventListener( 'pointerup', onPointerUp ); - - } - - scope.dispatchEvent( _endEvent ); - - state = STATE.NONE; - - } - - function onMouseDown( event ) { - - let mouseAction; - - switch ( event.button ) { - - case 0: - - mouseAction = scope.mouseButtons.LEFT; - break; - - case 1: - - mouseAction = scope.mouseButtons.MIDDLE; - break; - - case 2: - - mouseAction = scope.mouseButtons.RIGHT; - break; - - default: - - mouseAction = - 1; - - } - - switch ( mouseAction ) { - - case MOUSE.DOLLY: - - if ( scope.enableZoom === false ) return; - - handleMouseDownDolly( event ); - - state = STATE.DOLLY; - - break; - - case MOUSE.ROTATE: - - if ( event.ctrlKey || event.metaKey || event.shiftKey ) { - - if ( scope.enablePan === false ) return; - - handleMouseDownPan( event ); - - state = STATE.PAN; - - } else { - - if ( scope.enableRotate === false ) return; - - handleMouseDownRotate( event ); - - state = STATE.ROTATE; - - } - - break; - - case MOUSE.PAN: - - if ( event.ctrlKey || event.metaKey || event.shiftKey ) { - - if ( scope.enableRotate === false ) return; - - handleMouseDownRotate( event ); - - state = STATE.ROTATE; - - } else { - - if ( scope.enablePan === false ) return; - - handleMouseDownPan( event ); - - state = STATE.PAN; - - } - - break; - - default: - - state = STATE.NONE; - - } - - if ( state !== STATE.NONE ) { - - scope.dispatchEvent( _startEvent ); - - } - - } - - function onMouseMove( event ) { - - switch ( state ) { - - case STATE.ROTATE: - - if ( scope.enableRotate === false ) return; - - handleMouseMoveRotate( event ); - - break; - - case STATE.DOLLY: - - if ( scope.enableZoom === false ) return; - - handleMouseMoveDolly( event ); - - break; - - case STATE.PAN: - - if ( scope.enablePan === false ) return; - - handleMouseMovePan( event ); - - break; - - } - - } - - function onMouseWheel( event ) { - - if ( scope.enabled === false || scope.enableZoom === false || state !== STATE.NONE ) return; - - event.preventDefault(); - - scope.dispatchEvent( _startEvent ); - - handleMouseWheel( event ); - - scope.dispatchEvent( _endEvent ); - - } - - function onKeyDown( event ) { - - if ( scope.enabled === false || scope.enablePan === false ) return; - - handleKeyDown( event ); - - } - - function onTouchStart( event ) { - - trackPointer( event ); - - switch ( pointers.length ) { - - case 1: - - switch ( scope.touches.ONE ) { - - case TOUCH.ROTATE: - - if ( scope.enableRotate === false ) return; - - handleTouchStartRotate(); - - state = STATE.TOUCH_ROTATE; - - break; - - case TOUCH.PAN: - - if ( scope.enablePan === false ) return; - - handleTouchStartPan(); - - state = STATE.TOUCH_PAN; - - break; - - default: - - state = STATE.NONE; - - } - - break; - - case 2: - - switch ( scope.touches.TWO ) { - - case TOUCH.DOLLY_PAN: - - if ( scope.enableZoom === false && scope.enablePan === false ) return; - - handleTouchStartDollyPan(); - - state = STATE.TOUCH_DOLLY_PAN; - - break; - - case TOUCH.DOLLY_ROTATE: - - if ( scope.enableZoom === false && scope.enableRotate === false ) return; - - handleTouchStartDollyRotate(); - - state = STATE.TOUCH_DOLLY_ROTATE; - - break; - - default: - - state = STATE.NONE; - - } - - break; - - default: - - state = STATE.NONE; - - } - - if ( state !== STATE.NONE ) { - - scope.dispatchEvent( _startEvent ); - - } - - } - - function onTouchMove( event ) { - - trackPointer( event ); - - switch ( state ) { - - case STATE.TOUCH_ROTATE: - - if ( scope.enableRotate === false ) return; - - handleTouchMoveRotate( event ); - - scope.update(); - - break; - - case STATE.TOUCH_PAN: - - if ( scope.enablePan === false ) return; - - handleTouchMovePan( event ); - - scope.update(); - - break; - - case STATE.TOUCH_DOLLY_PAN: - - if ( scope.enableZoom === false && scope.enablePan === false ) return; - - handleTouchMoveDollyPan( event ); - - scope.update(); - - break; - - case STATE.TOUCH_DOLLY_ROTATE: - - if ( scope.enableZoom === false && scope.enableRotate === false ) return; - - handleTouchMoveDollyRotate( event ); - - scope.update(); - - break; - - default: - - state = STATE.NONE; - - } - - } - - function onContextMenu( event ) { - - if ( scope.enabled === false ) return; - - event.preventDefault(); - - } - - function addPointer( event ) { - - pointers.push( event ); - - } - - function removePointer( event ) { - - delete pointerPositions[ event.pointerId ]; - - for ( let i = 0; i < pointers.length; i ++ ) { - - if ( pointers[ i ].pointerId == event.pointerId ) { - - pointers.splice( i, 1 ); - return; - - } - - } - - } - - function trackPointer( event ) { - - let position = pointerPositions[ event.pointerId ]; - - if ( position === undefined ) { - - position = new Vector2$1(); - pointerPositions[ event.pointerId ] = position; - - } - - position.set( event.pageX, event.pageY ); - - } - - function getSecondPointerPosition( event ) { - - const pointer = ( event.pointerId === pointers[ 0 ].pointerId ) ? pointers[ 1 ] : pointers[ 0 ]; - - return pointerPositions[ pointer.pointerId ]; - - } - - // - - scope.domElement.addEventListener( 'contextmenu', onContextMenu ); - - scope.domElement.addEventListener( 'pointerdown', onPointerDown ); - scope.domElement.addEventListener( 'pointercancel', onPointerUp ); - scope.domElement.addEventListener( 'wheel', onMouseWheel, { passive: false } ); - - // force an update at start - - this.update(); - - } +function getFromTransactionCache(keys, cache, clone) { + try { + if (!cache) + return null; + if (cache.keys.length < keys.length) + return null; + const result = []; + for (let i = 0, j = 0; i < cache.keys.length && j < keys.length; ++i) { + if (cmp(cache.keys[i], keys[j]) !== 0) + continue; + result.push(clone ? deepClone(cache.values[i]) : cache.values[i]); + ++j; + } + return result.length === keys.length ? result : null; + } + catch (_a) { + return null; + } +} +const cacheExistingValuesMiddleware = { + stack: "dbcore", + level: -1, + create: (core) => { + return { + table: (tableName) => { + const table = core.table(tableName); + return { + ...table, + getMany: (req) => { + if (!req.cache) { + return table.getMany(req); + } + const cachedResult = getFromTransactionCache(req.keys, req.trans["_cache"], req.cache === "clone"); + if (cachedResult) { + return DexiePromise.resolve(cachedResult); + } + return table.getMany(req).then((res) => { + req.trans["_cache"] = { + keys: req.keys, + values: req.cache === "clone" ? deepClone(res) : res, + }; + return res; + }); + }, + mutate: (req) => { + if (req.type !== "add") + req.trans["_cache"] = null; + return table.mutate(req); + }, + }; + }, + }; + }, +}; +function isEmptyRange(node) { + return !("from" in node); } - -/** - * Full-screen textured quad shader - */ - -const CopyShader = { - - uniforms: { - - 'tDiffuse': { value: null }, - 'opacity': { value: 1.0 } - - }, - - vertexShader: /* glsl */` - - varying vec2 vUv; - - void main() { - - vUv = uv; - gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); - - }`, - - fragmentShader: /* glsl */` - - uniform float opacity; - - uniform sampler2D tDiffuse; - - varying vec2 vUv; - - void main() { - - gl_FragColor = texture2D( tDiffuse, vUv ); - gl_FragColor.a *= opacity; - - - }` - +const RangeSet = function (fromOrTree, to) { + if (this) { + extend(this, arguments.length ? { d: 1, from: fromOrTree, to: arguments.length > 1 ? to : fromOrTree } : { d: 0 }); + } + else { + const rv = new RangeSet(); + if (fromOrTree && ("d" in fromOrTree)) { + extend(rv, fromOrTree); + } + return rv; + } }; - -class Pass { - - constructor() { - - this.isPass = true; - - // if set to true, the pass is processed by the composer - this.enabled = true; - - // if set to true, the pass indicates to swap read and write buffer after rendering - this.needsSwap = true; - - // if set to true, the pass clears its buffer before rendering - this.clear = false; - - // if set to true, the result of the pass is rendered to screen. This is set automatically by EffectComposer. - this.renderToScreen = false; - - } - - setSize( /* width, height */ ) {} - - render( /* renderer, writeBuffer, readBuffer, deltaTime, maskActive */ ) { - - console.error( 'THREE.Pass: .render() must be implemented in derived pass.' ); - - } - - dispose() {} - +props(RangeSet.prototype, { + add(rangeSet) { + mergeRanges(this, rangeSet); + return this; + }, + addKey(key) { + addRange(this, key, key); + return this; + }, + addKeys(keys) { + keys.forEach(key => addRange(this, key, key)); + return this; + }, + [iteratorSymbol]() { + return getRangeSetIterator(this); + } +}); +function addRange(target, from, to) { + const diff = cmp(from, to); + if (isNaN(diff)) + return; + if (diff > 0) + throw RangeError(); + if (isEmptyRange(target)) + return extend(target, { from, to, d: 1 }); + const left = target.l; + const right = target.r; + if (cmp(to, target.from) < 0) { + left + ? addRange(left, from, to) + : (target.l = { from, to, d: 1, l: null, r: null }); + return rebalance(target); + } + if (cmp(from, target.to) > 0) { + right + ? addRange(right, from, to) + : (target.r = { from, to, d: 1, l: null, r: null }); + return rebalance(target); + } + if (cmp(from, target.from) < 0) { + target.from = from; + target.l = null; + target.d = right ? right.d + 1 : 1; + } + if (cmp(to, target.to) > 0) { + target.to = to; + target.r = null; + target.d = target.l ? target.l.d + 1 : 1; + } + const rightWasCutOff = !target.r; + if (left && !target.l) { + mergeRanges(target, left); + } + if (right && rightWasCutOff) { + mergeRanges(target, right); + } } - -// Helper for passes that need to fill the viewport with a single quad. - -const _camera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); - -// https://github.com/mrdoob/three.js/pull/21358 - -const _geometry = new BufferGeometry(); -_geometry.setAttribute( 'position', new Float32BufferAttribute( [ - 1, 3, 0, - 1, - 1, 0, 3, - 1, 0 ], 3 ) ); -_geometry.setAttribute( 'uv', new Float32BufferAttribute( [ 0, 2, 0, 0, 2, 0 ], 2 ) ); - -class FullScreenQuad { - - constructor( material ) { - - this._mesh = new Mesh( _geometry, material ); - - } - - dispose() { - - this._mesh.geometry.dispose(); - - } - - render( renderer ) { - - renderer.render( this._mesh, _camera ); - - } - - get material() { - - return this._mesh.material; - - } - - set material( value ) { - - this._mesh.material = value; - - } - +function mergeRanges(target, newSet) { + function _addRangeSet(target, { from, to, l, r }) { + addRange(target, from, to); + if (l) + _addRangeSet(target, l); + if (r) + _addRangeSet(target, r); + } + if (!isEmptyRange(newSet)) + _addRangeSet(target, newSet); } - -class ShaderPass extends Pass { - - constructor( shader, textureID ) { - - super(); - - this.textureID = ( textureID !== undefined ) ? textureID : 'tDiffuse'; - - if ( shader instanceof ShaderMaterial ) { - - this.uniforms = shader.uniforms; - - this.material = shader; - - } else if ( shader ) { - - this.uniforms = UniformsUtils.clone( shader.uniforms ); - - this.material = new ShaderMaterial( { - - defines: Object.assign( {}, shader.defines ), - uniforms: this.uniforms, - vertexShader: shader.vertexShader, - fragmentShader: shader.fragmentShader - - } ); - - } - - this.fsQuad = new FullScreenQuad( this.material ); - - } - - render( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) { - - if ( this.uniforms[ this.textureID ] ) { - - this.uniforms[ this.textureID ].value = readBuffer.texture; - - } - - this.fsQuad.material = this.material; - - if ( this.renderToScreen ) { - - renderer.setRenderTarget( null ); - this.fsQuad.render( renderer ); - - } else { - - renderer.setRenderTarget( writeBuffer ); - // TODO: Avoid using autoClear properties, see https://github.com/mrdoob/three.js/pull/15571#issuecomment-465669600 - if ( this.clear ) renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); - this.fsQuad.render( renderer ); - - } - - } - - dispose() { - - this.material.dispose(); - - this.fsQuad.dispose(); - - } - +function rangesOverlap(rangeSet1, rangeSet2) { + const i1 = getRangeSetIterator(rangeSet2); + let nextResult1 = i1.next(); + if (nextResult1.done) + return false; + let a = nextResult1.value; + const i2 = getRangeSetIterator(rangeSet1); + let nextResult2 = i2.next(a.from); + let b = nextResult2.value; + while (!nextResult1.done && !nextResult2.done) { + if (cmp(b.from, a.to) <= 0 && cmp(b.to, a.from) >= 0) + return true; + cmp(a.from, b.from) < 0 + ? (a = (nextResult1 = i1.next(b.from)).value) + : (b = (nextResult2 = i2.next(a.from)).value); + } + return false; } - -class MaskPass extends Pass { - - constructor( scene, camera ) { - - super(); - - this.scene = scene; - this.camera = camera; - - this.clear = true; - this.needsSwap = false; - - this.inverse = false; - - } - - render( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) { - - const context = renderer.getContext(); - const state = renderer.state; - - // don't update color or depth - - state.buffers.color.setMask( false ); - state.buffers.depth.setMask( false ); - - // lock buffers - - state.buffers.color.setLocked( true ); - state.buffers.depth.setLocked( true ); - - // set up stencil - - let writeValue, clearValue; - - if ( this.inverse ) { - - writeValue = 0; - clearValue = 1; - - } else { - - writeValue = 1; - clearValue = 0; - - } - - state.buffers.stencil.setTest( true ); - state.buffers.stencil.setOp( context.REPLACE, context.REPLACE, context.REPLACE ); - state.buffers.stencil.setFunc( context.ALWAYS, writeValue, 0xffffffff ); - state.buffers.stencil.setClear( clearValue ); - state.buffers.stencil.setLocked( true ); - - // draw into the stencil buffer - - renderer.setRenderTarget( readBuffer ); - if ( this.clear ) renderer.clear(); - renderer.render( this.scene, this.camera ); - - renderer.setRenderTarget( writeBuffer ); - if ( this.clear ) renderer.clear(); - renderer.render( this.scene, this.camera ); - - // unlock color and depth buffer for subsequent rendering - - state.buffers.color.setLocked( false ); - state.buffers.depth.setLocked( false ); - - // only render where stencil is set to 1 - - state.buffers.stencil.setLocked( false ); - state.buffers.stencil.setFunc( context.EQUAL, 1, 0xffffffff ); // draw if == 1 - state.buffers.stencil.setOp( context.KEEP, context.KEEP, context.KEEP ); - state.buffers.stencil.setLocked( true ); - - } - +function getRangeSetIterator(node) { + let state = isEmptyRange(node) ? null : { s: 0, n: node }; + return { + next(key) { + const keyProvided = arguments.length > 0; + while (state) { + switch (state.s) { + case 0: + state.s = 1; + if (keyProvided) { + while (state.n.l && cmp(key, state.n.from) < 0) + state = { up: state, n: state.n.l, s: 1 }; + } + else { + while (state.n.l) + state = { up: state, n: state.n.l, s: 1 }; + } + case 1: + state.s = 2; + if (!keyProvided || cmp(key, state.n.to) <= 0) + return { value: state.n, done: false }; + case 2: + if (state.n.r) { + state.s = 3; + state = { up: state, n: state.n.r, s: 0 }; + continue; + } + case 3: + state = state.up; + } + } + return { done: true }; + }, + }; } - -class ClearMaskPass extends Pass { - - constructor() { - - super(); - - this.needsSwap = false; - - } - - render( renderer /*, writeBuffer, readBuffer, deltaTime, maskActive */ ) { - - renderer.state.buffers.stencil.setLocked( false ); - renderer.state.buffers.stencil.setTest( false ); - - } - +function rebalance(target) { + var _a, _b; + const diff = (((_a = target.r) === null || _a === void 0 ? void 0 : _a.d) || 0) - (((_b = target.l) === null || _b === void 0 ? void 0 : _b.d) || 0); + const r = diff > 1 ? "r" : diff < -1 ? "l" : ""; + if (r) { + const l = r === "r" ? "l" : "r"; + const rootClone = { ...target }; + const oldRootRight = target[r]; + target.from = oldRootRight.from; + target.to = oldRootRight.to; + target[r] = oldRootRight[r]; + rootClone[r] = oldRootRight[l]; + target[l] = rootClone; + rootClone.d = computeDepth(rootClone); + } + target.d = computeDepth(target); +} +function computeDepth({ r, l }) { + return (r ? (l ? Math.max(r.d, l.d) : r.d) : l ? l.d : 0) + 1; } -class EffectComposer { - - constructor( renderer, renderTarget ) { - - this.renderer = renderer; - - this._pixelRatio = renderer.getPixelRatio(); - - if ( renderTarget === undefined ) { - - const size = renderer.getSize( new Vector2$1() ); - this._width = size.width; - this._height = size.height; - - renderTarget = new WebGLRenderTarget( this._width * this._pixelRatio, this._height * this._pixelRatio ); - renderTarget.texture.name = 'EffectComposer.rt1'; - - } else { - - this._width = renderTarget.width; - this._height = renderTarget.height; - - } - - this.renderTarget1 = renderTarget; - this.renderTarget2 = renderTarget.clone(); - this.renderTarget2.texture.name = 'EffectComposer.rt2'; - - this.writeBuffer = this.renderTarget1; - this.readBuffer = this.renderTarget2; - - this.renderToScreen = true; - - this.passes = []; - - this.copyPass = new ShaderPass( CopyShader ); - - this.clock = new Clock(); - - } - - swapBuffers() { - - const tmp = this.readBuffer; - this.readBuffer = this.writeBuffer; - this.writeBuffer = tmp; - - } - - addPass( pass ) { - - this.passes.push( pass ); - pass.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio ); - - } - - insertPass( pass, index ) { - - this.passes.splice( index, 0, pass ); - pass.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio ); - - } - - removePass( pass ) { - - const index = this.passes.indexOf( pass ); - - if ( index !== - 1 ) { - - this.passes.splice( index, 1 ); - - } - - } - - isLastEnabledPass( passIndex ) { - - for ( let i = passIndex + 1; i < this.passes.length; i ++ ) { - - if ( this.passes[ i ].enabled ) { - - return false; - - } - - } - - return true; - - } - - render( deltaTime ) { - - // deltaTime value is in seconds - - if ( deltaTime === undefined ) { - - deltaTime = this.clock.getDelta(); - - } - - const currentRenderTarget = this.renderer.getRenderTarget(); - - let maskActive = false; - - for ( let i = 0, il = this.passes.length; i < il; i ++ ) { - - const pass = this.passes[ i ]; - - if ( pass.enabled === false ) continue; - - pass.renderToScreen = ( this.renderToScreen && this.isLastEnabledPass( i ) ); - pass.render( this.renderer, this.writeBuffer, this.readBuffer, deltaTime, maskActive ); - - if ( pass.needsSwap ) { - - if ( maskActive ) { - - const context = this.renderer.getContext(); - const stencil = this.renderer.state.buffers.stencil; - - //context.stencilFunc( context.NOTEQUAL, 1, 0xffffffff ); - stencil.setFunc( context.NOTEQUAL, 1, 0xffffffff ); - - this.copyPass.render( this.renderer, this.writeBuffer, this.readBuffer, deltaTime ); - - //context.stencilFunc( context.EQUAL, 1, 0xffffffff ); - stencil.setFunc( context.EQUAL, 1, 0xffffffff ); - - } - - this.swapBuffers(); - - } - - if ( MaskPass !== undefined ) { - - if ( pass instanceof MaskPass ) { - - maskActive = true; - - } else if ( pass instanceof ClearMaskPass ) { - - maskActive = false; - - } - - } - - } - - this.renderer.setRenderTarget( currentRenderTarget ); - - } - - reset( renderTarget ) { - - if ( renderTarget === undefined ) { - - const size = this.renderer.getSize( new Vector2$1() ); - this._pixelRatio = this.renderer.getPixelRatio(); - this._width = size.width; - this._height = size.height; - - renderTarget = this.renderTarget1.clone(); - renderTarget.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio ); - - } - - this.renderTarget1.dispose(); - this.renderTarget2.dispose(); - this.renderTarget1 = renderTarget; - this.renderTarget2 = renderTarget.clone(); - - this.writeBuffer = this.renderTarget1; - this.readBuffer = this.renderTarget2; - - } - - setSize( width, height ) { - - this._width = width; - this._height = height; - - const effectiveWidth = this._width * this._pixelRatio; - const effectiveHeight = this._height * this._pixelRatio; - - this.renderTarget1.setSize( effectiveWidth, effectiveHeight ); - this.renderTarget2.setSize( effectiveWidth, effectiveHeight ); - - for ( let i = 0; i < this.passes.length; i ++ ) { - - this.passes[ i ].setSize( effectiveWidth, effectiveHeight ); - - } - - } - - setPixelRatio( pixelRatio ) { - - this._pixelRatio = pixelRatio; - - this.setSize( this._width, this._height ); - - } - - dispose() { - - this.renderTarget1.dispose(); - this.renderTarget2.dispose(); - - this.copyPass.dispose(); - - } - -} - -class RenderPass extends Pass { - - constructor( scene, camera, overrideMaterial, clearColor, clearAlpha ) { - - super(); - - this.scene = scene; - this.camera = camera; - - this.overrideMaterial = overrideMaterial; - - this.clearColor = clearColor; - this.clearAlpha = ( clearAlpha !== undefined ) ? clearAlpha : 0; - - this.clear = true; - this.clearDepth = false; - this.needsSwap = false; - this._oldClearColor = new Color(); - - } - - render( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) { - - const oldAutoClear = renderer.autoClear; - renderer.autoClear = false; - - let oldClearAlpha, oldOverrideMaterial; - - if ( this.overrideMaterial !== undefined ) { - - oldOverrideMaterial = this.scene.overrideMaterial; - - this.scene.overrideMaterial = this.overrideMaterial; - - } - - if ( this.clearColor ) { - - renderer.getClearColor( this._oldClearColor ); - oldClearAlpha = renderer.getClearAlpha(); - - renderer.setClearColor( this.clearColor, this.clearAlpha ); - - } - - if ( this.clearDepth ) { - - renderer.clearDepth(); - - } - - renderer.setRenderTarget( this.renderToScreen ? null : readBuffer ); - - // TODO: Avoid using autoClear properties, see https://github.com/mrdoob/three.js/pull/15571#issuecomment-465669600 - if ( this.clear ) renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); - renderer.render( this.scene, this.camera ); - - if ( this.clearColor ) { - - renderer.setClearColor( this._oldClearColor, oldClearAlpha ); - - } - - if ( this.overrideMaterial !== undefined ) { - - this.scene.overrideMaterial = oldOverrideMaterial; - - } - - renderer.autoClear = oldAutoClear; - - } - +const observabilityMiddleware = { + stack: "dbcore", + level: 0, + create: (core) => { + const dbName = core.schema.name; + const FULL_RANGE = new RangeSet(core.MIN_KEY, core.MAX_KEY); + return { + ...core, + table: (tableName) => { + const table = core.table(tableName); + const { schema } = table; + const { primaryKey } = schema; + const { extractKey, outbound } = primaryKey; + const tableClone = { + ...table, + mutate: (req) => { + const trans = req.trans; + const mutatedParts = trans.mutatedParts || (trans.mutatedParts = {}); + const getRangeSet = (indexName) => { + const part = `idb://${dbName}/${tableName}/${indexName}`; + return (mutatedParts[part] || + (mutatedParts[part] = new RangeSet())); + }; + const pkRangeSet = getRangeSet(""); + const delsRangeSet = getRangeSet(":dels"); + const { type } = req; + let [keys, newObjs] = req.type === "deleteRange" + ? [req.range] + : req.type === "delete" + ? [req.keys] + : req.values.length < 50 + ? [[], req.values] + : []; + const oldCache = req.trans["_cache"]; + return table.mutate(req).then((res) => { + if (isArray(keys)) { + if (type !== "delete") + keys = res.results; + pkRangeSet.addKeys(keys); + const oldObjs = getFromTransactionCache(keys, oldCache); + if (!oldObjs && type !== "add") { + delsRangeSet.addKeys(keys); + } + if (oldObjs || newObjs) { + trackAffectedIndexes(getRangeSet, schema, oldObjs, newObjs); + } + } + else if (keys) { + const range = { from: keys.lower, to: keys.upper }; + delsRangeSet.add(range); + pkRangeSet.add(range); + } + else { + pkRangeSet.add(FULL_RANGE); + delsRangeSet.add(FULL_RANGE); + schema.indexes.forEach(idx => getRangeSet(idx.name).add(FULL_RANGE)); + } + return res; + }); + }, + }; + const getRange = ({ query: { index, range }, }) => { + var _a, _b; + return [ + index, + new RangeSet((_a = range.lower) !== null && _a !== void 0 ? _a : core.MIN_KEY, (_b = range.upper) !== null && _b !== void 0 ? _b : core.MAX_KEY), + ]; + }; + const readSubscribers = { + get: (req) => [primaryKey, new RangeSet(req.key)], + getMany: (req) => [primaryKey, new RangeSet().addKeys(req.keys)], + count: getRange, + query: getRange, + openCursor: getRange, + }; + keys(readSubscribers).forEach(method => { + tableClone[method] = function (req) { + const { subscr } = PSD; + if (subscr) { + const getRangeSet = (indexName) => { + const part = `idb://${dbName}/${tableName}/${indexName}`; + return (subscr[part] || + (subscr[part] = new RangeSet())); + }; + const pkRangeSet = getRangeSet(""); + const delsRangeSet = getRangeSet(":dels"); + const [queriedIndex, queriedRanges] = readSubscribers[method](req); + getRangeSet(queriedIndex.name || "").add(queriedRanges); + if (!queriedIndex.isPrimaryKey) { + if (method === "count") { + delsRangeSet.add(FULL_RANGE); + } + else { + const keysPromise = method === "query" && + outbound && + req.values && + table.query({ + ...req, + values: false, + }); + return table[method].apply(this, arguments).then((res) => { + if (method === "query") { + if (outbound && req.values) { + return keysPromise.then(({ result: resultingKeys }) => { + pkRangeSet.addKeys(resultingKeys); + return res; + }); + } + const pKeys = req.values + ? res.result.map(extractKey) + : res.result; + if (req.values) { + pkRangeSet.addKeys(pKeys); + } + else { + delsRangeSet.addKeys(pKeys); + } + } + else if (method === "openCursor") { + const cursor = res; + const wantValues = req.values; + return (cursor && + Object.create(cursor, { + key: { + get() { + delsRangeSet.addKey(cursor.primaryKey); + return cursor.key; + }, + }, + primaryKey: { + get() { + const pkey = cursor.primaryKey; + delsRangeSet.addKey(pkey); + return pkey; + }, + }, + value: { + get() { + wantValues && pkRangeSet.addKey(cursor.primaryKey); + return cursor.value; + }, + }, + })); + } + return res; + }); + } + } + } + return table[method].apply(this, arguments); + }; + }); + return tableClone; + }, + }; + }, +}; +function trackAffectedIndexes(getRangeSet, schema, oldObjs, newObjs) { + function addAffectedIndex(ix) { + const rangeSet = getRangeSet(ix.name || ""); + function extractKey(obj) { + return obj != null ? ix.extractKey(obj) : null; + } + const addKeyOrKeys = (key) => ix.multiEntry && isArray(key) + ? key.forEach(key => rangeSet.addKey(key)) + : rangeSet.addKey(key); + (oldObjs || newObjs).forEach((_, i) => { + const oldKey = oldObjs && extractKey(oldObjs[i]); + const newKey = newObjs && extractKey(newObjs[i]); + if (cmp(oldKey, newKey) !== 0) { + if (oldKey != null) + addKeyOrKeys(oldKey); + if (newKey != null) + addKeyOrKeys(newKey); + } + }); + } + schema.indexes.forEach(addAffectedIndex); } -/** - * postprocessing v6.33.3 build Mon Oct 30 2023 - * https://github.com/pmndrs/postprocessing - * Copyright 2015-2023 Raoul van RĂ¼schen - * @license Zlib - */ - - -// src/utils/BackCompat.js -Number(REVISION.replace(/\D+/g, "")); - -const $e4ca8dcb0218f846$var$_geometry = new BufferGeometry(); -$e4ca8dcb0218f846$var$_geometry.setAttribute("position", new BufferAttribute$1(new Float32Array([ - -1, - -1, - 3, - -1, - -1, - 3 -]), 2)); -$e4ca8dcb0218f846$var$_geometry.setAttribute("uv", new BufferAttribute$1(new Float32Array([ - 0, - 0, - 2, - 0, - 0, - 2 -]), 2)); -// Recent three.js versions break setDrawRange or itemSize <3 position -$e4ca8dcb0218f846$var$_geometry.boundingSphere = new Sphere(); -$e4ca8dcb0218f846$var$_geometry.computeBoundingSphere = function() {}; -const $e4ca8dcb0218f846$var$_camera = new OrthographicCamera(); -class $e4ca8dcb0218f846$export$dcd670d73db751f5 { - constructor(material){ - this._mesh = new Mesh($e4ca8dcb0218f846$var$_geometry, material); - this._mesh.frustumCulled = false; - } - render(renderer) { - renderer.render(this._mesh, $e4ca8dcb0218f846$var$_camera); +class Dexie$1 { + constructor(name, options) { + this._middlewares = {}; + this.verno = 0; + const deps = Dexie$1.dependencies; + this._options = options = { + addons: Dexie$1.addons, + autoOpen: true, + indexedDB: deps.indexedDB, + IDBKeyRange: deps.IDBKeyRange, + ...options + }; + this._deps = { + indexedDB: options.indexedDB, + IDBKeyRange: options.IDBKeyRange + }; + const { addons, } = options; + this._dbSchema = {}; + this._versions = []; + this._storeNames = []; + this._allTables = {}; + this.idbdb = null; + this._novip = this; + const state = { + dbOpenError: null, + isBeingOpened: false, + onReadyBeingFired: null, + openComplete: false, + dbReadyResolve: nop, + dbReadyPromise: null, + cancelOpen: nop, + openCanceller: null, + autoSchema: true, + PR1398_maxLoop: 3 + }; + state.dbReadyPromise = new DexiePromise(resolve => { + state.dbReadyResolve = resolve; + }); + state.openCanceller = new DexiePromise((_, reject) => { + state.cancelOpen = reject; + }); + this._state = state; + this.name = name; + this.on = Events(this, "populate", "blocked", "versionchange", "close", { ready: [promisableChain, nop] }); + this.on.ready.subscribe = override(this.on.ready.subscribe, subscribe => { + return (subscriber, bSticky) => { + Dexie$1.vip(() => { + const state = this._state; + if (state.openComplete) { + if (!state.dbOpenError) + DexiePromise.resolve().then(subscriber); + if (bSticky) + subscribe(subscriber); + } + else if (state.onReadyBeingFired) { + state.onReadyBeingFired.push(subscriber); + if (bSticky) + subscribe(subscriber); + } + else { + subscribe(subscriber); + const db = this; + if (!bSticky) + subscribe(function unsubscribe() { + db.on.ready.unsubscribe(subscriber); + db.on.ready.unsubscribe(unsubscribe); + }); + } + }); + }; + }); + this.Collection = createCollectionConstructor(this); + this.Table = createTableConstructor(this); + this.Transaction = createTransactionConstructor(this); + this.Version = createVersionConstructor(this); + this.WhereClause = createWhereClauseConstructor(this); + this.on("versionchange", ev => { + if (ev.newVersion > 0) + console.warn(`Another connection wants to upgrade database '${this.name}'. Closing db now to resume the upgrade.`); + else + console.warn(`Another connection wants to delete database '${this.name}'. Closing db now to resume the delete request.`); + this.close(); + }); + this.on("blocked", ev => { + if (!ev.newVersion || ev.newVersion < ev.oldVersion) + console.warn(`Dexie.delete('${this.name}') was blocked`); + else + console.warn(`Upgrade '${this.name}' blocked by other connection holding version ${ev.oldVersion / 10}`); + }); + this._maxKey = getMaxKey(options.IDBKeyRange); + this._createTransaction = (mode, storeNames, dbschema, parentTransaction) => new this.Transaction(mode, storeNames, dbschema, this._options.chromeTransactionDurability, parentTransaction); + this._fireOnBlocked = ev => { + this.on("blocked").fire(ev); + connections + .filter(c => c.name === this.name && c !== this && !c._state.vcFired) + .map(c => c.on("versionchange").fire(ev)); + }; + this.use(virtualIndexMiddleware); + this.use(hooksMiddleware); + this.use(observabilityMiddleware); + this.use(cacheExistingValuesMiddleware); + this.vip = Object.create(this, { _vip: { value: true } }); + addons.forEach(addon => addon(this)); } - get material() { - return this._mesh.material; + version(versionNumber) { + if (isNaN(versionNumber) || versionNumber < 0.1) + throw new exceptions.Type(`Given version is not a positive number`); + versionNumber = Math.round(versionNumber * 10) / 10; + if (this.idbdb || this._state.isBeingOpened) + throw new exceptions.Schema("Cannot add version when database is open"); + this.verno = Math.max(this.verno, versionNumber); + const versions = this._versions; + var versionInstance = versions.filter(v => v._cfg.version === versionNumber)[0]; + if (versionInstance) + return versionInstance; + versionInstance = new this.Version(versionNumber); + versions.push(versionInstance); + versions.sort(lowerVersionFirst); + versionInstance.stores({}); + this._state.autoSchema = false; + return versionInstance; } - set material(value) { - this._mesh.material = value; + _whenReady(fn) { + return (this.idbdb && (this._state.openComplete || PSD.letThrough || this._vip)) ? fn() : new DexiePromise((resolve, reject) => { + if (this._state.openComplete) { + return reject(new exceptions.DatabaseClosed(this._state.dbOpenError)); + } + if (!this._state.isBeingOpened) { + if (!this._options.autoOpen) { + reject(new exceptions.DatabaseClosed()); + return; + } + this.open().catch(nop); + } + this._state.dbReadyPromise.then(resolve, reject); + }).then(fn); } - dispose() { - this._mesh.material.dispose(); - this._mesh.geometry.dispose(); + use({ stack, create, level, name }) { + if (name) + this.unuse({ stack, name }); + const middlewares = this._middlewares[stack] || (this._middlewares[stack] = []); + middlewares.push({ stack, create, level: level == null ? 10 : level, name }); + middlewares.sort((a, b) => a.level - b.level); + return this; } -} - - - -const $1ed45968c1160c3c$export$c9b263b9a17dffd7 = { - uniforms: { - "sceneDiffuse": { - value: null - }, - "sceneDepth": { - value: null - }, - "sceneNormal": { - value: null - }, - "projMat": { - value: new Matrix4() - }, - "viewMat": { - value: new Matrix4() - }, - "projViewMat": { - value: new Matrix4() - }, - "projectionMatrixInv": { - value: new Matrix4() - }, - "viewMatrixInv": { - value: new Matrix4() - }, - "cameraPos": { - value: new Vector3$1() - }, - "resolution": { - value: new Vector2$1() - }, - "time": { - value: 0.0 - }, - "samples": { - value: [] - }, - "bluenoise": { - value: null - }, - "distanceFalloff": { - value: 1.0 - }, - "radius": { - value: 5.0 - }, - "near": { - value: 0.1 - }, - "far": { - value: 1000.0 - }, - "logDepth": { - value: false - }, - "ortho": { - value: false - }, - "screenSpaceRadius": { - value: false + unuse({ stack, name, create }) { + if (stack && this._middlewares[stack]) { + this._middlewares[stack] = this._middlewares[stack].filter(mw => create ? mw.create !== create : + name ? mw.name !== name : + false); } - }, - depthWrite: false, - depthTest: false, - vertexShader: /* glsl */ ` -varying vec2 vUv; -void main() { - vUv = uv; - gl_Position = vec4(position, 1); -}`, - fragmentShader: /* glsl */ ` - #define SAMPLES 16 - #define FSAMPLES 16.0 -uniform sampler2D sceneDiffuse; -uniform highp sampler2D sceneNormal; -uniform highp sampler2D sceneDepth; -uniform mat4 projectionMatrixInv; -uniform mat4 viewMatrixInv; -uniform mat4 projMat; -uniform mat4 viewMat; -uniform mat4 projViewMat; -uniform vec3 cameraPos; -uniform vec2 resolution; -uniform float time; -uniform vec3[SAMPLES] samples; -uniform float radius; -uniform float distanceFalloff; -uniform float near; -uniform float far; -uniform bool logDepth; -uniform bool ortho; -uniform bool screenSpaceRadius; -uniform sampler2D bluenoise; - varying vec2 vUv; - highp float linearize_depth(highp float d, highp float zNear,highp float zFar) - { - return (zFar * zNear) / (zFar - d * (zFar - zNear)); + return this; } - highp float linearize_depth_ortho(highp float d, highp float nearZ, highp float farZ) { - return nearZ + (farZ - nearZ) * d; + open() { + return dexieOpen(this); } - highp float linearize_depth_log(highp float d, highp float nearZ,highp float farZ) { - float depth = pow(2.0, d * log2(farZ + 1.0)) - 1.0; - float a = farZ / (farZ - nearZ); - float b = farZ * nearZ / (nearZ - farZ); - float linDepth = a + b / depth; - return ortho ? linearize_depth_ortho( - linDepth, - nearZ, - farZ - ) :linearize_depth(linDepth, nearZ, farZ); + _close() { + const state = this._state; + const idx = connections.indexOf(this); + if (idx >= 0) + connections.splice(idx, 1); + if (this.idbdb) { + try { + this.idbdb.close(); + } + catch (e) { } + this._novip.idbdb = null; + } + state.dbReadyPromise = new DexiePromise(resolve => { + state.dbReadyResolve = resolve; + }); + state.openCanceller = new DexiePromise((_, reject) => { + state.cancelOpen = reject; + }); } - - vec3 getWorldPosLog(vec3 posS) { - vec2 uv = posS.xy; - float z = posS.z; - float nearZ =near; - float farZ = far; - float depth = pow(2.0, z * log2(farZ + 1.0)) - 1.0; - float a = farZ / (farZ - nearZ); - float b = farZ * nearZ / (nearZ - farZ); - float linDepth = a + b / depth; - vec4 clipVec = vec4(uv, linDepth, 1.0) * 2.0 - 1.0; - vec4 wpos = projectionMatrixInv * clipVec; - return wpos.xyz / wpos.w; + close() { + this._close(); + const state = this._state; + this._options.autoOpen = false; + state.dbOpenError = new exceptions.DatabaseClosed(); + if (state.isBeingOpened) + state.cancelOpen(state.dbOpenError); } - vec3 getWorldPos(float depth, vec2 coord) { - #ifdef LOGDEPTH - return getWorldPosLog(vec3(coord, depth)); - #endif - float z = depth * 2.0 - 1.0; - vec4 clipSpacePosition = vec4(coord * 2.0 - 1.0, z, 1.0); - vec4 viewSpacePosition = projectionMatrixInv * clipSpacePosition; - // Perspective division - vec4 worldSpacePosition = viewSpacePosition; - worldSpacePosition.xyz /= worldSpacePosition.w; - return worldSpacePosition.xyz; - } - - vec3 computeNormal(vec3 worldPos, vec2 vUv) { - ivec2 p = ivec2(vUv * resolution); - float c0 = texelFetch(sceneDepth, p, 0).x; - float l2 = texelFetch(sceneDepth, p - ivec2(2, 0), 0).x; - float l1 = texelFetch(sceneDepth, p - ivec2(1, 0), 0).x; - float r1 = texelFetch(sceneDepth, p + ivec2(1, 0), 0).x; - float r2 = texelFetch(sceneDepth, p + ivec2(2, 0), 0).x; - float b2 = texelFetch(sceneDepth, p - ivec2(0, 2), 0).x; - float b1 = texelFetch(sceneDepth, p - ivec2(0, 1), 0).x; - float t1 = texelFetch(sceneDepth, p + ivec2(0, 1), 0).x; - float t2 = texelFetch(sceneDepth, p + ivec2(0, 2), 0).x; - - float dl = abs((2.0 * l1 - l2) - c0); - float dr = abs((2.0 * r1 - r2) - c0); - float db = abs((2.0 * b1 - b2) - c0); - float dt = abs((2.0 * t1 - t2) - c0); - - vec3 ce = getWorldPos(c0, vUv).xyz; - - vec3 dpdx = (dl < dr) ? ce - getWorldPos(l1, (vUv - vec2(1.0 / resolution.x, 0.0))).xyz - : -ce + getWorldPos(r1, (vUv + vec2(1.0 / resolution.x, 0.0))).xyz; - vec3 dpdy = (db < dt) ? ce - getWorldPos(b1, (vUv - vec2(0.0, 1.0 / resolution.y))).xyz - : -ce + getWorldPos(t1, (vUv + vec2(0.0, 1.0 / resolution.y))).xyz; + delete() { + const hasArguments = arguments.length > 0; + const state = this._state; + return new DexiePromise((resolve, reject) => { + const doDelete = () => { + this.close(); + var req = this._deps.indexedDB.deleteDatabase(this.name); + req.onsuccess = wrap(() => { + _onDatabaseDeleted(this._deps, this.name); + resolve(); + }); + req.onerror = eventRejectHandler(reject); + req.onblocked = this._fireOnBlocked; + }; + if (hasArguments) + throw new exceptions.InvalidArgument("Arguments not allowed in db.delete()"); + if (state.isBeingOpened) { + state.dbReadyPromise.then(doDelete); + } + else { + doDelete(); + } + }); + } + backendDB() { + return this.idbdb; + } + isOpen() { + return this.idbdb !== null; + } + hasBeenClosed() { + const dbOpenError = this._state.dbOpenError; + return dbOpenError && (dbOpenError.name === 'DatabaseClosed'); + } + hasFailed() { + return this._state.dbOpenError !== null; + } + dynamicallyOpened() { + return this._state.autoSchema; + } + get tables() { + return keys(this._allTables).map(name => this._allTables[name]); + } + transaction() { + const args = extractTransactionArgs.apply(this, arguments); + return this._transaction.apply(this, args); + } + _transaction(mode, tables, scopeFunc) { + let parentTransaction = PSD.trans; + if (!parentTransaction || parentTransaction.db !== this || mode.indexOf('!') !== -1) + parentTransaction = null; + const onlyIfCompatible = mode.indexOf('?') !== -1; + mode = mode.replace('!', '').replace('?', ''); + let idbMode, storeNames; + try { + storeNames = tables.map(table => { + var storeName = table instanceof this.Table ? table.name : table; + if (typeof storeName !== 'string') + throw new TypeError("Invalid table argument to Dexie.transaction(). Only Table or String are allowed"); + return storeName; + }); + if (mode == "r" || mode === READONLY) + idbMode = READONLY; + else if (mode == "rw" || mode == READWRITE) + idbMode = READWRITE; + else + throw new exceptions.InvalidArgument("Invalid transaction mode: " + mode); + if (parentTransaction) { + if (parentTransaction.mode === READONLY && idbMode === READWRITE) { + if (onlyIfCompatible) { + parentTransaction = null; + } + else + throw new exceptions.SubTransaction("Cannot enter a sub-transaction with READWRITE mode when parent transaction is READONLY"); + } + if (parentTransaction) { + storeNames.forEach(storeName => { + if (parentTransaction && parentTransaction.storeNames.indexOf(storeName) === -1) { + if (onlyIfCompatible) { + parentTransaction = null; + } + else + throw new exceptions.SubTransaction("Table " + storeName + + " not included in parent transaction."); + } + }); + } + if (onlyIfCompatible && parentTransaction && !parentTransaction.active) { + parentTransaction = null; + } + } + } + catch (e) { + return parentTransaction ? + parentTransaction._promise(null, (_, reject) => { reject(e); }) : + rejection(e); + } + const enterTransaction = enterTransactionScope.bind(null, this, idbMode, storeNames, parentTransaction, scopeFunc); + return (parentTransaction ? + parentTransaction._promise(idbMode, enterTransaction, "lock") : + PSD.trans ? + usePSD(PSD.transless, () => this._whenReady(enterTransaction)) : + this._whenReady(enterTransaction)); + } + table(tableName) { + if (!hasOwn(this._allTables, tableName)) { + throw new exceptions.InvalidTable(`Table ${tableName} does not exist`); + } + return this._allTables[tableName]; + } +} - return normalize(cross(dpdx, dpdy)); +const symbolObservable = typeof Symbol !== "undefined" && "observable" in Symbol + ? Symbol.observable + : "@@observable"; +class Observable { + constructor(subscribe) { + this._subscribe = subscribe; + } + subscribe(x, error, complete) { + return this._subscribe(!x || typeof x === "function" ? { next: x, error, complete } : x); + } + [symbolObservable]() { + return this; + } } -mat3 makeRotationZ(float theta) { - float c = cos(theta); - float s = sin(theta); - return mat3(c, - s, 0, - s, c, 0, - 0, 0, 1); - } +function extendObservabilitySet(target, newSet) { + keys(newSet).forEach(part => { + const rangeSet = target[part] || (target[part] = new RangeSet()); + mergeRanges(rangeSet, newSet[part]); + }); + return target; +} -void main() { - vec4 diffuse = texture2D(sceneDiffuse, vUv); - float depth = texture2D(sceneDepth, vUv).x; - if (depth == 1.0) { - gl_FragColor = vec4(vec3(1.0), 1.0); - return; - } - vec3 worldPos = getWorldPos(depth, vUv); - #ifdef HALFRES - vec3 normal = texture2D(sceneNormal, vUv).rgb; - #else - vec3 normal = computeNormal(worldPos, vUv); - #endif - vec4 noise = texture2D(bluenoise, gl_FragCoord.xy / 128.0); - vec3 helperVec = vec3(0.0, 1.0, 0.0); - if (dot(helperVec, normal) > 0.99) { - helperVec = vec3(1.0, 0.0, 0.0); +function liveQuery(querier) { + let hasValue = false; + let currentValue = undefined; + const observable = new Observable((observer) => { + const scopeFuncIsAsync = isAsyncFunction(querier); + function execute(subscr) { + if (scopeFuncIsAsync) { + incrementExpectedAwaits(); + } + const exec = () => newScope(querier, { subscr, trans: null }); + const rv = PSD.trans + ? + usePSD(PSD.transless, exec) + : exec(); + if (scopeFuncIsAsync) { + rv.then(decrementExpectedAwaits, decrementExpectedAwaits); + } + return rv; } - vec3 tangent = normalize(cross(helperVec, normal)); - vec3 bitangent = cross(normal, tangent); - mat3 tbn = mat3(tangent, bitangent, normal) * makeRotationZ(noise.r * 2.0 * 3.1415962) ; - - float occluded = 0.0; - float totalWeight = 0.0; - float radiusToUse = screenSpaceRadius ? distance( - worldPos, - getWorldPos(depth, vUv + - vec2(radius, 0.0) / resolution) - ) : radius; - float distanceFalloffToUse =screenSpaceRadius ? - radiusToUse * distanceFalloff - : radiusToUse * distanceFalloff * 0.2; - float bias = (min( - 0.1, - distanceFalloffToUse * 0.1 - ) / near) * fwidth(distance(worldPos, cameraPos)) / radiusToUse; - float phi = 1.61803398875; - float offsetMove = 0.0; - float offsetMoveInv = 1.0 / FSAMPLES; - for(float i = 0.0; i < FSAMPLES; i++) { - vec3 sampleDirection = tbn * samples[int(i)]; - - float moveAmt = fract(noise.g + offsetMove); - offsetMove += offsetMoveInv; - - vec3 samplePos = worldPos + radiusToUse * moveAmt * sampleDirection; - vec4 offset = projMat * vec4(samplePos, 1.0); - offset.xyz /= offset.w; - offset.xyz = offset.xyz * 0.5 + 0.5; - - vec2 diff = gl_FragCoord.xy - floor(offset.xy * resolution); - // From Rabbid76's hbao - vec2 clipRangeCheck = step(vec2(0.0),offset.xy) * step(offset.xy, vec2(1.0)); - float sampleDepth = textureLod(sceneDepth, offset.xy, 0.0).x; - - #ifdef LOGDEPTH - - float distSample = linearize_depth_log(sampleDepth, near, far); - - #else - - float distSample = ortho ? linearize_depth_ortho(sampleDepth, near, far) : linearize_depth(sampleDepth, near, far); - - #endif - - float distWorld = ortho ? linearize_depth_ortho(offset.z, near, far) : linearize_depth(offset.z, near, far); - - float rangeCheck = smoothstep(0.0, 1.0, distanceFalloffToUse / (abs(distSample - distWorld))); - - float sampleValid = (clipRangeCheck.x * clipRangeCheck.y); - occluded += rangeCheck * float(sampleDepth != depth) * float(distSample + bias < distWorld) * step( - 1.0, - dot(diff, diff) - ) * sampleValid; - - totalWeight += sampleValid; - } - float occ = clamp(1.0 - occluded / (totalWeight == 0.0 ? 1.0 : totalWeight), 0.0, 1.0); - gl_FragColor = vec4(0.5 + 0.5 * normal, occ); -}` -}; - + let closed = false; + let accumMuts = {}; + let currentObs = {}; + const subscription = { + get closed() { + return closed; + }, + unsubscribe: () => { + closed = true; + globalEvents.storagemutated.unsubscribe(mutationListener); + }, + }; + observer.start && observer.start(subscription); + let querying = false, startedListening = false; + function shouldNotify() { + return keys(currentObs).some((key) => accumMuts[key] && rangesOverlap(accumMuts[key], currentObs[key])); + } + const mutationListener = (parts) => { + extendObservabilitySet(accumMuts, parts); + if (shouldNotify()) { + doQuery(); + } + }; + const doQuery = () => { + if (querying || closed) + return; + accumMuts = {}; + const subscr = {}; + const ret = execute(subscr); + if (!startedListening) { + globalEvents(DEXIE_STORAGE_MUTATED_EVENT_NAME, mutationListener); + startedListening = true; + } + querying = true; + Promise.resolve(ret).then((result) => { + hasValue = true; + currentValue = result; + querying = false; + if (closed) + return; + if (shouldNotify()) { + doQuery(); + } + else { + accumMuts = {}; + currentObs = subscr; + observer.next && observer.next(result); + } + }, (err) => { + querying = false; + hasValue = false; + observer.error && observer.error(err); + subscription.unsubscribe(); + }); + }; + doQuery(); + return subscription; + }); + observable.hasValue = () => hasValue; + observable.getValue = () => currentValue; + return observable; +} +let domDeps; +try { + domDeps = { + indexedDB: _global.indexedDB || _global.mozIndexedDB || _global.webkitIndexedDB || _global.msIndexedDB, + IDBKeyRange: _global.IDBKeyRange || _global.webkitIDBKeyRange + }; +} +catch (e) { + domDeps = { indexedDB: null, IDBKeyRange: null }; +} -const $12b21d24d1192a04$export$a815acccbd2c9a49 = { - uniforms: { - "sceneDiffuse": { - value: null - }, - "sceneDepth": { - value: null - }, - "tDiffuse": { - value: null - }, - "transparencyDWFalse": { - value: null - }, - "transparencyDWTrue": { - value: null - }, - "transparencyDWTrueDepth": { - value: null - }, - "transparencyAware": { - value: false - }, - "projMat": { - value: new Matrix4() - }, - "viewMat": { - value: new Matrix4() - }, - "projectionMatrixInv": { - value: new Matrix4() - }, - "viewMatrixInv": { - value: new Matrix4() - }, - "cameraPos": { - value: new Vector3$1() - }, - "resolution": { - value: new Vector2$1() - }, - "color": { - value: new Vector3$1(0, 0, 0) - }, - "blueNoise": { - value: null - }, - "downsampledDepth": { - value: null - }, - "time": { - value: 0.0 - }, - "intensity": { - value: 10.0 - }, - "renderMode": { - value: 0.0 - }, - "gammaCorrection": { - value: false - }, - "logDepth": { - value: false - }, - "ortho": { - value: false - }, - "near": { - value: 0.1 - }, - "far": { - value: 1000.0 - }, - "screenSpaceRadius": { - value: false - }, - "radius": { - value: 0.0 - }, - "distanceFalloff": { - value: 1.0 - }, - "fog": { - value: false - }, - "fogExp": { - value: false - }, - "fogDensity": { - value: 0.0 - }, - "fogNear": { - value: Infinity - }, - "fogFar": { - value: Infinity - }, - "colorMultiply": { - value: true +const Dexie = Dexie$1; +props(Dexie, { + ...fullNameExceptions, + delete(databaseName) { + const db = new Dexie(databaseName, { addons: [] }); + return db.delete(); + }, + exists(name) { + return new Dexie(name, { addons: [] }).open().then(db => { + db.close(); + return true; + }).catch('NoSuchDatabaseError', () => false); + }, + getDatabaseNames(cb) { + try { + return getDatabaseNames(Dexie.dependencies).then(cb); + } + catch (_a) { + return rejection(new exceptions.MissingAPI()); } }, - depthWrite: false, - depthTest: false, - vertexShader: /* glsl */ ` - varying vec2 vUv; - void main() { - vUv = uv; - gl_Position = vec4(position, 1); - }`, - fragmentShader: /* glsl */ ` - uniform sampler2D sceneDiffuse; - uniform highp sampler2D sceneDepth; - uniform highp sampler2D downsampledDepth; - uniform highp sampler2D transparencyDWFalse; - uniform highp sampler2D transparencyDWTrue; - uniform highp sampler2D transparencyDWTrueDepth; - uniform sampler2D tDiffuse; - uniform sampler2D blueNoise; - uniform vec2 resolution; - uniform vec3 color; - uniform mat4 projectionMatrixInv; - uniform mat4 viewMatrixInv; - uniform float intensity; - uniform float renderMode; - uniform float near; - uniform float far; - uniform bool gammaCorrection; - uniform bool logDepth; - uniform bool ortho; - uniform bool screenSpaceRadius; - uniform bool fog; - uniform bool fogExp; - uniform bool colorMultiply; - uniform bool transparencyAware; - uniform float fogDensity; - uniform float fogNear; - uniform float fogFar; - uniform float radius; - uniform float distanceFalloff; - uniform vec3 cameraPos; - varying vec2 vUv; - highp float linearize_depth(highp float d, highp float zNear,highp float zFar) - { - return (zFar * zNear) / (zFar - d * (zFar - zNear)); - } - highp float linearize_depth_ortho(highp float d, highp float nearZ, highp float farZ) { - return nearZ + (farZ - nearZ) * d; - } - highp float linearize_depth_log(highp float d, highp float nearZ,highp float farZ) { - float depth = pow(2.0, d * log2(farZ + 1.0)) - 1.0; - float a = farZ / (farZ - nearZ); - float b = farZ * nearZ / (nearZ - farZ); - float linDepth = a + b / depth; - return ortho ? linearize_depth_ortho( - linDepth, - nearZ, - farZ - ) :linearize_depth(linDepth, nearZ, farZ); - } - vec3 getWorldPosLog(vec3 posS) { - vec2 uv = posS.xy; - float z = posS.z; - float nearZ =near; - float farZ = far; - float depth = pow(2.0, z * log2(farZ + 1.0)) - 1.0; - float a = farZ / (farZ - nearZ); - float b = farZ * nearZ / (nearZ - farZ); - float linDepth = a + b / depth; - vec4 clipVec = vec4(uv, linDepth, 1.0) * 2.0 - 1.0; - vec4 wpos = projectionMatrixInv * clipVec; - return wpos.xyz / wpos.w; - } - vec3 getWorldPos(float depth, vec2 coord) { - // if (logDepth) { - #ifdef LOGDEPTH - return getWorldPosLog(vec3(coord, depth)); - #endif - // } - float z = depth * 2.0 - 1.0; - vec4 clipSpacePosition = vec4(coord * 2.0 - 1.0, z, 1.0); - vec4 viewSpacePosition = projectionMatrixInv * clipSpacePosition; - // Perspective division - vec4 worldSpacePosition = viewSpacePosition; - worldSpacePosition.xyz /= worldSpacePosition.w; - return worldSpacePosition.xyz; - } - - vec3 computeNormal(vec3 worldPos, vec2 vUv) { - ivec2 p = ivec2(vUv * resolution); - float c0 = texelFetch(sceneDepth, p, 0).x; - float l2 = texelFetch(sceneDepth, p - ivec2(2, 0), 0).x; - float l1 = texelFetch(sceneDepth, p - ivec2(1, 0), 0).x; - float r1 = texelFetch(sceneDepth, p + ivec2(1, 0), 0).x; - float r2 = texelFetch(sceneDepth, p + ivec2(2, 0), 0).x; - float b2 = texelFetch(sceneDepth, p - ivec2(0, 2), 0).x; - float b1 = texelFetch(sceneDepth, p - ivec2(0, 1), 0).x; - float t1 = texelFetch(sceneDepth, p + ivec2(0, 1), 0).x; - float t2 = texelFetch(sceneDepth, p + ivec2(0, 2), 0).x; - - float dl = abs((2.0 * l1 - l2) - c0); - float dr = abs((2.0 * r1 - r2) - c0); - float db = abs((2.0 * b1 - b2) - c0); - float dt = abs((2.0 * t1 - t2) - c0); - - vec3 ce = getWorldPos(c0, vUv).xyz; - - vec3 dpdx = (dl < dr) ? ce - getWorldPos(l1, (vUv - vec2(1.0 / resolution.x, 0.0))).xyz - : -ce + getWorldPos(r1, (vUv + vec2(1.0 / resolution.x, 0.0))).xyz; - vec3 dpdy = (db < dt) ? ce - getWorldPos(b1, (vUv - vec2(0.0, 1.0 / resolution.y))).xyz - : -ce + getWorldPos(t1, (vUv + vec2(0.0, 1.0 / resolution.y))).xyz; - - return normalize(cross(dpdx, dpdy)); - } + defineClass() { + function Class(content) { + extend(this, content); + } + return Class; + }, + ignoreTransaction(scopeFunc) { + return PSD.trans ? + usePSD(PSD.transless, scopeFunc) : + scopeFunc(); + }, + vip, + async: function (generatorFn) { + return function () { + try { + var rv = awaitIterator(generatorFn.apply(this, arguments)); + if (!rv || typeof rv.then !== 'function') + return DexiePromise.resolve(rv); + return rv; + } + catch (e) { + return rejection(e); + } + }; + }, + spawn: function (generatorFn, args, thiz) { + try { + var rv = awaitIterator(generatorFn.apply(thiz, args || [])); + if (!rv || typeof rv.then !== 'function') + return DexiePromise.resolve(rv); + return rv; + } + catch (e) { + return rejection(e); + } + }, + currentTransaction: { + get: () => PSD.trans || null + }, + waitFor: function (promiseOrFunction, optionalTimeout) { + const promise = DexiePromise.resolve(typeof promiseOrFunction === 'function' ? + Dexie.ignoreTransaction(promiseOrFunction) : + promiseOrFunction) + .timeout(optionalTimeout || 60000); + return PSD.trans ? + PSD.trans.waitFor(promise) : + promise; + }, + Promise: DexiePromise, + debug: { + get: () => debug, + set: value => { + setDebug(value, value === 'dexie' ? () => true : dexieStackFrameFilter); + } + }, + derive: derive, + extend: extend, + props: props, + override: override, + Events: Events, + on: globalEvents, + liveQuery, + extendObservabilitySet, + getByKeyPath: getByKeyPath, + setByKeyPath: setByKeyPath, + delByKeyPath: delByKeyPath, + shallowClone: shallowClone, + deepClone: deepClone, + getObjectDiff: getObjectDiff, + cmp, + asap: asap$1, + minKey: minKey, + addons: [], + connections: connections, + errnames: errnames, + dependencies: domDeps, + semVer: DEXIE_VERSION, + version: DEXIE_VERSION.split('.') + .map(n => parseInt(n)) + .reduce((p, c, i) => p + (c / Math.pow(10, i * 2))), +}); +Dexie.maxKey = getMaxKey(Dexie.dependencies.IDBKeyRange); - #include - #include - void main() { - //vec4 texel = texture2D(tDiffuse, vUv);//vec3(0.0); - vec4 sceneTexel = texture2D(sceneDiffuse, vUv); - float depth = texture2D( - sceneDepth, - vUv - ).x; - #ifdef HALFRES - vec4 texel; - if (depth == 1.0) { - texel = vec4(0.0, 0.0, 0.0, 1.0); - } else { - vec3 worldPos = getWorldPos(depth, vUv); - vec3 normal = computeNormal(getWorldPos(depth, vUv), vUv); - // vec4 texel = texture2D(tDiffuse, vUv); - // Find closest depth; - float totalWeight = 0.0; - float radiusToUse = screenSpaceRadius ? distance( - worldPos, - getWorldPos(depth, vUv + - vec2(radius, 0.0) / resolution) - ) : radius; - float distanceFalloffToUse =screenSpaceRadius ? - radiusToUse * distanceFalloff - : distanceFalloff; - for(float x = -1.0; x <= 1.0; x++) { - for(float y = -1.0; y <= 1.0; y++) { - vec2 offset = vec2(x, y); - ivec2 p = ivec2( - (vUv * resolution * 0.5) + offset - ); - vec2 pUv = vec2(p) / (resolution * 0.5); - float sampleDepth = texelFetch(downsampledDepth,p, 0).x; - vec4 sampleInfo = texelFetch(tDiffuse, p, 0); - vec3 normalSample = sampleInfo.xyz * 2.0 - 1.0; - vec3 worldPosSample = getWorldPos(sampleDepth, pUv); - float tangentPlaneDist = abs(dot(worldPosSample - worldPos, normal)); - float rangeCheck = exp(-1.0 * tangentPlaneDist * (1.0 / distanceFalloffToUse)) * max(dot(normal, normalSample), 0.0); - float weight = rangeCheck; - totalWeight += weight; - texel += sampleInfo * weight; +if (typeof dispatchEvent !== 'undefined' && typeof addEventListener !== 'undefined') { + globalEvents(DEXIE_STORAGE_MUTATED_EVENT_NAME, updatedParts => { + if (!propagatingLocally) { + let event; + if (isIEOrEdge) { + event = document.createEvent('CustomEvent'); + event.initCustomEvent(STORAGE_MUTATED_DOM_EVENT_NAME, true, true, updatedParts); + } + else { + event = new CustomEvent(STORAGE_MUTATED_DOM_EVENT_NAME, { + detail: updatedParts + }); } + propagatingLocally = true; + dispatchEvent(event); + propagatingLocally = false; } - if (totalWeight == 0.0) { - texel = texture2D(tDiffuse, vUv); - } else { - texel /= totalWeight; + }); + addEventListener(STORAGE_MUTATED_DOM_EVENT_NAME, ({ detail }) => { + if (!propagatingLocally) { + propagateLocally(detail); } + }); +} +function propagateLocally(updateParts) { + let wasMe = propagatingLocally; + try { + propagatingLocally = true; + globalEvents.storagemutated.fire(updateParts); } - #else - vec4 texel = texture2D(tDiffuse, vUv); - #endif + finally { + propagatingLocally = wasMe; + } +} +let propagatingLocally = false; - #ifdef LOGDEPTH - texel.a = clamp(texel.a, 0.0, 1.0); - if (texel.a == 0.0) { - texel.a = 1.0; +if (typeof BroadcastChannel !== 'undefined') { + const bc = new BroadcastChannel(STORAGE_MUTATED_DOM_EVENT_NAME); + if (typeof bc.unref === 'function') { + bc.unref(); + } + globalEvents(DEXIE_STORAGE_MUTATED_EVENT_NAME, (changedParts) => { + if (!propagatingLocally) { + bc.postMessage(changedParts); } - #endif - - float finalAo = pow(texel.a, intensity); - float fogFactor; - float fogDepth = distance( - cameraPos, - getWorldPos(depth, vUv) - ); - if (fog) { - if (fogExp) { - fogFactor = 1.0 - exp( - fogDensity * fogDensity * fogDepth * fogDepth ); - } else { - fogFactor = smoothstep( fogNear, fogFar, fogDepth ); + }); + bc.onmessage = (ev) => { + if (ev.data) + propagateLocally(ev.data); + }; +} +else if (typeof self !== 'undefined' && typeof navigator !== 'undefined') { + globalEvents(DEXIE_STORAGE_MUTATED_EVENT_NAME, (changedParts) => { + try { + if (!propagatingLocally) { + if (typeof localStorage !== 'undefined') { + localStorage.setItem(STORAGE_MUTATED_DOM_EVENT_NAME, JSON.stringify({ + trig: Math.random(), + changedParts, + })); + } + if (typeof self['clients'] === 'object') { + [...self['clients'].matchAll({ includeUncontrolled: true })].forEach((client) => client.postMessage({ + type: STORAGE_MUTATED_DOM_EVENT_NAME, + changedParts, + })); + } } } - if (transparencyAware) { - float transparencyDWOff = texture2D(transparencyDWFalse, vUv).a; - float transparencyDWOn = texture2D(transparencyDWTrue, vUv).a; - float adjustmentFactorOff = transparencyDWOff; - float adjustmentFactorOn = (1.0 - transparencyDWOn) * ( - texture2D(transparencyDWTrueDepth, vUv).r == texture2D(sceneDepth, vUv).r ? 1.0 : 0.0 - ); - float adjustmentFactor = max(adjustmentFactorOff, adjustmentFactorOn); - finalAo = mix(finalAo, 1.0, adjustmentFactor); - } - finalAo = mix(finalAo, 1.0, fogFactor); - vec3 aoApplied = color * mix(vec3(1.0), sceneTexel.rgb, float(colorMultiply)); - if (renderMode == 0.0) { - gl_FragColor = vec4( mix(sceneTexel.rgb, aoApplied, 1.0 - finalAo), sceneTexel.a); - } else if (renderMode == 1.0) { - gl_FragColor = vec4( mix(vec3(1.0), aoApplied, 1.0 - finalAo), sceneTexel.a); - } else if (renderMode == 2.0) { - gl_FragColor = vec4( sceneTexel.rgb, sceneTexel.a); - } else if (renderMode == 3.0) { - if (vUv.x < 0.5) { - gl_FragColor = vec4( sceneTexel.rgb, sceneTexel.a); - } else if (abs(vUv.x - 0.5) < 1.0 / resolution.x) { - gl_FragColor = vec4(1.0); - } else { - gl_FragColor = vec4( mix(sceneTexel.rgb, aoApplied, 1.0 - finalAo), sceneTexel.a); - } - } else if (renderMode == 4.0) { - if (vUv.x < 0.5) { - gl_FragColor = vec4( sceneTexel.rgb, sceneTexel.a); - } else if (abs(vUv.x - 0.5) < 1.0 / resolution.x) { - gl_FragColor = vec4(1.0); - } else { - gl_FragColor = vec4( mix(vec3(1.0), aoApplied, 1.0 - finalAo), sceneTexel.a); + catch (_a) { } + }); + if (typeof addEventListener !== 'undefined') { + addEventListener('storage', (ev) => { + if (ev.key === STORAGE_MUTATED_DOM_EVENT_NAME) { + const data = JSON.parse(ev.newValue); + if (data) + propagateLocally(data.changedParts); } - } - #include - if (gammaCorrection) { - gl_FragColor = LinearTosRGB(gl_FragColor); - } + }); } - ` -}; + const swContainer = self.document && navigator.serviceWorker; + if (swContainer) { + swContainer.addEventListener('message', propagateMessageLocally); + } +} +function propagateMessageLocally({ data }) { + if (data && data.type === STORAGE_MUTATED_DOM_EVENT_NAME) { + propagateLocally(data.changedParts); + } +} +DexiePromise.rejectionMapper = mapError; +setDebug(debug, dexieStackFrameFilter); +class ModelDatabase extends Dexie$1 { + constructor() { + super("ModelDatabase"); + this.version(2).stores({ + models: "id, file", + }); + } +} -const $e52378cd0f5a973d$export$57856b59f317262e = { - uniforms: { - "sceneDiffuse": { - value: null - }, - "sceneDepth": { - value: null - }, - "tDiffuse": { - value: null - }, - "projMat": { - value: new Matrix4() - }, - "viewMat": { - value: new Matrix4() - }, - "projectionMatrixInv": { - value: new Matrix4() - }, - "viewMatrixInv": { - value: new Matrix4() - }, - "cameraPos": { - value: new Vector3$1() - }, - "resolution": { - value: new Vector2$1() - }, - "time": { - value: 0.0 - }, - "r": { - value: 5.0 - }, - "blueNoise": { - value: null - }, - "radius": { - value: 12.0 - }, - "worldRadius": { - value: 5.0 - }, - "index": { - value: 0.0 - }, - "poissonDisk": { - value: [] - }, - "distanceFalloff": { - value: 1.0 - }, - "near": { - value: 0.1 - }, - "far": { - value: 1000.0 - }, - "logDepth": { - value: false - }, - "screenSpaceRadius": { - value: false - } - }, - depthWrite: false, - depthTest: false, - vertexShader: /* glsl */ ` - varying vec2 vUv; - void main() { - vUv = uv; - gl_Position = vec4(position, 1.0); - }`, - fragmentShader: /* glsl */ ` - uniform sampler2D sceneDiffuse; - uniform highp sampler2D sceneDepth; - uniform sampler2D tDiffuse; - uniform sampler2D blueNoise; - uniform mat4 projectionMatrixInv; - uniform mat4 viewMatrixInv; - uniform vec2 resolution; - uniform float r; - uniform float radius; - uniform float worldRadius; - uniform float index; - uniform float near; - uniform float far; - uniform float distanceFalloff; - uniform bool logDepth; - uniform bool screenSpaceRadius; - varying vec2 vUv; - - highp float linearize_depth(highp float d, highp float zNear,highp float zFar) - { - highp float z_n = 2.0 * d - 1.0; - return 2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear)); +// TODO: Implement UI elements (this is probably just for 3d scans) +/** + * A tool to cache files using the browser's IndexedDB API. This might + * save loading time and infrastructure costs for files that need to be + * fetched from the cloud. + */ +class LocalCacher extends Component { + /** The IDs of all the stored files. */ + get ids() { + const serialized = localStorage.getItem(this._storedModels) || "[]"; + return JSON.parse(serialized); } - highp float linearize_depth_log(highp float d, highp float nearZ,highp float farZ) { - float depth = pow(2.0, d * log2(farZ + 1.0)) - 1.0; - float a = farZ / (farZ - nearZ); - float b = farZ * nearZ / (nearZ - farZ); - float linDepth = a + b / depth; - return linearize_depth(linDepth, nearZ, farZ); - } - highp float linearize_depth_ortho(highp float d, highp float nearZ, highp float farZ) { - return nearZ + (farZ - nearZ) * d; - } - vec3 getWorldPosLog(vec3 posS) { - vec2 uv = posS.xy; - float z = posS.z; - float nearZ =near; - float farZ = far; - float depth = pow(2.0, z * log2(farZ + 1.0)) - 1.0; - float a = farZ / (farZ - nearZ); - float b = farZ * nearZ / (nearZ - farZ); - float linDepth = a + b / depth; - vec4 clipVec = vec4(uv, linDepth, 1.0) * 2.0 - 1.0; - vec4 wpos = projectionMatrixInv * clipVec; - return wpos.xyz / wpos.w; - } - vec3 getWorldPos(float depth, vec2 coord) { - #ifdef LOGDEPTH - return getWorldPosLog(vec3(coord, depth)); - #endif - - float z = depth * 2.0 - 1.0; - vec4 clipSpacePosition = vec4(coord * 2.0 - 1.0, z, 1.0); - vec4 viewSpacePosition = projectionMatrixInv * clipSpacePosition; - // Perspective division - vec4 worldSpacePosition = viewSpacePosition; - worldSpacePosition.xyz /= worldSpacePosition.w; - return worldSpacePosition.xyz; + constructor(components) { + super(components); + /** Fires when a file has been loaded from cache. */ + this.onFileLoaded = new Event(); + /** Fires when a file has been saved into cache. */ + this.onItemSaved = new Event(); + /** {@link Disposable.onDisposed} */ + this.onDisposed = new Event(); + /** {@link Component.enabled} */ + this.enabled = true; + /** {@link UI.uiElement} */ + this.uiElement = new UIElement(); + this.cards = []; + this._storedModels = "open-bim-components-stored-files"; + components.tools.add(LocalCacher.uuid, this); + this._db = new ModelDatabase(); + if (components.uiEnabled) { + this.setUI(components); + } } - #include - #define NUM_SAMPLES 16 - uniform vec2 poissonDisk[NUM_SAMPLES]; - void main() { - const float pi = 3.14159; - vec2 texelSize = vec2(1.0 / resolution.x, 1.0 / resolution.y); - vec2 uv = vUv; - vec4 data = texture2D(tDiffuse, vUv); - float occlusion = data.a; - float baseOcc = data.a; - vec3 normal = data.rgb * 2.0 - 1.0; - float count = 1.0; - float d = texture2D(sceneDepth, vUv).x; - if (d == 1.0) { - gl_FragColor = data; - return; + /** + * {@link Component.get}. + * @param id the ID of the file to fetch. + */ + async get(id) { + if (this.exists(id)) { + await this._db.open(); + const result = await this.getModelFromLocalCache(id); + this._db.close(); + return result; } - vec3 worldPos = getWorldPos(d, vUv); - float size = radius; - float angle; - if (index == 0.0) { - angle = texture2D(blueNoise, gl_FragCoord.xy / 128.0).x * PI2; - } else if (index == 1.0) { - angle = texture2D(blueNoise, gl_FragCoord.xy / 128.0).y * PI2; - } else if (index == 2.0) { - angle = texture2D(blueNoise, gl_FragCoord.xy / 128.0).z * PI2; - } else { - angle = texture2D(blueNoise, gl_FragCoord.xy / 128.0).w * PI2; + return null; + } + /** + * Saves the file with the given ID. + * @param id the ID to assign to the file. + * @param url the URL where the file is located. + */ + async save(id, url) { + this.addStoredID(id); + const rawData = await fetch(url); + const file = await rawData.blob(); + await this._db.open(); + await this._db.models.add({ + id, + file, + }); + this._db.close(); + } + /** + * Checks if there's a file stored with the given ID. + * @param id to check. + */ + exists(id) { + const stored = localStorage.getItem(id); + return stored !== null; + } + /** + * Deletes the files stored in the given ids. + * @param ids the identifiers of the files to delete. + */ + async delete(ids) { + await this._db.open(); + for (const id of ids) { + if (this.exists(id)) { + this.removeStoredID(id); + await this._db.models.where("id").equals(id).delete(); + } } - - mat2 rotationMatrix = mat2(cos(angle), -sin(angle), sin(angle), cos(angle)); - float radiusToUse = screenSpaceRadius ? distance( - worldPos, - getWorldPos(d, vUv + - vec2(worldRadius, 0.0) / resolution) - ) : worldRadius; - float distanceFalloffToUse =screenSpaceRadius ? - radiusToUse * distanceFalloff - : radiusToUse * distanceFalloff * 0.2; - - - for(int i = 0; i < NUM_SAMPLES; i++) { - vec2 offset = (rotationMatrix * poissonDisk[i]) * texelSize * size; - vec4 dataSample = texture2D(tDiffuse, uv + offset); - float occSample = dataSample.a; - vec3 normalSample = dataSample.rgb * 2.0 - 1.0; - float dSample = texture2D(sceneDepth, uv + offset).x; - vec3 worldPosSample = getWorldPos(dSample, uv + offset); - float tangentPlaneDist = abs(dot(worldPosSample - worldPos, normal)); - float rangeCheck = dSample == 1.0 ? 0.0 :exp(-1.0 * tangentPlaneDist * (1.0 / distanceFalloffToUse)) * max(dot(normal, normalSample), 0.0) * (1.0 - abs(occSample - baseOcc)); - occlusion += occSample * rangeCheck; - count += rangeCheck; + this._db.close(); + } + /** Deletes all the stored files. */ + async deleteAll() { + await this._db.open(); + this.clearStoredIDs(); + await this._db.delete(); + this._db = new ModelDatabase(); + this._db.close(); + } + /** {@link Disposable.dispose} */ + async dispose() { + this.onFileLoaded.reset(); + this.onItemSaved.reset(); + for (const card of this.cards) { + await card.dispose(); } - if (count > 0.0) { - occlusion /= count; + this.cards = []; + await this.uiElement.dispose(); + this._db = null; + await this.onDisposed.trigger(LocalCacher.uuid); + this.onDisposed.reset(); + } + setUI(components) { + const main = new Button(components); + main.materialIcon = "storage"; + main.tooltip = "Local cacher"; + const saveButton = new Button(components); + saveButton.label = "Save"; + saveButton.materialIcon = "save"; + const loadButton = new Button(components); + loadButton.label = "Download"; + loadButton.materialIcon = "download"; + main.addChild(saveButton, loadButton); + const floatingMenu = new FloatingWindow(components, "file-list-menu"); + this.uiElement.set({ main, loadButton, saveButton, floatingMenu }); + floatingMenu.title = "Saved Files"; + floatingMenu.visible = false; + const savedFilesMenuHTML = floatingMenu.get(); + savedFilesMenuHTML.style.left = "70px"; + savedFilesMenuHTML.style.top = "100px"; + savedFilesMenuHTML.style.width = "340px"; + savedFilesMenuHTML.style.height = "400px"; + const renderer = this.components.renderer.get(); + const viewerContainer = renderer.domElement.parentElement; + viewerContainer.appendChild(floatingMenu.get()); + } + async getModelFromLocalCache(id) { + const found = await this._db.models.where("id").equals(id).toArray(); + return found[0].file; + } + clearStoredIDs() { + const ids = this.ids; + for (const id of ids) { + this.removeStoredID(id); } - #ifdef LOGDEPTH - occlusion = clamp(occlusion, 0.0, 1.0); - if (occlusion == 0.0) { - occlusion = 1.0; - } - #endif - gl_FragColor = vec4(0.5 + 0.5 * normal, occlusion); } - ` -}; - - + removeStoredID(id) { + localStorage.removeItem(id); + const allIDs = this.ids; + const ids = allIDs.filter((savedId) => savedId !== id); + this.setStoredIDs(ids); + } + addStoredID(id) { + const time = performance.now().toString(); + localStorage.setItem(id, time); + const ids = this.ids; + ids.push(id); + this.setStoredIDs(ids); + } + setStoredIDs(ids) { + localStorage.setItem(this._storedModels, JSON.stringify(ids)); + } +} +LocalCacher.uuid = "22ae591a-3a67-4988-86c6-68d7b83febf2"; +ToolComponent.libraryUUIDs.add(LocalCacher.uuid); -const $26aca173e0984d99$export$1efdf491687cd442 = { - uniforms: { - "sceneDepth": { - value: null - }, - "resolution": { - value: new Vector2$1() - }, - "near": { - value: 0.1 - }, - "far": { - value: 1000.0 - }, - "viewMatrixInv": { - value: new Matrix4() - }, - "projectionMatrixInv": { - value: new Matrix4() - }, - "logDepth": { - value: false +class SimpleSVGViewport extends Component { + get enabled() { + return this._enabled; + } + set enabled(value) { + this._enabled = value; + this.resize(); + this._undoList = []; + this.uiElement.get("toolbar").visible = value; + if (value) { + this._viewport.classList.remove("pointer-events-none"); } - }, - depthWrite: false, - depthTest: false, - vertexShader: /* glsl */ ` - varying vec2 vUv; - void main() { - vUv = uv; - gl_Position = vec4(position, 1); - }`, - fragmentShader: /* glsl */ ` - uniform highp sampler2D sceneDepth; - uniform vec2 resolution; - uniform float near; - uniform float far; - uniform bool logDepth; - uniform mat4 viewMatrixInv; - uniform mat4 projectionMatrixInv; - varying vec2 vUv; - layout(location = 1) out vec4 gNormal; - vec3 getWorldPosLog(vec3 posS) { - vec2 uv = posS.xy; - float z = posS.z; - float nearZ =near; - float farZ = far; - float depth = pow(2.0, z * log2(farZ + 1.0)) - 1.0; - float a = farZ / (farZ - nearZ); - float b = farZ * nearZ / (nearZ - farZ); - float linDepth = a + b / depth; - vec4 clipVec = vec4(uv, linDepth, 1.0) * 2.0 - 1.0; - vec4 wpos = projectionMatrixInv * clipVec; - return wpos.xyz / wpos.w; - } - vec3 getWorldPos(float depth, vec2 coord) { - if (logDepth) { - return getWorldPosLog(vec3(coord, depth)); + else { + this.clear(); + this.uiElement.get("settingsWindow").visible = false; + this._viewport.classList.add("pointer-events-none"); } - float z = depth * 2.0 - 1.0; - vec4 clipSpacePosition = vec4(coord * 2.0 - 1.0, z, 1.0); - vec4 viewSpacePosition = projectionMatrixInv * clipSpacePosition; - // Perspective division - vec4 worldSpacePosition = viewSpacePosition; - worldSpacePosition.xyz /= worldSpacePosition.w; - return worldSpacePosition.xyz; } - - vec3 computeNormal(vec3 worldPos, vec2 vUv) { - ivec2 p = ivec2(vUv * resolution); - float c0 = texelFetch(sceneDepth, p, 0).x; - float l2 = texelFetch(sceneDepth, p - ivec2(2, 0), 0).x; - float l1 = texelFetch(sceneDepth, p - ivec2(1, 0), 0).x; - float r1 = texelFetch(sceneDepth, p + ivec2(1, 0), 0).x; - float r2 = texelFetch(sceneDepth, p + ivec2(2, 0), 0).x; - float b2 = texelFetch(sceneDepth, p - ivec2(0, 2), 0).x; - float b1 = texelFetch(sceneDepth, p - ivec2(0, 1), 0).x; - float t1 = texelFetch(sceneDepth, p + ivec2(0, 1), 0).x; - float t2 = texelFetch(sceneDepth, p + ivec2(0, 2), 0).x; - - float dl = abs((2.0 * l1 - l2) - c0); - float dr = abs((2.0 * r1 - r2) - c0); - float db = abs((2.0 * b1 - b2) - c0); - float dt = abs((2.0 * t1 - t2) - c0); - - vec3 ce = getWorldPos(c0, vUv).xyz; - - vec3 dpdx = (dl < dr) ? ce - getWorldPos(l1, (vUv - vec2(1.0 / resolution.x, 0.0))).xyz - : -ce + getWorldPos(r1, (vUv + vec2(1.0 / resolution.x, 0.0))).xyz; - vec3 dpdy = (db < dt) ? ce - getWorldPos(b1, (vUv - vec2(0.0, 1.0 / resolution.y))).xyz - : -ce + getWorldPos(t1, (vUv + vec2(0.0, 1.0 / resolution.y))).xyz; - - return normalize(cross(dpdx, dpdy)); - } - void main() { - vec2 uv = vUv - vec2(0.5) / resolution; - vec2 pixelSize = vec2(1.0) / resolution; - vec2[] uvSamples = vec2[4]( - uv, - uv + vec2(pixelSize.x, 0.0), - uv + vec2(0.0, pixelSize.y), - uv + pixelSize - ); - float depth00 = texture2D(sceneDepth, uvSamples[0]).r; - float depth10 = texture2D(sceneDepth, uvSamples[1]).r; - float depth01 = texture2D(sceneDepth, uvSamples[2]).r; - float depth11 = texture2D(sceneDepth, uvSamples[3]).r; - float minDepth = min(min(depth00, depth10), min(depth01, depth11)); - float maxDepth = max(max(depth00, depth10), max(depth01, depth11)); - float targetDepth = minDepth; - // Checkerboard pattern to avoid artifacts - if (mod(gl_FragCoord.x + gl_FragCoord.y, 2.0) > 0.5) { - targetDepth = maxDepth; + set config(value) { + this._config = { ...this._config, ...value }; + } + get config() { + return this._config; + } + constructor(components, config) { + super(components); + this.uiElement = new UIElement(); + this.id = generateUUID().toLowerCase(); + this._enabled = false; + /** {@link Disposable.onDisposed} */ + this.onDisposed = new Event(); + this._viewport = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + this._size = new Vector2$1(); + this._undoList = []; + this.onResize = () => { + this.resize(); + }; + const defaultConfig = { + fillColor: "transparent", + strokeColor: "#ff0000", + strokeWidth: 4, + }; + this.config = { ...defaultConfig, ...(config !== null && config !== void 0 ? config : {}) }; + this._viewport.classList.add("absolute", "top-0", "right-0"); + // this._viewport.setAttribute("preserveAspectRatio", "xMidYMid") + this._viewport.setAttribute("width", "100%"); + this._viewport.setAttribute("height", "100%"); + // const renderer = this._components.renderer; + // const rendererSize = renderer.getSize(); + // const width = rendererSize.x + // const height = rendererSize.y + // this._viewport.setAttribute("viewBox", `0 0 ${width} ${height}`); + this.setUI(); + this.enabled = false; + this.components.ui.viewerContainer.append(this._viewport); + this.setupEvents(true); + } + async dispose() { + this._undoList = []; + this.uiElement.dispose(); + await this.onDisposed.trigger(); + this.onDisposed.reset(); + } + get() { + return this._viewport; + } + clear() { + const viewport = this.get(); + this._undoList = []; + while (viewport.firstChild) { + viewport.removeChild(viewport.firstChild); } - int chosenIndex = 0; - float[] samples = float[4](depth00, depth10, depth01, depth11); - for(int i = 0; i < 4; ++i) { - if (samples[i] == targetDepth) { - chosenIndex = i; - break; - } - } - gl_FragColor = vec4(samples[chosenIndex], 0.0, 0.0, 1.0); - gNormal = vec4(computeNormal( - getWorldPos(samples[chosenIndex], uvSamples[chosenIndex]), uvSamples[chosenIndex] - ), 0.0); - }` -}; - - - - - - - - - -var $06269ad78f3c5fdf$export$2e2bcd8739ae039 = `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`; - - -Uint8Array.from(atob(($06269ad78f3c5fdf$export$2e2bcd8739ae039)), (c)=>c.charCodeAt(0)); - - - -const $05f6997e4b65da14$var$bluenoiseBits = Uint8Array.from(atob(($06269ad78f3c5fdf$export$2e2bcd8739ae039)), (c)=>c.charCodeAt(0)); -/** - * - * @param {*} timerQuery - * @param {THREE.WebGLRenderer} gl - * @param {N8AOPass} pass - */ function $05f6997e4b65da14$var$checkTimerQuery(timerQuery, gl, pass) { - const available = gl.getQueryParameter(timerQuery, gl.QUERY_RESULT_AVAILABLE); - if (available) { - const elapsedTimeInNs = gl.getQueryParameter(timerQuery, gl.QUERY_RESULT); - const elapsedTimeInMs = elapsedTimeInNs / 1000000; - pass.lastTime = elapsedTimeInMs; - } else // If the result is not available yet, check again after a delay - setTimeout(()=>{ - $05f6997e4b65da14$var$checkTimerQuery(timerQuery, gl, pass); - }, 1); -} -class $05f6997e4b65da14$export$2d57db20b5eb5e0a extends (Pass) { - /** - * - * @param {THREE.Scene} scene - * @param {THREE.Camera} camera - * @param {number} width - * @param {number} height - * - * @property {THREE.Scene} scene - * @property {THREE.Camera} camera - * @property {number} width - * @property {number} height - */ constructor(scene, camera, width = 512, height = 512){ - super(); - this.width = width; - this.height = height; - this.clear = true; - this.camera = camera; - this.scene = scene; - /** - * @type {Proxy & { - * aoSamples: number, - * aoRadius: number, - * denoiseSamples: number, - * denoiseRadius: number, - * distanceFalloff: number, - * intensity: number, - * denoiseIterations: number, - * renderMode: 0 | 1 | 2 | 3 | 4, - * color: THREE.Color, - * gammaCorrection: boolean, - * logarithmicDepthBuffer: boolean - * screenSpaceRadius: boolean, - * halfRes: boolean, - * depthAwareUpsampling: boolean, - * autoRenderBeauty: boolean - * colorMultiply: boolean - * } - */ this.configuration = new Proxy({ - aoSamples: 16, - aoRadius: 5.0, - denoiseSamples: 8, - denoiseRadius: 12, - distanceFalloff: 1.0, - intensity: 5, - denoiseIterations: 2.0, - renderMode: 0, - color: new Color(0, 0, 0), - gammaCorrection: true, - logarithmicDepthBuffer: false, - screenSpaceRadius: false, - halfRes: false, - depthAwareUpsampling: true, - autoRenderBeauty: true, - colorMultiply: true, - transparencyAware: false, - stencil: false - }, { - set: (target, propName, value)=>{ - const oldProp = target[propName]; - target[propName] = value; - if (propName === "aoSamples" && oldProp !== value) this.configureAOPass(this.configuration.logarithmicDepthBuffer); - if (propName === "denoiseSamples" && oldProp !== value) this.configureDenoisePass(this.configuration.logarithmicDepthBuffer); - if (propName === "halfRes" && oldProp !== value) { - this.configureAOPass(this.configuration.logarithmicDepthBuffer); - this.configureHalfResTargets(); - this.configureEffectCompositer(this.configuration.logarithmicDepthBuffer); - this.setSize(this.width, this.height); - } - if (propName === "depthAwareUpsampling" && oldProp !== value) this.configureEffectCompositer(this.configuration.logarithmicDepthBuffer); - if (propName === "transparencyAware" && oldProp !== value) { - this.autoDetectTransparency = false; - this.configureTransparencyTarget(); - } - if (propName === "stencil" && oldProp !== value) { - /* this.beautyRenderTarget.stencilBuffer = value; - this.beautyRenderTarget.depthTexture.format = value ? THREE.DepthStencilFormat : THREE.DepthFormat; - this.beautyRenderTarget.depthTexture.type = value ? THREE.UnsignedInt248Type : THREE.UnsignedIntType; - this.beautyRenderTarget.depthTexture.needsUpdate = true; - this.beautyRenderTarget.needsUpdate = true;*/ this.beautyRenderTarget.dispose(); - this.beautyRenderTarget = new WebGLRenderTarget(this.width, this.height, { - minFilter: LinearFilter, - magFilter: NearestFilter, - type: HalfFloatType, - format: RGBAFormat, - stencilBuffer: value - }); - this.beautyRenderTarget.depthTexture = new DepthTexture(this.width, this.height, value ? UnsignedInt248Type : UnsignedIntType); - this.beautyRenderTarget.depthTexture.format = value ? DepthStencilFormat : DepthFormat; - } - return true; - } - }); - /** @type {THREE.Vector3[]} */ this.samples = []; - /** @type {THREE.Vector2[]} */ this.samplesDenoise = []; - this.autoDetectTransparency = true; - this.beautyRenderTarget = new WebGLRenderTarget(this.width, this.height, { - minFilter: LinearFilter, - magFilter: NearestFilter, - type: HalfFloatType, - format: RGBAFormat, - stencilBuffer: false - }); - this.beautyRenderTarget.depthTexture = new DepthTexture(this.width, this.height, UnsignedIntType); - this.beautyRenderTarget.depthTexture.format = DepthFormat; - this.configureEffectCompositer(this.configuration.logarithmicDepthBuffer); - this.configureSampleDependentPasses(); - this.configureHalfResTargets(); - this.detectTransparency(); - this.configureTransparencyTarget(); - this.writeTargetInternal = new WebGLRenderTarget(this.width, this.height, { - minFilter: LinearFilter, - magFilter: LinearFilter, - depthBuffer: false - }); - this.readTargetInternal = new WebGLRenderTarget(this.width, this.height, { - minFilter: LinearFilter, - magFilter: LinearFilter, - depthBuffer: false - }); - /** @type {THREE.DataTexture} */ this.bluenoise = new DataTexture($05f6997e4b65da14$var$bluenoiseBits, 128, 128); - this.bluenoise.colorSpace = NoColorSpace; - this.bluenoise.wrapS = RepeatWrapping; - this.bluenoise.wrapT = RepeatWrapping; - this.bluenoise.minFilter = NearestFilter; - this.bluenoise.magFilter = NearestFilter; - this.bluenoise.needsUpdate = true; - this.lastTime = 0; - this._r = new Vector2$1(); - this._c = new Color(); } - configureHalfResTargets() { - if (this.configuration.halfRes) { - this.depthDownsampleTarget = /*new THREE.WebGLRenderTarget(this.width / 2, this.height / 2, { - minFilter: THREE.NearestFilter, - magFilter: THREE.NearestFilter, - depthBuffer: false, - format: THREE.RedFormat, - type: THREE.FloatType - });*/ new WebGLMultipleRenderTargets(this.width / 2, this.height / 2, 2); - this.depthDownsampleTarget.texture[0].format = RedFormat; - this.depthDownsampleTarget.texture[0].type = FloatType; - this.depthDownsampleTarget.texture[0].minFilter = NearestFilter; - this.depthDownsampleTarget.texture[0].magFilter = NearestFilter; - this.depthDownsampleTarget.texture[0].depthBuffer = false; - this.depthDownsampleTarget.texture[1].format = RGBAFormat; - this.depthDownsampleTarget.texture[1].type = HalfFloatType; - this.depthDownsampleTarget.texture[1].minFilter = NearestFilter; - this.depthDownsampleTarget.texture[1].magFilter = NearestFilter; - this.depthDownsampleTarget.texture[1].depthBuffer = false; - this.depthDownsampleQuad = new ($e4ca8dcb0218f846$export$dcd670d73db751f5)(new ShaderMaterial(($26aca173e0984d99$export$1efdf491687cd442))); - } else { - if (this.depthDownsampleTarget) { - this.depthDownsampleTarget.dispose(); - this.depthDownsampleTarget = null; - } - if (this.depthDownsampleQuad) { - this.depthDownsampleQuad.dispose(); - this.depthDownsampleQuad = null; - } - } + getDrawing() { + return this.get().childNodes; } - detectTransparency() { - if (this.autoDetectTransparency) { - let isTransparency = false; - this.scene.traverse((obj)=>{ - if (obj.material && obj.material.transparent) isTransparency = true; - }); - this.configuration.transparencyAware = isTransparency; + // setDrawing() { + // if (!this.enabled) { } + // } + /** {@link Resizeable.resize}. */ + resize() { + const renderer = this.components.renderer; + const rendererSize = renderer.getSize(); + const width = this.enabled ? rendererSize.x : 0; + const height = this.enabled ? rendererSize.y : 0; + this._size.set(width, height); + // this._viewport.setAttribute("viewBox", `0 0 ${this._size.x} ${this._size.y}`); + } + /** {@link Resizeable.getSize}. */ + getSize() { + return this._size; + } + setupEvents(active) { + if (active) { + window.addEventListener("resize", this.onResize); + } + else { + window.removeEventListener("resize", this.onResize); } } - configureTransparencyTarget() { - if (this.configuration.transparencyAware) { - this.transparencyRenderTargetDWFalse = new WebGLRenderTarget(this.width, this.height, { - minFilter: LinearFilter, - magFilter: NearestFilter, - type: HalfFloatType, - format: RGBAFormat - }); - this.transparencyRenderTargetDWTrue = new WebGLRenderTarget(this.width, this.height, { - minFilter: LinearFilter, - magFilter: NearestFilter, - type: HalfFloatType, - format: RGBAFormat - }); - this.transparencyRenderTargetDWTrue.depthTexture = new DepthTexture(this.width, this.height, UnsignedIntType); - this.depthCopyPass = new ($e4ca8dcb0218f846$export$dcd670d73db751f5)(new ShaderMaterial({ - uniforms: { - depthTexture: { - value: this.beautyRenderTarget.depthTexture - } - }, - vertexShader: /* glsl */ ` - varying vec2 vUv; - void main() { - vUv = uv; - gl_Position = vec4(position, 1); - }`, - fragmentShader: /* glsl */ ` - uniform sampler2D depthTexture; - varying vec2 vUv; - void main() { - gl_FragDepth = texture2D(depthTexture, vUv).r + 0.00001; - gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); - } - ` - })); - } else { - if (this.transparencyRenderTargetDWFalse) { - this.transparencyRenderTargetDWFalse.dispose(); - this.transparencyRenderTargetDWFalse = null; - } - if (this.transparencyRenderTargetDWTrue) { - this.transparencyRenderTargetDWTrue.dispose(); - this.transparencyRenderTargetDWTrue = null; + setUI() { + var _a, _b; + const undoDrawingBtn = new Button(this.components, { + materialIconName: "undo", + }); + undoDrawingBtn.onClick.add(() => { + if (this._viewport.lastChild) { + this._undoList.push(this._viewport.lastChild); + this._viewport.lastChild.remove(); } - if (this.depthCopyPass) { - this.depthCopyPass.dispose(); - this.depthCopyPass = null; + }); + const redoDrawingBtn = new Button(this.components, { + materialIconName: "redo", + }); + redoDrawingBtn.onClick.add(() => { + const childNode = this._undoList[this._undoList.length - 1]; + if (childNode) { + this._undoList.pop(); + this._viewport.append(childNode); } - } - } - renderTransparency(renderer) { - const oldBackground = this.scene.background; - const oldClearColor = renderer.getClearColor(new Color()); - const oldClearAlpha = renderer.getClearAlpha(); - const oldVisibility = new Map(); - const oldAutoClearDepth = renderer.autoClearDepth; - this.scene.traverse((obj)=>{ - oldVisibility.set(obj, obj.visible); }); - // Override the state - this.scene.background = null; - renderer.autoClearDepth = false; - renderer.setClearColor(new Color(0, 0, 0), 0); - this.depthCopyPass.material.uniforms.depthTexture.value = this.beautyRenderTarget.depthTexture; - // Render out transparent objects WITHOUT depth write - renderer.setRenderTarget(this.transparencyRenderTargetDWFalse); - this.scene.traverse((obj)=>{ - if (obj.material) obj.visible = oldVisibility.get(obj) && obj.material.transparent && !obj.material.depthWrite && !obj.userData.treatAsOpaque; + const clearDrawingBtn = new Button(this.components, { + materialIconName: "delete", }); - renderer.clear(true, true, true); - this.depthCopyPass.render(renderer); - renderer.render(this.scene, this.camera); - // Render out transparent objects WITH depth write - renderer.setRenderTarget(this.transparencyRenderTargetDWTrue); - this.scene.traverse((obj)=>{ - if (obj.material) obj.visible = oldVisibility.get(obj) && obj.material.transparent && obj.material.depthWrite && !obj.userData.treatAsOpaque; + clearDrawingBtn.onClick.add(() => this.clear()); + // #region Settings window + const settingsWindow = new FloatingWindow(this.components, this.id); + settingsWindow.title = "Drawing Settings"; + settingsWindow.visible = false; + const viewerContainer = this.components.renderer.get().domElement + .parentElement; + viewerContainer.append(settingsWindow.get()); + const strokeWidth = new RangeInput(this.components); + strokeWidth.label = "Stroke Width"; + strokeWidth.min = 2; + strokeWidth.max = 6; + strokeWidth.value = 4; + // strokeWidth.id = this.id; + strokeWidth.onChange.add((value) => { + // @ts-ignore + this.config = { strokeWidth: value }; }); - renderer.clear(true, true, true); - this.depthCopyPass.render(renderer); - renderer.render(this.scene, this.camera); - // Restore - this.scene.traverse((obj)=>{ - obj.visible = oldVisibility.get(obj); + const strokeColorInput = new ColorInput(this.components); + strokeColorInput.label = "Stroke Color"; + strokeColorInput.value = (_a = this.config.strokeColor) !== null && _a !== void 0 ? _a : "#BCF124"; + // strokeColorInput.name = "stroke-color"; + // strokeColorInput.id = this.id; + strokeColorInput.onChange.add((value) => { + this.config = { strokeColor: value }; }); - renderer.setClearColor(oldClearColor, oldClearAlpha); - this.scene.background = oldBackground; - renderer.autoClearDepth = oldAutoClearDepth; + const fillColorInput = new ColorInput(this.components); + strokeColorInput.label = "Fill Color"; + strokeColorInput.value = (_b = this.config.fillColor) !== null && _b !== void 0 ? _b : "#BCF124"; + // strokeColorInput.name = "fill-color"; + // strokeColorInput.id = this.id; + fillColorInput.onChange.add((value) => { + this.config = { fillColor: value }; + }); + settingsWindow.addChild(strokeColorInput, fillColorInput, strokeWidth); + const settingsBtn = new Button(this.components, { + materialIconName: "settings", + }); + settingsBtn.onClick.add(() => { + settingsWindow.visible = !settingsWindow.visible; + settingsBtn.active = settingsWindow.visible; + }); + settingsWindow.onHidden.add(() => (settingsBtn.active = false)); + const toolbar = new Toolbar(this.components, { position: "right" }); + toolbar.addChild(settingsBtn, undoDrawingBtn, redoDrawingBtn, clearDrawingBtn); + this.uiElement.set({ toolbar, settingsWindow }); } - configureSampleDependentPasses() { - this.configureAOPass(this.configuration.logarithmicDepthBuffer); - this.configureDenoisePass(this.configuration.logarithmicDepthBuffer); +} + +// TODO: Clean up and document +// TODO: Disable / enable instance color for instance meshes +/** + * A tool to easily handle the materials of massive amounts of + * objects and scene background easily. + */ +class MaterialManager extends Component { + constructor(components) { + super(components); + /** {@link Component.enabled} */ + this.enabled = true; + this._originalBackground = null; + /** {@link Disposable.onDisposed} */ + this.onDisposed = new Event(); + this._originals = {}; + this._list = {}; + this.components.tools.add(MaterialManager.uuid, this); } - configureAOPass(logarithmicDepthBuffer = false) { - this.samples = this.generateHemisphereSamples(this.configuration.aoSamples); - const e = { - ...($1ed45968c1160c3c$export$c9b263b9a17dffd7) - }; - e.fragmentShader = e.fragmentShader.replace("16", this.configuration.aoSamples).replace("16.0", this.configuration.aoSamples + ".0"); - if (logarithmicDepthBuffer) e.fragmentShader = "#define LOGDEPTH\n" + e.fragmentShader; - if (this.configuration.halfRes) e.fragmentShader = "#define HALFRES\n" + e.fragmentShader; - if (this.effectShaderQuad) { - this.effectShaderQuad.material.dispose(); - this.effectShaderQuad.material = new ShaderMaterial(e); - } else this.effectShaderQuad = new ($e4ca8dcb0218f846$export$dcd670d73db751f5)(new ShaderMaterial(e)); + /** + * {@link Component.get}. + * @return list of created materials. + */ + get() { + return Object.keys(this._list); } - configureDenoisePass(logarithmicDepthBuffer = false) { - this.samplesDenoise = this.generateDenoiseSamples(this.configuration.denoiseSamples, 11); - const p = { - ...($e52378cd0f5a973d$export$57856b59f317262e) - }; - p.fragmentShader = p.fragmentShader.replace("16", this.configuration.denoiseSamples); - if (logarithmicDepthBuffer) p.fragmentShader = "#define LOGDEPTH\n" + p.fragmentShader; - if (this.poissonBlurQuad) { - this.poissonBlurQuad.material.dispose(); - this.poissonBlurQuad.material = new ShaderMaterial(p); - } else this.poissonBlurQuad = new ($e4ca8dcb0218f846$export$dcd670d73db751f5)(new ShaderMaterial(p)); + /** + * Turns the specified material styles on or off. + * + * @param active whether to turn it on or off. + * @param ids the ids of the style to turn on or off. + */ + set(active, ids = Object.keys(this._list)) { + for (const id of ids) { + const { material, meshes } = this._list[id]; + for (const mesh of meshes) { + if (active) { + if (!this._originals[mesh.uuid]) { + this._originals[mesh.uuid] = { material: mesh.material }; + } + if (mesh instanceof THREE$1.InstancedMesh && mesh.instanceColor) { + this._originals[mesh.uuid].instances = mesh.instanceColor; + mesh.instanceColor = null; + } + mesh.material = material; + } + else { + if (!this._originals[mesh.uuid]) + continue; + mesh.material = this._originals[mesh.uuid].material; + const instances = this._originals[mesh.uuid].instances; + if (mesh instanceof THREE$1.InstancedMesh && instances) { + mesh.instanceColor = instances; + } + } + } + } } - configureEffectCompositer(logarithmicDepthBuffer = false) { - const e = { - ...($12b21d24d1192a04$export$a815acccbd2c9a49) - }; - if (logarithmicDepthBuffer) e.fragmentShader = "#define LOGDEPTH\n" + e.fragmentShader; - if (this.configuration.halfRes && this.configuration.depthAwareUpsampling) e.fragmentShader = "#define HALFRES\n" + e.fragmentShader; - if (this.effectCompositerQuad) { - this.effectCompositerQuad.material.dispose(); - this.effectCompositerQuad.material = new ShaderMaterial(e); - } else this.effectCompositerQuad = new ($e4ca8dcb0218f846$export$dcd670d73db751f5)(new ShaderMaterial(e)); + /** {@link Disposable.dispose} */ + async dispose() { + for (const id in this._list) { + const { material } = this._list[id]; + material.dispose(); + } + this._list = {}; + this._originals = {}; + await this.onDisposed.trigger(MaterialManager.uuid); + this.onDisposed.reset(); } /** - * - * @param {Number} n - * @returns {THREE.Vector3[]} - */ generateHemisphereSamples(n) { - const points = []; - for(let k = 0; k < n; k++){ - const theta = 2.399963 * k; - let r = Math.sqrt(k + 0.5) / Math.sqrt(n); - const x = r * Math.cos(theta); - const y = r * Math.sin(theta); - // Project to hemisphere - const z = Math.sqrt(1 - (x * x + y * y)); - points.push(new Vector3$1(x, y, z)); + * Sets the color of the background of the scene. + * + * @param color: the color to apply. + */ + setBackgroundColor(color) { + const scene = this.components.scene.get(); + if (!this._originalBackground) { + this._originalBackground = scene.background; + } + if (this._originalBackground) { + scene.background = color; } - return points; } /** - * - * @param {number} numSamples - * @param {number} numRings - * @returns {THREE.Vector2[]} - */ generateDenoiseSamples(numSamples, numRings) { - const angleStep = 2 * Math.PI * numRings / numSamples; - const invNumSamples = 1.0 / numSamples; - const radiusStep = invNumSamples; - const samples = []; - let radius = invNumSamples; - let angle = 0; - for(let i = 0; i < numSamples; i++){ - samples.push(new Vector2$1(Math.cos(angle), Math.sin(angle)).multiplyScalar(Math.pow(radius, 0.75))); - radius += radiusStep; - angle += angleStep; - } - return samples; - } - setSize(width, height) { - this.width = width; - this.height = height; - const c = this.configuration.halfRes ? 0.5 : 1; - this.beautyRenderTarget.setSize(width, height); - this.writeTargetInternal.setSize(width * c, height * c); - this.readTargetInternal.setSize(width * c, height * c); - if (this.configuration.halfRes) this.depthDownsampleTarget.setSize(width * c, height * c); - if (this.configuration.transparencyAware) { - this.transparencyRenderTargetDWFalse.setSize(width, height); - this.transparencyRenderTargetDWTrue.setSize(width, height); - } - } - render(renderer, writeBuffer, readBuffer, deltaTime, maskActive) { - if (renderer.capabilities.logarithmicDepthBuffer !== this.configuration.logarithmicDepthBuffer) { - this.configuration.logarithmicDepthBuffer = renderer.capabilities.logarithmicDepthBuffer; - this.configureAOPass(this.configuration.logarithmicDepthBuffer); - this.configureDenoisePass(this.configuration.logarithmicDepthBuffer); - this.configureEffectCompositer(this.configuration.logarithmicDepthBuffer); - } - this.detectTransparency(); - let gl; - let ext; - let timerQuery; - if (this.debugMode) { - gl = renderer.getContext(); - ext = gl.getExtension("EXT_disjoint_timer_query_webgl2"); - if (ext === null) { - console.error("EXT_disjoint_timer_query_webgl2 not available, disabling debug mode."); - this.debugMode = false; - } - } - if (this.configuration.autoRenderBeauty) { - renderer.setRenderTarget(this.beautyRenderTarget); - renderer.render(this.scene, this.camera); - if (this.configuration.transparencyAware) this.renderTransparency(renderer); - } - if (this.debugMode) { - timerQuery = gl.createQuery(); - gl.beginQuery(ext.TIME_ELAPSED_EXT, timerQuery); - } - const xrEnabled = renderer.xr.enabled; - renderer.xr.enabled = false; - this.camera.updateMatrixWorld(); - this._r.set(this.width, this.height); - let trueRadius = this.configuration.aoRadius; - if (this.configuration.halfRes && this.configuration.screenSpaceRadius) trueRadius *= 0.5; - if (this.configuration.halfRes) { - renderer.setRenderTarget(this.depthDownsampleTarget); - this.depthDownsampleQuad.material.uniforms.sceneDepth.value = this.beautyRenderTarget.depthTexture; - this.depthDownsampleQuad.material.uniforms.resolution.value = this._r; - this.depthDownsampleQuad.material.uniforms["near"].value = this.camera.near; - this.depthDownsampleQuad.material.uniforms["far"].value = this.camera.far; - this.depthDownsampleQuad.material.uniforms["projectionMatrixInv"].value = this.camera.projectionMatrixInverse; - this.depthDownsampleQuad.material.uniforms["viewMatrixInv"].value = this.camera.matrixWorld; - this.depthDownsampleQuad.material.uniforms["logDepth"].value = this.configuration.logarithmicDepthBuffer; - this.depthDownsampleQuad.render(renderer); - } - this.effectShaderQuad.material.uniforms["sceneDiffuse"].value = this.beautyRenderTarget.texture; - this.effectShaderQuad.material.uniforms["sceneDepth"].value = this.configuration.halfRes ? this.depthDownsampleTarget.texture[0] : this.beautyRenderTarget.depthTexture; - this.effectShaderQuad.material.uniforms["sceneNormal"].value = this.configuration.halfRes ? this.depthDownsampleTarget.texture[1] : null; - this.effectShaderQuad.material.uniforms["projMat"].value = this.camera.projectionMatrix; - this.effectShaderQuad.material.uniforms["viewMat"].value = this.camera.matrixWorldInverse; - this.effectShaderQuad.material.uniforms["projViewMat"].value = this.camera.projectionMatrix.clone().multiply(this.camera.matrixWorldInverse.clone()); - this.effectShaderQuad.material.uniforms["projectionMatrixInv"].value = this.camera.projectionMatrixInverse; - this.effectShaderQuad.material.uniforms["viewMatrixInv"].value = this.camera.matrixWorld; - this.effectShaderQuad.material.uniforms["cameraPos"].value = this.camera.getWorldPosition(new Vector3$1()); - this.effectShaderQuad.material.uniforms["resolution"].value = this.configuration.halfRes ? this._r.clone().multiplyScalar(0.5).floor() : this._r; - this.effectShaderQuad.material.uniforms["time"].value = performance.now() / 1000; - this.effectShaderQuad.material.uniforms["samples"].value = this.samples; - this.effectShaderQuad.material.uniforms["bluenoise"].value = this.bluenoise; - this.effectShaderQuad.material.uniforms["radius"].value = trueRadius; - this.effectShaderQuad.material.uniforms["distanceFalloff"].value = this.configuration.distanceFalloff; - this.effectShaderQuad.material.uniforms["near"].value = this.camera.near; - this.effectShaderQuad.material.uniforms["far"].value = this.camera.far; - this.effectShaderQuad.material.uniforms["logDepth"].value = renderer.capabilities.logarithmicDepthBuffer; - this.effectShaderQuad.material.uniforms["ortho"].value = this.camera.isOrthographicCamera; - this.effectShaderQuad.material.uniforms["screenSpaceRadius"].value = this.configuration.screenSpaceRadius; - // Start the AO - renderer.setRenderTarget(this.writeTargetInternal); - this.effectShaderQuad.render(renderer); - // End the AO - // Start the blur - for(let i = 0; i < this.configuration.denoiseIterations; i++){ - [this.writeTargetInternal, this.readTargetInternal] = [ - this.readTargetInternal, - this.writeTargetInternal - ]; - this.poissonBlurQuad.material.uniforms["tDiffuse"].value = this.readTargetInternal.texture; - this.poissonBlurQuad.material.uniforms["sceneDepth"].value = this.configuration.halfRes ? this.depthDownsampleTarget.texture[0] : this.beautyRenderTarget.depthTexture; - this.poissonBlurQuad.material.uniforms["projMat"].value = this.camera.projectionMatrix; - this.poissonBlurQuad.material.uniforms["viewMat"].value = this.camera.matrixWorldInverse; - this.poissonBlurQuad.material.uniforms["projectionMatrixInv"].value = this.camera.projectionMatrixInverse; - this.poissonBlurQuad.material.uniforms["viewMatrixInv"].value = this.camera.matrixWorld; - this.poissonBlurQuad.material.uniforms["cameraPos"].value = this.camera.getWorldPosition(new Vector3$1()); - this.poissonBlurQuad.material.uniforms["resolution"].value = this.configuration.halfRes ? this._r.clone().multiplyScalar(0.5).floor() : this._r; - this.poissonBlurQuad.material.uniforms["time"].value = performance.now() / 1000; - this.poissonBlurQuad.material.uniforms["blueNoise"].value = this.bluenoise; - this.poissonBlurQuad.material.uniforms["radius"].value = this.configuration.denoiseRadius * (this.configuration.halfRes ? 0.5 : 1); - this.poissonBlurQuad.material.uniforms["worldRadius"].value = trueRadius; - this.poissonBlurQuad.material.uniforms["distanceFalloff"].value = this.configuration.distanceFalloff; - this.poissonBlurQuad.material.uniforms["index"].value = i; - this.poissonBlurQuad.material.uniforms["poissonDisk"].value = this.samplesDenoise; - this.poissonBlurQuad.material.uniforms["near"].value = this.camera.near; - this.poissonBlurQuad.material.uniforms["far"].value = this.camera.far; - this.poissonBlurQuad.material.uniforms["logDepth"].value = renderer.capabilities.logarithmicDepthBuffer; - this.poissonBlurQuad.material.uniforms["screenSpaceRadius"].value = this.configuration.screenSpaceRadius; - renderer.setRenderTarget(this.writeTargetInternal); - this.poissonBlurQuad.render(renderer); - } - // Now, we have the blurred AO in writeTargetInternal - // End the blur - // Start the composition - if (this.configuration.transparencyAware) { - this.effectCompositerQuad.material.uniforms["transparencyDWFalse"].value = this.transparencyRenderTargetDWFalse.texture; - this.effectCompositerQuad.material.uniforms["transparencyDWTrue"].value = this.transparencyRenderTargetDWTrue.texture; - this.effectCompositerQuad.material.uniforms["transparencyDWTrueDepth"].value = this.transparencyRenderTargetDWTrue.depthTexture; - this.effectCompositerQuad.material.uniforms["transparencyAware"].value = true; - } - this.effectCompositerQuad.material.uniforms["sceneDiffuse"].value = this.beautyRenderTarget.texture; - this.effectCompositerQuad.material.uniforms["sceneDepth"].value = this.beautyRenderTarget.depthTexture; - this.effectCompositerQuad.material.uniforms["near"].value = this.camera.near; - this.effectCompositerQuad.material.uniforms["far"].value = this.camera.far; - this.effectCompositerQuad.material.uniforms["projectionMatrixInv"].value = this.camera.projectionMatrixInverse; - this.effectCompositerQuad.material.uniforms["viewMatrixInv"].value = this.camera.matrixWorld; - this.effectCompositerQuad.material.uniforms["logDepth"].value = renderer.capabilities.logarithmicDepthBuffer; - this.effectCompositerQuad.material.uniforms["ortho"].value = this.camera.isOrthographicCamera; - this.effectCompositerQuad.material.uniforms["downsampledDepth"].value = this.configuration.halfRes ? this.depthDownsampleTarget.texture[0] : this.beautyRenderTarget.depthTexture; - this.effectCompositerQuad.material.uniforms["resolution"].value = this._r; - this.effectCompositerQuad.material.uniforms["blueNoise"].value = this.bluenoise; - this.effectCompositerQuad.material.uniforms["intensity"].value = this.configuration.intensity; - this.effectCompositerQuad.material.uniforms["renderMode"].value = this.configuration.renderMode; - this.effectCompositerQuad.material.uniforms["screenSpaceRadius"].value = this.configuration.screenSpaceRadius; - this.effectCompositerQuad.material.uniforms["radius"].value = trueRadius; - this.effectCompositerQuad.material.uniforms["distanceFalloff"].value = this.configuration.distanceFalloff; - this.effectCompositerQuad.material.uniforms["gammaCorrection"].value = this.configuration.gammaCorrection; - this.effectCompositerQuad.material.uniforms["tDiffuse"].value = this.writeTargetInternal.texture; - this.effectCompositerQuad.material.uniforms["color"].value = this._c.copy(this.configuration.color).convertSRGBToLinear(); - this.effectCompositerQuad.material.uniforms["colorMultiply"].value = this.configuration.colorMultiply; - this.effectCompositerQuad.material.uniforms["cameraPos"].value = this.camera.getWorldPosition(new Vector3$1()); - this.effectCompositerQuad.material.uniforms["fog"].value = !!this.scene.fog; - if (this.scene.fog) { - if (this.scene.fog.isFog) { - this.effectCompositerQuad.material.uniforms["fogExp"].value = false; - this.effectCompositerQuad.material.uniforms["fogNear"].value = this.scene.fog.near; - this.effectCompositerQuad.material.uniforms["fogFar"].value = this.scene.fog.far; - } else if (this.scene.fog.isFogExp2) { - this.effectCompositerQuad.material.uniforms["fogExp"].value = true; - this.effectCompositerQuad.material.uniforms["fogDensity"].value = this.scene.fog.density; - } else console.error(`Unsupported fog type ${this.scene.fog.constructor.name} in SSAOPass.`); - } - renderer.setRenderTarget(this.renderToScreen ? null : writeBuffer); - this.effectCompositerQuad.render(renderer); - if (this.debugMode) { - gl.endQuery(ext.TIME_ELAPSED_EXT); - $05f6997e4b65da14$var$checkTimerQuery(timerQuery, gl, this); + * Resets the scene background to the color that was being used + * before applying the material manager. + */ + resetBackgroundColor() { + const scene = this.components.scene.get(); + if (this._originalBackground) { + scene.background = this._originalBackground; } - renderer.xr.enabled = xrEnabled; - } - /** - * Enables the debug mode of the AO, meaning the lastTime value will be updated. - */ enableDebugMode() { - this.debugMode = true; - } - /** - * Disables the debug mode of the AO, meaning the lastTime value will not be updated. - */ disableDebugMode() { - this.debugMode = false; } /** - * Sets the display mode of the AO - * @param {"Combined" | "AO" | "No AO" | "Split" | "Split AO"} mode - The display mode. - */ setDisplayMode(mode) { - this.configuration.renderMode = [ - "Combined", - "AO", - "No AO", - "Split", - "Split AO" - ].indexOf(mode); + * Creates a new material style. + * @param id the identifier of the style to create. + * @param material the material of the style. + */ + addMaterial(id, material) { + if (this._list[id]) { + throw new Error("This ID already exists!"); + } + this._list[id] = { material, meshes: new Set() }; } /** - * - * @param {"Performance" | "Low" | "Medium" | "High" | "Ultra"} mode - */ setQualityMode(mode) { - if (mode === "Performance") { - this.configuration.aoSamples = 8; - this.configuration.denoiseSamples = 4; - this.configuration.denoiseRadius = 12; - } else if (mode === "Low") { - this.configuration.aoSamples = 16; - this.configuration.denoiseSamples = 4; - this.configuration.denoiseRadius = 12; - } else if (mode === "Medium") { - this.configuration.aoSamples = 16; - this.configuration.denoiseSamples = 8; - this.configuration.denoiseRadius = 12; - } else if (mode === "High") { - this.configuration.aoSamples = 64; - this.configuration.denoiseSamples = 8; - this.configuration.denoiseRadius = 6; - } else if (mode === "Ultra") { - this.configuration.aoSamples = 64; - this.configuration.denoiseSamples = 16; - this.configuration.denoiseRadius = 6; + * Assign meshes to a certain style. + * @param id the identifier of the style. + * @param meshes the meshes to assign to the style. + */ + addMeshes(id, meshes) { + if (!this._list[id]) { + throw new Error("This ID doesn't exists!"); + } + for (const mesh of meshes) { + this._list[id].meshes.add(mesh); } } } +MaterialManager.uuid = "24989d27-fa2f-4797-8b08-35918f74e502"; +ToolComponent.libraryUUIDs.add(MaterialManager.uuid); + +// OrbitControls performs orbiting, dollying (zooming), and panning. +// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). +// +// Orbit - left mouse / touch: one-finger move +// Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish +// Pan - right mouse, or left mouse + ctrl/meta/shiftKey, or arrow keys / touch: two-finger move + +const _changeEvent = { type: 'change' }; +const _startEvent = { type: 'start' }; +const _endEvent = { type: 'end' }; + +class OrbitControls extends EventDispatcher$1 { + + constructor( object, domElement ) { + + super(); + + this.object = object; + this.domElement = domElement; + this.domElement.style.touchAction = 'none'; // disable touch scroll + + // Set to false to disable this control + this.enabled = true; + + // "target" sets the location of focus, where the object orbits around + this.target = new Vector3$1(); + + // How far you can dolly in and out ( PerspectiveCamera only ) + this.minDistance = 0; + this.maxDistance = Infinity; + + // How far you can zoom in and out ( OrthographicCamera only ) + this.minZoom = 0; + this.maxZoom = Infinity; + + // How far you can orbit vertically, upper and lower limits. + // Range is 0 to Math.PI radians. + this.minPolarAngle = 0; // radians + this.maxPolarAngle = Math.PI; // radians + + // How far you can orbit horizontally, upper and lower limits. + // If set, the interval [ min, max ] must be a sub-interval of [ - 2 PI, 2 PI ], with ( max - min < 2 PI ) + this.minAzimuthAngle = - Infinity; // radians + this.maxAzimuthAngle = Infinity; // radians + + // Set to true to enable damping (inertia) + // If damping is enabled, you must call controls.update() in your animation loop + this.enableDamping = false; + this.dampingFactor = 0.05; + + // This option actually enables dollying in and out; left as "zoom" for backwards compatibility. + // Set to false to disable zooming + this.enableZoom = true; + this.zoomSpeed = 1.0; + + // Set to false to disable rotating + this.enableRotate = true; + this.rotateSpeed = 1.0; + + // Set to false to disable panning + this.enablePan = true; + this.panSpeed = 1.0; + this.screenSpacePanning = true; // if false, pan orthogonal to world-space direction camera.up + this.keyPanSpeed = 7.0; // pixels moved per arrow key push + + // Set to true to automatically rotate around the target + // If auto-rotate is enabled, you must call controls.update() in your animation loop + this.autoRotate = false; + this.autoRotateSpeed = 2.0; // 30 seconds per orbit when fps is 60 + + // The four arrow keys + this.keys = { LEFT: 'ArrowLeft', UP: 'ArrowUp', RIGHT: 'ArrowRight', BOTTOM: 'ArrowDown' }; + + // Mouse buttons + this.mouseButtons = { LEFT: MOUSE.ROTATE, MIDDLE: MOUSE.DOLLY, RIGHT: MOUSE.PAN }; + + // Touch fingers + this.touches = { ONE: TOUCH.ROTATE, TWO: TOUCH.DOLLY_PAN }; + + // for reset + this.target0 = this.target.clone(); + this.position0 = this.object.position.clone(); + this.zoom0 = this.object.zoom; + + // the target DOM element for key events + this._domElementKeyEvents = null; + + // + // public methods + // + + this.getPolarAngle = function () { + + return spherical.phi; + + }; + + this.getAzimuthalAngle = function () { + + return spherical.theta; + + }; + + this.getDistance = function () { + + return this.object.position.distanceTo( this.target ); + + }; + + this.listenToKeyEvents = function ( domElement ) { + + domElement.addEventListener( 'keydown', onKeyDown ); + this._domElementKeyEvents = domElement; + + }; + + this.stopListenToKeyEvents = function () { + + this._domElementKeyEvents.removeEventListener( 'keydown', onKeyDown ); + this._domElementKeyEvents = null; + + }; + + this.saveState = function () { + + scope.target0.copy( scope.target ); + scope.position0.copy( scope.object.position ); + scope.zoom0 = scope.object.zoom; + + }; + + this.reset = function () { + + scope.target.copy( scope.target0 ); + scope.object.position.copy( scope.position0 ); + scope.object.zoom = scope.zoom0; + + scope.object.updateProjectionMatrix(); + scope.dispatchEvent( _changeEvent ); + + scope.update(); + + state = STATE.NONE; + + }; + + // this method is exposed, but perhaps it would be better if we can make it private... + this.update = function () { + + const offset = new Vector3$1(); + + // so camera.up is the orbit axis + const quat = new Quaternion$1().setFromUnitVectors( object.up, new Vector3$1( 0, 1, 0 ) ); + const quatInverse = quat.clone().invert(); + + const lastPosition = new Vector3$1(); + const lastQuaternion = new Quaternion$1(); + + const twoPI = 2 * Math.PI; + + return function update() { + + const position = scope.object.position; + + offset.copy( position ).sub( scope.target ); + + // rotate offset to "y-axis-is-up" space + offset.applyQuaternion( quat ); + + // angle from z-axis around y-axis + spherical.setFromVector3( offset ); + + if ( scope.autoRotate && state === STATE.NONE ) { + + rotateLeft( getAutoRotationAngle() ); + + } + + if ( scope.enableDamping ) { + + spherical.theta += sphericalDelta.theta * scope.dampingFactor; + spherical.phi += sphericalDelta.phi * scope.dampingFactor; + + } else { + + spherical.theta += sphericalDelta.theta; + spherical.phi += sphericalDelta.phi; + + } + + // restrict theta to be between desired limits + + let min = scope.minAzimuthAngle; + let max = scope.maxAzimuthAngle; + + if ( isFinite( min ) && isFinite( max ) ) { + + if ( min < - Math.PI ) min += twoPI; else if ( min > Math.PI ) min -= twoPI; + + if ( max < - Math.PI ) max += twoPI; else if ( max > Math.PI ) max -= twoPI; + + if ( min <= max ) { + + spherical.theta = Math.max( min, Math.min( max, spherical.theta ) ); + + } else { + + spherical.theta = ( spherical.theta > ( min + max ) / 2 ) ? + Math.max( min, spherical.theta ) : + Math.min( max, spherical.theta ); + + } + + } + + // restrict phi to be between desired limits + spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) ); + + spherical.makeSafe(); + + + spherical.radius *= scale; + + // restrict radius to be between desired limits + spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) ); + + // move target to panned location + + if ( scope.enableDamping === true ) { + + scope.target.addScaledVector( panOffset, scope.dampingFactor ); + + } else { + + scope.target.add( panOffset ); + + } + + offset.setFromSpherical( spherical ); + + // rotate offset back to "camera-up-vector-is-up" space + offset.applyQuaternion( quatInverse ); + + position.copy( scope.target ).add( offset ); + + scope.object.lookAt( scope.target ); + + if ( scope.enableDamping === true ) { + + sphericalDelta.theta *= ( 1 - scope.dampingFactor ); + sphericalDelta.phi *= ( 1 - scope.dampingFactor ); + + panOffset.multiplyScalar( 1 - scope.dampingFactor ); + + } else { + + sphericalDelta.set( 0, 0, 0 ); + + panOffset.set( 0, 0, 0 ); + + } + + scale = 1; + + // update condition is: + // min(camera displacement, camera rotation in radians)^2 > EPS + // using small-angle approximation cos(x/2) = 1 - x^2 / 8 + + if ( zoomChanged || + lastPosition.distanceToSquared( scope.object.position ) > EPS || + 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) { + + scope.dispatchEvent( _changeEvent ); + + lastPosition.copy( scope.object.position ); + lastQuaternion.copy( scope.object.quaternion ); + zoomChanged = false; + + return true; + + } + + return false; + + }; + + }(); + + this.dispose = function () { + + scope.domElement.removeEventListener( 'contextmenu', onContextMenu ); + + scope.domElement.removeEventListener( 'pointerdown', onPointerDown ); + scope.domElement.removeEventListener( 'pointercancel', onPointerUp ); + scope.domElement.removeEventListener( 'wheel', onMouseWheel ); + + scope.domElement.removeEventListener( 'pointermove', onPointerMove ); + scope.domElement.removeEventListener( 'pointerup', onPointerUp ); + + + if ( scope._domElementKeyEvents !== null ) { + + scope._domElementKeyEvents.removeEventListener( 'keydown', onKeyDown ); + scope._domElementKeyEvents = null; + + } + + //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here? + + }; + + // + // internals + // + + const scope = this; + + const STATE = { + NONE: - 1, + ROTATE: 0, + DOLLY: 1, + PAN: 2, + TOUCH_ROTATE: 3, + TOUCH_PAN: 4, + TOUCH_DOLLY_PAN: 5, + TOUCH_DOLLY_ROTATE: 6 + }; + + let state = STATE.NONE; + + const EPS = 0.000001; + + // current position in spherical coordinates + const spherical = new Spherical(); + const sphericalDelta = new Spherical(); + + let scale = 1; + const panOffset = new Vector3$1(); + let zoomChanged = false; + + const rotateStart = new Vector2$1(); + const rotateEnd = new Vector2$1(); + const rotateDelta = new Vector2$1(); + + const panStart = new Vector2$1(); + const panEnd = new Vector2$1(); + const panDelta = new Vector2$1(); + + const dollyStart = new Vector2$1(); + const dollyEnd = new Vector2$1(); + const dollyDelta = new Vector2$1(); + + const pointers = []; + const pointerPositions = {}; + + function getAutoRotationAngle() { + + return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; + + } + + function getZoomScale() { + + return Math.pow( 0.95, scope.zoomSpeed ); + + } + + function rotateLeft( angle ) { + + sphericalDelta.theta -= angle; + + } + + function rotateUp( angle ) { + + sphericalDelta.phi -= angle; + + } + + const panLeft = function () { + + const v = new Vector3$1(); + + return function panLeft( distance, objectMatrix ) { + + v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix + v.multiplyScalar( - distance ); + + panOffset.add( v ); + + }; + + }(); + + const panUp = function () { + + const v = new Vector3$1(); + + return function panUp( distance, objectMatrix ) { + + if ( scope.screenSpacePanning === true ) { + + v.setFromMatrixColumn( objectMatrix, 1 ); + + } else { + + v.setFromMatrixColumn( objectMatrix, 0 ); + v.crossVectors( scope.object.up, v ); + + } + + v.multiplyScalar( distance ); + + panOffset.add( v ); + + }; + + }(); + + // deltaX and deltaY are in pixels; right and down are positive + const pan = function () { + + const offset = new Vector3$1(); + + return function pan( deltaX, deltaY ) { + + const element = scope.domElement; + + if ( scope.object.isPerspectiveCamera ) { + + // perspective + const position = scope.object.position; + offset.copy( position ).sub( scope.target ); + let targetDistance = offset.length(); + + // half of the fov is center to top of screen + targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 ); + + // we use only clientHeight here so aspect ratio does not distort speed + panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix ); + panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix ); + + } else if ( scope.object.isOrthographicCamera ) { + + // orthographic + panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix ); + panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix ); + + } else { + + // camera neither orthographic nor perspective + console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); + scope.enablePan = false; + + } + + }; + + }(); + + function dollyOut( dollyScale ) { + + if ( scope.object.isPerspectiveCamera ) { + + scale /= dollyScale; + + } else if ( scope.object.isOrthographicCamera ) { + + scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) ); + scope.object.updateProjectionMatrix(); + zoomChanged = true; + + } else { + + console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); + scope.enableZoom = false; + + } + + } + + function dollyIn( dollyScale ) { + + if ( scope.object.isPerspectiveCamera ) { + + scale *= dollyScale; + + } else if ( scope.object.isOrthographicCamera ) { + + scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) ); + scope.object.updateProjectionMatrix(); + zoomChanged = true; + + } else { + + console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); + scope.enableZoom = false; + + } + + } + + // + // event callbacks - update the object state + // + + function handleMouseDownRotate( event ) { + + rotateStart.set( event.clientX, event.clientY ); + + } + + function handleMouseDownDolly( event ) { + + dollyStart.set( event.clientX, event.clientY ); + + } + + function handleMouseDownPan( event ) { + + panStart.set( event.clientX, event.clientY ); + + } + + function handleMouseMoveRotate( event ) { + + rotateEnd.set( event.clientX, event.clientY ); + + rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed ); + + const element = scope.domElement; + + rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height + + rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight ); + + rotateStart.copy( rotateEnd ); + + scope.update(); + + } + + function handleMouseMoveDolly( event ) { + + dollyEnd.set( event.clientX, event.clientY ); + + dollyDelta.subVectors( dollyEnd, dollyStart ); + + if ( dollyDelta.y > 0 ) { + + dollyOut( getZoomScale() ); + + } else if ( dollyDelta.y < 0 ) { + + dollyIn( getZoomScale() ); + + } + + dollyStart.copy( dollyEnd ); + + scope.update(); + + } + + function handleMouseMovePan( event ) { + + panEnd.set( event.clientX, event.clientY ); + + panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed ); + + pan( panDelta.x, panDelta.y ); + + panStart.copy( panEnd ); + + scope.update(); + + } + + function handleMouseWheel( event ) { + + if ( event.deltaY < 0 ) { + + dollyIn( getZoomScale() ); + + } else if ( event.deltaY > 0 ) { + + dollyOut( getZoomScale() ); + + } + + scope.update(); + + } + + function handleKeyDown( event ) { + + let needsUpdate = false; + + switch ( event.code ) { + + case scope.keys.UP: + + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + + rotateUp( 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight ); + + } else { + + pan( 0, scope.keyPanSpeed ); + + } + + needsUpdate = true; + break; + + case scope.keys.BOTTOM: + + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + + rotateUp( - 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight ); + + } else { + + pan( 0, - scope.keyPanSpeed ); + + } + + needsUpdate = true; + break; + + case scope.keys.LEFT: + + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + + rotateLeft( 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight ); + + } else { + + pan( scope.keyPanSpeed, 0 ); + + } + + needsUpdate = true; + break; + + case scope.keys.RIGHT: + + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + + rotateLeft( - 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight ); + + } else { + + pan( - scope.keyPanSpeed, 0 ); + + } + + needsUpdate = true; + break; + + } + + if ( needsUpdate ) { + + // prevent the browser from scrolling on cursor keys + event.preventDefault(); + + scope.update(); + + } + + + } + + function handleTouchStartRotate() { + + if ( pointers.length === 1 ) { + + rotateStart.set( pointers[ 0 ].pageX, pointers[ 0 ].pageY ); + + } else { + + const x = 0.5 * ( pointers[ 0 ].pageX + pointers[ 1 ].pageX ); + const y = 0.5 * ( pointers[ 0 ].pageY + pointers[ 1 ].pageY ); + + rotateStart.set( x, y ); + + } + + } + + function handleTouchStartPan() { + + if ( pointers.length === 1 ) { + + panStart.set( pointers[ 0 ].pageX, pointers[ 0 ].pageY ); + + } else { + + const x = 0.5 * ( pointers[ 0 ].pageX + pointers[ 1 ].pageX ); + const y = 0.5 * ( pointers[ 0 ].pageY + pointers[ 1 ].pageY ); + + panStart.set( x, y ); + + } + + } + + function handleTouchStartDolly() { + + const dx = pointers[ 0 ].pageX - pointers[ 1 ].pageX; + const dy = pointers[ 0 ].pageY - pointers[ 1 ].pageY; + + const distance = Math.sqrt( dx * dx + dy * dy ); + + dollyStart.set( 0, distance ); + + } + + function handleTouchStartDollyPan() { + + if ( scope.enableZoom ) handleTouchStartDolly(); + + if ( scope.enablePan ) handleTouchStartPan(); + + } + + function handleTouchStartDollyRotate() { + + if ( scope.enableZoom ) handleTouchStartDolly(); + + if ( scope.enableRotate ) handleTouchStartRotate(); + + } + + function handleTouchMoveRotate( event ) { + + if ( pointers.length == 1 ) { + + rotateEnd.set( event.pageX, event.pageY ); + + } else { + + const position = getSecondPointerPosition( event ); + + const x = 0.5 * ( event.pageX + position.x ); + const y = 0.5 * ( event.pageY + position.y ); + + rotateEnd.set( x, y ); + + } + + rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed ); + + const element = scope.domElement; + + rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height + + rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight ); + + rotateStart.copy( rotateEnd ); + + } + + function handleTouchMovePan( event ) { + + if ( pointers.length === 1 ) { + + panEnd.set( event.pageX, event.pageY ); + + } else { + + const position = getSecondPointerPosition( event ); + + const x = 0.5 * ( event.pageX + position.x ); + const y = 0.5 * ( event.pageY + position.y ); + + panEnd.set( x, y ); + + } + + panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed ); + + pan( panDelta.x, panDelta.y ); + + panStart.copy( panEnd ); + + } + + function handleTouchMoveDolly( event ) { + + const position = getSecondPointerPosition( event ); + + const dx = event.pageX - position.x; + const dy = event.pageY - position.y; + + const distance = Math.sqrt( dx * dx + dy * dy ); + + dollyEnd.set( 0, distance ); + + dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) ); + + dollyOut( dollyDelta.y ); + + dollyStart.copy( dollyEnd ); + + } + + function handleTouchMoveDollyPan( event ) { + + if ( scope.enableZoom ) handleTouchMoveDolly( event ); + + if ( scope.enablePan ) handleTouchMovePan( event ); + + } + + function handleTouchMoveDollyRotate( event ) { + + if ( scope.enableZoom ) handleTouchMoveDolly( event ); + + if ( scope.enableRotate ) handleTouchMoveRotate( event ); + + } + + // + // event handlers - FSM: listen for events and reset state + // + + function onPointerDown( event ) { + + if ( scope.enabled === false ) return; + + if ( pointers.length === 0 ) { + + scope.domElement.setPointerCapture( event.pointerId ); + + scope.domElement.addEventListener( 'pointermove', onPointerMove ); + scope.domElement.addEventListener( 'pointerup', onPointerUp ); + + } + + // + + addPointer( event ); + + if ( event.pointerType === 'touch' ) { + + onTouchStart( event ); + + } else { + + onMouseDown( event ); + + } + + } + + function onPointerMove( event ) { + + if ( scope.enabled === false ) return; + + if ( event.pointerType === 'touch' ) { + + onTouchMove( event ); + + } else { + + onMouseMove( event ); + + } + + } + + function onPointerUp( event ) { + + removePointer( event ); + + if ( pointers.length === 0 ) { + + scope.domElement.releasePointerCapture( event.pointerId ); + + scope.domElement.removeEventListener( 'pointermove', onPointerMove ); + scope.domElement.removeEventListener( 'pointerup', onPointerUp ); + + } + + scope.dispatchEvent( _endEvent ); + + state = STATE.NONE; + + } + + function onMouseDown( event ) { + + let mouseAction; + + switch ( event.button ) { + + case 0: + + mouseAction = scope.mouseButtons.LEFT; + break; + + case 1: + + mouseAction = scope.mouseButtons.MIDDLE; + break; + + case 2: + + mouseAction = scope.mouseButtons.RIGHT; + break; + + default: + + mouseAction = - 1; + + } + + switch ( mouseAction ) { + + case MOUSE.DOLLY: + + if ( scope.enableZoom === false ) return; -/** - * Gamma Correction Shader - * http://en.wikipedia.org/wiki/gamma_correction - */ + handleMouseDownDolly( event ); -const GammaCorrectionShader = { + state = STATE.DOLLY; - uniforms: { + break; - 'tDiffuse': { value: null } + case MOUSE.ROTATE: - }, + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { - vertexShader: /* glsl */` + if ( scope.enablePan === false ) return; - varying vec2 vUv; + handleMouseDownPan( event ); - void main() { + state = STATE.PAN; - vUv = uv; - gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + } else { - }`, + if ( scope.enableRotate === false ) return; - fragmentShader: /* glsl */` + handleMouseDownRotate( event ); - uniform sampler2D tDiffuse; + state = STATE.ROTATE; - varying vec2 vUv; + } - void main() { + break; - vec4 tex = texture2D( tDiffuse, vUv ); + case MOUSE.PAN: - gl_FragColor = LinearTosRGB( tex ); + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { - }` + if ( scope.enableRotate === false ) return; -}; + handleMouseDownRotate( event ); -/** - * Object to control the {@link CameraProjection} of the {@link OrthoPerspectiveCamera}. - */ -class ProjectionManager { - get projection() { - return this._currentProjection; - } - constructor(components, camera) { - this.components = components; - this._previousDistance = -1; - this.matchOrthoDistanceEnabled = false; - this._camera = camera; - const perspective = "Perspective"; - this._currentCamera = camera.get(perspective); - this._currentProjection = perspective; - } - /** - * Sets the {@link CameraProjection} of the {@link OrthoPerspectiveCamera}. - * - * @param projection - the new projection to set. If it is the current projection, - * it will have no effect. - */ - async setProjection(projection) { - if (this.projection === projection) - return; - if (projection === "Orthographic") { - this.setOrthoCamera(); - } - else { - await this.setPerspectiveCamera(); - } - await this.updateActiveCamera(); - } - setOrthoCamera() { - // Matching orthographic camera to perspective camera - // Resource: https://stackoverflow.com/questions/48758959/what-is-required-to-convert-threejs-perspective-camera-to-orthographic - if (this._camera.currentMode.id === "FirstPerson") { - return; - } - this._previousDistance = this._camera.controls.distance; - this._camera.controls.distance = 200; - const { width, height } = this.getDims(); - this.setupOrthoCamera(height, width); - this._currentCamera = this._camera.get("Orthographic"); - this._currentProjection = "Orthographic"; - } - // This small delay is needed to hide weirdness during the transition - async updateActiveCamera() { - await new Promise((resolve) => { - setTimeout(() => { - this._camera.activeCamera = this._currentCamera; - resolve(); - }, 50); - }); - } - getDims() { - const lineOfSight = new THREE$1.Vector3(); - this._camera.get("Perspective").getWorldDirection(lineOfSight); - const target = new THREE$1.Vector3(); - this._camera.controls.getTarget(target); - const distance = target - .clone() - .sub(this._camera.get("Perspective").position); - const depth = distance.dot(lineOfSight); - const dims = this.components.renderer.getSize(); - const aspect = dims.x / dims.y; - const camera = this._camera.get("Perspective"); - const height = depth * 2 * Math.atan((camera.fov * (Math.PI / 180)) / 2); - const width = height * aspect; - return { width, height }; - } - setupOrthoCamera(height, width) { - this._camera.controls.mouseButtons.wheel = CameraControls.ACTION.ZOOM; - this._camera.controls.mouseButtons.middle = CameraControls.ACTION.ZOOM; - const pCamera = this._camera.get("Perspective"); - const oCamera = this._camera.get("Orthographic"); - oCamera.zoom = 1; - oCamera.left = width / -2; - oCamera.right = width / 2; - oCamera.top = height / 2; - oCamera.bottom = height / -2; - oCamera.updateProjectionMatrix(); - oCamera.position.copy(pCamera.position); - oCamera.quaternion.copy(pCamera.quaternion); - this._camera.controls.camera = oCamera; - } - getDistance() { - // this handles ortho zoom to perpective distance - const pCamera = this._camera.get("Perspective"); - const oCamera = this._camera.get("Orthographic"); - // this is the reverse of - // const height = depth * 2 * Math.atan((pCamera.fov * (Math.PI / 180)) / 2); - // accounting for zoom - const depth = (oCamera.top - oCamera.bottom) / - oCamera.zoom / - (2 * Math.atan((pCamera.fov * (Math.PI / 180)) / 2)); - return depth; - } - async setPerspectiveCamera() { - this._camera.controls.mouseButtons.wheel = CameraControls.ACTION.DOLLY; - this._camera.controls.mouseButtons.middle = CameraControls.ACTION.DOLLY; - const pCamera = this._camera.get("Perspective"); - const oCamera = this._camera.get("Orthographic"); - pCamera.position.copy(oCamera.position); - pCamera.quaternion.copy(oCamera.quaternion); - this._camera.controls.mouseButtons.wheel = CameraControls.ACTION.DOLLY; - if (this.matchOrthoDistanceEnabled) { - this._camera.controls.distance = this.getDistance(); - } - else { - this._camera.controls.distance = this._previousDistance; - } - await this._camera.controls.zoomTo(1); - pCamera.updateProjectionMatrix(); - this._camera.controls.camera = pCamera; - this._currentCamera = pCamera; - this._currentProjection = "Perspective"; - } -} + state = STATE.ROTATE; -/** - * A {@link NavigationMode} that allows 3D navigation and panning - * like in many 3D and CAD softwares. - */ -class OrbitMode { - constructor(camera) { - this.camera = camera; - /** {@link NavigationMode.enabled} */ - this.enabled = true; - /** {@link NavigationMode.id} */ - this.id = "Orbit"; - /** {@link NavigationMode.projectionChanged} */ - this.projectionChanged = new Event(); - this.activateOrbitControls(); - } - /** {@link NavigationMode.toggle} */ - toggle(active) { - this.enabled = active; - if (active) { - this.activateOrbitControls(); - } - } - activateOrbitControls() { - const controls = this.camera.controls; - controls.minDistance = 1; - controls.maxDistance = 300; - const position = new THREE$1.Vector3(); - controls.getPosition(position); - const distance = position.length(); - controls.distance = distance; - controls.truckSpeed = 2; - const { rotation } = this.camera.get(); - const direction = new THREE$1.Vector3(0, 0, -1).applyEuler(rotation); - const target = position.addScaledVector(direction, distance); - controls.moveTo(target.x, target.y, target.z); - } -} + } else { -/** - * A {@link NavigationMode} that allows first person navigation, - * simulating FPS video games. - */ -class FirstPersonMode { - constructor(camera) { - this.camera = camera; - /** {@link NavigationMode.enabled} */ - this.enabled = false; - /** {@link NavigationMode.id} */ - this.id = "FirstPerson"; - /** {@link NavigationMode.projectionChanged} */ - this.projectionChanged = new Event(); - } - /** {@link NavigationMode.toggle} */ - toggle(active) { - this.enabled = active; - if (active) { - const projection = this.camera.getProjection(); - if (projection !== "Perspective") { - this.camera.setNavigationMode("Orbit"); - return; - } - this.setupFirstPersonCamera(); - } - } - setupFirstPersonCamera() { - const controls = this.camera.controls; - const newTargetPosition = new THREE$1.Vector3(); - controls.distance--; - controls.getPosition(newTargetPosition); - controls.minDistance = 1; - controls.maxDistance = 1; - controls.distance = 1; - controls.moveTo(newTargetPosition.x, newTargetPosition.y, newTargetPosition.z); - controls.truckSpeed = 50; - controls.mouseButtons.wheel = CameraControls.ACTION.DOLLY; - controls.touches.two = CameraControls.ACTION.TOUCH_ZOOM_TRUCK; - } -} + if ( scope.enablePan === false ) return; -/** - * A {@link NavigationMode} that allows to navigate floorplans in 2D, - * like many BIM tools. - */ -class PlanMode { - constructor(camera) { - this.camera = camera; - /** {@link NavigationMode.enabled} */ - this.enabled = false; - /** {@link NavigationMode.id} */ - this.id = "Plan"; - /** {@link NavigationMode.projectionChanged} */ - this.projectionChanged = new Event(); - this.mouseInitialized = false; - this.defaultAzimuthSpeed = camera.controls.azimuthRotateSpeed; - this.defaultPolarSpeed = camera.controls.polarRotateSpeed; - } - /** {@link NavigationMode.toggle} */ - toggle(active) { - this.enabled = active; - const controls = this.camera.controls; - controls.azimuthRotateSpeed = active ? 0 : this.defaultAzimuthSpeed; - controls.polarRotateSpeed = active ? 0 : this.defaultPolarSpeed; - if (!this.mouseInitialized) { - this.mouseAction1 = controls.touches.one; - this.mouseAction2 = controls.touches.two; - this.mouseInitialized = true; - } - if (active) { - controls.mouseButtons.left = CameraControls.ACTION.TRUCK; - controls.touches.one = CameraControls.ACTION.TOUCH_TRUCK; - controls.touches.two = CameraControls.ACTION.TOUCH_ZOOM; - } - else { - controls.mouseButtons.left = CameraControls.ACTION.ROTATE; - controls.touches.one = this.mouseAction1; - controls.touches.two = this.mouseAction2; - } - } -} + handleMouseDownPan( event ); -/** - * A flexible camera that uses - * [yomotsu's cameracontrols](https://github.com/yomotsu/camera-controls) to - * easily control the camera in 2D and 3D. It supports multiple navigation - * modes, such as 2D floor plan navigation, first person and 3D orbit. - */ -class OrthoPerspectiveCamera extends SimpleCamera { - constructor(components) { - super(components); - /** - * Event that fires when the {@link CameraProjection} changes. - */ - this.projectionChanged = new Event(); - this._userInputButtons = {}; - this._frustumSize = 50; - this._navigationModes = new Map(); - this.uiElement = new UIElement(); - this._orthoCamera = this.newOrthoCamera(); - this._navigationModes.set("Orbit", new OrbitMode(this)); - this._navigationModes.set("FirstPerson", new FirstPersonMode(this)); - this._navigationModes.set("Plan", new PlanMode(this)); - this.currentMode = this._navigationModes.get("Orbit"); - this.currentMode.toggle(true, { preventTargetAdjustment: true }); - this.toggleEvents(true); - this._projectionManager = new ProjectionManager(components, this); - components.onInitialized.add(() => { - if (components.uiEnabled) - this.setUI(); - }); - this.onAspectUpdated.add(() => this.setOrthoCameraAspect()); - } - setUI() { - const mainButton = new Button(this.components); - mainButton.materialIcon = "video_camera_back"; - mainButton.tooltip = "Camera"; - const projection = new Button(this.components, { - materialIconName: "camera", - name: "Projection", - }); - const perspective = new Button(this.components, { name: "Perspective" }); - perspective.active = true; - perspective.onClick.add(() => this.setProjection("Perspective")); - const orthographic = new Button(this.components, { name: "Orthographic" }); - orthographic.onClick.add(() => this.setProjection("Orthographic")); - projection.addChild(perspective, orthographic); - const navigation = new Button(this.components, { - materialIconName: "open_with", - name: "Navigation", - }); - const orbit = new Button(this.components, { name: "Orbit Around" }); - orbit.onClick.add(() => this.setNavigationMode("Orbit")); - const plan = new Button(this.components, { name: "Plan View" }); - plan.onClick.add(() => this.setNavigationMode("Plan")); - const firstPerson = new Button(this.components, { name: "First person" }); - firstPerson.onClick.add(() => this.setNavigationMode("FirstPerson")); - navigation.addChild(orbit, plan, firstPerson); - mainButton.addChild(navigation, projection); - this.projectionChanged.add((camera) => { - if (camera instanceof THREE$1.PerspectiveCamera) { - perspective.active = true; - orthographic.active = false; - } - else { - perspective.active = false; - orthographic.active = true; - } - }); - this.uiElement.set({ main: mainButton }); - } - /** {@link Disposable.dispose} */ - async dispose() { - await super.dispose(); - this.toggleEvents(false); - this._orthoCamera.removeFromParent(); - } - /** - * Similar to {@link Component.get}, but with an optional argument - * to specify which camera to get. - * - * @param projection - The camera corresponding to the - * {@link CameraProjection} specified. If no projection is specified, - * the active camera will be returned. - */ - get(projection) { - if (!projection) { - return this.activeCamera; - } - return projection === "Orthographic" - ? this._orthoCamera - : this._perspectiveCamera; - } - /** Returns the current {@link CameraProjection}. */ - getProjection() { - return this._projectionManager.projection; - } - /** Match Ortho zoom with Perspective distance when changing projection mode */ - set matchOrthoDistanceEnabled(value) { - this._projectionManager.matchOrthoDistanceEnabled = value; - } - /** - * Changes the current {@link CameraProjection} from Ortographic to Perspective - * and Viceversa. - */ - async toggleProjection() { - const projection = this.getProjection(); - const newProjection = projection === "Perspective" ? "Orthographic" : "Perspective"; - await this.setProjection(newProjection); - } - /** - * Sets the current {@link CameraProjection}. This triggers the event - * {@link projectionChanged}. - * - * @param projection - The new {@link CameraProjection} to set. - */ - async setProjection(projection) { - await this._projectionManager.setProjection(projection); - await this.projectionChanged.trigger(this.activeCamera); - } - /** - * Allows or prevents all user input. - * - * @param active - whether to enable or disable user inputs. - */ - toggleUserInput(active) { - if (active) { - this.enableUserInput(); - } - else { - this.disableUserInput(); - } - } - /** - * Sets a new {@link NavigationMode} and disables the previous one. - * - * @param mode - The {@link NavigationMode} to set. - */ - setNavigationMode(mode) { - if (this.currentMode.id === mode) - return; - this.currentMode.toggle(false); - if (!this._navigationModes.has(mode)) { - throw new Error("The specified mode does not exist!"); - } - this.currentMode = this._navigationModes.get(mode); - this.currentMode.toggle(true); - } - /** - * Make the camera view fit all the specified meshes. - * - * @param meshes the meshes to fit. If it is not defined, it will - * evaluate {@link Components.meshes}. - * @param offset the distance to the fit object - */ - async fit(meshes = this.components.meshes, offset = 1.5) { - if (!this.enabled) - return; - const maxNum = Number.MAX_VALUE; - const minNum = Number.MIN_VALUE; - const min = new THREE$1.Vector3(maxNum, maxNum, maxNum); - const max = new THREE$1.Vector3(minNum, minNum, minNum); - for (const mesh of meshes) { - const box = new THREE$1.Box3().setFromObject(mesh); - if (box.min.x < min.x) - min.x = box.min.x; - if (box.min.y < min.y) - min.y = box.min.y; - if (box.min.z < min.z) - min.z = box.min.z; - if (box.max.x > max.x) - max.x = box.max.x; - if (box.max.y > max.y) - max.y = box.max.y; - if (box.max.z > max.z) - max.z = box.max.z; - } - const box = new THREE$1.Box3(min, max); - const sceneSize = new THREE$1.Vector3(); - box.getSize(sceneSize); - const sceneCenter = new THREE$1.Vector3(); - box.getCenter(sceneCenter); - const radius = Math.max(sceneSize.x, sceneSize.y, sceneSize.z) * offset; - const sphere = new THREE$1.Sphere(sceneCenter, radius); - await this.controls.fitToSphere(sphere, true); - } - disableUserInput() { - this._userInputButtons.left = this.controls.mouseButtons.left; - this._userInputButtons.right = this.controls.mouseButtons.right; - this._userInputButtons.middle = this.controls.mouseButtons.middle; - this._userInputButtons.wheel = this.controls.mouseButtons.wheel; - this.controls.mouseButtons.left = 0; - this.controls.mouseButtons.right = 0; - this.controls.mouseButtons.middle = 0; - this.controls.mouseButtons.wheel = 0; - } - enableUserInput() { - if (Object.keys(this._userInputButtons).length === 0) - return; - this.controls.mouseButtons.left = this._userInputButtons.left; - this.controls.mouseButtons.right = this._userInputButtons.right; - this.controls.mouseButtons.middle = this._userInputButtons.middle; - this.controls.mouseButtons.wheel = this._userInputButtons.wheel; - } - newOrthoCamera() { - const dims = this.components.renderer.getSize(); - const aspect = dims.x / dims.y; - return new THREE$1.OrthographicCamera((this._frustumSize * aspect) / -2, (this._frustumSize * aspect) / 2, this._frustumSize / 2, this._frustumSize / -2, 0.1, 1000); - } - setOrthoCameraAspect() { - const size = this.components.renderer.getSize(); - const aspect = size.x / size.y; - this._orthoCamera.left = (-this._frustumSize * aspect) / 2; - this._orthoCamera.right = (this._frustumSize * aspect) / 2; - this._orthoCamera.top = this._frustumSize / 2; - this._orthoCamera.bottom = -this._frustumSize / 2; - this._orthoCamera.updateProjectionMatrix(); - } - toggleEvents(active) { - const modes = Object.values(this._navigationModes); - for (const mode of modes) { - if (active) { - mode.projectionChanged.on(this.projectionChanged.trigger); - } - else { - mode.projectionChanged.reset(); - } - } - } -} + state = STATE.PAN; + + } -// Gets the plane information (ax + by + cz = d) of each face, where: -// - (a, b, c) is the normal vector of the plane -// - d is the signed distance to the origin -function getPlaneDistanceMaterial() { - return new THREE$1.ShaderMaterial({ - side: 2, - clipping: true, - uniforms: {}, - vertexShader: ` - varying vec4 vColor; - - #include - - void main() { - #include - - vec4 absPosition = vec4(position, 1.0); - vec3 trueNormal = normal; - - #ifdef USE_INSTANCING - absPosition = instanceMatrix * absPosition; - trueNormal = (instanceMatrix * vec4(normal, 0.)).xyz; - #endif - - absPosition = modelMatrix * absPosition; - trueNormal = (normalize(modelMatrix * vec4(trueNormal, 0.))).xyz; - - vec3 planePosition = absPosition.xyz / 40.; - float d = abs(dot(trueNormal, planePosition)); - vColor = vec4(abs(trueNormal), d); - gl_Position = projectionMatrix * viewMatrix * absPosition; - - #include - #include - } - `, - fragmentShader: ` - varying vec4 vColor; - - #include - - void main() { - #include - gl_FragColor = vColor; - } - `, - }); -} + break; -// Gets the plane information (ax + by + cz = d) of each face, where: -// - (a, b, c) is the normal vector of the plane -// - d is the signed distance to the origin -function getProjectedNormalMaterial() { - return new THREE$1.ShaderMaterial({ - side: 2, - clipping: true, - uniforms: {}, - vertexShader: ` - varying vec3 vCameraPosition; - varying vec3 vPosition; - varying vec3 vNormal; - - #include - - void main() { - #include - - vec4 absPosition = vec4(position, 1.0); - vNormal = normal; - - #ifdef USE_INSTANCING - absPosition = instanceMatrix * absPosition; - vNormal = (instanceMatrix * vec4(normal, 0.)).xyz; - #endif - - absPosition = modelMatrix * absPosition; - vNormal = (normalize(modelMatrix * vec4(vNormal, 0.))).xyz; - - gl_Position = projectionMatrix * viewMatrix * absPosition; - - vCameraPosition = cameraPosition; - vPosition = absPosition.xyz; - - #include - #include - } - `, - fragmentShader: ` - varying vec3 vCameraPosition; - varying vec3 vPosition; - varying vec3 vNormal; - - #include - - void main() { - #include - vec3 cameraPixelVec = normalize(vCameraPosition - vPosition); - float difference = abs(dot(vNormal, cameraPixelVec)); - - // This achieves a double gloss effect: when the surface is perpendicular and when it's parallel - difference = abs((difference * 2.) - 1.); - - gl_FragColor = vec4(difference, difference, difference, 1.); - } - `, - }); -} + default: -// Follows the structure of -// https://github.com/mrdoob/three.js/blob/master/examples/jsm/postprocessing/OutlinePass.js -class CustomEffectsPass extends Pass { - get lineColor() { - return this._lineColor; - } - set lineColor(lineColor) { - this._lineColor = lineColor; - const material = this.fsQuad.material; - material.uniforms.lineColor.value.set(lineColor); - } - get tolerance() { - return this._tolerance; - } - set tolerance(value) { - this._tolerance = value; - const material = this.fsQuad.material; - material.uniforms.tolerance.value = value; - } - get opacity() { - return this._opacity; - } - set opacity(value) { - this._opacity = value; - const material = this.fsQuad.material; - material.uniforms.opacity.value = value; - } - get glossEnabled() { - return this._glossEnabled; - } - set glossEnabled(active) { - if (active === this._glossEnabled) - return; - this._glossEnabled = active; - const material = this.fsQuad.material; - material.uniforms.glossEnabled.value = active ? 1 : 0; - } - get glossExponent() { - return this._glossExponent; - } - set glossExponent(value) { - this._glossExponent = value; - const material = this.fsQuad.material; - material.uniforms.glossExponent.value = value; - } - get minGloss() { - return this._minGloss; - } - set minGloss(value) { - this._minGloss = value; - const material = this.fsQuad.material; - material.uniforms.minGloss.value = value; - } - get maxGloss() { - new THREE$1.MeshBasicMaterial().color.convertLinearToSRGB(); - return this._maxGloss; - } - set maxGloss(value) { - this._maxGloss = value; - const material = this.fsQuad.material; - material.uniforms.maxGloss.value = value; - } - get outlineEnabled() { - return this._outlineEnabled; - } - set outlineEnabled(active) { - if (active === this._outlineEnabled) - return; - this._outlineEnabled = active; - const material = this.fsQuad.material; - material.uniforms.outlineEnabled.value = active ? 1 : 0; - } - constructor(resolution, components, scene, camera) { - super(); - this.excludedMeshes = []; - this.outlinedMeshes = {}; - this._outlineScene = new THREE$1.Scene(); - this._outlineEnabled = false; - this._lineColor = 0x999999; - this._opacity = 0.4; - this._tolerance = 3; - this._glossEnabled = true; - this._glossExponent = 1.9; - this._minGloss = -0.1; - this._maxGloss = 0.1; - this._outlinesNeedsUpdate = false; - this.components = components; - this.renderScene = scene; - this.renderCamera = camera; - this.resolution = new THREE$1.Vector2(resolution.x, resolution.y); - this.fsQuad = new FullScreenQuad(); - this.fsQuad.material = this.createOutlinePostProcessMaterial(); - this.planeBuffer = this.newRenderTarget(); - this.glossBuffer = this.newRenderTarget(); - this.outlineBuffer = this.newRenderTarget(); - const normalMaterial = getPlaneDistanceMaterial(); - normalMaterial.clippingPlanes = components.renderer.clippingPlanes; - this.normalOverrideMaterial = normalMaterial; - const glossMaterial = getProjectedNormalMaterial(); - glossMaterial.clippingPlanes = components.renderer.clippingPlanes; - this.glossOverrideMaterial = glossMaterial; - } - async dispose() { - this.planeBuffer.dispose(); - this.glossBuffer.dispose(); - this.outlineBuffer.dispose(); - this.normalOverrideMaterial.dispose(); - this.glossOverrideMaterial.dispose(); - this.fsQuad.dispose(); - this.excludedMeshes = []; - this._outlineScene.children = []; - const disposer = await this.components.tools.get(Disposer); - for (const name in this.outlinedMeshes) { - const style = this.outlinedMeshes[name]; - for (const mesh of style.meshes) { - disposer.destroy(mesh, true, true); - } - style.material.dispose(); - } - } - setSize(width, height) { - this.planeBuffer.setSize(width, height); - this.glossBuffer.setSize(width, height); - this.outlineBuffer.setSize(width, height); - this.resolution.set(width, height); - const material = this.fsQuad.material; - material.uniforms.screenSize.value.set(this.resolution.x, this.resolution.y, 1 / this.resolution.x, 1 / this.resolution.y); - } - render(renderer, writeBuffer, readBuffer) { - // Turn off writing to the depth buffer - // because we need to read from it in the subsequent passes. - const depthBufferValue = writeBuffer.depthBuffer; - writeBuffer.depthBuffer = false; - // 1. Re-render the scene to capture all normals in a texture. - const previousOverrideMaterial = this.renderScene.overrideMaterial; - const previousBackground = this.renderScene.background; - this.renderScene.background = null; - for (const mesh of this.excludedMeshes) { - mesh.visible = false; - } - // Render normal pass - renderer.setRenderTarget(this.planeBuffer); - this.renderScene.overrideMaterial = this.normalOverrideMaterial; - renderer.render(this.renderScene, this.renderCamera); - // Render gloss pass - if (this._glossEnabled) { - renderer.setRenderTarget(this.glossBuffer); - this.renderScene.overrideMaterial = this.glossOverrideMaterial; - renderer.render(this.renderScene, this.renderCamera); - } - this.renderScene.overrideMaterial = previousOverrideMaterial; - // Render outline pass - if (this._outlineEnabled) { - let outlinedMeshesFound = false; - for (const name in this.outlinedMeshes) { - const style = this.outlinedMeshes[name]; - for (const mesh of style.meshes) { - outlinedMeshesFound = true; - mesh.userData.materialPreOutline = mesh.material; - mesh.material = style.material; - mesh.userData.groupsPreOutline = mesh.geometry.groups; - mesh.geometry.groups = []; - if (mesh instanceof THREE$1.InstancedMesh) { - mesh.userData.colorPreOutline = mesh.instanceColor; - mesh.instanceColor = null; - } - mesh.userData.parentPreOutline = mesh.parent; - this._outlineScene.add(mesh); - } - } - // This way, when there are no outlines meshes, it clears the outlines buffer only once - // and then skips this render - if (outlinedMeshesFound || this._outlinesNeedsUpdate) { - renderer.setRenderTarget(this.outlineBuffer); - renderer.render(this._outlineScene, this.renderCamera); - this._outlinesNeedsUpdate = outlinedMeshesFound; - } - for (const name in this.outlinedMeshes) { - const style = this.outlinedMeshes[name]; - for (const mesh of style.meshes) { - mesh.material = mesh.userData.materialPreOutline; - mesh.geometry.groups = mesh.userData.groupsPreOutline; - if (mesh instanceof THREE$1.InstancedMesh) { - mesh.instanceColor = mesh.userData.colorPreOutline; - } - if (mesh.userData.parentPreOutline) { - mesh.userData.parentPreOutline.add(mesh); - } - mesh.userData.materialPreOutline = undefined; - mesh.userData.groupsPreOutline = undefined; - mesh.userData.colorPreOutline = undefined; - mesh.userData.parentPreOutline = undefined; - } - } - } - for (const mesh of this.excludedMeshes) { - mesh.visible = true; - } - this.renderScene.background = previousBackground; - const material = this.fsQuad.material; - material.uniforms.planeBuffer.value = this.planeBuffer.texture; - material.uniforms.glossBuffer.value = this.glossBuffer.texture; - material.uniforms.outlineBuffer.value = this.outlineBuffer.texture; - material.uniforms.sceneColorBuffer.value = readBuffer.texture; - if (this.renderToScreen) { - // If this is the last effect, then renderToScreen is true. - // So we should render to the screen by setting target null - // Otherwise, just render into the writeBuffer that the next effect will use as its read buffer. - renderer.setRenderTarget(null); - this.fsQuad.render(renderer); - } - else { - renderer.setRenderTarget(writeBuffer); - this.fsQuad.render(renderer); - } - // Reset the depthBuffer value so we continue writing to it in the next render. - writeBuffer.depthBuffer = depthBufferValue; - } - get vertexShader() { - return ` - varying vec2 vUv; - void main() { - vUv = uv; - gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); - } - `; - } - get fragmentShader() { - return ` - uniform sampler2D sceneColorBuffer; - uniform sampler2D planeBuffer; - uniform sampler2D glossBuffer; - uniform sampler2D outlineBuffer; - uniform vec4 screenSize; - uniform vec3 lineColor; - - uniform float outlineEnabled; - - uniform int width; - uniform float opacity; - uniform float tolerance; - uniform float glossExponent; - uniform float minGloss; - uniform float maxGloss; - uniform float glossEnabled; + state = STATE.NONE; + + } + + if ( state !== STATE.NONE ) { + + scope.dispatchEvent( _startEvent ); + + } + + } + + function onMouseMove( event ) { + + switch ( state ) { + + case STATE.ROTATE: + + if ( scope.enableRotate === false ) return; + + handleMouseMoveRotate( event ); + + break; + + case STATE.DOLLY: + + if ( scope.enableZoom === false ) return; + + handleMouseMoveDolly( event ); + + break; + + case STATE.PAN: + + if ( scope.enablePan === false ) return; + + handleMouseMovePan( event ); + + break; + + } + + } + + function onMouseWheel( event ) { + + if ( scope.enabled === false || scope.enableZoom === false || state !== STATE.NONE ) return; + + event.preventDefault(); + + scope.dispatchEvent( _startEvent ); + + handleMouseWheel( event ); + + scope.dispatchEvent( _endEvent ); + + } + + function onKeyDown( event ) { + + if ( scope.enabled === false || scope.enablePan === false ) return; + + handleKeyDown( event ); + + } + + function onTouchStart( event ) { + + trackPointer( event ); + + switch ( pointers.length ) { + + case 1: + + switch ( scope.touches.ONE ) { + + case TOUCH.ROTATE: + + if ( scope.enableRotate === false ) return; + + handleTouchStartRotate(); + + state = STATE.TOUCH_ROTATE; + + break; + + case TOUCH.PAN: + + if ( scope.enablePan === false ) return; + + handleTouchStartPan(); + + state = STATE.TOUCH_PAN; + + break; + + default: + + state = STATE.NONE; + + } + + break; + + case 2: + + switch ( scope.touches.TWO ) { + + case TOUCH.DOLLY_PAN: + + if ( scope.enableZoom === false && scope.enablePan === false ) return; + + handleTouchStartDollyPan(); + + state = STATE.TOUCH_DOLLY_PAN; + + break; + + case TOUCH.DOLLY_ROTATE: + + if ( scope.enableZoom === false && scope.enableRotate === false ) return; + + handleTouchStartDollyRotate(); + + state = STATE.TOUCH_DOLLY_ROTATE; + + break; + + default: + + state = STATE.NONE; + + } + + break; + + default: + + state = STATE.NONE; + + } + + if ( state !== STATE.NONE ) { + + scope.dispatchEvent( _startEvent ); + + } + + } + + function onTouchMove( event ) { + + trackPointer( event ); + + switch ( state ) { + + case STATE.TOUCH_ROTATE: + + if ( scope.enableRotate === false ) return; - varying vec2 vUv; + handleTouchMoveRotate( event ); - vec4 getValue(sampler2D buffer, int x, int y) { - return texture2D(buffer, vUv + screenSize.zw * vec2(x, y)); - } + scope.update(); - float normalDiff(vec3 normal1, vec3 normal2) { - return ((dot(normal1, normal2) - 1.) * -1.) / 2.; - } + break; - // Returns 0 if it's background, 1 if it's not - float getIsBackground(vec3 normal) { - float background = 1.0; - background *= step(normal.x, 0.); - background *= step(normal.y, 0.); - background *= step(normal.z, 0.); - background = (background - 1.) * -1.; - return background; - } + case STATE.TOUCH_PAN: - void main() { - - vec4 sceneColor = getValue(sceneColorBuffer, 0, 0); - vec3 normSceneColor = normalize(sceneColor.rgb); - - vec4 plane = getValue(planeBuffer, 0, 0); - vec3 normal = plane.xyz; - float distance = plane.w; - - vec3 normalTop = getValue(planeBuffer, 0, width).rgb; - vec3 normalBottom = getValue(planeBuffer, 0, -width).rgb; - vec3 normalRight = getValue(planeBuffer, width, 0).rgb; - vec3 normalLeft = getValue(planeBuffer, -width, 0).rgb; - vec3 normalTopRight = getValue(planeBuffer, width, width).rgb; - vec3 normalTopLeft = getValue(planeBuffer, -width, width).rgb; - vec3 normalBottomRight = getValue(planeBuffer, width, -width).rgb; - vec3 normalBottomLeft = getValue(planeBuffer, -width, -width).rgb; - - float distanceTop = getValue(planeBuffer, 0, width).a; - float distanceBottom = getValue(planeBuffer, 0, -width).a; - float distanceRight = getValue(planeBuffer, width, 0).a; - float distanceLeft = getValue(planeBuffer, -width, 0).a; - float distanceTopRight = getValue(planeBuffer, width, width).a; - float distanceTopLeft = getValue(planeBuffer, -width, width).a; - float distanceBottomRight = getValue(planeBuffer, width, -width).a; - float distanceBottomLeft = getValue(planeBuffer, -width, -width).a; - - vec3 sceneColorTop = normalize(getValue(sceneColorBuffer, 1, 0).rgb); - vec3 sceneColorBottom = normalize(getValue(sceneColorBuffer, -1, 0).rgb); - vec3 sceneColorLeft = normalize(getValue(sceneColorBuffer, 0, -1).rgb); - vec3 sceneColorRight = normalize(getValue(sceneColorBuffer, 0, 1).rgb); - vec3 sceneColorTopRight = normalize(getValue(sceneColorBuffer, 1, 1).rgb); - vec3 sceneColorBottomRight = normalize(getValue(sceneColorBuffer, -1, 1).rgb); - vec3 sceneColorTopLeft = normalize(getValue(sceneColorBuffer, 1, 1).rgb); - vec3 sceneColorBottomLeft = normalize(getValue(sceneColorBuffer, -1, 1).rgb); + if ( scope.enablePan === false ) return; - // Checks if the planes of this texel and the neighbour texels are different + handleTouchMovePan( event ); - float planeDiff = 0.0; + scope.update(); - planeDiff += step(0.001, normalDiff(normal, normalTop)); - planeDiff += step(0.001, normalDiff(normal, normalBottom)); - planeDiff += step(0.001, normalDiff(normal, normalLeft)); - planeDiff += step(0.001, normalDiff(normal, normalRight)); - planeDiff += step(0.001, normalDiff(normal, normalTopRight)); - planeDiff += step(0.001, normalDiff(normal, normalTopLeft)); - planeDiff += step(0.001, normalDiff(normal, normalBottomRight)); - planeDiff += step(0.001, normalDiff(normal, normalBottomLeft)); - - planeDiff += step(0.001, normalDiff(normSceneColor, sceneColorTop)); - planeDiff += step(0.001, normalDiff(normSceneColor, sceneColorBottom)); - planeDiff += step(0.001, normalDiff(normSceneColor, sceneColorLeft)); - planeDiff += step(0.001, normalDiff(normSceneColor, sceneColorRight)); - planeDiff += step(0.001, normalDiff(normSceneColor, sceneColorTopRight)); - planeDiff += step(0.001, normalDiff(normSceneColor, sceneColorTopLeft)); - planeDiff += step(0.001, normalDiff(normSceneColor, sceneColorBottomRight)); - planeDiff += step(0.001, normalDiff(normSceneColor, sceneColorBottomLeft)); + break; - planeDiff += step(0.001, abs(distance - distanceTop)); - planeDiff += step(0.001, abs(distance - distanceBottom)); - planeDiff += step(0.001, abs(distance - distanceLeft)); - planeDiff += step(0.001, abs(distance - distanceRight)); - planeDiff += step(0.001, abs(distance - distanceTopRight)); - planeDiff += step(0.001, abs(distance - distanceTopLeft)); - planeDiff += step(0.001, abs(distance - distanceBottomRight)); - planeDiff += step(0.001, abs(distance - distanceBottomLeft)); + case STATE.TOUCH_DOLLY_PAN: - // Add extra background outline + if ( scope.enableZoom === false && scope.enablePan === false ) return; - int width2 = width + 1; - vec3 normalTop2 = getValue(planeBuffer, 0, width2).rgb; - vec3 normalBottom2 = getValue(planeBuffer, 0, -width2).rgb; - vec3 normalRight2 = getValue(planeBuffer, width2, 0).rgb; - vec3 normalLeft2 = getValue(planeBuffer, -width2, 0).rgb; - vec3 normalTopRight2 = getValue(planeBuffer, width2, width2).rgb; - vec3 normalTopLeft2 = getValue(planeBuffer, -width2, width2).rgb; - vec3 normalBottomRight2 = getValue(planeBuffer, width2, -width2).rgb; - vec3 normalBottomLeft2 = getValue(planeBuffer, -width2, -width2).rgb; + handleTouchMoveDollyPan( event ); - planeDiff += -(getIsBackground(normalTop2) - 1.); - planeDiff += -(getIsBackground(normalBottom2) - 1.); - planeDiff += -(getIsBackground(normalRight2) - 1.); - planeDiff += -(getIsBackground(normalLeft2) - 1.); - planeDiff += -(getIsBackground(normalTopRight2) - 1.); - planeDiff += -(getIsBackground(normalBottomRight2) - 1.); - planeDiff += -(getIsBackground(normalBottomRight2) - 1.); - planeDiff += -(getIsBackground(normalBottomLeft2) - 1.); + scope.update(); - // Tolerance sets the minimum amount of differences to consider - // this texel an edge + break; - float line = step(tolerance, planeDiff); + case STATE.TOUCH_DOLLY_ROTATE: - // Exclude background and apply opacity + if ( scope.enableZoom === false && scope.enableRotate === false ) return; - float background = getIsBackground(normal); - line *= background; - line *= opacity; - - // Add gloss - - vec3 gloss = getValue(glossBuffer, 0, 0).xyz; - float diffGloss = abs(maxGloss - minGloss); - vec3 glossExpVector = vec3(glossExponent,glossExponent,glossExponent); - gloss = min(pow(gloss, glossExpVector), vec3(1.,1.,1.)); - gloss *= diffGloss; - gloss += minGloss; - vec4 glossedColor = sceneColor + vec4(gloss, 1.) * glossEnabled; - - vec4 corrected = mix(sceneColor, glossedColor, background); - - // Draw lines - - corrected = mix(corrected, vec4(lineColor, 1.), line); - - // Add outline - - vec4 outlinePreview =getValue(outlineBuffer, 0, 0); - float outlineColorCorrection = 1. / max(0.2, outlinePreview.a); - vec3 outlineColor = outlinePreview.rgb * outlineColorCorrection; - - // thickness between 10 and 2, opacity between 1 and 0.2 - int outlineThickness = int(outlinePreview.a * 10.); - - float outlineDiff = 0.; - - outlineDiff += step(0.1, getValue(outlineBuffer, 0, 0).a); - outlineDiff += step(0.1, getValue(outlineBuffer, 1, 0).a); - outlineDiff += step(0.1, getValue(outlineBuffer, -1, 0).a); - outlineDiff += step(0.1, getValue(outlineBuffer, 0, -1).a); - outlineDiff += step(0.1, getValue(outlineBuffer, 0, 1).a); - outlineDiff += step(0.1, getValue(outlineBuffer, outlineThickness, 0).a); - outlineDiff += step(0.1, getValue(outlineBuffer, -outlineThickness, 0).a); - outlineDiff += step(0.1, getValue(outlineBuffer, 0, -outlineThickness).a); - outlineDiff += step(0.1, getValue(outlineBuffer, 0, outlineThickness).a); - outlineDiff += step(0.1, getValue(outlineBuffer, outlineThickness, outlineThickness).a); - outlineDiff += step(0.1, getValue(outlineBuffer, -outlineThickness, outlineThickness).a); - outlineDiff += step(0.1, getValue(outlineBuffer, -outlineThickness, -outlineThickness).a); - outlineDiff += step(0.1, getValue(outlineBuffer, outlineThickness, -outlineThickness).a); - - float outLine = step(4., outlineDiff) * step(outlineDiff, 12.) * outlineEnabled; - corrected = mix(corrected, vec4(outlineColor, 1.), outLine); - - gl_FragColor = corrected; - } - `; - } - createOutlinePostProcessMaterial() { - return new THREE$1.ShaderMaterial({ - uniforms: { - opacity: { value: this._opacity }, - debugVisualize: { value: 0 }, - sceneColorBuffer: { value: null }, - tolerance: { value: this._tolerance }, - planeBuffer: { value: null }, - glossBuffer: { value: null }, - outlineBuffer: { value: null }, - glossEnabled: { value: 1 }, - minGloss: { value: this._minGloss }, - maxGloss: { value: this._maxGloss }, - outlineEnabled: { value: 0 }, - glossExponent: { value: this._glossExponent }, - width: { value: 1 }, - lineColor: { value: new THREE$1.Color(this._lineColor) }, - screenSize: { - value: new THREE$1.Vector4(this.resolution.x, this.resolution.y, 1 / this.resolution.x, 1 / this.resolution.y), - }, - }, - vertexShader: this.vertexShader, - fragmentShader: this.fragmentShader, - }); - } - newRenderTarget() { - const planeBuffer = new THREE$1.WebGLRenderTarget(this.resolution.x, this.resolution.y); - planeBuffer.texture.colorSpace = "srgb-linear"; - planeBuffer.texture.format = THREE$1.RGBAFormat; - planeBuffer.texture.type = THREE$1.HalfFloatType; - planeBuffer.texture.minFilter = THREE$1.NearestFilter; - planeBuffer.texture.magFilter = THREE$1.NearestFilter; - planeBuffer.texture.generateMipmaps = false; - planeBuffer.stencilBuffer = false; - return planeBuffer; - } -} + handleTouchMoveDollyRotate( event ); -// source: https://discourse.threejs.org/t/how-to-render-full-outlines-as-a-post-process-tutorial/22674 -class Postproduction { - get basePass() { - if (!this._basePass) { - throw new Error("Custom effects not initialized!"); - } - return this._basePass; - } - get gammaPass() { - if (!this._gammaPass) { - throw new Error("Custom effects not initialized!"); - } - return this._gammaPass; - } - get customEffects() { - if (!this._customEffects) { - throw new Error("Custom effects not initialized!"); - } - return this._customEffects; - } - get n8ao() { - if (!this._n8ao) { - throw new Error("Custom effects not initialized!"); - } - return this._n8ao; - } - get enabled() { - return this._enabled; - } - set enabled(active) { - if (!this._initialized) { - this.initialize(); - } - this._enabled = active; - } - get settings() { - return { ...this._settings }; - } - constructor(components, renderer) { - this.components = components; - this.renderer = renderer; - this.excludedItems = new Set(); - this.overrideClippingPlanes = false; - this._enabled = false; - this._initialized = false; - this._settings = { - gamma: true, - custom: true, - ao: false, - }; - this._renderTarget = new THREE$1.WebGLRenderTarget(window.innerWidth, window.innerHeight); - this._renderTarget.texture.colorSpace = "srgb-linear"; - this.composer = new EffectComposer(this.renderer, this._renderTarget); - this.composer.setSize(window.innerWidth, window.innerHeight); - } - async dispose() { - var _a, _b, _c, _d; - this._renderTarget.dispose(); - (_a = this._depthTexture) === null || _a === void 0 ? void 0 : _a.dispose(); - await ((_b = this._customEffects) === null || _b === void 0 ? void 0 : _b.dispose()); - (_c = this._gammaPass) === null || _c === void 0 ? void 0 : _c.dispose(); - (_d = this._n8ao) === null || _d === void 0 ? void 0 : _d.dispose(); - this.excludedItems.clear(); - } - setPasses(settings) { - // This check can prevent some bugs - let settingsChanged = false; - for (const name in settings) { - const key = name; - if (this.settings[key] !== settings[key]) { - settingsChanged = true; - break; - } - } - if (!settingsChanged) { - return; - } - for (const name in settings) { - const key = name; - if (this._settings[key] !== undefined) { - this._settings[key] = settings[key]; - } - } - this.updatePasses(); - } - setSize(width, height) { - if (this._initialized) { - this.composer.setSize(width, height); - this.basePass.setSize(width, height); - this.n8ao.setSize(width, height); - this.customEffects.setSize(width, height); - this.gammaPass.setSize(width, height); - } - } - update() { - if (!this._enabled) - return; - this.composer.render(); - } - updateCamera() { - const camera = this.components.camera.get(); - if (this._n8ao) { - this._n8ao.camera = camera; - } - if (this._customEffects) { - this._customEffects.renderCamera = camera; - } - if (this._basePass) { - this._basePass.camera = camera; - } - } - initialize() { - const scene = this.overrideScene || this.components.scene.get(); - const camera = this.overrideCamera || this.components.camera.get(); - if (!scene || !camera) - return; - if (this.components.camera instanceof OrthoPerspectiveCamera) { - this.components.camera.projectionChanged.add(() => { - this.updateCamera(); - }); - } - const renderer = this.components.renderer; - if (!this.overrideClippingPlanes) { - this.renderer.clippingPlanes = renderer.clippingPlanes; - } - this.renderer.outputColorSpace = "srgb"; - this.renderer.toneMapping = THREE$1.NoToneMapping; - this.newBasePass(scene, camera); - this.newSaoPass(scene, camera); - this.newGammaPass(); - this.newCustomPass(scene, camera); - this._initialized = true; - this.updatePasses(); - } - updateProjection(camera) { - this.composer.passes.forEach((pass) => { - // @ts-ignore - pass.camera = camera; - }); - this.update(); - } - updatePasses() { - for (const pass of this.composer.passes) { - this.composer.removePass(pass); - } - if (this._basePass) { - this.composer.addPass(this.basePass); - } - if (this._settings.gamma) { - this.composer.addPass(this.gammaPass); - } - if (this._settings.ao) { - this.composer.addPass(this.n8ao); - } - if (this._settings.custom) { - this.composer.addPass(this.customEffects); - } - } - newCustomPass(scene, camera) { - this._customEffects = new CustomEffectsPass(new THREE$1.Vector2(window.innerWidth, window.innerHeight), this.components, scene, camera); - } - newGammaPass() { - this._gammaPass = new ShaderPass(GammaCorrectionShader); - } - newSaoPass(scene, camera) { - const { width, height } = this.components.renderer.getSize(); - this._n8ao = new $05f6997e4b65da14$export$2d57db20b5eb5e0a(scene, camera, width, height); - // this.composer.addPass(this.n8ao); - const { configuration } = this._n8ao; - configuration.aoSamples = 16; - configuration.denoiseSamples = 1; - configuration.denoiseRadius = 13; - configuration.aoRadius = 1; - configuration.distanceFalloff = 4; - configuration.aoRadius = 1; - configuration.intensity = 4; - configuration.halfRes = true; - configuration.color = new THREE$1.Color().setHex(0xcccccc, "srgb-linear"); - } - newBasePass(scene, camera) { - this._basePass = new RenderPass(scene, camera); - } -} + scope.update(); + + break; + + default: + + state = STATE.NONE; + + } + + } + + function onContextMenu( event ) { + + if ( scope.enabled === false ) return; + + event.preventDefault(); + + } + + function addPointer( event ) { + + pointers.push( event ); + + } + + function removePointer( event ) { + + delete pointerPositions[ event.pointerId ]; + + for ( let i = 0; i < pointers.length; i ++ ) { + + if ( pointers[ i ].pointerId == event.pointerId ) { + + pointers.splice( i, 1 ); + return; + + } + + } + + } + + function trackPointer( event ) { + + let position = pointerPositions[ event.pointerId ]; + + if ( position === undefined ) { + + position = new Vector2$1(); + pointerPositions[ event.pointerId ] = position; + + } + + position.set( event.pageX, event.pageY ); + + } + + function getSecondPointerPosition( event ) { + + const pointer = ( event.pointerId === pointers[ 0 ].pointerId ) ? pointers[ 1 ] : pointers[ 0 ]; + + return pointerPositions[ pointer.pointerId ]; + + } + + // + + scope.domElement.addEventListener( 'contextmenu', onContextMenu ); + + scope.domElement.addEventListener( 'pointerdown', onPointerDown ); + scope.domElement.addEventListener( 'pointercancel', onPointerUp ); + scope.domElement.addEventListener( 'wheel', onMouseWheel, { passive: false } ); + + // force an update at start + + this.update(); + + } -/** - * Renderer that uses efficient postproduction effects (e.g. Ambient Occlusion). - */ -class PostproductionRenderer extends SimpleRenderer { - constructor(components, container, parameters) { - super(components, container, parameters); - this.postproduction = new Postproduction(components, this._renderer); - this.setPostproductionSize(); - this.onResize.add((size) => this.resizePostproduction(size)); - } - /** {@link Updateable.update} */ - async update() { - if (!this.enabled) - return; - await this.onBeforeUpdate.trigger(); - const scene = this.overrideScene || this.components.scene.get(); - const camera = this.overrideCamera || this.components.camera.get(); - if (!scene || !camera) - return; - if (this.postproduction.enabled) { - this.postproduction.composer.render(); - } - else { - this._renderer.render(scene, camera); - } - this._renderer2D.render(scene, camera); - await this.onAfterUpdate.trigger(); - } - /** {@link Disposable.dispose}. */ - async dispose() { - await super.dispose(); - await this.postproduction.dispose(); - } - resizePostproduction(size) { - if (this.postproduction) { - this.setPostproductionSize(size); - } - } - setPostproductionSize(size) { - if (!this.container) - return; - const width = size ? size.x : this.container.clientWidth; - const height = size ? size.y : this.container.clientHeight; - this.postproduction.setSize(width, height); - } } /** @@ -99425,1659 +100152,971 @@ const IfcElements = { 2391383451: "IFCVIBRATIONISOLATOR", 2391406946: "IFCWALL", 2474470126: "IFCMOTORCONNECTION", - 2769231204: "IFCVIRTUALELEMENT", - 2814081492: "IFCENGINE", - 2906023776: "IFCBEAMSTANDARDCASE", - 2938176219: "IFCBURNER", - 2979338954: "IFCBUILDINGELEMENTPART", - 3024970846: "IFCRAMP", - 3026737570: "IFCTUBEBUNDLE", - 3027962421: "IFCSLABSTANDARDCASE", - 3040386961: "IFCDISTRIBUTIONFLOWELEMENT", - 3053780830: "IFCSANITARYTERMINAL", - 3079942009: "IFCOPENINGSTANDARDCASE", - 3087945054: "IFCALARM", - 3101698114: "IFCSURFACEFEATURE", - 3127900445: "IFCSLABELEMENTEDCASE", - 3132237377: "IFCFLOWMOVINGDEVICE", - 3171933400: "IFCPLATE", - 3221913625: "IFCCOMMUNICATIONSAPPLIANCE", - 3242481149: "IFCDOORSTANDARDCASE", - 3283111854: "IFCRAMPFLIGHT", - 3296154744: "IFCCHIMNEY", - 3304561284: "IFCWINDOW", - 3310460725: "IFCELECTRICFLOWSTORAGEDEVICE", - 3319311131: "IFCHEATEXCHANGER", - 3415622556: "IFCFAN", - 3420628829: "IFCSOLARDEVICE", - 3493046030: "IFCGEOGRAPHICELEMENT", - 3495092785: "IFCCURTAINWALL", - 3508470533: "IFCFLOWTREATMENTDEVICE", - 3512223829: "IFCWALLSTANDARDCASE", - 3518393246: "IFCDUCTSEGMENT", - 3571504051: "IFCCOMPRESSOR", - 3588315303: "IFCOPENINGELEMENT", - 3612865200: "IFCPIPESEGMENT", - 3640358203: "IFCCOOLINGTOWER", - 3651124850: "IFCPROJECTIONELEMENT", - 3694346114: "IFCOUTLET", - 3747195512: "IFCEVAPORATIVECOOLER", - 3758799889: "IFCCABLECARRIERSEGMENT", - 3824725483: "IFCTENDON", - 3825984169: "IFCTRANSFORMER", - 3902619387: "IFCCHILLER", - 4074379575: "IFCDAMPER", - 4086658281: "IFCSENSOR", - 4123344466: "IFCELEMENTASSEMBLY", - 4136498852: "IFCCOOLEDBEAM", - 4156078855: "IFCWALLELEMENTEDCASE", - 4175244083: "IFCINTERCEPTOR", - 4207607924: "IFCVALVE", - 4217484030: "IFCCABLESEGMENT", - 4237592921: "IFCWASTETERMINAL", - 4252922144: "IFCSTAIRFLIGHT", - 4278956645: "IFCFLOWFITTING", - 4288193352: "IFCACTUATOR", - 4292641817: "IFCUNITARYEQUIPMENT", - 3009204131: "IFCGRID", -}; - -class IfcCategories { - getAll(webIfc, modelID) { - const elementsCategories = {}; - const categoriesIDs = Object.keys(IfcElements).map((e) => parseInt(e, 10)); - for (let i = 0; i < categoriesIDs.length; i++) { - const element = categoriesIDs[i]; - const lines = webIfc.GetLineIDsWithType(modelID, element); - const size = lines.size(); - for (let i = 0; i < size; i++) { - elementsCategories[lines.get(i)] = element; - } - } - return elementsCategories; - } -} - -const GeometryTypes = new Set([ - 1123145078, 574549367, 1675464909, 2059837836, 3798115385, 32440307, - 3125803723, 3207858831, 2740243338, 2624227202, 4240577450, 3615266464, - 3724593414, 220341763, 477187591, 1878645084, 1300840506, 3303107099, - 1607154358, 1878645084, 846575682, 1351298697, 2417041796, 3049322572, - 3331915920, 1416205885, 776857604, 3285139300, 3958052878, 2827736869, - 2732653382, 673634403, 3448662350, 4142052618, 2924175390, 803316827, - 2556980723, 1809719519, 2205249479, 807026263, 3737207727, 1660063152, - 2347385850, 3940055652, 2705031697, 3732776249, 2485617015, 2611217952, - 1704287377, 2937912522, 2770003689, 1281925730, 1484403080, 3448662350, - 4142052618, 3800577675, 4006246654, 3590301190, 1383045692, 2775532180, - 2047409740, 370225590, 3593883385, 2665983363, 4124623270, 812098782, - 3649129432, 987898635, 1105321065, 3510044353, 1635779807, 2603310189, - 3406155212, 1310608509, 4261334040, 2736907675, 3649129432, 1136057603, - 1260505505, 4182860854, 2713105998, 2898889636, 59481748, 3749851601, - 3486308946, 3150382593, 1062206242, 3264961684, 15328376, 1485152156, - 370225590, 1981873012, 2859738748, 45288368, 2614616156, 2732653382, - 775493141, 2147822146, 2601014836, 2629017746, 1186437898, 2367409068, - 1213902940, 3632507154, 3900360178, 476780140, 1472233963, 2804161546, - 3008276851, 738692330, 374418227, 315944413, 3905492369, 3570813810, - 2571569899, 178912537, 2294589976, 1437953363, 2133299955, 572779678, - 3092502836, 388784114, 2624227202, 1425443689, 3057273783, 2347385850, - 1682466193, 2519244187, 2839578677, 3958567839, 2513912981, 2830218821, - 427810014, -]); - -/** - * Object to export all the properties from an IFC to a JS object. - */ -class IfcJsonExporter { - constructor() { - this.onLoadProgress = new Event(); - this.onPropertiesSerialized = new Event(); - this._progress = 0; - } - /** - * Exports all the properties of an IFC into an array of JS objects. - * @webIfc The instance of [web-ifc]{@link https://github.com/ifcjs/web-ifc} to use. - * @modelID ID of the IFC model whose properties to extract. - */ - async export(webIfc, modelID) { - const geometriesIDs = await this.getAllGeometriesIDs(modelID, webIfc); - let properties = {}; - properties.coordinationMatrix = webIfc.GetCoordinationMatrix(modelID); - const allLinesIDs = await webIfc.GetAllLines(modelID); - const linesCount = allLinesIDs.size(); - this._progress = 0.1; - let counter = 0; - for (let i = 0; i < linesCount; i++) { - const id = allLinesIDs.get(i); - if (!geometriesIDs.has(id)) { - try { - properties[id] = await webIfc.GetLine(modelID, id); - } - catch (e) { - console.log(`Properties of the element ${id} could not be processed`); - } - counter++; - } - if (this.size !== undefined && counter > this.size) { - await this.onPropertiesSerialized.trigger(properties); - properties = null; - properties = {}; - counter = 0; - } - if (i / linesCount > this._progress) { - await this.onLoadProgress.trigger({ - progress: i, - total: linesCount, - }); - this._progress += 0.1; - } - } - await this.onPropertiesSerialized.trigger(properties); - properties = null; - } - async getAllGeometriesIDs(modelID, webIfc) { - // Exclude location info of spatial structure - const placementIDs = new Set(); - const structures = new Set(); - this.getStructure(IFCPROJECT, structures, webIfc); - this.getStructure(IFCSITE, structures, webIfc); - this.getStructure(IFCBUILDING, structures, webIfc); - this.getStructure(IFCBUILDINGSTOREY, structures, webIfc); - this.getStructure(IFCSPACE, structures, webIfc); - for (const id of structures) { - const properties = webIfc.GetLine(0, id); - const placementRef = properties.ObjectPlacement; - if (!placementRef || placementRef.value === null) { - continue; - } - const placementID = placementRef.value; - placementIDs.add(placementID); - const placementProps = webIfc.GetLine(0, placementID); - const relPlacementID = placementProps.RelativePlacement; - if (!relPlacementID || relPlacementID.value === null) { - continue; - } - placementIDs.add(relPlacementID.value); - const relPlacement = webIfc.GetLine(0, relPlacementID.value); - const location = relPlacement.Location; - if (location && location.value !== null) { - placementIDs.add(location.value); - } - } - const geometriesIDs = new Set(); - const geomTypesArray = Array.from(GeometryTypes); - for (let i = 0; i < geomTypesArray.length; i++) { - const category = geomTypesArray[i]; - // eslint-disable-next-line no-await-in-loop - const ids = await webIfc.GetLineIDsWithType(modelID, category); - const idsSize = ids.size(); - for (let j = 0; j < idsSize; j++) { - const id = ids.get(j); - if (placementIDs.has(id)) { - continue; - } - geometriesIDs.add(id); - } - } - return geometriesIDs; - } - getStructure(type, result, webIfc) { - const found = webIfc.GetLineIDsWithType(0, type); - const size = found.size(); - for (let i = 0; i < size; i++) { - const id = found.get(i); - result.add(id); - } - } -} - -class Units { - constructor() { - this.factor = 1; - this.complement = 1; - } - apply(matrix) { - const scale = this.getScaleMatrix(); - const result = scale.multiply(matrix); - matrix.copy(result); - } - setUp(webIfc) { - var _a; - this.factor = 1; - const length = this.getLengthUnits(webIfc); - if (!length) { - return; - } - const isLengthNull = length === undefined || length === null; - const isValueNull = length.Name === undefined || length.Name === null; - if (isLengthNull || isValueNull) { - return; - } - if (length.Name.value === "FOOT") { - this.factor = 0.3048; - } - else if (((_a = length.Prefix) === null || _a === void 0 ? void 0 : _a.value) === "MILLI") { - this.complement = 0.001; - } - } - getLengthUnits(webIfc) { - try { - const allUnitsAssigns = webIfc.GetLineIDsWithType(0, IFCUNITASSIGNMENT); - const unitsAssign = allUnitsAssigns.get(0); - const unitsAssignProps = webIfc.GetLine(0, unitsAssign); - for (const units of unitsAssignProps.Units) { - if (!units || units.value === null || units.value === undefined) { - continue; - } - const unitsProps = webIfc.GetLine(0, units.value); - if (unitsProps.UnitType && unitsProps.UnitType.value === "LENGTHUNIT") { - return unitsProps; - } - } - return null; - } - catch (e) { - console.log("Could not get units"); - return null; - } - } - getScaleMatrix() { - const f = this.factor; - // prettier-ignore - return new THREE$1.Matrix4().fromArray([ - f, 0, 0, 0, - 0, f, 0, 0, - 0, 0, f, 0, - 0, 0, 0, 1, - ]); - } -} - -class SpatialStructure { - constructor() { - this.itemsByFloor = {}; - this._units = new Units(); - } - // TODO: Maybe make this more flexible so that it also support more exotic spatial structures? - async setUp(webIfc) { - this._units.setUp(webIfc); - this.cleanUp(); - try { - const spatialRels = webIfc.GetLineIDsWithType(0, IFCRELCONTAINEDINSPATIALSTRUCTURE); - const allRooms = new Set(); - const rooms = webIfc.GetLineIDsWithType(0, IFCSPACE); - for (let i = 0; i < rooms.size(); i++) { - allRooms.add(rooms.get(i)); - } - // First add rooms (if any) to floors - const aggregates = webIfc.GetLineIDsWithType(0, IFCRELAGGREGATES); - const aggregatesSize = aggregates.size(); - for (let i = 0; i < aggregatesSize; i++) { - const id = aggregates.get(i); - const properties = webIfc.GetLine(0, id); - if (!properties || - !properties.RelatingObject || - !properties.RelatedObjects) { - continue; - } - const parentID = properties.RelatingObject.value; - const childsIDs = properties.RelatedObjects; - for (const child of childsIDs) { - const childID = child.value; - if (allRooms.has(childID)) { - this.itemsByFloor[childID] = parentID; - } - } - } - // Now add items contained in floors and rooms - // If items contained in room, look for the floor where that room is and assign it to it - const itemsContainedInRooms = {}; - const spatialRelsSize = spatialRels.size(); - for (let i = 0; i < spatialRelsSize; i++) { - const id = spatialRels.get(i); - const properties = webIfc.GetLine(0, id); - if (!properties || - !properties.RelatingStructure || - !properties.RelatedElements) { - continue; - } - const structureID = properties.RelatingStructure.value; - const relatedItems = properties.RelatedElements; - if (allRooms.has(structureID)) { - for (const related of relatedItems) { - if (!itemsContainedInRooms[structureID]) { - itemsContainedInRooms[structureID] = []; - } - const id = related.value; - itemsContainedInRooms[structureID].push(id); - } - } - else { - for (const related of relatedItems) { - const id = related.value; - this.itemsByFloor[id] = structureID; - } - } - } - for (const roomID in itemsContainedInRooms) { - const roomFloor = this.itemsByFloor[roomID]; - if (roomFloor !== undefined) { - const items = itemsContainedInRooms[roomID]; - for (const item of items) { - this.itemsByFloor[item] = roomFloor; - } - } - } - // Finally, add nested items (e.g. elements of curtain walls) - for (let i = 0; i < aggregatesSize; i++) { - const id = aggregates.get(i); - const properties = webIfc.GetLine(0, id); - if (!properties || - !properties.RelatingObject || - !properties.RelatedObjects) { - continue; - } - const parentID = properties.RelatingObject.value; - const childsIDs = properties.RelatedObjects; - for (const child of childsIDs) { - const childID = child.value; - const parentStructure = this.itemsByFloor[parentID]; - if (parentStructure !== undefined) { - this.itemsByFloor[childID] = parentStructure; - } - } + 2769231204: "IFCVIRTUALELEMENT", + 2814081492: "IFCENGINE", + 2906023776: "IFCBEAMSTANDARDCASE", + 2938176219: "IFCBURNER", + 2979338954: "IFCBUILDINGELEMENTPART", + 3024970846: "IFCRAMP", + 3026737570: "IFCTUBEBUNDLE", + 3027962421: "IFCSLABSTANDARDCASE", + 3040386961: "IFCDISTRIBUTIONFLOWELEMENT", + 3053780830: "IFCSANITARYTERMINAL", + 3079942009: "IFCOPENINGSTANDARDCASE", + 3087945054: "IFCALARM", + 3101698114: "IFCSURFACEFEATURE", + 3127900445: "IFCSLABELEMENTEDCASE", + 3132237377: "IFCFLOWMOVINGDEVICE", + 3171933400: "IFCPLATE", + 3221913625: "IFCCOMMUNICATIONSAPPLIANCE", + 3242481149: "IFCDOORSTANDARDCASE", + 3283111854: "IFCRAMPFLIGHT", + 3296154744: "IFCCHIMNEY", + 3304561284: "IFCWINDOW", + 3310460725: "IFCELECTRICFLOWSTORAGEDEVICE", + 3319311131: "IFCHEATEXCHANGER", + 3415622556: "IFCFAN", + 3420628829: "IFCSOLARDEVICE", + 3493046030: "IFCGEOGRAPHICELEMENT", + 3495092785: "IFCCURTAINWALL", + 3508470533: "IFCFLOWTREATMENTDEVICE", + 3512223829: "IFCWALLSTANDARDCASE", + 3518393246: "IFCDUCTSEGMENT", + 3571504051: "IFCCOMPRESSOR", + 3588315303: "IFCOPENINGELEMENT", + 3612865200: "IFCPIPESEGMENT", + 3640358203: "IFCCOOLINGTOWER", + 3651124850: "IFCPROJECTIONELEMENT", + 3694346114: "IFCOUTLET", + 3747195512: "IFCEVAPORATIVECOOLER", + 3758799889: "IFCCABLECARRIERSEGMENT", + 3824725483: "IFCTENDON", + 3825984169: "IFCTRANSFORMER", + 3902619387: "IFCCHILLER", + 4074379575: "IFCDAMPER", + 4086658281: "IFCSENSOR", + 4123344466: "IFCELEMENTASSEMBLY", + 4136498852: "IFCCOOLEDBEAM", + 4156078855: "IFCWALLELEMENTEDCASE", + 4175244083: "IFCINTERCEPTOR", + 4207607924: "IFCVALVE", + 4217484030: "IFCCABLESEGMENT", + 4237592921: "IFCWASTETERMINAL", + 4252922144: "IFCSTAIRFLIGHT", + 4278956645: "IFCFLOWFITTING", + 4288193352: "IFCACTUATOR", + 4292641817: "IFCUNITARYEQUIPMENT", + 3009204131: "IFCGRID", +}; + +class IfcCategories { + getAll(webIfc, modelID) { + const elementsCategories = {}; + const categoriesIDs = Object.keys(IfcElements).map((e) => parseInt(e, 10)); + for (let i = 0; i < categoriesIDs.length; i++) { + const element = categoriesIDs[i]; + const lines = webIfc.GetLineIDsWithType(modelID, element); + const size = lines.size(); + for (let i = 0; i < size; i++) { + elementsCategories[lines.get(i)] = element; } } - catch (e) { - console.log("Could not get floors."); - } - } - cleanUp() { - this.itemsByFloor = {}; + return elementsCategories; } } -/** Configuration of the IFC-fragment conversion. */ -class IfcFragmentSettings { - constructor() { - /** Whether to extract the IFC properties into a JSON. */ - this.includeProperties = true; - /** - * Generate the geometry for categories that are not included by default, - * like IFCSPACE. - */ - this.optionalCategories = [IFCSPACE]; - /** Whether to use the coordination data coming from the IFC files. */ - this.coordinate = true; - /** Path of the WASM for [web-ifc](https://github.com/ifcjs/web-ifc). */ - this.wasm = { - path: "", - absolute: false, - }; - /** List of categories that won't be converted to fragments. */ - this.excludedCategories = new Set(); - /** Whether to save the absolute location of all IFC items. */ - this.saveLocations = false; - /** Loader settings for [web-ifc](https://github.com/ifcjs/web-ifc). */ - this.webIfc = { - COORDINATE_TO_ORIGIN: true, - USE_FAST_BOOLS: true, - OPTIMIZE_PROFILES: true, - }; - } -} +const GeometryTypes = new Set([ + 1123145078, 574549367, 1675464909, 2059837836, 3798115385, 32440307, + 3125803723, 3207858831, 2740243338, 2624227202, 4240577450, 3615266464, + 3724593414, 220341763, 477187591, 1878645084, 1300840506, 3303107099, + 1607154358, 1878645084, 846575682, 1351298697, 2417041796, 3049322572, + 3331915920, 1416205885, 776857604, 3285139300, 3958052878, 2827736869, + 2732653382, 673634403, 3448662350, 4142052618, 2924175390, 803316827, + 2556980723, 1809719519, 2205249479, 807026263, 3737207727, 1660063152, + 2347385850, 3940055652, 2705031697, 3732776249, 2485617015, 2611217952, + 1704287377, 2937912522, 2770003689, 1281925730, 1484403080, 3448662350, + 4142052618, 3800577675, 4006246654, 3590301190, 1383045692, 2775532180, + 2047409740, 370225590, 3593883385, 2665983363, 4124623270, 812098782, + 3649129432, 987898635, 1105321065, 3510044353, 1635779807, 2603310189, + 3406155212, 1310608509, 4261334040, 2736907675, 3649129432, 1136057603, + 1260505505, 4182860854, 2713105998, 2898889636, 59481748, 3749851601, + 3486308946, 3150382593, 1062206242, 3264961684, 15328376, 1485152156, + 370225590, 1981873012, 2859738748, 45288368, 2614616156, 2732653382, + 775493141, 2147822146, 2601014836, 2629017746, 1186437898, 2367409068, + 1213902940, 3632507154, 3900360178, 476780140, 1472233963, 2804161546, + 3008276851, 738692330, 374418227, 315944413, 3905492369, 3570813810, + 2571569899, 178912537, 2294589976, 1437953363, 2133299955, 572779678, + 3092502836, 388784114, 2624227202, 1425443689, 3057273783, 2347385850, + 1682466193, 2519244187, 2839578677, 3958567839, 2513912981, 2830218821, + 427810014, +]); /** - * A simple implementation of bounding box that works for fragments. The resulting bbox is not 100% precise, but - * it's fast, and should suffice for general use cases such as camera zooming or general boundary determination. + * Object to export all the properties from an IFC to a JS object. */ -class FragmentBoundingBox extends Component { - constructor(components) { - super(components); - /** {@link Component.enabled} */ - this.enabled = true; - /** {@link Disposable.onDisposed} */ - this.onDisposed = new Event(); - this._meshes = []; - this.components.tools.add(FragmentBoundingBox.uuid, this); - this._absoluteMin = FragmentBoundingBox.newBound(true); - this._absoluteMax = FragmentBoundingBox.newBound(false); - } - static getDimensions(bbox) { - const { min, max } = bbox; - const width = Math.abs(max.x - min.x); - const height = Math.abs(max.y - min.y); - const depth = Math.abs(max.z - min.z); - const center = new THREE$1.Vector3(); - center.subVectors(max, min).divideScalar(2).add(min); - return { width, height, depth, center }; - } - static newBound(positive) { - const factor = positive ? 1 : -1; - return new THREE$1.Vector3(factor * Number.MAX_VALUE, factor * Number.MAX_VALUE, factor * Number.MAX_VALUE); - } - static getBounds(points, min, max) { - const maxPoint = max || this.newBound(false); - const minPoint = min || this.newBound(true); - for (const point of points) { - if (point.x < minPoint.x) - minPoint.x = point.x; - if (point.y < minPoint.y) - minPoint.y = point.y; - if (point.z < minPoint.z) - minPoint.z = point.z; - if (point.x > maxPoint.x) - maxPoint.x = point.x; - if (point.y > maxPoint.y) - maxPoint.y = point.y; - if (point.z > maxPoint.z) - maxPoint.z = point.z; - } - return new THREE$1.Box3(min, max); - } - /** {@link Disposable.dispose} */ - async dispose() { - const disposer = this.components.tools.get(Disposer); - for (const mesh of this._meshes) { - disposer.destroy(mesh); - } - this._meshes = []; - await this.onDisposed.trigger(FragmentBoundingBox.uuid); - this.onDisposed.reset(); - } - get() { - const min = this._absoluteMin.clone(); - const max = this._absoluteMax.clone(); - return new THREE$1.Box3(min, max); - } - getSphere() { - const min = this._absoluteMin.clone(); - const max = this._absoluteMax.clone(); - const dx = Math.abs((max.x - min.x) / 2); - const dy = Math.abs((max.y - min.y) / 2); - const dz = Math.abs((max.z - min.z) / 2); - const center = new THREE$1.Vector3(min.x + dx, min.y + dy, min.z + dz); - const radius = center.distanceTo(min); - return new THREE$1.Sphere(center, radius); - } - getMesh() { - const bbox = new THREE$1.Box3(this._absoluteMin, this._absoluteMax); - const dimensions = FragmentBoundingBox.getDimensions(bbox); - const { width, height, depth, center } = dimensions; - const box = new THREE$1.BoxGeometry(width, height, depth); - const mesh = new THREE$1.Mesh(box); - this._meshes.push(mesh); - mesh.position.copy(center); - return mesh; - } - reset() { - this._absoluteMin = FragmentBoundingBox.newBound(true); - this._absoluteMax = FragmentBoundingBox.newBound(false); - } - add(group) { - for (const frag of group.items) { - this.addMesh(frag.mesh); - } - } - addMesh(mesh) { - if (!mesh.geometry.index) { - return; - } - const bbox = FragmentBoundingBox.getFragmentBounds(mesh); - mesh.updateMatrix(); - const meshTransform = mesh.matrix; - const instanceTransform = new THREE$1.Matrix4(); - for (let i = 0; i < mesh.count; i++) { - mesh.getMatrixAt(i, instanceTransform); - const min = bbox.min.clone(); - const max = bbox.max.clone(); - min.applyMatrix4(instanceTransform); - min.applyMatrix4(meshTransform); - max.applyMatrix4(instanceTransform); - max.applyMatrix4(meshTransform); - if (min.x < this._absoluteMin.x) - this._absoluteMin.x = min.x; - if (min.y < this._absoluteMin.y) - this._absoluteMin.y = min.y; - if (min.z < this._absoluteMin.z) - this._absoluteMin.z = min.z; - if (min.x > this._absoluteMax.x) - this._absoluteMax.x = min.x; - if (min.y > this._absoluteMax.y) - this._absoluteMax.y = min.y; - if (min.z > this._absoluteMax.z) - this._absoluteMax.z = min.z; - if (max.x > this._absoluteMax.x) - this._absoluteMax.x = max.x; - if (max.y > this._absoluteMax.y) - this._absoluteMax.y = max.y; - if (max.z > this._absoluteMax.z) - this._absoluteMax.z = max.z; - if (max.x < this._absoluteMin.x) - this._absoluteMin.x = max.x; - if (max.y < this._absoluteMin.y) - this._absoluteMin.y = max.y; - if (max.z < this._absoluteMin.z) - this._absoluteMin.z = max.z; - } - } - static getFragmentBounds(mesh) { - const position = mesh.geometry.attributes.position; - const maxNum = Number.MAX_VALUE; - const minNum = -maxNum; - const min = new THREE$1.Vector3(maxNum, maxNum, maxNum); - const max = new THREE$1.Vector3(minNum, minNum, minNum); - if (!mesh.geometry.index) { - throw new Error("Geometry must be indexed!"); - } - const indices = Array.from(mesh.geometry.index.array); - for (const index of indices) { - const x = position.getX(index); - const y = position.getY(index); - const z = position.getZ(index); - if (x < min.x) - min.x = x; - if (y < min.y) - min.y = y; - if (z < min.z) - min.z = z; - if (x > max.x) - max.x = x; - if (y > max.y) - max.y = y; - if (z > max.z) - max.z = z; - } - return new THREE$1.Box3(min, max); - } -} -FragmentBoundingBox.uuid = "d1444724-dba6-4cdd-a0c7-68ee1450d166"; -ToolComponent.libraryUUIDs.add(FragmentBoundingBox.uuid); - -class DataConverter { - constructor(components) { - this.settings = new IfcFragmentSettings(); - this.categories = {}; - this._model = new FragmentsGroup(); - this._ifcCategories = new IfcCategories(); - this._fragmentKey = 0; - this._keyFragmentMap = {}; - this._itemKeyMap = {}; - this._propertyExporter = new IfcJsonExporter(); - this._spatialTree = new SpatialStructure(); - this.components = components; - } - cleanUp() { - this._fragmentKey = 0; - this._spatialTree.cleanUp(); - this.categories = {}; - this._model = new FragmentsGroup(); - this._ifcCategories = new IfcCategories(); - this._propertyExporter = new IfcJsonExporter(); - this._keyFragmentMap = {}; - this._itemKeyMap = {}; - } - saveIfcCategories(webIfc) { - this.categories = this._ifcCategories.getAll(webIfc, 0); - } - async generate(webIfc, geometries, civilItems) { - await this._spatialTree.setUp(webIfc); - this.createAllFragments(geometries, civilItems); - await this.saveModelData(webIfc); - return this._model; - } - async saveModelData(webIfc) { - const itemsData = this.getFragmentsGroupData(); - this._model.keyFragments = this._keyFragmentMap; - this._model.data = itemsData; - this._model.coordinationMatrix = this.getCoordinationMatrix(webIfc); - this._model.properties = await this.getModelProperties(webIfc); - this._model.uuid = this.getProjectID(webIfc) || this._model.uuid; - this._model.ifcMetadata = this.getIfcMetadata(webIfc); - this._model.boundingBox = await this.getBoundingBox(); - } - async getBoundingBox() { - const bbox = await this.components.tools.get(FragmentBoundingBox); - bbox.reset(); - bbox.add(this._model); - return bbox.get(); - } - getIfcMetadata(webIfc) { - const { FILE_NAME, FILE_DESCRIPTION } = WEBIFC; - const name = this.getMetadataEntry(webIfc, FILE_NAME); - const description = this.getMetadataEntry(webIfc, FILE_DESCRIPTION); - const schema = webIfc.GetModelSchema(0) || "IFC2X3"; - const maxExpressID = webIfc.GetMaxExpressID(0); - return { name, description, schema, maxExpressID }; - } - getMetadataEntry(webIfc, type) { - let description = ""; - const descriptionData = webIfc.GetHeaderLine(0, type) || ""; - if (!descriptionData) - return description; - for (const arg of descriptionData.arguments) { - if (arg === null || arg === undefined) { - continue; - } - if (Array.isArray(arg)) { - for (const subArg of arg) { - if (!subArg) - continue; - description += `${subArg.value}|`; - } - } - else { - description += `${arg.value}|`; - } - } - return description; - } - getProjectID(webIfc) { - const projectsIDs = webIfc.GetLineIDsWithType(0, IFCPROJECT); - const projectID = projectsIDs.get(0); - const project = webIfc.GetLine(0, projectID); - return project.GlobalId.value; - } - getCoordinationMatrix(webIfc) { - const coordArray = webIfc.GetCoordinationMatrix(0); - return new THREE$1.Matrix4().fromArray(coordArray); - } - async getModelProperties(webIfc) { - if (!this.settings.includeProperties) { - return {}; - } - return new Promise((resolve) => { - this._propertyExporter.onPropertiesSerialized.add((properties) => { - resolve(properties); - }); - this._propertyExporter.export(webIfc, 0); - }); +class IfcJsonExporter { + constructor() { + this.onLoadProgress = new Event(); + this.onPropertiesSerialized = new Event(); + this._progress = 0; } - createAllFragments(geometries, civilItems) { - const uniqueItems = {}; - const matrix = new THREE$1.Matrix4(); - const color = new THREE$1.Color(); - console.log(civilItems); - // Add alignments data - if (civilItems.IfcAlignment) { - const horizontalAlignments = new IfcAlignmentData(); - const verticalAlignments = new IfcAlignmentData(); - const realAlignments = new IfcAlignmentData(); - let countH = 0; - let countV = 0; - let countR = 0; - const valuesH = []; - const valuesV = []; - const valuesR = []; - for (const alignment of civilItems.IfcAlignment) { - horizontalAlignments.alignmentIndex.push(countH); - verticalAlignments.alignmentIndex.push(countV); - if (alignment.horizontal) { - for (const hAlignment of alignment.horizontal) { - horizontalAlignments.curveIndex.push(countH); - for (const point of hAlignment.points) { - valuesH.push(point.x); - valuesH.push(point.y); - countH++; - } - } - } - if (alignment.vertical) { - for (const vAlignment of alignment.vertical) { - verticalAlignments.curveIndex.push(countV); - for (const point of vAlignment.points) { - valuesV.push(point.x); - valuesV.push(point.y); - countV++; - } - } - } - if (alignment.curve3D) { - for (const rAlignment of alignment.curve3D) { - realAlignments.curveIndex.push(countR); - for (const point of rAlignment.points) { - valuesR.push(point.x); - valuesR.push(point.y); - valuesR.push(point.z); - countR++; - } - } - } - } - horizontalAlignments.coordinates = new Float32Array(valuesH); - verticalAlignments.coordinates = new Float32Array(valuesV); - realAlignments.coordinates = new Float32Array(valuesR); - this._model.ifcCivil = { - horizontalAlignments, - verticalAlignments, - realAlignments, - }; - } - for (const id in geometries) { - const { buffer, instances } = geometries[id]; - const transparent = instances[0].color.w !== 1; - const opacity = transparent ? 0.4 : 1; - const material = new THREE$1.MeshLambertMaterial({ transparent, opacity }); - // This prevents z-fighting for ifc spaces - if (opacity !== 1) { - material.depthWrite = false; - material.polygonOffset = true; - material.polygonOffsetFactor = 5; - material.polygonOffsetUnits = 1; - } - if (instances.length === 1) { - const instance = instances[0]; - const { x, y, z, w } = instance.color; - const matID = `${x}-${y}-${z}-${w}`; - if (!uniqueItems[matID]) { - material.color = new THREE$1.Color().setRGB(x, y, z, "srgb"); - uniqueItems[matID] = { material, geometries: [], expressIDs: [] }; - } - matrix.fromArray(instance.matrix); - buffer.applyMatrix4(matrix); - uniqueItems[matID].geometries.push(buffer); - uniqueItems[matID].expressIDs.push(instance.expressID.toString()); - continue; - } - const fragment = new Fragment$1(buffer, material, instances.length); - this._keyFragmentMap[this._fragmentKey] = fragment.id; - const previousIDs = new Set(); - for (let i = 0; i < instances.length; i++) { - const instance = instances[i]; - matrix.fromArray(instance.matrix); - const { expressID } = instance; - let instanceID = expressID.toString(); - let isComposite = false; - if (!previousIDs.has(expressID)) { - previousIDs.add(expressID); + /** + * Exports all the properties of an IFC into an array of JS objects. + * @webIfc The instance of [web-ifc]{@link https://github.com/ifcjs/web-ifc} to use. + * @modelID ID of the IFC model whose properties to extract. + */ + async export(webIfc, modelID) { + const geometriesIDs = await this.getAllGeometriesIDs(modelID, webIfc); + let properties = {}; + properties.coordinationMatrix = webIfc.GetCoordinationMatrix(modelID); + const allLinesIDs = await webIfc.GetAllLines(modelID); + const linesCount = allLinesIDs.size(); + this._progress = 0.1; + let counter = 0; + for (let i = 0; i < linesCount; i++) { + const id = allLinesIDs.get(i); + if (!geometriesIDs.has(id)) { + try { + properties[id] = await webIfc.GetLine(modelID, id); } - else { - if (!fragment.composites[expressID]) { - fragment.composites[expressID] = 1; - } - const count = fragment.composites[expressID]; - instanceID = toCompositeID(expressID, count); - isComposite = true; - fragment.composites[expressID]++; + catch (e) { + console.log(`Properties of the element ${id} could not be processed`); } - fragment.setInstance(i, { - ids: [instanceID], - transform: matrix, + counter++; + } + if (this.size !== undefined && counter > this.size) { + await this.onPropertiesSerialized.trigger(properties); + properties = null; + properties = {}; + counter = 0; + } + if (i / linesCount > this._progress) { + await this.onLoadProgress.trigger({ + progress: i, + total: linesCount, }); - const { x, y, z } = instance.color; - color.setRGB(x, y, z, "srgb"); - fragment.mesh.setColorAt(i, color); - if (!isComposite) { - this.saveExpressID(expressID.toString()); - } + this._progress += 0.1; } - fragment.mesh.updateMatrix(); - this._model.items.push(fragment); - this._model.add(fragment.mesh); - this._fragmentKey++; } - const transform = new THREE$1.Matrix4(); - for (const matID in uniqueItems) { - const { material, geometries, expressIDs } = uniqueItems[matID]; - const geometriesByItem = {}; - for (let i = 0; i < expressIDs.length; i++) { - const id = expressIDs[i]; - if (!geometriesByItem[id]) { - geometriesByItem[id] = []; - } - geometriesByItem[id].push(geometries[i]); + await this.onPropertiesSerialized.trigger(properties); + properties = null; + } + async getAllGeometriesIDs(modelID, webIfc) { + // Exclude location info of spatial structure + const placementIDs = new Set(); + const structures = new Set(); + this.getStructure(IFCPROJECT, structures, webIfc); + this.getStructure(IFCSITE, structures, webIfc); + this.getStructure(IFCBUILDING, structures, webIfc); + this.getStructure(IFCBUILDINGSTOREY, structures, webIfc); + this.getStructure(IFCSPACE, structures, webIfc); + for (const id of structures) { + const properties = webIfc.GetLine(0, id); + const placementRef = properties.ObjectPlacement; + if (!placementRef || placementRef.value === null) { + continue; } - const sortedGeometries = []; - const sortedIDs = []; - for (const id in geometriesByItem) { - sortedIDs.push(id); - const geometries = geometriesByItem[id]; - if (geometries.length) { - const merged = mergeGeometries(geometries); - sortedGeometries.push(merged); - } - else { - sortedGeometries.push(geometries[0]); - } - for (const geometry of geometries) { - geometry.dispose(); - } + const placementID = placementRef.value; + placementIDs.add(placementID); + const placementProps = webIfc.GetLine(0, placementID); + const relPlacementID = placementProps.RelativePlacement; + if (!relPlacementID || relPlacementID.value === null) { + continue; } - const geometry = GeometryUtils.merge([sortedGeometries], true); - const fragment = new Fragment$1(geometry, material, 1); - this._keyFragmentMap[this._fragmentKey] = fragment.id; - for (const id of sortedIDs) { - this.saveExpressID(id); + placementIDs.add(relPlacementID.value); + const relPlacement = webIfc.GetLine(0, relPlacementID.value); + const location = relPlacement.Location; + if (location && location.value !== null) { + placementIDs.add(location.value); } - this._fragmentKey++; - fragment.setInstance(0, { ids: sortedIDs, transform }); - this._model.items.push(fragment); - this._model.add(fragment.mesh); } - } - saveExpressID(expressID) { - if (!this._itemKeyMap[expressID]) { - this._itemKeyMap[expressID] = []; + const geometriesIDs = new Set(); + const geomTypesArray = Array.from(GeometryTypes); + for (let i = 0; i < geomTypesArray.length; i++) { + const category = geomTypesArray[i]; + // eslint-disable-next-line no-await-in-loop + const ids = await webIfc.GetLineIDsWithType(modelID, category); + const idsSize = ids.size(); + for (let j = 0; j < idsSize; j++) { + const id = ids.get(j); + if (placementIDs.has(id)) { + continue; + } + geometriesIDs.add(id); + } } - this._itemKeyMap[expressID].push(this._fragmentKey); + return geometriesIDs; } - getFragmentsGroupData() { - const itemsData = {}; - for (const id in this._itemKeyMap) { - const keys = []; - const rels = []; - const idNum = parseInt(id, 10); - const level = this._spatialTree.itemsByFloor[idNum] || 0; - const category = this.categories[idNum] || 0; - rels.push(level, category); - for (const key of this._itemKeyMap[id]) { - keys.push(key); - } - itemsData[idNum] = [keys, rels]; + getStructure(type, result, webIfc) { + const found = webIfc.GetLineIDsWithType(0, type); + const size = found.size(); + for (let i = 0; i < size; i++) { + const id = found.get(i); + result.add(id); } - return itemsData; } } -class GeometryReader { +class Units { constructor() { - this.saveLocations = false; - this.items = {}; - this.locations = {}; - this.CivilItems = { - IfcAlignment: [], - IfcCrossSection2D: [], - IfcCrossSection3D: [], - }; - } - get webIfc() { - if (!this._webIfc) { - throw new Error("web-ifc not found!"); - } - return this._webIfc; + this.factor = 1; + this.complement = 1; } - cleanUp() { - this.items = {}; - this.locations = {}; - this._webIfc = null; + apply(matrix) { + const scale = this.getScaleMatrix(); + const result = scale.multiply(matrix); + matrix.copy(result); } - streamMesh(webifc, mesh, forceTransparent = false) { - this._webIfc = webifc; - const size = mesh.geometries.size(); - const totalTransform = new THREE$1.Vector3(); - const tempMatrix = new THREE$1.Matrix4(); - const tempVector = new THREE$1.Vector3(); - for (let i = 0; i < size; i++) { - const geometry = mesh.geometries.get(i); - const geometryID = geometry.geometryExpressID; - if (this.saveLocations) { - tempVector.set(0, 0, 0); - tempMatrix.fromArray(geometry.flatTransformation); - tempVector.applyMatrix4(tempMatrix); - totalTransform.add(tempVector); - } - // Transparent geometries need to be separated - const isColorTransparent = geometry.color.w !== 1; - const isTransparent = isColorTransparent || forceTransparent; - const prefix = isTransparent ? "-" : "+"; - const idWithTransparency = prefix + geometryID; - if (forceTransparent) - geometry.color.w = 0.1; - if (!this.items[idWithTransparency]) { - const buffer = this.newBufferGeometry(geometryID); - if (!buffer) - continue; - this.items[idWithTransparency] = { buffer, instances: [] }; - } - this.items[idWithTransparency].instances.push({ - color: { ...geometry.color }, - matrix: geometry.flatTransformation, - expressID: mesh.expressID, - }); + setUp(webIfc) { + var _a; + this.factor = 1; + const length = this.getLengthUnits(webIfc); + if (!length) { + return; } - if (this.saveLocations) { - const { x, y, z } = totalTransform.divideScalar(size); - this.locations[mesh.expressID] = [x, y, z]; + const isLengthNull = length === undefined || length === null; + const isValueNull = length.Name === undefined || length.Name === null; + if (isLengthNull || isValueNull) { + return; + } + if (length.Name.value === "FOOT") { + this.factor = 0.3048; + } + else if (((_a = length.Prefix) === null || _a === void 0 ? void 0 : _a.value) === "MILLI") { + this.complement = 0.001; } } - streamAlignment(webifc) { - this.CivilItems.IfcAlignment = webifc.GetAllAlignments(0); - } - streamCrossSection(webifc) { - this.CivilItems.IfcCrossSection2D = webifc.GetAllCrossSections2D(0); - this.CivilItems.IfcCrossSection3D = webifc.GetAllCrossSections3D(0); - } - newBufferGeometry(geometryID) { - const geometry = this.webIfc.GetGeometry(0, geometryID); - const verts = this.getVertices(geometry); - if (!verts.length) + getLengthUnits(webIfc) { + try { + const allUnitsAssigns = webIfc.GetLineIDsWithType(0, IFCUNITASSIGNMENT); + const unitsAssign = allUnitsAssigns.get(0); + const unitsAssignProps = webIfc.GetLine(0, unitsAssign); + for (const units of unitsAssignProps.Units) { + if (!units || units.value === null || units.value === undefined) { + continue; + } + const unitsProps = webIfc.GetLine(0, units.value); + if (unitsProps.UnitType && unitsProps.UnitType.value === "LENGTHUNIT") { + return unitsProps; + } + } return null; - const indices = this.getIndices(geometry); - if (!indices.length) + } + catch (e) { + console.log("Could not get units"); return null; - const buffer = this.constructBuffer(verts, indices); - // @ts-ignore - geometry.delete(); - return buffer; - } - getIndices(geometryData) { - const indices = this.webIfc.GetIndexArray(geometryData.GetIndexData(), geometryData.GetIndexDataSize()); - return indices; - } - getVertices(geometryData) { - const verts = this.webIfc.GetVertexArray(geometryData.GetVertexData(), geometryData.GetVertexDataSize()); - return verts; - } - constructBuffer(vertexData, indexData) { - const geometry = new THREE$1.BufferGeometry(); - const posFloats = new Float32Array(vertexData.length / 2); - const normFloats = new Float32Array(vertexData.length / 2); - for (let i = 0; i < vertexData.length; i += 6) { - posFloats[i / 2] = vertexData[i]; - posFloats[i / 2 + 1] = vertexData[i + 1]; - posFloats[i / 2 + 2] = vertexData[i + 2]; - normFloats[i / 2] = vertexData[i + 3]; - normFloats[i / 2 + 1] = vertexData[i + 4]; - normFloats[i / 2 + 2] = vertexData[i + 5]; } - geometry.setAttribute("position", new THREE$1.BufferAttribute(posFloats, 3)); - geometry.setAttribute("normal", new THREE$1.BufferAttribute(normFloats, 3)); - geometry.setIndex(new THREE$1.BufferAttribute(indexData, 1)); - return geometry; + } + getScaleMatrix() { + const f = this.factor; + // prettier-ignore + return new THREE$1.Matrix4().fromArray([ + f, 0, 0, 0, + 0, f, 0, 0, + 0, 0, f, 0, + 0, 0, 0, 1, + ]); } } -/** - * Reads all the geometry of the IFC file and generates a set of - * [fragments](https://github.com/ifcjs/fragment). It can also return the - * properties as a JSON file, as well as other sets of information within - * the IFC file. - */ -class FragmentIfcLoader extends Component { - constructor(components) { - super(components); - /** {@link Disposable.onDisposed} */ - this.onDisposed = new Event(); - this.enabled = true; - this.uiElement = new UIElement(); - this.onIfcLoaded = new Event(); - // For debugging purposes - // isolatedItems = new Set(); - this.onLocationsSaved = new Event(); - this._webIfc = new IfcAPI2(); - this._geometry = new GeometryReader(); - this._converter = new DataConverter(components); - this.components.tools.add(FragmentIfcLoader.uuid, this); - if (components.uiEnabled) { - this.setupUI(); - } - } - get() { - return this._webIfc; - } - get settings() { - return this._converter.settings; - } - /** {@link Disposable.dispose} */ - async dispose() { - this._geometry.cleanUp(); - this._converter.cleanUp(); - this.onIfcLoaded.reset(); - this.onLocationsSaved.reset(); - this.uiElement.dispose(); - this._webIfc = null; - this._geometry = null; - this._converter = null; - await this.onDisposed.trigger(FragmentIfcLoader.uuid); - this.onDisposed.reset(); +class SpatialStructure { + constructor() { + this.itemsByFloor = {}; + this._units = new Units(); } - /** Loads the IFC file and converts it to a set of fragments. */ - async load(data, name) { - if (this.settings.saveLocations) { - this._geometry.saveLocations = true; - } - const before = performance.now(); - await this.readIfcFile(data); - await this.readAllGeometries(); - await this._geometry.streamAlignment(this._webIfc); - await this._geometry.streamCrossSection(this._webIfc); - const items = this._geometry.items; - const civItems = this._geometry.CivilItems; - const model = await this._converter.generate(this._webIfc, items, civItems); - model.name = name; - if (this.settings.saveLocations) { - await this.onLocationsSaved.trigger(this._geometry.locations); - } - const fragments = this.components.tools.get(FragmentManager); - if (this.settings.coordinate) { - const isFirstModel = fragments.groups.length === 0; - if (isFirstModel) { - fragments.baseCoordinationModel = model.uuid; + // TODO: Maybe make this more flexible so that it also support more exotic spatial structures? + async setUp(webIfc) { + this._units.setUp(webIfc); + this.cleanUp(); + try { + const spatialRels = webIfc.GetLineIDsWithType(0, IFCRELCONTAINEDINSPATIALSTRUCTURE); + const allRooms = new Set(); + const rooms = webIfc.GetLineIDsWithType(0, IFCSPACE); + for (let i = 0; i < rooms.size(); i++) { + allRooms.add(rooms.get(i)); } - else { - fragments.coordinate([model]); + // First add rooms (if any) to floors + const aggregates = webIfc.GetLineIDsWithType(0, IFCRELAGGREGATES); + const aggregatesSize = aggregates.size(); + for (let i = 0; i < aggregatesSize; i++) { + const id = aggregates.get(i); + const properties = webIfc.GetLine(0, id); + if (!properties || + !properties.RelatingObject || + !properties.RelatedObjects) { + continue; + } + const parentID = properties.RelatingObject.value; + const childsIDs = properties.RelatedObjects; + for (const child of childsIDs) { + const childID = child.value; + if (allRooms.has(childID)) { + this.itemsByFloor[childID] = parentID; + } + } } - } - this.cleanUp(); - fragments.groups.push(model); - for (const fragment of model.items) { - fragment.group = model; - fragments.list[fragment.id] = fragment; - this.components.meshes.push(fragment.mesh); - } - await this.onIfcLoaded.trigger(model); - console.log(`Loading the IFC took ${performance.now() - before} ms!`); - return model; - } - setupUI() { - const main = new Button(this.components); - main.materialIcon = "upload_file"; - main.tooltip = "Load IFC"; - const toast = new ToastNotification(this.components, { - message: "IFC model successfully loaded!", - }); - main.onClick.add(() => { - const fileOpener = document.createElement("input"); - fileOpener.type = "file"; - fileOpener.accept = ".ifc"; - fileOpener.style.display = "none"; - fileOpener.onchange = async () => { - const fragments = this.components.tools.get(FragmentManager); - if (fileOpener.files === null || fileOpener.files.length === 0) - return; - const file = fileOpener.files[0]; - const buffer = await file.arrayBuffer(); - const data = new Uint8Array(buffer); - const model = await this.load(data, file.name); - const scene = this.components.scene.get(); - scene.add(model); - toast.visible = true; - await fragments.updateWindow(); - fileOpener.remove(); - }; - fileOpener.click(); - }); - this.components.ui.add(toast); - toast.visible = false; - this.uiElement.set({ main, toast }); - } - async readIfcFile(data) { - const { path, absolute } = this.settings.wasm; - this._webIfc.SetWasmPath(path, absolute); - await this._webIfc.Init(); - return this._webIfc.OpenModel(data, this.settings.webIfc); - } - async readAllGeometries() { - this._converter.saveIfcCategories(this._webIfc); - // Some categories (like IfcSpace) need to be created explicitly - const optionals = this.settings.optionalCategories; - // Force IFC space to be transparent - if (optionals.includes(IFCSPACE)) { - const index = optionals.indexOf(IFCSPACE); - optionals.splice(index, 1); - this._webIfc.StreamAllMeshesWithTypes(0, [IFCSPACE], (mesh) => { - if (this.isExcluded(mesh.expressID)) { - return; + // Now add items contained in floors and rooms + // If items contained in room, look for the floor where that room is and assign it to it + const itemsContainedInRooms = {}; + const spatialRelsSize = spatialRels.size(); + for (let i = 0; i < spatialRelsSize; i++) { + const id = spatialRels.get(i); + const properties = webIfc.GetLine(0, id); + if (!properties || + !properties.RelatingStructure || + !properties.RelatedElements) { + continue; } - this._geometry.streamMesh(this._webIfc, mesh, true); - }); - } - // Load rest of optional categories (if any) - if (optionals.length) { - this._webIfc.StreamAllMeshesWithTypes(0, optionals, (mesh) => { - if (this.isExcluded(mesh.expressID)) { - return; + const structureID = properties.RelatingStructure.value; + const relatedItems = properties.RelatedElements; + if (allRooms.has(structureID)) { + for (const related of relatedItems) { + if (!itemsContainedInRooms[structureID]) { + itemsContainedInRooms[structureID] = []; + } + const id = related.value; + itemsContainedInRooms[structureID].push(id); + } + } + else { + for (const related of relatedItems) { + const id = related.value; + this.itemsByFloor[id] = structureID; + } + } + } + for (const roomID in itemsContainedInRooms) { + const roomFloor = this.itemsByFloor[roomID]; + if (roomFloor !== undefined) { + const items = itemsContainedInRooms[roomID]; + for (const item of items) { + this.itemsByFloor[item] = roomFloor; + } + } + } + // Finally, add nested items (e.g. elements of curtain walls) + for (let i = 0; i < aggregatesSize; i++) { + const id = aggregates.get(i); + const properties = webIfc.GetLine(0, id); + if (!properties || + !properties.RelatingObject || + !properties.RelatedObjects) { + continue; + } + const parentID = properties.RelatingObject.value; + const childsIDs = properties.RelatedObjects; + for (const child of childsIDs) { + const childID = child.value; + const parentStructure = this.itemsByFloor[parentID]; + if (parentStructure !== undefined) { + this.itemsByFloor[childID] = parentStructure; + } } - this._geometry.streamMesh(this._webIfc, mesh); - }); - } - // Load common categories - this._webIfc.StreamAllMeshes(0, (mesh) => { - if (this.isExcluded(mesh.expressID)) { - return; } - this._geometry.streamMesh(this._webIfc, mesh); - }); - // Load civil items - this._geometry.streamAlignment(this._webIfc); - this._geometry.streamCrossSection(this._webIfc); - } - cleanIfcApi() { - this._webIfc = null; - this._webIfc = new IfcAPI2(); + } + catch (e) { + console.log("Could not get floors."); + } } cleanUp() { - this.cleanIfcApi(); - this._geometry.cleanUp(); - this._converter.cleanUp(); + this.itemsByFloor = {}; } - isExcluded(id) { - const category = this._converter.categories[id]; - return this.settings.excludedCategories.has(category); +} + +/** Configuration of the IFC-fragment conversion. */ +class IfcFragmentSettings { + constructor() { + /** Whether to extract the IFC properties into a JSON. */ + this.includeProperties = true; + /** + * Generate the geometry for categories that are not included by default, + * like IFCSPACE. + */ + this.optionalCategories = [IFCSPACE]; + /** Whether to use the coordination data coming from the IFC files. */ + this.coordinate = true; + /** Path of the WASM for [web-ifc](https://github.com/ifcjs/web-ifc). */ + this.wasm = { + path: "", + absolute: false, + }; + /** List of categories that won't be converted to fragments. */ + this.excludedCategories = new Set(); + /** Whether to save the absolute location of all IFC items. */ + this.saveLocations = false; + /** Loader settings for [web-ifc](https://github.com/ifcjs/web-ifc). */ + this.webIfc = { + COORDINATE_TO_ORIGIN: true, + USE_FAST_BOOLS: true, + OPTIMIZE_PROFILES: true, + }; } } -FragmentIfcLoader.uuid = "a659add7-1418-4771-a0d6-7d4d438e4624"; -ToolComponent.libraryUUIDs.add(FragmentIfcLoader.uuid); -class FragmentHighlighter extends Component { - get outlineEnabled() { - return this._outlineEnabled; +class DataConverter { + constructor(components) { + this.settings = new IfcFragmentSettings(); + this.categories = {}; + this._model = new FragmentsGroup(); + this._ifcCategories = new IfcCategories(); + this._fragmentKey = 0; + this._keyFragmentMap = {}; + this._itemKeyMap = {}; + this._propertyExporter = new IfcJsonExporter(); + this._spatialTree = new SpatialStructure(); + this.components = components; } - set outlineEnabled(value) { - this._outlineEnabled = value; - if (!value) { - delete this._postproduction.customEffects.outlinedMeshes.fragments; - } + cleanUp() { + this._fragmentKey = 0; + this._spatialTree.cleanUp(); + this.categories = {}; + this._model = new FragmentsGroup(); + this._ifcCategories = new IfcCategories(); + this._propertyExporter = new IfcJsonExporter(); + this._keyFragmentMap = {}; + this._itemKeyMap = {}; } - get _postproduction() { - if (!(this.components.renderer instanceof PostproductionRenderer)) { - throw new Error("Postproduction renderer is needed for outlines!"); - } - const renderer = this.components.renderer; - return renderer.postproduction; + saveIfcCategories(webIfc) { + this.categories = this._ifcCategories.getAll(webIfc, 0); } - constructor(components) { - super(components); - /** {@link Disposable.onDisposed} */ - this.onDisposed = new Event(); - /** {@link Updateable.onBeforeUpdate} */ - this.onBeforeUpdate = new Event(); - /** {@link Updateable.onAfterUpdate} */ - this.onAfterUpdate = new Event(); - this.enabled = true; - this.highlightMats = {}; - this.events = {}; - this.multiple = "ctrlKey"; - this.zoomFactor = 1.5; - this.zoomToSelection = false; - this.selection = {}; - this.excludeOutline = new Set(); - this.fillEnabled = true; - this.outlineMaterial = new THREE$1.MeshBasicMaterial({ - color: "white", - transparent: true, - depthTest: false, - depthWrite: false, - opacity: 0.4, - }); - this._eventsActive = false; - this._outlineEnabled = true; - this._outlinedMeshes = {}; - this._invisibleMaterial = new THREE$1.MeshBasicMaterial({ visible: false }); - this._tempMatrix = new THREE$1.Matrix4(); - this.config = { - selectName: "select", - hoverName: "hover", - selectionMaterial: new THREE$1.MeshBasicMaterial({ - color: "#BCF124", - transparent: true, - opacity: 0.85, - depthTest: true, - }), - hoverMaterial: new THREE$1.MeshBasicMaterial({ - color: "#6528D7", - transparent: true, - opacity: 0.2, - depthTest: true, - }), - autoHighlightOnClick: true, - cullHighlightMesh: true, - }; - this._mouseState = { - down: false, - moved: false, - }; - this.onFragmentsDisposed = (data) => { - this.disposeOutlinedMeshes(data.fragmentIDs); - }; - this.onSetup = new Event(); - this.onMouseDown = () => { - if (!this.enabled) - return; - this._mouseState.down = true; - }; - this.onMouseUp = async (event) => { - if (!this.enabled) - return; - if (event.target !== this.components.renderer.get().domElement) - return; - this._mouseState.down = false; - if (this._mouseState.moved || event.button !== 0) { - this._mouseState.moved = false; - return; - } - this._mouseState.moved = false; - if (this.config.autoHighlightOnClick) { - const mult = this.multiple === "none" ? true : !event[this.multiple]; - await this.highlight(this.config.selectName, mult, this.zoomToSelection); - } - }; - this.onMouseMove = async () => { - if (!this.enabled) - return; - if (this._mouseState.moved) { - await this.clearFills(this.config.hoverName); - return; - } - this._mouseState.moved = this._mouseState.down; - await this.highlight(this.config.hoverName, true, false); - }; - this.components.tools.add(FragmentHighlighter.uuid, this); - const fragmentManager = components.tools.get(FragmentManager); - fragmentManager.onFragmentsDisposed.add(this.onFragmentsDisposed); + async generate(webIfc, geometries, civilItems) { + await this._spatialTree.setUp(webIfc); + this.createAllFragments(geometries, civilItems); + await this.saveModelData(webIfc); + return this._model; } - get() { - return this.highlightMats; + async saveModelData(webIfc) { + const itemsData = this.getFragmentsGroupData(); + this._model.keyFragments = this._keyFragmentMap; + this._model.data = itemsData; + this._model.coordinationMatrix = this.getCoordinationMatrix(webIfc); + this._model.properties = await this.getModelProperties(webIfc); + this._model.uuid = this.getProjectID(webIfc) || this._model.uuid; + this._model.ifcMetadata = this.getIfcMetadata(webIfc); + this._model.boundingBox = await this.getBoundingBox(); } - getHoveredSelection() { - return this.selection[this.config.hoverName]; + async getBoundingBox() { + const bbox = await this.components.tools.get(FragmentBoundingBox); + bbox.reset(); + bbox.add(this._model); + return bbox.get(); } - disposeOutlinedMeshes(fragmentIDs) { - for (const id of fragmentIDs) { - const mesh = this._outlinedMeshes[id]; - if (!mesh) - continue; - mesh.geometry.dispose(); - delete this._outlinedMeshes[id]; - } + getIfcMetadata(webIfc) { + const { FILE_NAME, FILE_DESCRIPTION } = WEBIFC; + const name = this.getMetadataEntry(webIfc, FILE_NAME); + const description = this.getMetadataEntry(webIfc, FILE_DESCRIPTION); + const schema = webIfc.GetModelSchema(0) || "IFC2X3"; + const maxExpressID = webIfc.GetMaxExpressID(0); + return { name, description, schema, maxExpressID }; } - async dispose() { - this.setupEvents(false); - this.config.hoverMaterial.dispose(); - this.config.selectionMaterial.dispose(); - this.onBeforeUpdate.reset(); - this.onAfterUpdate.reset(); - for (const matID in this.highlightMats) { - const mats = this.highlightMats[matID] || []; - for (const mat of mats) { - mat.dispose(); + getMetadataEntry(webIfc, type) { + let description = ""; + const descriptionData = webIfc.GetHeaderLine(0, type) || ""; + if (!descriptionData) + return description; + for (const arg of descriptionData.arguments) { + if (arg === null || arg === undefined) { + continue; + } + if (Array.isArray(arg)) { + for (const subArg of arg) { + if (!subArg) + continue; + description += `${subArg.value}|`; + } + } + else { + description += `${arg.value}|`; } } - this.disposeOutlinedMeshes(Object.keys(this._outlinedMeshes)); - this.outlineMaterial.dispose(); - this._invisibleMaterial.dispose(); - this.highlightMats = {}; - this.selection = {}; - for (const name in this.events) { - this.events[name].onClear.reset(); - this.events[name].onHighlight.reset(); - } - this.onSetup.reset(); - const fragmentManager = this.components.tools.get(FragmentManager); - fragmentManager.onFragmentsDisposed.remove(this.onFragmentsDisposed); - this.events = {}; - await this.onDisposed.trigger(FragmentHighlighter.uuid); - this.onDisposed.reset(); + return description; } - async add(name, material) { - if (this.highlightMats[name]) { - throw new Error("A highlight with this name already exists."); - } - this.highlightMats[name] = material; - this.selection[name] = {}; - this.events[name] = { - onHighlight: new Event(), - onClear: new Event(), - }; - await this.update(); + getProjectID(webIfc) { + const projectsIDs = webIfc.GetLineIDsWithType(0, IFCPROJECT); + const projectID = projectsIDs.get(0); + const project = webIfc.GetLine(0, projectID); + return project.GlobalId.value; } - /** {@link Updateable.update} */ - async update() { - if (!this.fillEnabled) { - return; - } - this.onBeforeUpdate.trigger(this); - const fragments = this.components.tools.get(FragmentManager); - for (const fragmentID in fragments.list) { - const fragment = fragments.list[fragmentID]; - this.addHighlightToFragment(fragment); - const outlinedMesh = this._outlinedMeshes[fragmentID]; - if (outlinedMesh) { - fragment.mesh.updateMatrixWorld(true); - outlinedMesh.applyMatrix4(fragment.mesh.matrixWorld); - } - } - this.onAfterUpdate.trigger(this); + getCoordinationMatrix(webIfc) { + const coordArray = webIfc.GetCoordinationMatrix(0); + return new THREE$1.Matrix4().fromArray(coordArray); } - async highlight(name, removePrevious = true, zoomToSelection = this.zoomToSelection) { - var _a; - if (!this.enabled) - return null; - this.checkSelection(name); - const fragments = this.components.tools.get(FragmentManager); - const fragList = []; - const meshes = fragments.meshes; - const result = this.components.raycaster.castRay(meshes); - if (!result) { - await this.clear(name); - return null; - } - const mesh = result.object; - const geometry = mesh.geometry; - const index = (_a = result.face) === null || _a === void 0 ? void 0 : _a.a; - const instanceID = result.instanceId; - if (!geometry || index === undefined || instanceID === undefined) { - return null; - } - if (removePrevious) { - await this.clear(name); - } - if (!this.selection[name][mesh.uuid]) { - this.selection[name][mesh.uuid] = new Set(); + async getModelProperties(webIfc) { + if (!this.settings.includeProperties) { + return {}; } - fragList.push(mesh.fragment); - const blockID = mesh.fragment.getVertexBlockID(geometry, index); - const itemID = mesh.fragment - .getItemID(instanceID, blockID) - .replace(/\..*/, ""); - const idNum = parseInt(itemID, 10); - this.selection[name][mesh.uuid].add(itemID); - this.addComposites(mesh, idNum, name); - await this.regenerate(name, mesh.uuid); - const group = mesh.fragment.group; - if (group) { - const keys = group.data[idNum][0]; - for (let i = 0; i < keys.length; i++) { - const fragKey = keys[i]; - const fragID = group.keyFragments[fragKey]; - const fragment = fragments.list[fragID]; - fragList.push(fragment); - if (!this.selection[name][fragID]) { - this.selection[name][fragID] = new Set(); + return new Promise((resolve) => { + this._propertyExporter.onPropertiesSerialized.add((properties) => { + resolve(properties); + }); + this._propertyExporter.export(webIfc, 0); + }); + } + createAllFragments(geometries, civilItems) { + const uniqueItems = {}; + const matrix = new THREE$1.Matrix4(); + const color = new THREE$1.Color(); + console.log(civilItems); + // Add alignments data + if (civilItems.IfcAlignment) { + const horizontalAlignments = new IfcAlignmentData(); + const verticalAlignments = new IfcAlignmentData(); + const realAlignments = new IfcAlignmentData(); + let countH = 0; + let countV = 0; + let countR = 0; + const valuesH = []; + const valuesV = []; + const valuesR = []; + for (const alignment of civilItems.IfcAlignment) { + horizontalAlignments.alignmentIndex.push(countH); + verticalAlignments.alignmentIndex.push(countV); + if (alignment.horizontal) { + for (const hAlignment of alignment.horizontal) { + horizontalAlignments.curveIndex.push(countH); + for (const point of hAlignment.points) { + valuesH.push(point.x); + valuesH.push(point.y); + countH++; + } + } + } + if (alignment.vertical) { + for (const vAlignment of alignment.vertical) { + verticalAlignments.curveIndex.push(countV); + for (const point of vAlignment.points) { + valuesV.push(point.x); + valuesV.push(point.y); + countV++; + } + } + } + if (alignment.curve3D) { + for (const rAlignment of alignment.curve3D) { + realAlignments.curveIndex.push(countR); + for (const point of rAlignment.points) { + valuesR.push(point.x); + valuesR.push(point.y); + valuesR.push(point.z); + countR++; + } + } } - this.selection[name][fragID].add(itemID); - this.addComposites(fragment.mesh, idNum, name); - await this.regenerate(name, fragID); } + horizontalAlignments.coordinates = new Float32Array(valuesH); + verticalAlignments.coordinates = new Float32Array(valuesV); + realAlignments.coordinates = new Float32Array(valuesR); + this._model.ifcCivil = { + horizontalAlignments, + verticalAlignments, + realAlignments, + }; } - await this.events[name].onHighlight.trigger(this.selection[name]); - if (zoomToSelection) { - await this.zoomSelection(name); - } - return { id: itemID, fragments: fragList }; - } - async highlightByID(name, ids, removePrevious = true, zoomToSelection = this.zoomToSelection) { - if (!this.enabled) - return; - if (removePrevious) { - await this.clear(name); + for (const id in geometries) { + const { buffer, instances } = geometries[id]; + const transparent = instances[0].color.w !== 1; + const opacity = transparent ? 0.4 : 1; + const material = new THREE$1.MeshLambertMaterial({ transparent, opacity }); + // This prevents z-fighting for ifc spaces + if (opacity !== 1) { + material.depthWrite = false; + material.polygonOffset = true; + material.polygonOffsetFactor = 5; + material.polygonOffsetUnits = 1; + } + if (instances.length === 1) { + const instance = instances[0]; + const { x, y, z, w } = instance.color; + const matID = `${x}-${y}-${z}-${w}`; + if (!uniqueItems[matID]) { + material.color = new THREE$1.Color().setRGB(x, y, z, "srgb"); + uniqueItems[matID] = { material, geometries: [], expressIDs: [] }; + } + matrix.fromArray(instance.matrix); + buffer.applyMatrix4(matrix); + uniqueItems[matID].geometries.push(buffer); + uniqueItems[matID].expressIDs.push(instance.expressID.toString()); + continue; + } + const fragment = new Fragment$1(buffer, material, instances.length); + this._keyFragmentMap[this._fragmentKey] = fragment.id; + const previousIDs = new Set(); + for (let i = 0; i < instances.length; i++) { + const instance = instances[i]; + matrix.fromArray(instance.matrix); + const { expressID } = instance; + let instanceID = expressID.toString(); + let isComposite = false; + if (!previousIDs.has(expressID)) { + previousIDs.add(expressID); + } + else { + if (!fragment.composites[expressID]) { + fragment.composites[expressID] = 1; + } + const count = fragment.composites[expressID]; + instanceID = toCompositeID(expressID, count); + isComposite = true; + fragment.composites[expressID]++; + } + fragment.setInstance(i, { + ids: [instanceID], + transform: matrix, + }); + const { x, y, z } = instance.color; + color.setRGB(x, y, z, "srgb"); + fragment.mesh.setColorAt(i, color); + if (!isComposite) { + this.saveExpressID(expressID.toString()); + } + } + fragment.mesh.updateMatrix(); + this._model.items.push(fragment); + this._model.add(fragment.mesh); + this._fragmentKey++; } - const styles = this.selection[name]; - for (const fragID in ids) { - if (!styles[fragID]) { - styles[fragID] = new Set(); + const transform = new THREE$1.Matrix4(); + for (const matID in uniqueItems) { + const { material, geometries, expressIDs } = uniqueItems[matID]; + const geometriesByItem = {}; + for (let i = 0; i < expressIDs.length; i++) { + const id = expressIDs[i]; + if (!geometriesByItem[id]) { + geometriesByItem[id] = []; + } + geometriesByItem[id].push(geometries[i]); } - const fragments = this.components.tools.get(FragmentManager); - const fragment = fragments.list[fragID]; - const idsNum = new Set(); - for (const id of ids[fragID]) { - styles[fragID].add(id); - idsNum.add(parseInt(id, 10)); + const sortedGeometries = []; + const sortedIDs = []; + for (const id in geometriesByItem) { + sortedIDs.push(id); + const geometries = geometriesByItem[id]; + if (geometries.length) { + const merged = mergeGeometries(geometries); + sortedGeometries.push(merged); + } + else { + sortedGeometries.push(geometries[0]); + } + for (const geometry of geometries) { + geometry.dispose(); + } } - for (const id of idsNum) { - this.addComposites(fragment.mesh, id, name); + const geometry = GeometryUtils.merge([sortedGeometries], true); + const fragment = new Fragment$1(geometry, material, 1); + this._keyFragmentMap[this._fragmentKey] = fragment.id; + for (const id of sortedIDs) { + this.saveExpressID(id); } - await this.regenerate(name, fragID); - } - await this.events[name].onHighlight.trigger(this.selection[name]); - if (zoomToSelection) { - await this.zoomSelection(name); + this._fragmentKey++; + fragment.setInstance(0, { ids: sortedIDs, transform }); + this._model.items.push(fragment); + this._model.add(fragment.mesh); } } - /** - * Clears any selection previously made by calling {@link highlight}. - */ - async clear(name) { - await this.clearFills(name); - if (!name || !this.excludeOutline.has(name)) { - await this.clearOutlines(); + saveExpressID(expressID) { + if (!this._itemKeyMap[expressID]) { + this._itemKeyMap[expressID] = []; } + this._itemKeyMap[expressID].push(this._fragmentKey); } - async setup(config) { - if (config === null || config === void 0 ? void 0 : config.selectionMaterial) { - this.config.selectionMaterial.dispose(); - } - if (config === null || config === void 0 ? void 0 : config.hoverMaterial) { - this.config.hoverMaterial.dispose(); + getFragmentsGroupData() { + const itemsData = {}; + for (const id in this._itemKeyMap) { + const keys = []; + const rels = []; + const idNum = parseInt(id, 10); + const level = this._spatialTree.itemsByFloor[idNum] || 0; + const category = this.categories[idNum] || 0; + rels.push(level, category); + for (const key of this._itemKeyMap[id]) { + keys.push(key); + } + itemsData[idNum] = [keys, rels]; } - this.config = { ...this.config, ...config }; - this.outlineMaterial.color.set(0xf0ff7a); - this.excludeOutline.add(this.config.hoverName); - await this.add(this.config.selectName, [this.config.selectionMaterial]); - await this.add(this.config.hoverName, [this.config.hoverMaterial]); - this.setupEvents(true); - this.enabled = true; - this.onSetup.trigger(this); + return itemsData; } - async regenerate(name, fragID) { - if (this.fillEnabled) { - await this.updateFragmentFill(name, fragID); - } - if (this._outlineEnabled) { - await this.updateFragmentOutline(name, fragID); - } +} + +class GeometryReader { + constructor() { + this.saveLocations = false; + this.items = {}; + this.locations = {}; + this.CivilItems = { + IfcAlignment: [], + IfcCrossSection2D: [], + IfcCrossSection3D: [], + }; } - async zoomSelection(name) { - if (!this.fillEnabled && !this._outlineEnabled) { - return; - } - const bbox = this.components.tools.get(FragmentBoundingBox); - const fragments = this.components.tools.get(FragmentManager); - bbox.reset(); - const selected = this.selection[name]; - if (!Object.keys(selected).length) { - return; - } - for (const fragID in selected) { - const fragment = fragments.list[fragID]; - if (this.fillEnabled) { - const highlight = fragment.fragments[name]; - if (highlight) { - bbox.addMesh(highlight.mesh); - } - } - if (this._outlineEnabled && this._outlinedMeshes[fragID]) { - bbox.addMesh(this._outlinedMeshes[fragID]); - } + get webIfc() { + if (!this._webIfc) { + throw new Error("web-ifc not found!"); } - const sphere = bbox.getSphere(); - sphere.radius *= this.zoomFactor; - const camera = this.components.camera; - await camera.controls.fitToSphere(sphere, true); + return this._webIfc; } - addComposites(mesh, itemID, name) { - const composites = mesh.fragment.composites[itemID]; - if (composites) { - for (let i = 1; i < composites; i++) { - const compositeID = toCompositeID(itemID, i); - this.selection[name][mesh.uuid].add(compositeID); - } - } + cleanUp() { + this.items = {}; + this.locations = {}; + this._webIfc = null; } - async clearStyle(name) { - const fragments = this.components.tools.get(FragmentManager); - for (const fragID in this.selection[name]) { - const fragment = fragments.list[fragID]; - if (!fragment) - continue; - const selection = fragment.fragments[name]; - if (selection) { - selection.mesh.removeFromParent(); + streamMesh(webifc, mesh, forceTransparent = false) { + this._webIfc = webifc; + const size = mesh.geometries.size(); + const totalTransform = new THREE$1.Vector3(); + const tempMatrix = new THREE$1.Matrix4(); + const tempVector = new THREE$1.Vector3(); + for (let i = 0; i < size; i++) { + const geometry = mesh.geometries.get(i); + const geometryID = geometry.geometryExpressID; + if (this.saveLocations) { + tempVector.set(0, 0, 0); + tempMatrix.fromArray(geometry.flatTransformation); + tempVector.applyMatrix4(tempMatrix); + totalTransform.add(tempVector); } - } - await this.events[name].onClear.trigger(null); - this.selection[name] = {}; - } - async updateFragmentFill(name, fragmentID) { - const fragments = this.components.tools.get(FragmentManager); - const ids = this.selection[name][fragmentID]; - const fragment = fragments.list[fragmentID]; - if (!fragment) - return; - const selection = fragment.fragments[name]; - if (!selection) - return; - const fragmentParent = fragment.mesh.parent; - if (!fragmentParent) - return; - fragmentParent.add(selection.mesh); - const isBlockFragment = selection.blocks.count > 1; - if (isBlockFragment) { - fragment.getInstance(0, this._tempMatrix); - selection.setInstance(0, { - ids: Array.from(fragment.ids), - transform: this._tempMatrix, + // Transparent geometries need to be separated + const isColorTransparent = geometry.color.w !== 1; + const isTransparent = isColorTransparent || forceTransparent; + const prefix = isTransparent ? "-" : "+"; + const idWithTransparency = prefix + geometryID; + if (forceTransparent) + geometry.color.w = 0.1; + if (!this.items[idWithTransparency]) { + const buffer = this.newBufferGeometry(geometryID); + if (!buffer) + continue; + this.items[idWithTransparency] = { buffer, instances: [] }; + } + this.items[idWithTransparency].instances.push({ + color: { ...geometry.color }, + matrix: geometry.flatTransformation, + expressID: mesh.expressID, }); - selection.blocks.setVisibility(true, ids, true); } - else { - let i = 0; - for (const id of ids) { - selection.mesh.count = i + 1; - const { instanceID } = fragment.getInstanceAndBlockID(id); - fragment.getInstance(instanceID, this._tempMatrix); - selection.setInstance(i, { ids: [id], transform: this._tempMatrix }); - i++; - } + if (this.saveLocations) { + const { x, y, z } = totalTransform.divideScalar(size); + this.locations[mesh.expressID] = [x, y, z]; } } - checkSelection(name) { - if (!this.selection[name]) { - throw new Error(`Selection ${name} does not exist.`); - } + streamAlignment(webifc) { + this.CivilItems.IfcAlignment = webifc.GetAllAlignments(0); } - addHighlightToFragment(fragment) { - for (const name in this.highlightMats) { - if (!fragment.fragments[name]) { - const material = this.highlightMats[name]; - const subFragment = fragment.addFragment(name, material); - if (this.config.cullHighlightMesh) { - const culler = this.components.tools.get(ScreenCuller); - if (name !== this.config.selectName && - name !== this.config.hoverName) { - culler.add(subFragment.mesh); - } - } - if (fragment.blocks.count > 1) { - subFragment.setInstance(0, { - ids: Array.from(fragment.ids), - transform: this._tempMatrix, - }); - subFragment.blocks.setVisibility(false); - } - subFragment.mesh.renderOrder = 2; - subFragment.mesh.frustumCulled = false; - } + streamCrossSection(webifc) { + this.CivilItems.IfcCrossSection2D = webifc.GetAllCrossSections2D(0); + this.CivilItems.IfcCrossSection3D = webifc.GetAllCrossSections3D(0); + } + newBufferGeometry(geometryID) { + const geometry = this.webIfc.GetGeometry(0, geometryID); + const verts = this.getVertices(geometry); + if (!verts.length) + return null; + const indices = this.getIndices(geometry); + if (!indices.length) + return null; + const buffer = this.constructBuffer(verts, indices); + // @ts-ignore + geometry.delete(); + return buffer; + } + getIndices(geometryData) { + const indices = this.webIfc.GetIndexArray(geometryData.GetIndexData(), geometryData.GetIndexDataSize()); + return indices; + } + getVertices(geometryData) { + const verts = this.webIfc.GetVertexArray(geometryData.GetVertexData(), geometryData.GetVertexDataSize()); + return verts; + } + constructBuffer(vertexData, indexData) { + const geometry = new THREE$1.BufferGeometry(); + const posFloats = new Float32Array(vertexData.length / 2); + const normFloats = new Float32Array(vertexData.length / 2); + for (let i = 0; i < vertexData.length; i += 6) { + posFloats[i / 2] = vertexData[i]; + posFloats[i / 2 + 1] = vertexData[i + 1]; + posFloats[i / 2 + 2] = vertexData[i + 2]; + normFloats[i / 2] = vertexData[i + 3]; + normFloats[i / 2 + 1] = vertexData[i + 4]; + normFloats[i / 2 + 2] = vertexData[i + 5]; } + geometry.setAttribute("position", new THREE$1.BufferAttribute(posFloats, 3)); + geometry.setAttribute("normal", new THREE$1.BufferAttribute(normFloats, 3)); + geometry.setIndex(new THREE$1.BufferAttribute(indexData, 1)); + return geometry; } - async clearFills(name) { - const names = name ? [name] : Object.keys(this.selection); - for (const name of names) { - await this.clearStyle(name); +} + +/** + * Reads all the geometry of the IFC file and generates a set of + * [fragments](https://github.com/ifcjs/fragment). It can also return the + * properties as a JSON file, as well as other sets of information within + * the IFC file. + */ +class FragmentIfcLoader extends Component { + constructor(components) { + super(components); + /** {@link Disposable.onDisposed} */ + this.onDisposed = new Event(); + this.enabled = true; + this.uiElement = new UIElement(); + this.onIfcLoaded = new Event(); + // For debugging purposes + // isolatedItems = new Set(); + this.onLocationsSaved = new Event(); + this._webIfc = new IfcAPI2(); + this._geometry = new GeometryReader(); + this._converter = new DataConverter(components); + this.components.tools.add(FragmentIfcLoader.uuid, this); + if (components.uiEnabled) { + this.setupUI(); } } - async clearOutlines() { - const fragments = this.components.tools.get(FragmentManager); - const effects = this._postproduction.customEffects; - const fragmentsOutline = effects.outlinedMeshes.fragments; - if (fragmentsOutline) { - fragmentsOutline.meshes.clear(); + get() { + return this._webIfc; + } + get settings() { + return this._converter.settings; + } + /** {@link Disposable.dispose} */ + async dispose() { + this._geometry.cleanUp(); + this._converter.cleanUp(); + this.onIfcLoaded.reset(); + this.onLocationsSaved.reset(); + this.uiElement.dispose(); + this._webIfc = null; + this._geometry = null; + this._converter = null; + await this.onDisposed.trigger(FragmentIfcLoader.uuid); + this.onDisposed.reset(); + } + /** Loads the IFC file and converts it to a set of fragments. */ + async load(data, name) { + if (this.settings.saveLocations) { + this._geometry.saveLocations = true; } - for (const fragID in this._outlinedMeshes) { - const fragment = fragments.list[fragID]; - const isBlockFragment = fragment.blocks.count > 1; - const mesh = this._outlinedMeshes[fragID]; - if (isBlockFragment) { - mesh.geometry.setIndex([]); + const before = performance.now(); + await this.readIfcFile(data); + await this.readAllGeometries(); + await this._geometry.streamAlignment(this._webIfc); + await this._geometry.streamCrossSection(this._webIfc); + const items = this._geometry.items; + const civItems = this._geometry.CivilItems; + const model = await this._converter.generate(this._webIfc, items, civItems); + model.name = name; + if (this.settings.saveLocations) { + await this.onLocationsSaved.trigger(this._geometry.locations); + } + const fragments = this.components.tools.get(FragmentManager); + if (this.settings.coordinate) { + const isFirstModel = fragments.groups.length === 0; + if (isFirstModel) { + fragments.baseCoordinationModel = model.uuid; } else { - mesh.count = 0; + fragments.coordinate([model]); } } - } - async updateFragmentOutline(name, fragmentID) { - const fragments = this.components.tools.get(FragmentManager); - if (!this.selection[name][fragmentID]) { - return; - } - if (this.excludeOutline.has(name)) { - return; + this.cleanUp(); + fragments.groups.push(model); + for (const fragment of model.items) { + fragment.group = model; + fragments.list[fragment.id] = fragment; + this.components.meshes.push(fragment.mesh); } - const ids = this.selection[name][fragmentID]; - const fragment = fragments.list[fragmentID]; - if (!fragment) - return; - const geometry = fragment.mesh.geometry; - const customEffects = this._postproduction.customEffects; - if (!customEffects.outlinedMeshes.fragments) { - customEffects.outlinedMeshes.fragments = { - meshes: new Set(), - material: this.outlineMaterial, + await this.onIfcLoaded.trigger(model); + console.log(`Loading the IFC took ${performance.now() - before} ms!`); + return model; + } + setupUI() { + const main = new Button(this.components); + main.materialIcon = "upload_file"; + main.tooltip = "Load IFC"; + const toast = new ToastNotification(this.components, { + message: "IFC model successfully loaded!", + }); + main.onClick.add(() => { + const fileOpener = document.createElement("input"); + fileOpener.type = "file"; + fileOpener.accept = ".ifc"; + fileOpener.style.display = "none"; + fileOpener.onchange = async () => { + const fragments = this.components.tools.get(FragmentManager); + if (fileOpener.files === null || fileOpener.files.length === 0) + return; + const file = fileOpener.files[0]; + const buffer = await file.arrayBuffer(); + const data = new Uint8Array(buffer); + const model = await this.load(data, file.name); + const scene = this.components.scene.get(); + scene.add(model); + toast.visible = true; + await fragments.updateWindow(); + fileOpener.remove(); }; + fileOpener.click(); + }); + this.components.ui.add(toast); + toast.visible = false; + this.uiElement.set({ main, toast }); + } + async readIfcFile(data) { + const { path, absolute } = this.settings.wasm; + this._webIfc.SetWasmPath(path, absolute); + await this._webIfc.Init(); + return this._webIfc.OpenModel(data, this.settings.webIfc); + } + async readAllGeometries() { + this._converter.saveIfcCategories(this._webIfc); + // Some categories (like IfcSpace) need to be created explicitly + const optionals = this.settings.optionalCategories; + // Force IFC space to be transparent + if (optionals.includes(IFCSPACE)) { + const index = optionals.indexOf(IFCSPACE); + optionals.splice(index, 1); + this._webIfc.StreamAllMeshesWithTypes(0, [IFCSPACE], (mesh) => { + if (this.isExcluded(mesh.expressID)) { + return; + } + this._geometry.streamMesh(this._webIfc, mesh, true); + }); } - const outlineEffect = customEffects.outlinedMeshes.fragments; - // Create a copy of the original fragment mesh for outline - if (!this._outlinedMeshes[fragmentID]) { - const newGeometry = new THREE$1.BufferGeometry(); - newGeometry.attributes = geometry.attributes; - newGeometry.index = geometry.index; - const newMesh = new THREE$1.InstancedMesh(newGeometry, this._invisibleMaterial, fragment.capacity); - newMesh.frustumCulled = false; - newMesh.renderOrder = 999; - fragment.mesh.updateMatrixWorld(true); - newMesh.applyMatrix4(fragment.mesh.matrixWorld); - this._outlinedMeshes[fragmentID] = newMesh; - const scene = this.components.scene.get(); - scene.add(newMesh); - } - const outlineMesh = this._outlinedMeshes[fragmentID]; - outlineEffect.meshes.add(outlineMesh); - const isBlockFragment = fragment.blocks.count > 1; - if (isBlockFragment) { - const indices = fragment.mesh.geometry.index.array; - const newIndex = []; - const idsSet = new Set(ids); - for (let i = 0; i < indices.length - 2; i += 3) { - const index = indices[i]; - const blockID = fragment.mesh.geometry.attributes.blockID.array; - const block = blockID[index]; - const itemID = fragment.mesh.fragment.getItemID(0, block); - if (idsSet.has(itemID)) { - newIndex.push(indices[i], indices[i + 1], indices[i + 2]); + // Load rest of optional categories (if any) + if (optionals.length) { + this._webIfc.StreamAllMeshesWithTypes(0, optionals, (mesh) => { + if (this.isExcluded(mesh.expressID)) { + return; } - } - outlineMesh.geometry.setIndex(newIndex); + this._geometry.streamMesh(this._webIfc, mesh); + }); } - else { - let counter = 0; - for (const id of ids) { - const { instanceID } = fragment.getInstanceAndBlockID(id); - fragment.mesh.getMatrixAt(instanceID, this._tempMatrix); - outlineMesh.setMatrixAt(counter++, this._tempMatrix); + // Load common categories + this._webIfc.StreamAllMeshes(0, (mesh) => { + if (this.isExcluded(mesh.expressID)) { + return; } - outlineMesh.count = counter; - outlineMesh.instanceMatrix.needsUpdate = true; - } + this._geometry.streamMesh(this._webIfc, mesh); + }); + // Load civil items + this._geometry.streamAlignment(this._webIfc); + this._geometry.streamCrossSection(this._webIfc); } - setupEvents(active) { - const container = this.components.renderer.get().domElement; - if (active === this._eventsActive) { - return; - } - this._eventsActive = active; - if (active) { - container.addEventListener("mousedown", this.onMouseDown); - container.addEventListener("mouseup", this.onMouseUp); - container.addEventListener("mousemove", this.onMouseMove); - } - else { - container.removeEventListener("mousedown", this.onMouseDown); - container.removeEventListener("mouseup", this.onMouseUp); - container.removeEventListener("mousemove", this.onMouseMove); - } + cleanIfcApi() { + this._webIfc = null; + this._webIfc = new IfcAPI2(); + } + cleanUp() { + this.cleanIfcApi(); + this._geometry.cleanUp(); + this._converter.cleanUp(); + } + isExcluded(id) { + const category = this._converter.categories[id]; + return this.settings.excludedCategories.has(category); } } -FragmentHighlighter.uuid = "cb8a76f2-654a-4b50-80c6-66fd83cafd77"; -ToolComponent.libraryUUIDs.add(FragmentHighlighter.uuid); +FragmentIfcLoader.uuid = "a659add7-1418-4771-a0d6-7d4d438e4624"; +ToolComponent.libraryUUIDs.add(FragmentIfcLoader.uuid); class FragmentTreeItem extends Component { get children() { diff --git a/src/core/ScreenCuller/index.html b/src/core/ScreenCuller/index.html index 585cd9540..35b0773f3 100644 --- a/src/core/ScreenCuller/index.html +++ b/src/core/ScreenCuller/index.html @@ -94,6 +94,7 @@ */ const culler = new OBC.ScreenCuller(components); + await culler.setup() /*MD diff --git a/src/core/ScreenCuller/index.ts b/src/core/ScreenCuller/index.ts index 707229057..3312b7e46 100644 --- a/src/core/ScreenCuller/index.ts +++ b/src/core/ScreenCuller/index.ts @@ -1,22 +1,30 @@ import * as THREE from "three"; import { Material } from "three"; -import { FragmentsGroup } from "bim-fragment"; -import { Component, Disposable, Event } from "../../base-types"; +import { FragmentMesh } from "bim-fragment"; +import { Component, Configurable, Disposable, Event } from "../../base-types"; import { Components } from "../Components"; import { readPixelsAsync } from "./src/screen-culler-helper"; import { Disposer } from "../Disposer"; import { ToolComponent } from "../ToolsComponent"; import { FragmentManager } from "../../fragments/FragmentManager"; +import { FragmentHighlighter } from "../../fragments/FragmentHighlighter"; // TODO: Work at the instance level instead of the mesh level? +export interface ScreenCullerConfig { + updateInterval?: number; + rtWidth?: number; + rtHeight?: number; + autoUpdate?: boolean; +} + /** * A tool to handle big scenes efficiently by automatically hiding the objects * that are not visible to the camera. */ export class ScreenCuller extends Component> - implements Disposable + implements Disposable, Configurable { static readonly uuid = "69f2a50d-c266-44fc-b1bd-fa4d34be89e6" as const; @@ -41,9 +49,9 @@ export class ScreenCuller */ renderDebugFrame = false; - private readonly renderer: THREE.WebGLRenderer; - private readonly renderTarget: THREE.WebGLRenderTarget; - private readonly bufferSize: number; + readonly renderer: THREE.WebGLRenderer; + private renderTarget: THREE.WebGLRenderTarget | null = null; + private bufferSize: number | null = null; private readonly materialCache: Map; private readonly worker: Worker; @@ -64,24 +72,15 @@ export class ScreenCuller // Alternative scene and meshes to make the visibility check private readonly _scene = new THREE.Scene(); - private readonly _buffer: Uint8Array; - - constructor( - components: Components, - readonly updateInterval = 1000, - readonly rtWidth = 512, - readonly rtHeight = 512, - readonly autoUpdate = true - ) { + private _buffer: Uint8Array | null = null; + + constructor(components: Components) { super(components); components.tools.add(ScreenCuller.uuid, this); this.renderer = new THREE.WebGLRenderer(); const planes = this.components.renderer.clippingPlanes; this.renderer.clippingPlanes = planes; - this.renderTarget = new THREE.WebGLRenderTarget(rtWidth, rtHeight); - this.bufferSize = rtWidth * rtHeight * 4; - this._buffer = new Uint8Array(this.bufferSize); this.materialCache = new Map(); const code = ` @@ -102,7 +101,23 @@ export class ScreenCuller const blob = new Blob([code], { type: "application/javascript" }); this.worker = new Worker(URL.createObjectURL(blob)); this.worker.addEventListener("message", this.handleWorkerMessage); + } + + config: Required = { + updateInterval: 1000, + rtWidth: 512, + rtHeight: 512, + autoUpdate: true, + }; + readonly onSetup = new Event(); + async setup(config?: Partial) { + this.config = { ...this.config, ...config }; + const { autoUpdate, updateInterval, rtHeight, rtWidth } = this.config; + this.renderTarget = new THREE.WebGLRenderTarget(rtWidth, rtHeight); + this.bufferSize = rtWidth * rtHeight * 4; + this._buffer = new Uint8Array(this.bufferSize); if (autoUpdate) window.setInterval(this.updateVisibility, updateInterval); + this.onSetup.trigger(this); } /** @@ -120,10 +135,11 @@ export class ScreenCuller this._recentlyHiddenMeshes.clear(); this._scene.children.length = 0; this.onViewUpdated.reset(); + this.onSetup.reset(); this.worker.terminate(); this.renderer.dispose(); - this.renderTarget.dispose(); - (this._buffer as any) = null; + this.renderTarget?.dispose(); + this._buffer = null; this._transparentMat.dispose(); this._meshColorMap.clear(); this._visibleMeshes = []; @@ -206,15 +222,18 @@ export class ScreenCuller colorMesh.applyMatrix4(mesh.matrix); colorMesh.updateMatrix(); - const parent = mesh.parent; - if (parent instanceof FragmentsGroup) { - const manager = this.components.tools.get(FragmentManager); - const coordinationModel = manager.groups.find( - (model) => model.uuid === manager.baseCoordinationModel - ); - if (coordinationModel) { - colorMesh.applyMatrix4(parent.coordinationMatrix.clone().invert()); - colorMesh.applyMatrix4(coordinationModel.coordinationMatrix); + if (mesh instanceof FragmentMesh) { + const fragment = mesh.fragment; + const parent = fragment.group; + if (parent) { + const manager = this.components.tools.get(FragmentManager); + const coordinationModel = manager.groups.find( + (model) => model.uuid === manager.baseCoordinationModel + ); + if (coordinationModel) { + colorMesh.applyMatrix4(parent.coordinationMatrix.clone().invert()); + colorMesh.applyMatrix4(coordinationModel.coordinationMatrix); + } } } @@ -230,13 +249,13 @@ export class ScreenCuller * not true. */ updateVisibility = async (force?: boolean) => { - if (!this.enabled) return; + if (!(this.enabled && this._buffer)) return; if (!this.needsUpdate && !force) return; const camera = this.components.camera.get(); camera.updateMatrix(); - this.renderer.setSize(this.rtWidth, this.rtHeight); + this.renderer.setSize(this.config.rtWidth, this.config.rtHeight); this.renderer.setRenderTarget(this.renderTarget); this.renderer.render(this._scene, camera); @@ -245,8 +264,8 @@ export class ScreenCuller context, 0, 0, - this.rtWidth, - this.rtHeight, + this.config.rtWidth, + this.config.rtHeight, context.RGBA, context.UNSIGNED_BYTE, this._buffer @@ -281,6 +300,21 @@ export class ScreenCuller mesh.visible = true; this._currentVisibleMeshes.add(mesh.uuid); this._recentlyHiddenMeshes.delete(mesh.uuid); + if (mesh instanceof FragmentMesh) { + const highlighter = this.components.tools.get(FragmentHighlighter); + const { cullHighlightMeshes, selectName } = highlighter.config; + if (!cullHighlightMeshes) { + continue; + } + const fragments = mesh.fragment.fragments; + for (const name in fragments) { + if (name === selectName) { + continue; + } + const fragment = fragments[name]; + fragment.mesh.visible = true; + } + } } } @@ -289,6 +323,21 @@ export class ScreenCuller const mesh = this._meshes.get(uuid); if (mesh === undefined) continue; mesh.visible = false; + if (mesh instanceof FragmentMesh) { + const highlighter = this.components.tools.get(FragmentHighlighter); + const { cullHighlightMeshes, selectName } = highlighter.config; + if (!cullHighlightMeshes) { + continue; + } + const fragments = mesh.fragment.fragments; + for (const name in fragments) { + if (name === selectName) { + continue; + } + const fragment = fragments[name]; + fragment.mesh.visible = false; + } + } } await this.onViewUpdated.trigger(); diff --git a/src/fragments/FragmentBoundingBox/index.ts b/src/fragments/FragmentBoundingBox/index.ts index cb0aa263d..4d373275c 100644 --- a/src/fragments/FragmentBoundingBox/index.ts +++ b/src/fragments/FragmentBoundingBox/index.ts @@ -2,7 +2,9 @@ import * as THREE from "three"; import { FragmentsGroup } from "bim-fragment"; import { InstancedMesh } from "three"; import { Component, Disposable, Event } from "../../base-types"; -import { Components, Disposer, ToolComponent } from "../../core"; +import { Components } from "../../core/Components"; +import { Disposer } from "../../core/Disposer"; +import { ToolComponent } from "../../core/ToolsComponent"; /** * A simple implementation of bounding box that works for fragments. The resulting bbox is not 100% precise, but diff --git a/src/fragments/FragmentHighlighter/index.ts b/src/fragments/FragmentHighlighter/index.ts index 523e301db..175e8a5fe 100644 --- a/src/fragments/FragmentHighlighter/index.ts +++ b/src/fragments/FragmentHighlighter/index.ts @@ -1,21 +1,18 @@ import * as THREE from "three"; import { Fragment, FragmentMesh } from "bim-fragment"; import { - Component, Disposable, Updateable, Event, FragmentIdMap, Configurable, } from "../../base-types"; +import { Component } from "../../base-types/component"; import { FragmentManager } from "../FragmentManager"; import { FragmentBoundingBox } from "../FragmentBoundingBox"; -import { - Components, - ScreenCuller, - SimpleCamera, - ToolComponent, -} from "../../core"; +import { Components } from "../../core/Components"; +import { SimpleCamera } from "../../core/SimpleCamera"; +import { ToolComponent } from "../../core/ToolsComponent"; import { toCompositeID } from "../../utils"; import { PostproductionRenderer } from "../../navigation/PostproductionRenderer"; @@ -38,7 +35,7 @@ export interface FragmentHighlighterConfig { selectionMaterial: THREE.Material; hoverMaterial: THREE.Material; autoHighlightOnClick: boolean; - cullHighlightMesh: boolean; + cullHighlightMeshes: boolean; } export class FragmentHighlighter @@ -105,7 +102,7 @@ export class FragmentHighlighter depthTest: true, }), autoHighlightOnClick: true, - cullHighlightMesh: true, + cullHighlightMeshes: true, }; private _mouseState = { @@ -476,15 +473,7 @@ export class FragmentHighlighter if (!fragment.fragments[name]) { const material = this.highlightMats[name]; const subFragment = fragment.addFragment(name, material); - if (this.config.cullHighlightMesh) { - const culler = this.components.tools.get(ScreenCuller); - if ( - name !== this.config.selectName && - name !== this.config.hoverName - ) { - culler.add(subFragment.mesh); - } - } + subFragment.group = fragment.group; if (fragment.blocks.count > 1) { subFragment.setInstance(0, { ids: Array.from(fragment.ids),