UNPKG

@giro3d/giro3d

Version:

A JS/WebGL framework for 3D geospatial data visualization

158 lines (127 loc) 5.22 kB
/* * Copyright (c) 2015-2018, IGN France. * Copyright (c) 2018-2026, Giro3D team. * SPDX-License-Identifier: MIT */ import type GUI from 'lil-gui'; import type { Controller } from 'lil-gui'; import type { Vector3 } from 'three'; import { Object3D } from 'three'; import type Instance from '../../core/Instance'; import type PointOfView from '../../core/PointOfView'; import { isBufferGeometry, isInterleavedBufferAttribute, isVector3 } from '../../utils/predicates'; import Panel from '../Panel'; class OutlinerPropertyView extends Panel { protected _folders: GUI[]; private _object: Object3D | null = null; public constructor(parentGui: GUI, instance: Instance) { super(parentGui, instance, 'Properties'); this._folders = []; this.gui.domElement.style.overflow = 'auto'; this.gui.domElement.style.maxHeight = '200px'; this.gui.open(true); this.populateProperties(new Object3D()); } public createControllers(obj: object, gui: GUI): void { if (obj == null) { return; } const notify = (): void => this.instance.notifyChange(); const entries = Object.entries(obj); entries.forEach(([name, value]) => { if (!name.startsWith('___outliner')) { switch (typeof value) { case 'string': case 'number': case 'bigint': case 'boolean': if (value != null) { this._controllers.push(gui.add(obj, name).onChange(notify)); } break; } } }); } /** * @param obj - The object to update. */ public updateObject(obj: Object3D): void { obj.updateMatrixWorld(true); obj.updateWorldMatrix(true, true); this.notify(); } public goToObject(): void { if (this._object != null) { const pov = this.instance.view.goTo(this._object); this.updateControlsWithDefaultView(pov); } } private updateControlsWithDefaultView(defaultView: PointOfView | null): void { const controls = this.instance.view.controls; if (defaultView && controls && 'target' in controls && isVector3(controls.target)) { controls.target.copy(defaultView.target); } } public populateProperties(obj: Object3D): void { while (this._controllers.length > 0) { this._controllers.pop()?.destroy(); } while (this._folders.length > 0) { this._folders.pop()?.destroy(); } this._object = obj; this.addController(this, 'goToObject'); this.createControllers(obj, this.gui); const position = this.gui.addFolder('Position'); position.close(); this._folders.push(position); const update = (): void => this.updateObject(obj); function bindVector<K extends string & keyof Vector3>( gui: GUI, v: Vector3, key: K, ): Controller { return gui.add(v, key).step(0.01); } this._controllers.push(bindVector(position, obj.position, 'x').onChange(update)); this._controllers.push(bindVector(position, obj.position, 'y').onChange(update)); this._controllers.push(bindVector(position, obj.position, 'z').onChange(update)); const scale = this.gui.addFolder('Scale'); scale.close(); this._folders.push(scale); this._controllers.push(bindVector(scale, obj.scale, 'x').onChange(update)); this._controllers.push(bindVector(scale, obj.scale, 'y').onChange(update)); this._controllers.push(bindVector(scale, obj.scale, 'z').onChange(update)); if ('material' in obj && obj.material != null) { const material = this.gui.addFolder('Material'); this._folders.push(material); material.close(); this.createControllers(obj.material, material); } if ('geometry' in obj && isBufferGeometry(obj.geometry)) { const geometry = this.gui.addFolder('Geometry'); this._folders.push(geometry); geometry.close(); this.createControllers(obj.geometry, geometry); if (obj.geometry.attributes != null) { const attrs = obj.geometry.attributes; const attributes = geometry.addFolder('Attributes'); Object.keys(attrs).forEach(p => { const attrValue = attrs[p]; if (p && attrValue != null) { const attr = attributes.addFolder(p); attr.close(); attr.add(attrValue, 'normalized'); attr.add(attrValue, 'count'); attr.add(attrValue, 'itemSize'); if (!isInterleavedBufferAttribute(attrValue)) { attr.add(attrValue, 'usage'); } } }); } } } } export default OutlinerPropertyView;