UNPKG

ng-cw-v12

Version:

Angular UI Component Library

750 lines 188 kB
import { Component, Input } from '@angular/core'; import * as THREE from 'three'; import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'; import { RoomEnvironment } from 'three/examples/jsm/environments/RoomEnvironment'; import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min'; import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader.js'; import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js'; import * as i0 from "@angular/core"; import * as i1 from "./local.service"; import * as i2 from "@angular/common"; export class ObjViewerComponent { constructor(eleRef, ls, ngZone) { this.eleRef = eleRef; this.ls = ls; this.ngZone = ngZone; /** 摄像机初始方位 */ this.ncCameraPlacement = 'default'; /** 背景图片URL */ this._bgUrl = ''; /** 操作提示 */ this._tips = false; /** 控制面板 */ this._control = false; /** 背景是否透明 */ this._alpha = false; this.showLoading = false; this.loadingText = ''; this.loadingText2 = ''; this.domId = '#objViewer'; this.cameraPlacement = 'default'; this.size = 0; this.state = { toneMapping: THREE.LinearToneMapping, toneMappingExposure: 0, background: false, backgroundColor: '#191919', useBgUrl: false, useBgSphere: false, axesHelper: false, wireframe: false, autoRotate: false, autoRotateSpeed: 0.5, ambientLight: true, ambientLightColor: '#FFFFFF', ambientLightIntensity: 2, directionalLight: true, directionalLightColor: '#FFFFFF', directionalLightIntensity: 2, directionalLightPosition: { x: 0, y: 0, z: 0 }, directionalLightHelper: false, directionalLight2: true, directionalLightColor2: '#FFFFFF', directionalLightIntensity2: 2, directionalLightPosition2: { x: 0, y: 0, z: 0 }, directionalLightHelper2: false, pointLight: false, pointLightColor: '#FFFFFF', pointLightIntensity: 1, pointLightDistance: 0, pointLightDecay: 0.1, pointLightPosition: { x: 0, y: 0, z: 0 }, pointLightHelper: false }; } /** 是否启用背景 */ set ncBg(value) { this.state.background = value !== null && value !== undefined && value !== false && value !== 'false'; } /** 背景色值 */ set ncBgColor(value) { this.state.backgroundColor = value; } set ncBgUrl(value) { this._bgUrl = value; this.state.useBgUrl = value ? true : false; //传了值,默认启用背景图 this.state.useBgSphere = value ? true : false; //传了值,默认启用背景球 } get ncBgUrl() { return this._bgUrl; } set ncTips(value) { this._tips = value !== null && value !== undefined && value !== false && value !== 'false'; } get ncTips() { return this._tips; } set ncControl(value) { this._control = value !== null && value !== undefined && value !== false && value !== 'false'; } get ncControl() { return this._control; } set ncAlpha(value) { this._alpha = value !== null && value !== undefined && value !== false && value !== 'false'; } get ncAlpha() { return this._alpha; } /** 自动旋转 */ set ncAutoRotate(value) { this.state.autoRotate = value !== null && value !== undefined && value !== false && value !== 'false'; } /** 自动旋转速度 */ set ncAutoRotateSpeed(value) { this.state.autoRotateSpeed = value; } ngOnChanges(changes) { if (changes['ncObjInfo'] && !changes['ncObjInfo'].firstChange) { this.updateModel(); } if (changes['ncCameraPlacement'] && !changes['ncCameraPlacement'].firstChange) { this.setCameraPlacement(); } if (changes['ncBg'] && !changes['ncBg'].firstChange || changes['ncBgColor'] && !changes['ncBgColor'].firstChange || changes['ncBgUrl'] && !changes['ncBgUrl'].firstChange) { this.updateBackground(); } if (changes['ncAutoRotate'] && !changes['ncAutoRotate'].firstChange || changes['ncAutoRotateSpeed'] && !changes['ncAutoRotateSpeed'].firstChange) { this.toggleAutoRotate(); } } ngAfterViewInit() { this.onWindowResize = this.onWindowResize.bind(this); this.animate = this.animate.bind(this); if (this.ls.isBrowser) { this.ngZone.runOutsideAngular(() => { window.addEventListener('resize', this.onWindowResize); }); } setTimeout(() => { this.ngZone.runOutsideAngular(() => { this.init(); }); }); } ngOnDestroy() { if (this.ls.isBrowser) { window.removeEventListener('resize', this.onWindowResize); } this.webglLoseContext(); } init() { let objArea = this.eleRef.nativeElement.querySelector(this.domId); this.domWidth = objArea.offsetWidth; this.domHeight = objArea.offsetHeight; this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: this.ncAlpha }); this.renderer.setPixelRatio(objArea.devicePixelRatio); this.renderer.setSize(this.domWidth, this.domHeight); this.renderer.outputColorSpace = THREE.SRGBColorSpace; objArea.appendChild(this.renderer.domElement); this.scene = new THREE.Scene(); const environment = new RoomEnvironment(); const pmremGenerator = new THREE.PMREMGenerator(this.renderer); this.scene.environment = pmremGenerator.fromScene(environment).texture; this.updateModel(); } render() { this.renderer.render(this.scene, this.camera); } animate() { requestAnimationFrame(this.animate); // required if controls.enableDamping or controls.autoRotate are set to true if (this.controls) { this.controls.update(); } if (this.scene && this.camera) { this.renderer.render(this.scene, this.camera); } } onWindowResize() { setTimeout(() => { let objArea = this.eleRef.nativeElement.querySelector(this.domId); const width = objArea.offsetWidth; const height = objArea.offsetHeight; this.renderer.setPixelRatio(objArea.devicePixelRatio); this.renderer.setSize(width, height); this.render(); }); } //更新模型 updateModel() { if (!this.ncObjInfo.objUrl || !this.ncObjInfo.mtlUrl) { return; } this.showLoading = true; this.loadingText = '模型加载中...'; this.loadingText2 = ''; if (this.objScene) { this.scene.remove(this.objScene); this.objScene = null; } let baseUrl = this.ncObjInfo.objUrl.split('/').slice(0, -1).join('/'); // 创建新的LoadingManager来重置进度计数 const manager = new THREE.LoadingManager(); manager.onProgress = (url, loaded, total) => { this.loadingText = `正在加载资源(${loaded}/${total})`; if (loaded == total) { this.showLoading = false; } }; new MTLLoader(manager).load(this.ncObjInfo.mtlUrl, (MaterialCreator) => { // 在preload()之前先处理材质信息,如果有贴图,则替换为obj同目录下的图片 for (let name in MaterialCreator.materialsInfo) { let materialInfo = MaterialCreator.materialsInfo[name]; // 需要处理的贴图类型 const mapTypes = [ 'map_kd', 'map_ks', 'map_ka', 'map_d', 'map_bump', 'bump', 'disp', 'decal' // 贴花贴图 ]; mapTypes.map((type) => { if (materialInfo.hasOwnProperty(type)) { let map_type_url = materialInfo[type]; let map_type_name = map_type_url.split(/[\/\\]/).pop(); materialInfo[type] = baseUrl + '/' + map_type_name; } }); } MaterialCreator.preload(); new OBJLoader(manager).setMaterials(MaterialCreator).load(this.ncObjInfo.objUrl, (object) => { this.objScene = object; const box = new THREE.Box3().setFromObject(this.objScene); this.size = box.getSize(new THREE.Vector3()).length(); this.center = box.getCenter(new THREE.Vector3()); //将坐标系原点设置在模型中心 this.objScene.position.x -= this.center.x; this.objScene.position.y -= this.center.y; this.objScene.position.z -= this.center.z; //PerspectiveCamera( fov : Number, aspect : Number, near : Number, far : Number ) // fov — 摄像机视锥体垂直视野角度,眼睛张开的角度,0闭眼 // aspect — 摄像机视锥体长宽比 // near — 摄像机视锥体近端面,能看到的最近垂直距离 // far — 摄像机视锥体远端面,能看到的最远垂直距离 this.camera = new THREE.PerspectiveCamera(50, this.domWidth / this.domHeight, this.size / 100, this.size * 100); this.camera.updateProjectionMatrix(); //position-摄像机位置 //lookAt-摄像机望向位置 this.setCameraPlacement(); //controls this.controls = new OrbitControls(this.camera, this.renderer.domElement); //根据模型size初始化state部分参数 this.state.directionalLightPosition.x = this.size; this.state.directionalLightPosition.y = this.size / 2; this.state.directionalLightPosition.z = this.size; this.state.directionalLightPosition2.x = -this.size; this.state.directionalLightPosition2.y = -this.size / 2; this.state.directionalLightPosition2.z = -this.size; this.state.pointLightPosition.x = this.size / 2; this.state.pointLightPosition.y = 0; this.state.pointLightPosition.z = 0; this.state.pointLightDistance = this.size; //添加默认光源(添加至camera,光源固定位置,不随场景旋转) // this.scene.add(this.camera); // let defaultLight = new THREE.DirectionalLight('#FFFFFF', 1.5); // defaultLight.position.set(0, 0, this.size / 2); // this.camera.add(defaultLight); //添加GUI控制面板 if (this.ncControl) { this.addGUI(); } else { if (this.gui) { this.gui.destroy(); this.gui = null; } } //根据state参数初始化相关设置 this.updateToneMapping(); this.updateToneMappingExposure(); this.updateBackground(); this.toggleAxesHelper(); this.toggleWireframe(); this.toggleAutoRotate(); this.toggleAmbientLight(); // this.updateAmbientLight(); this.toggleDirectionalLight(); // this.updateDirectionalLight(); this.toggleDirectionalLightHelper(); this.toggleDirectionalLight2(); // this.updateDirectionalLight2(); this.toggleDirectionalLightHelper2(); this.togglePointLight(); // this.updatePointLight(); this.togglePointLightHelper(); this.scene.add(this.objScene); this.animate(); }, xhr => { if (xhr.loaded != xhr.total) { this.loadingText2 = '模型下载 ' + (xhr.loaded / xhr.total * 100).toFixed(0) + ' %'; } else { this.loadingText2 = ''; } }); }); } //设置相机角度和望向位置 setCameraPlacement() { switch (this.cameraPlacement) { case 'left': //x-红 this.camera.position.set(-this.size, 0, 0); this.camera.lookAt(0, 0, 0); break; case 'right': //x-红 this.camera.position.set(this.size, 0, 0); this.camera.lookAt(0, 0, 0); break; case 'top': //y-绿 this.camera.position.set(0, this.size, 0); this.camera.lookAt(0, 0, 0); break; case 'bottom': //y-绿 this.camera.position.set(0, -this.size, 0); this.camera.lookAt(0, 0, 0); break; case 'front': //z-蓝 this.camera.position.set(0, 0, this.size); this.camera.lookAt(0, 0, 0); break; case 'back': //z-蓝 this.camera.position.set(0, 0, -this.size); this.camera.lookAt(0, 0, 0); break; default: this.camera.position.set(this.size / 1.3, this.size / 1.5, this.size / 1.3); this.camera.lookAt(0, 0, 0); break; } } //add GUI addGUI() { //折叠参数 let closeState = { gui: false, displayFolder: false, ambientLightFolder: true, directionalLightFolder: true, directionalLightHelperFolder: false, directionalLightFolder2: true, directionalLightHelperFolder2: false, pointLightFolder: true, pointlLightHelperFolder: false }; if (this.gui) { //记录上个gui的面板折叠值 closeState = { gui: this.gui._closed, displayFolder: this.gui.children[0]._closed, ambientLightFolder: this.gui.children[1]._closed, directionalLightFolder: this.gui.children[2]._closed, directionalLightHelperFolder: this.gui.children[2].children[3]._closed, directionalLightFolder2: this.gui.children[3]._closed, directionalLightHelperFolder2: this.gui.children[3].children[3]._closed, pointLightFolder: this.gui.children[4]._closed, pointlLightHelperFolder: this.gui.children[4].children[5]._closed }; this.gui.destroy(); this.gui = null; } this.gui = new GUI({ title: '控制面板', container: this.eleRef.nativeElement.querySelector('#control') }); if (closeState.gui) { this.gui.close(); } const displayFolder = this.gui.addFolder('显示设置'); if (closeState.displayFolder) { displayFolder.close(); } displayFolder.add(this.state, 'toneMapping', { 'ACESFilmic': THREE.ACESFilmicToneMapping, 'Linear': THREE.LinearToneMapping, 'Reinhard': THREE.ReinhardToneMapping, 'Cineon': THREE.CineonToneMapping, 'AgX': THREE.AgXToneMapping, 'Neutral': THREE.NeutralToneMapping }).name('色调').onChange(() => this.updateToneMapping()); displayFolder.add(this.state, 'toneMappingExposure', -10, 5, 0.01).name('曝光').onChange(() => this.updateToneMappingExposure()); displayFolder.add(this.state, 'background').name('启用场景背景').listen().onChange(() => this.updateBackground()); displayFolder.addColor(this.state, 'backgroundColor').name('场景背景色').listen().onChange(() => this.updateBackground()); //传入背景图才显示背景图设置 if (this.ncBgUrl) { displayFolder.add(this.state, 'useBgUrl').name('使用背景贴图').listen().onChange(() => this.updateBackground()); displayFolder.add(this.state, 'useBgSphere').name('球形背景贴图').listen().onChange(() => this.updateBackground()); } displayFolder.add(this.state, 'axesHelper').name('坐标系').onChange(() => this.toggleAxesHelper()); displayFolder.add(this.state, 'wireframe').name('线框模式').onChange(() => this.toggleWireframe()); displayFolder.add(this.state, 'autoRotate').listen().name('自动旋转').onChange(() => this.toggleAutoRotate()); displayFolder.add(this.state, 'autoRotateSpeed', -10.0, 10.0, 0.1).listen().name('旋转速度').onChange(() => this.toggleAutoRotate()); const ambientLightFolder = this.gui.addFolder('环境光'); if (closeState.ambientLightFolder) { ambientLightFolder.close(); } ambientLightFolder.add(this.state, 'ambientLight').name('启用').onChange(() => this.toggleAmbientLight()); ambientLightFolder.addColor(this.state, 'ambientLightColor').name('颜色').onChange(() => this.updateAmbientLight()); ambientLightFolder.add(this.state, 'ambientLightIntensity', 0, 10, 0.1).name('强度').onChange(() => this.updateAmbientLight()); const directionalLightFolder = this.gui.addFolder('定向光1'); if (closeState.directionalLightFolder) { directionalLightFolder.close(); } directionalLightFolder.add(this.state, 'directionalLight').name('启用').onChange(() => this.toggleDirectionalLight()); directionalLightFolder.addColor(this.state, 'directionalLightColor').name('颜色').onChange(() => this.updateDirectionalLight()); directionalLightFolder.add(this.state, 'directionalLightIntensity', 0, 10, 0.1).name('强度').onChange(() => this.updateDirectionalLight()); const directionalLightHelperFolder = directionalLightFolder.addFolder('定向光位置'); if (closeState.directionalLightHelperFolder) { directionalLightHelperFolder.close(); } directionalLightHelperFolder.add(this.state, 'directionalLightHelper').listen().name('位置模拟').onChange(() => this.toggleDirectionalLightHelper()); directionalLightHelperFolder.add(this.state.directionalLightPosition, 'x', -this.size, this.size).name('x(坐标系-红)').decimals(2).onChange(() => this.updateDirectionalLight()); directionalLightHelperFolder.add(this.state.directionalLightPosition, 'y', -this.size, this.size).name('y(坐标系-绿)').decimals(2).onChange(() => this.updateDirectionalLight()); directionalLightHelperFolder.add(this.state.directionalLightPosition, 'z', -this.size, this.size).name('z(坐标系-蓝)').decimals(2).onChange(() => this.updateDirectionalLight()); const directionalLightFolder2 = this.gui.addFolder('定向光2'); if (closeState.directionalLightFolder2) { directionalLightFolder2.close(); } directionalLightFolder2.add(this.state, 'directionalLight2').name('启用').onChange(() => this.toggleDirectionalLight2()); directionalLightFolder2.addColor(this.state, 'directionalLightColor2').name('颜色').onChange(() => this.updateDirectionalLight2()); directionalLightFolder2.add(this.state, 'directionalLightIntensity2', 0, 10, 0.1).name('强度').onChange(() => this.updateDirectionalLight2()); const directionalLightHelperFolder2 = directionalLightFolder2.addFolder('定向光位置'); if (closeState.directionalLightHelperFolder2) { directionalLightHelperFolder2.close(); } directionalLightHelperFolder2.add(this.state, 'directionalLightHelper2').listen().name('位置模拟').onChange(() => this.toggleDirectionalLightHelper2()); directionalLightHelperFolder2.add(this.state.directionalLightPosition2, 'x', -this.size, this.size).name('x(坐标系-红)').decimals(2).onChange(() => this.updateDirectionalLight2()); directionalLightHelperFolder2.add(this.state.directionalLightPosition2, 'y', -this.size, this.size).name('y(坐标系-绿)').decimals(2).onChange(() => this.updateDirectionalLight2()); directionalLightHelperFolder2.add(this.state.directionalLightPosition2, 'z', -this.size, this.size).name('z(坐标系-蓝)').decimals(2).onChange(() => this.updateDirectionalLight2()); const pointLightFolder = this.gui.addFolder('点光源'); if (closeState.pointLightFolder) { pointLightFolder.close(); } pointLightFolder.add(this.state, 'pointLight').name('启用').onChange(() => this.togglePointLight()); pointLightFolder.addColor(this.state, 'pointLightColor').name('颜色').onChange(() => this.updatePointLight()); pointLightFolder.add(this.state, 'pointLightIntensity', 0, 10, 0.1).name('强度').onChange(() => this.updatePointLight()); pointLightFolder.add(this.state, 'pointLightDistance', 0, this.size).name('范围').decimals(2).onChange(() => this.updatePointLight()); pointLightFolder.add(this.state, 'pointLightDecay', 0, 2, 0.1).name('衰减').onChange(() => this.updatePointLight()); const pointlLightHelperFolder = pointLightFolder.addFolder('点光源位置'); if (closeState.pointlLightHelperFolder) { pointlLightHelperFolder.close(); } pointlLightHelperFolder.add(this.state, 'pointLightHelper').listen().name('位置模拟').onChange(() => this.togglePointLightHelper()); pointlLightHelperFolder.add(this.state.pointLightPosition, 'x', -this.size, this.size).name('x(坐标系-红)').decimals(2).onChange(() => this.updatePointLight()); pointlLightHelperFolder.add(this.state.pointLightPosition, 'y', -this.size, this.size).name('y(坐标系-绿)').decimals(2).onChange(() => this.updatePointLight()); pointlLightHelperFolder.add(this.state.pointLightPosition, 'z', -this.size, this.size).name('z(坐标系-蓝)').decimals(2).onChange(() => this.updatePointLight()); } //色调 updateToneMapping() { this.renderer.toneMapping = this.state.toneMapping; } //曝光 updateToneMappingExposure() { this.renderer.toneMappingExposure = Math.pow(2, this.state.toneMappingExposure); } //背景 updateBackground() { // 移除已存在的背景球体 if (this.backgroundSphere) { this.scene.remove(this.backgroundSphere); this.backgroundSphere = null; } //启用背景 if (this.state.background) { //使用背景图 if (this.state.useBgUrl && this.ncBgUrl) { //使用球体背景 if (this.state.useBgSphere) { // 创建球形几何体 const geometry = new THREE.SphereGeometry(this.size * 50, 32, 32); // 将法线朝内,这样纹理会显示在球体内侧 geometry.scale(-1, 1, 1); // 加载背景图纹理 new THREE.TextureLoader().load(this.ncBgUrl, (texture) => { texture.colorSpace = THREE.SRGBColorSpace; const material = new THREE.MeshBasicMaterial({ map: texture }); this.backgroundSphere = new THREE.Mesh(geometry, material); this.scene.add(this.backgroundSphere); }); } else { //使用平面背景图 new THREE.TextureLoader().load(this.ncBgUrl, (texture) => { texture.colorSpace = THREE.SRGBColorSpace; this.scene.background = texture; }); } } else { //使用背景色 this.scene.background = new THREE.Color(this.state.backgroundColor); } } else { //关闭背景 this.scene.background = null; } } toggleAxesHelper() { //x-红,y-绿,z-蓝 if (this.state.axesHelper) { if (this.axesHelper) { this.scene.remove(this.axesHelper); this.axesHelper = null; } this.axesHelper = new THREE.AxesHelper(this.size); this.scene.add(this.axesHelper); } else { if (this.axesHelper) { this.scene.remove(this.axesHelper); this.axesHelper = null; } } } //线框模式 toggleWireframe() { this.traverseMaterials(this.objScene, (material) => { material.wireframe = this.state.wireframe; if (material instanceof THREE.PointsMaterial) { material.size = 1; } }); } //遍历材质 traverseMaterials(object, callback) { object.traverse((node) => { if (!node.geometry) return; const materials = Array.isArray(node.material) ? node.material : [node.material]; materials.forEach(callback); }); } //自动旋转 toggleAutoRotate() { this.controls.autoRotate = this.state.autoRotate; this.controls.autoRotateSpeed = this.state.autoRotateSpeed; } toggleAmbientLight() { if (this.state.ambientLight) { if (this.ambientLight) { this.scene.remove(this.ambientLight); this.ambientLight = null; } this.ambientLight = new THREE.AmbientLight(this.state.ambientLightColor, this.state.ambientLightIntensity); this.scene.add(this.ambientLight); } else { if (this.ambientLight) { this.scene.remove(this.ambientLight); this.ambientLight = null; } } } //更新环境光 updateAmbientLight() { if (this.state.ambientLight) { this.ambientLight.color.set(this.state.ambientLightColor); this.ambientLight.intensity = this.state.ambientLightIntensity; } } toggleDirectionalLight() { if (this.state.directionalLight) { if (this.directionalLight) { this.scene.remove(this.directionalLight); this.directionalLight = null; } this.directionalLight = new THREE.DirectionalLight(this.state.directionalLightColor, this.state.directionalLightIntensity); this.directionalLight.position.set(this.state.directionalLightPosition.x, this.state.directionalLightPosition.y, this.state.directionalLightPosition.z); this.scene.add(this.directionalLight); } else { if (this.directionalLight) { this.scene.remove(this.directionalLight); this.directionalLight = null; } //关闭位置模拟 this.state.directionalLightHelper = false; this.toggleDirectionalLightHelper(); } } //更新定向光 updateDirectionalLight() { if (this.state.directionalLight) { this.directionalLight.color.set(this.state.directionalLightColor); this.directionalLight.intensity = this.state.directionalLightIntensity; this.directionalLight.position.set(this.state.directionalLightPosition.x, this.state.directionalLightPosition.y, this.state.directionalLightPosition.z); if (this.directionalLightHelper) { this.directionalLightHelper.update(); } } } toggleDirectionalLightHelper() { if (this.state.directionalLightHelper) { if (this.directionalLight) { //开启定向光才能开启位置模拟 if (this.directionalLightHelper) { this.scene.remove(this.directionalLightHelper); this.directionalLightHelper = null; } this.directionalLightHelper = new THREE.DirectionalLightHelper(this.directionalLight, this.size / 20, 0xff0000); this.scene.add(this.directionalLightHelper); } else { this.state.directionalLightHelper = false; } } else { if (this.directionalLightHelper) { this.scene.remove(this.directionalLightHelper); this.directionalLightHelper = null; } } } toggleDirectionalLight2() { if (this.state.directionalLight2) { if (this.directionalLight2) { this.scene.remove(this.directionalLight2); this.directionalLight2 = null; } this.directionalLight2 = new THREE.DirectionalLight(this.state.directionalLightColor2, this.state.directionalLightIntensity2); this.directionalLight2.position.set(this.state.directionalLightPosition2.x, this.state.directionalLightPosition2.y, this.state.directionalLightPosition2.z); this.scene.add(this.directionalLight2); } else { if (this.directionalLight2) { this.scene.remove(this.directionalLight2); this.directionalLight2 = null; } //关闭位置模拟 this.state.directionalLightHelper2 = false; this.toggleDirectionalLightHelper2(); } } //更新定向光2 updateDirectionalLight2() { if (this.state.directionalLight2) { this.directionalLight2.color.set(this.state.directionalLightColor2); this.directionalLight2.intensity = this.state.directionalLightIntensity2; this.directionalLight2.position.set(this.state.directionalLightPosition2.x, this.state.directionalLightPosition2.y, this.state.directionalLightPosition2.z); if (this.directionalLightHelper2) { this.directionalLightHelper2.update(); } } } toggleDirectionalLightHelper2() { if (this.state.directionalLightHelper2) { if (this.directionalLight2) { //开启定向光才能开启位置模拟 if (this.directionalLightHelper2) { this.scene.remove(this.directionalLightHelper2); this.directionalLightHelper2 = null; } this.directionalLightHelper2 = new THREE.DirectionalLightHelper(this.directionalLight2, this.size / 20, 0xffff00); this.scene.add(this.directionalLightHelper2); } else { this.state.directionalLightHelper2 = false; } } else { if (this.directionalLightHelper2) { this.scene.remove(this.directionalLightHelper2); this.directionalLightHelper2 = null; } } } togglePointLight() { if (this.state.pointLight) { if (this.pointLight) { this.scene.remove(this.pointLight); this.pointLight = null; } //PointLight( color : Integer, intensity : Float, distance : Number, decay : Float ) this.pointLight = new THREE.PointLight(this.state.pointLightColor, this.state.pointLightIntensity, this.state.pointLightDistance, this.state.pointLightDecay); this.pointLight.position.set(this.state.pointLightPosition.x, this.state.pointLightPosition.y, this.state.pointLightPosition.z); this.scene.add(this.pointLight); } else { if (this.pointLight) { this.scene.remove(this.pointLight); this.pointLight = null; } //关闭位置模拟 this.state.pointLightHelper = false; this.togglePointLightHelper(); } } //更新点光源 updatePointLight() { if (this.state.pointLight) { this.pointLight.color.set(this.state.pointLightColor); this.pointLight.intensity = this.state.pointLightIntensity; this.pointLight.decay = this.state.pointLightDecay; this.pointLight.distance = this.state.pointLightDistance; this.pointLight.position.set(this.state.pointLightPosition.x, this.state.pointLightPosition.y, this.state.pointLightPosition.z); if (this.pointLightHelper) { this.pointLightHelper.update(); } } } togglePointLightHelper() { if (this.state.pointLightHelper) { if (this.pointLight) { //开启点光源才能开启位置模拟 if (this.pointLightHelper) { this.scene.remove(this.pointLightHelper); this.pointLightHelper = null; } this.pointLightHelper = new THREE.PointLightHelper(this.pointLight, this.size / 20, 0x2bff00); this.scene.add(this.pointLightHelper); } else { this.state.pointLightHelper = false; } } else { if (this.pointLightHelper) { this.scene.remove(this.pointLightHelper); this.pointLightHelper = null; } } } //webgl销毁 webglLoseContext() { let objCanvasDom = this.eleRef.nativeElement.querySelector(`${this.domId} canvas`); if (objCanvasDom) { //手动销毁WebGL context对象(最多16个,超出的话最先加载的会被移除) let gl = objCanvasDom.getContext('webgl2'); gl.getExtension('WEBGL_lose_context').loseContext(); //dom移除 objCanvasDom.remove(); } } } ObjViewerComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: ObjViewerComponent, deps: [{ token: i0.ElementRef }, { token: i1.LocalService }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component }); ObjViewerComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.1.5", type: ObjViewerComponent, selector: "nc-obj-viewer", inputs: { ncObjInfo: "ncObjInfo", ncCameraPlacement: "ncCameraPlacement", ncBg: "ncBg", ncBgColor: "ncBgColor", ncBgUrl: "ncBgUrl", ncTips: "ncTips", ncControl: "ncControl", ncAlpha: "ncAlpha", ncAutoRotate: "ncAutoRotate", ncAutoRotateSpeed: "ncAutoRotateSpeed" }, usesOnChanges: true, ngImport: i0, template: "<div id=\"objViewer\">\r\n <div class=\"loading\" *ngIf=\"showLoading\">\r\n <div class=\"nc-loading\">\r\n <svg class=\"nc-loading-svg\" version=\"1.1\" viewBox=\"0 0 60 60\" xml:space=\"preserve\"\r\n xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\r\n <defs>\r\n <clipPath id=\"d-5\">\r\n <use width=\"100%\" height=\"100%\" overflow=\"visible\" xlink:href=\"#c\" />\r\n </clipPath>\r\n </defs>\r\n <g transform=\"translate(4.4 -.117)\">\r\n <defs>\r\n <path id=\"a\" d=\"m-5-1h62v62h-62z\" />\r\n </defs>\r\n <clipPath id=\"b\">\r\n <use width=\"100%\" height=\"100%\" overflow=\"visible\" xlink:href=\"#a\" />\r\n </clipPath>\r\n <g clip-path=\"url(#b)\">\r\n <defs>\r\n <path id=\"c\" d=\"m-5-1h62v62h-62z\" />\r\n </defs>\r\n <clipPath id=\"d\">\r\n <use width=\"100%\" height=\"100%\" overflow=\"visible\" xlink:href=\"#c\" />\r\n </clipPath>\r\n <g style=\"stroke:var(--ov_logo_border_color);\" stroke=\"#000\" stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\" stroke-miterlimit=\"10\">\r\n <path transform=\"translate(.039092 .02257)\"\r\n d=\"m38.361 22.877-12.4-7.3 9.2e-5 -14.177 24.8 14.2v28.8l-12.4-7.3226z\"\r\n clip-path=\"url(#d)\" fill=\"#4688b4\" />\r\n <path transform=\"rotate(120,26,30.082)\"\r\n d=\"m38.4 22.923-12.522-7.0887 0.14178-14.4 24.78 14.166v28.8l-12.522-7.0883z\"\r\n clip-path=\"url(#d-5)\" fill=\"#64c0ff\" />\r\n <path d=\"m26 30v14.4l12.4-7.3v-14.2z\" clip-path=\"url(#d)\" fill=\"#294e67\" />\r\n <path d=\"m13.6 37.1v-14.2l12.4-7.3v-14.177l-24.8 14.177v28.8z\" clip-path=\"url(#d)\"\r\n fill=\"#294e67\" />\r\n <path d=\"m26 15.6-12.4 7.3 12.4 7.1 12.4-7.1z\" clip-path=\"url(#d)\" fill=\"#64c0ff\" />\r\n <path d=\"m13.6 22.9 12.4 7.1v14.4l-12.4-7.3z\" clip-path=\"url(#d)\" fill=\"#4688b4\" />\r\n </g>\r\n </g>\r\n </g>\r\n </svg>\r\n <div class=\"ncTips\">\r\n <div>{{loadingText}}</div>\r\n <div>{{loadingText2}}</div>\r\n </div>\r\n </div>\r\n </div>\r\n <div id=\"tips\" *ngIf=\"ncTips\">\r\n <div class=\"tip-row\">\r\n <div class=\"row-img\">\r\n <img\r\n src=\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNS4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4KCjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgdmVyc2lvbj0iMS4xIgogICBpZD0iTGF5ZXJfMSIKICAgeD0iMHB4IgogICB5PSIwcHgiCiAgIHdpZHRoPSI2NHB4IgogICBoZWlnaHQ9IjY0cHgiCiAgIHZpZXdCb3g9IjAgMCA2NCA2NCIKICAgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgNjQgNjQiCiAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgIGlua3NjYXBlOnZlcnNpb249IjAuNDguNCByOTkzOSIKICAgc29kaXBvZGk6ZG9jbmFtZT0iTW91c2VMZWZ0LnN2ZyI+PG1ldGFkYXRhCiAgIGlkPSJtZXRhZGF0YTIxIj48cmRmOlJERj48Y2M6V29yawogICAgICAgcmRmOmFib3V0PSIiPjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PjxkYzp0eXBlCiAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+PGRjOnRpdGxlIC8+PC9jYzpXb3JrPjwvcmRmOlJERj48L21ldGFkYXRhPjxkZWZzCiAgIGlkPSJkZWZzMTkiIC8+PHNvZGlwb2RpOm5hbWVkdmlldwogICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICBib3JkZXJvcGFjaXR5PSIxIgogICBvYmplY3R0b2xlcmFuY2U9IjEwIgogICBncmlkdG9sZXJhbmNlPSIxMCIKICAgZ3VpZGV0b2xlcmFuY2U9IjEwIgogICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMCIKICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIxMjgyIgogICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4ODYiCiAgIGlkPSJuYW1lZHZpZXcxNyIKICAgc2hvd2dyaWQ9ImZhbHNlIgogICBpbmtzY2FwZTp6b29tPSIzLjY4NzUiCiAgIGlua3NjYXBlOmN4PSItMTAuMzA1MDg1IgogICBpbmtzY2FwZTpjeT0iMzIiCiAgIGlua3NjYXBlOndpbmRvdy14PSIxMDQiCiAgIGlua3NjYXBlOndpbmRvdy15PSIxMzYiCiAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjAiCiAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9ImczIiAvPgo8ZwogICBpZD0iZzMiPgoJPGcKICAgaWQ9Imc1IgogICBzdHlsZT0iZmlsbDojNGQ0ZDRkO3N0cm9rZTojNGQ0ZDRkIj4KCQk8cGF0aAogICBmaWxsPSIjMDIwMjAyIgogICBkPSJNMzIuODU0LDIwLjAyOGMtMC4wMTUtMS4yNzQsMC41NzYtMi4yNjksMS4xNTEtMy4yMzZjMC43NTgtMS4yNzgsMS41NDMtMi42LDAuOTk3LTQuNDE3ICAgIGMtMC41NTUtMS42NjctMS42MDctMi40ODUtMi41MzktMy4yMTJjLTEuMjU0LTAuOTczLTIuMTU5LTEuNjc2LTEuNjg3LTQuMzQzbDAuMDQxLTAuMjI5bC0xLjM1LTAuMjM4TDI5LjQyNyw0LjU4ICAgIGMtMC42MTUsMy40ODQsMC44NzgsNC42NDMsMi4xOTUsNS42NjZjMC44NSwwLjY2MywxLjY1MywxLjI4OCwyLjA3MiwyLjU0MmMwLjM2NiwxLjIyNC0wLjE4LDIuMTQzLTAuODY5LDMuMzA2ICAgIGMtMC42NDEsMS4wODEtMS4zNjYsMi4zLTEuMzQ5LDMuOTMzYy02LjgzNywwLjAwNy0xMy44MjMsMC4yOTEtMTMuODIzLDI0LjU5YzAsMTAuOTUxLDguNzk5LDE1LjAzMiwxNC4zNDcsMTUuMDMyICAgIGM1LjU0NSwwLDE0LjM0Ny00LjA4MSwxNC4zNDctMTUuMDMyQzQ2LjM0NywyMC4zNDcsMzkuMjI4LDIwLjA0OCwzMi44NTQsMjAuMDI4eiBNMzIuNjg1LDIxLjM5OSAgICBjNS42NDMsMC4wMTIsMTAuOTI3LDAuMjM2LDEyLjA2MywxNi4xNTNjLTMuODYsMS44MDUtNy45MTYsMi43NjUtMTIuMDYzLDIuODZ2LTIuOTY2YzAuODQ1LTAuMTYyLDEuNDgxLTAuOTA5LDEuNDgxLTEuNzgydi04Ljk1NiAgICBjMC0wLjg3Ni0wLjYzNy0xLjYyMS0xLjQ4MS0xLjc4NFYyMS4zOTl6IE0zMS42NTgsMjYuMjU0aDAuNjg1YzAuMjQ5LDAsMC40NTMsMC4yMDUsMC40NTMsMC40NTR2OC45NTYgICAgYzAsMC4yNDktMC4yMDQsMC40NDktMC40NTMsMC40NDloLTAuNjg1Yy0wLjI0OSwwLTAuNDUyLTAuMi0wLjQ1Mi0wLjQ0OXYtOC45NTZDMzEuMjA2LDI2LjQ1OSwzMS40MDksMjYuMjU0LDMxLjY1OCwyNi4yNTR6ICAgICBNMzEuMzEzLDIxLjM5OXYzLjUyNWMtMC44NDUsMC4xNjMtMS40NzksMC45MS0xLjQ3OSwxLjc4NHY4Ljk1NmMwLDAuODczLDAuNjM0LDEuNjIsMS40NzksMS43ODJ2Mi45NjYgICAgYy02LjMxMS0wLjE0OC0xMC45MDUtMi4yNTItMTIuMDYzLTIuODM5QzIwLjM4MSwyMS42MzMsMjUuNjY4LDIxLjQxMSwzMS4zMTMsMjEuMzk5eiBNMzIsNTguMjc0ICAgIGMtNS4wMTYsMC0xMi45NzYtMy43MDctMTIuOTc2LTEzLjY1OGMwLTEuOTgzLDAuMDQ1LTMuODUzLDAuMTM0LTUuNTYzYzIuMDAzLDAuOTI5LDYuNjk2LDIuNzQsMTIuODY4LDIuNzQgICAgYzQuMzk0LDAsOC43MDQtMC45MzQsMTIuODEzLTIuNzcxYzAuMDg5LDEuNzE4LDAuMTM1LDMuNiwwLjEzNSw1LjU5NUM0NC45NzQsNTQuNTY3LDM3LjAxNiw1OC4yNzQsMzIsNTguMjc0eiIKICAgaWQ9InBhdGg3IgogICBzdHlsZT0iZmlsbDojNGQ0ZDRkO3N0cm9rZTojNGQ0ZDRkIiAvPgoJPC9nPgoJCgkKPHBhdGgKICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtzdHJva2U6I2ZmZmZmZjtzdHJva2Utd2lkdGg6MC4yNzExODY0NCIKICAgZD0ibSAzMi45MjIwMzQsMzguOTUwNzkzIGMgMCwtMS4xMzU4MjUgMC4xMTQ5MTksLTEuNDU4MDQ1IDAuNjc3OTY2LC0xLjkwMDkzOCBsIDAuNjc3OTY2LC0wLjUzMzI4OCAwLC01LjI4MDc4IGMgMCwtNS4yNzEyNDkgLTAuMDAxMiwtNS4yODIwMDMgLTAuNjc3OTY2LC01Ljk1ODc0NSAtMC41NTc0MzksLTAuNTU3NDM5IC0wLjY3Nzk2NiwtMC45NDA5MzUgLTAuNjc3OTY2LC0yLjE1NzE2NSBsIDAsLTEuNDc5MTk5IDEuNTM1NDg3LDAgYyA1LjY1NzU1NCwwIDguNTg0MzM4LDMuNzI4NTI5IDkuNzE4NTUsMTIuMzgwNzkxIDAuNDk4NDM0LDMuODAyMjY1IDAuNTAzNDgsMy41MjU3MzEgLTAuMDY3NiwzLjcwNDU3MiAtMC4yNjEwMTcsMC4wODE3NCAtMS41Mjg2OTQsMC41MTIwMTYgLTIuODE3MDU5LDAuOTU2MTY3IC0yLjAzNzIwNiwwLjcwMjMwNiAtNC4wNDgwMzIsMS4xMTMwNDQgLTcuNDg4MDI2LDEuNTI5NTI4IGwgLTAuODgxMzU2LDAuMTA2NzA3IDAsLTEuMzY3NjUgeiIKICAgaWQ9InBhdGgzNzY4IgogICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPjxwYXRoCiAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7c3Ryb2tlOiNmZmZmZmY7c3Ryb2tlLXdpZHRoOjAuMjcxMTg2NDQiCiAgIGQ9Im0gMzEuNDg1MDgxLDM1Ljg0MjE0MyBjIC0wLjEwNDU5MSwtMC4xMDQ1OTEgLTAuMTkwMTY2LC0yLjIzMTM0NyAtMC4xOTAxNjYsLTQuNzI2MTIzIDAsLTMuOTgzOTk0IDAuMDU1MDIsLTQuNTU3MDcxIDAuNDUyMTI3LC00LjcwOTQ1NiAwLjI0ODY3LC0wLjA5NTQyIDAuNTU2ODAyLC0wLjA2ODgyIDAuNjg0NzM4LDAuMDU5MTIgMC4xMjc5MzcsMC4xMjc5MzYgMC4xOTkwNTYsMi4yOTY0ODEgMC4xNTgwNDMsNC44MTg5ODggLTAuMDYzMTksMy44ODY0MDUgLTAuMTM4NjcsNC41OTg2ODMgLTAuNDk0NTcyLDQuNjY3MDA5IC0wLjIzMTAwMywwLjA0NDM1IC0wLjUwNTU3OSwtMC4wMDQ5IC0wLjYxMDE3LC0wLjEwOTUzMyBsIDAsMCB6IgogICBpZD0icGF0aDM3NzAiCiAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+PHBhdGgKICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtzdHJva2U6I2ZmZmZmZjtzdHJva2Utd2lkdGg6MC4yNzExODY0NCIKICAgZD0ibSAzMC40NDA0MzMsNTcuOTQ1MzE2IGMgLTMuMDY0MDUzLC0wLjQyMTEzIC02Ljc3NzIzLC0yLjQ3NDk1MyAtOC40NTc2MjEsLTQuNjc4MDYgLTIuMTU4OCwtMi44MzAzMzMgLTMuMTEzOTE4LC02LjczNzcxNiAtMi43Njk1OTIsLTExLjMzMDM4NSBsIDAuMTkzMTIxLC0yLjU3NTg3MiAyLjA3OTg4LDAuNzM1NjI4IGMgMy41MjcwMjYsMS4yNDc0NjYgNi4zMzc3MDEsMS43MTkwNjggMTAuMjE1NDc0LDEuNzE0MDUgNC4xNzE3NzEsLTAuMDA1NCA2LjY0Mjk4MSwtMC4zODg2OCAxMC4yNTgzNiwtMS41OTEwNTggMi4xNzg5NzMsLTAuNzI0NjY4IDIuNjkyMTkzLC0wLjgxNTg1NiAyLjgxMzQ0NCwtMC40OTk4ODMgMC4wODE1NywwLjIxMjU2IDAuMDg2NjQsMi4xMTk4NjQgMC4wMTEyNyw0LjIzODQ1MyAtMC4xMDYyMTcsMi45ODU4MSAtMC4yNjY1MzEsNC4yMTI5MzggLTAuNzEyOTM1LDUuNDU3MjA4IC0xLjQ1MDIxNyw0LjA0MjIwNiAtNC40MDExMTgsNi44NDQxNTYgLTguNDM3OTM4LDguMDEyMDE3IC0yLjMxNDc0NywwLjY2OTY2MSAtMy4zNDMxNCwwLjc3MjIxNSAtNS4xOTM0NjUsMC41MTc5MDIgeiIKICAgaWQ9InBhdGgzNzcyIgogICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPjwvZz4KPHBhdGgKICAgc3R5bGU9ImZpbGw6IzY2Q0NGRjtzdHJva2U6IzY2Q0NGRjtzdHJva2Utd2lkdGg6MC4yNzExODY0Mzk5OTk5OTk5NyIKICAgZD0ibSAyOC43NDA1NTYsNDAuMDYzNjgyIGMgLTIuNDU3Nzk1LC0wLjI4NDk5NSAtNS4yOTE1MjIsLTAuOTc2ODgxIC03LjQ3OTUzOSwtMS44MjYyMTIgLTAuOTY5NDkyLC0wLjM3NjMzMiAtMS43OTI5MzksLTAuNzA0OTYxIC0xLjgyOTg4NCwtMC43MzAyODcgLTAuMTUyNTY0LC0wLjEwNDU4NCAwLjc2NjAxNiwtNi4yNDI1NDEgMS4xNjgwMSwtNy44MDQ2NTYgMS40MjExOTEsLTUuNTIyNjMxIDQuMzA0NDc3LC04LjA2MTg0OSA5LjE1NDI0MSwtOC4wNjE4NDkgbCAxLjU0MTUzMSwwIDAsMS42MjcxMTkgYyAwLDEuMjM3MTI1IC0wLjA5MzgxLDEuNjI3MTE4IC0wLjM5MTQwMiwxLjYyNzExOCAtMC4yMTUyNzEsMCAtMC41ODEzNzMsMC4yNzEyMjEgLTAuODEzNTU5LDAuNjAyNzE0IC0wLjM0NTg4OSwwLjQ5MzgyNiAtMC40MjIxNTcsMS41Mjc1MjQgLTAuNDIyMTU3LDUuNzIxNzA2IGwgMCw1LjExODk5MiAwLjgxMzU1OSwwLjc1OTk3OCBjIDAuNjc1ODQ2LDAuNjMxMzM1IDAuODEzNTU5LDAuOTcxMDgzIDAuODEzNTU5LDIuMDA3MTA4IDAsMC44MzU5MjEgLTAuMTExNzcxLDEuMjM1Mzc4IC0wLjMzODk4MywxLjIxMTQ4OSAtMC4xODY0NCwtMC4wMTk2IC0xLjE4MzM2LC0wLjEzMzU1MSAtMi4yMTUzNzYsLTAuMjUzMjIgbCAwLDAgeiIKICAgaWQ9InBhdGgzNzY2IgogICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPjwvc3ZnPg==\">\r\n </div>\r\n <div class=\"row-word\">\u65CB\u8F6C</div>\r\n </div>\r\n <div class=\"tip-row\">\r\n <div class=\"row-img\">\r\n <img\r\n src=\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNS4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4KCjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgdmVyc2lvbj0iMS4xIgogICBpZD0iTGF5ZXJfMSIKICAgeD0iMHB4IgogICB5PSIwcHgiCiAgIHdpZHRoPSI2NHB4IgogICBoZWlnaHQ9IjY0cHgiCiAgIHZpZXdCb3g9IjAgMCA2NCA2NCIKICAgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgNjQgNjQiCiAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgIGlua3NjYXBlOnZlcnNpb249IjAuNDguNCByOTkzOSIKICAgc29kaXBvZGk6ZG9jbmFtZT0iTW91c2VSaWdodC5zdmciPjxtZXRhZGF0YQogICBpZD0ibWV0YWRhdGEyMSI+PHJkZjpSREY+PGNjOldvcmsKICAgICAgIHJkZjphYm91dD0iIj48ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD48ZGM6dHlwZQogICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPjxkYzp0aXRsZSAvPjwvY2M6V29yaz48L3JkZjpSREY+PC9tZXRhZGF0YT48ZGVmcwogICBpZD0iZGVmczE5IiAvPjxzb2RpcG9kaTpuYW1lZHZpZXcKICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgYm9yZGVyb3BhY2l0eT0iMSIKICAgb2JqZWN0dG9sZXJhbmNlPSIxMCIKICAgZ3JpZHRvbGVyYW5jZT0iMTAiCiAgIGd1aWRldG9sZXJhbmNlPSIxMCIKICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAiCiAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTI4MiIKICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODg2IgogICBpZD0ibmFtZWR2aWV3MTciCiAgIHNob3dncmlkPSJmYWxzZSIKICAgaW5rc2NhcGU6em9vbT0iMy42ODc1IgogICBpbmtzY2FwZTpjeD0iMzIiCiAgIGlua3NjYXBlOmN5PSIzMiIKICAgaW5rc2NhcGU6d2luZG93LXg9IjMxIgogICBpbmtzY2FwZTp3aW5kb3cteT0iMTUiCiAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjAiCiAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9ImczIiAvPgo8ZwogICBpZD0iZzMiPgoJPGcKICAgaWQ9Imc1IgogICBzdHlsZT0ic3Ryb2tlOiM0ZDRkNGQ7ZmlsbDojNGQ0ZDRkIj4KCQk8cGF0aAogICBmaWxsPSIjMDIwMjAyIgogICBkPSJNMzIuODU0LDIwLjAyOGMtMC4wMTUtMS4yNzQsMC41NzYtMi4yNjksMS4xNTEtMy4yMzZjMC43NTgtMS4yNzgsMS41NDMtMi42LDAuOTk3LTQuNDE3ICAgIGMtMC41NTUtMS42NjctMS42MDctMi40ODUtMi41MzktMy4yMTJjLTEuMjU0LTAuOTczLTIuMTU5LTEuNjc2LTEuNjg3LTQuMzQzbDAuMDQxLTAuMjI5bC0xLjM1LTAuMjM4TDI5LjQyNyw0LjU4ICAgIGMtMC42MTUsMy40ODQsMC44NzgsNC42NDMsMi4xOTUsNS42NjZjMC44NSwwLjY2MywxLjY1MywxLjI4OCwyLjA3MiwyLjU0MmMwLjM2NiwxLjIyNC0wLjE4LDIuMTQzLTAuODY5LDMuMzA2ICAgIGMtMC42NDEsMS4wODEtMS4zNjYsMi4zLTEuMzQ5LDMuOTMzYy02LjgzNywwLjAwNy0xMy44MjMsMC4yOTEtMTMuODIzLDI0LjU5YzAsMTAuOTUxLDguNzk5LDE1LjAzMiwxNC4zNDcsMTUuMDMyICAgIGM1LjU0NSwwLDE0LjM0Ny00LjA4MSwxNC4zNDctMTUuMDMyQzQ2LjM0NywyMC4zNDcsMzkuMjI4LDIwLjA0OCwzMi44NTQsMjAuMDI4eiBNMzIuNjg1LDIxLjM5OSAgICBjNS42NDMsMC4wMTIsMTAuOTI3LDAuMjM2LDEyLjA2MywxNi4xNTNjLTMuODYsMS44MDUtNy45MTYsMi43NjUtMTIuMDYzLDIuODZ2LTIuOTY2YzAuODQ1LTAuMTYyLDEuNDgxLTAuOTA5LDEuNDgxLTEuNzgydi04Ljk1NiAgICBjMC0wLjg3Ni0wLjYzNy0xLjYyMS0xLjQ4MS0xLjc4NFYyMS4zOTl6IE0zMS42NTgsMjYuMjU0aDAuNjg1YzAuMjQ5LDAsMC40NTMsMC4yMDUsMC40NTMsMC40NTR2OC45NTYgICAgYzAsMC4yNDktMC4yMDQsMC40NDktMC40NTMsMC40NDloLTAuNjg1Yy0wLjI0OSwwLTAuNDUyLTAuMi0wLjQ1Mi0wLjQ0OXYtOC45NTZDMzEuMjA2LDI2LjQ1OSwzMS40MDksMjYuMjU0LDMxLjY1OCwyNi4yNTR6ICAgICBNMzEuMzEzLDIxLjM5OXYzLjUyNWMtMC44NDUsMC4xNjMtMS40NzksMC45MS0xLjQ3OSwxLjc4NHY4Ljk1NmMwLDAuODczLDAuNjM0LDEuNjIsMS40NzksMS43ODJ2Mi45NjYgICAgYy02LjMxMS0wLjE0OC0xMC45MDUtMi4yNTItMTIuMDYzLTIuODM5QzIwLjM4MSwyMS42MzMsMjUuNjY4LDIxLjQxMSwzMS4zMTMsMjEuMzk5eiBNMzIsNTguMjc0ICAgIGMtNS4wMTYsMC0xMi45NzYtMy43MDctMTIuOTc2LTEzLjY1OGMwLTEuOTgzLDAuMDQ1LTMuODUzLDAuMTM0LTUuNTYzYzIuMDAzLDAuOTI5LDYuNjk2LDIuNzQsMTIuODY4LDIuNzQgICAgYzQuMzk0LDAsOC43MDQtMC45MzQsMTIuODEzLTIuNzcxYzAuMDg5LDEuNzE4LDAuMTM1LDMuNiwwLjEzNSw1LjU5NUM0NC45NzQsNTQuNTY3LDM3LjAxNiw1OC4yNzQsMzIsNTguMjc0eiIKICAgaWQ9InBhdGg3IgogICBzdHlsZT0ic3Ryb2tlOiM0ZDRkNGQ7ZmlsbDojNGQ0ZDRkIiAvPgoJPC9nPgoJCgkKPHBhdGgKICAgc3R5bGU9ImZpbGw6IzY1RkQwMDtzdHJva2U6IzY1RkQwMDtzdHJva2Utd2lkdGg6MC4yNzExODY0Mzk5OTk5OTk5NyIKICAgZD0ibSAzMi45MjIwMzQsMzguOTUwNzkzIGMgMCwtMS4xMzU4MjUgMC4xMTQ5MTksLTEuNDU4MDQ1IDAuNjc3OTY2LC0xLjkwMDkzOCBsIDAuNjc3OTY2LC0wLjUzMzI4OCAwLC01LjI4MDc4IGMgMCwtNS4yNzEyNDkgLTAuMDAxMiwtNS4yODIwMDMgLTAuNjc3OTY2LC01Ljk1ODc0NSAtMC41NTc0MzksLTAuNTU3NDM5IC0wLjY3Nzk2NiwtMC45NDA5MzUgLTAuNjc3OTY2LC0yLjE1NzE2NSBsIDAsLTEuNDc5MTk5IDEuNTM1NDg3LDAgYyA1LjY1NzU1NCwwIDguNTg0MzM4LDMuNzI4NTI5IDkuNzE4NTUsMTIuMzgwNzkxIDAuNDk4NDM0LDMuODAyMjY1IDAuNTAzNDgsMy41MjU3MzEgLTAuMDY3NiwzLjcwNDU3MiAtMC4yNjEwMTcsMC4wODE3NCAtMS41Mjg2OTQsMC41MTIwMTYgLTIuODE3MDU