UNPKG

molstar

Version:

A comprehensive macromolecular library.

139 lines (138 loc) 6.92 kB
/** * Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ import { createRenderObject, getNextMaterialId } from '../../mol-gl/render-object'; import { MeshBuilder } from '../../mol-geo/geometry/mesh/mesh-builder'; import { addSphere } from '../../mol-geo/geometry/mesh/builder/sphere'; import { Mesh } from '../../mol-geo/geometry/mesh/mesh'; import { ParamDefinition as PD } from '../../mol-util/param-definition'; import { Scene } from '../../mol-gl/scene'; import { Sphere3D } from '../../mol-math/geometry'; import { ColorNames } from '../../mol-util/color/names'; import { sphereVertexCount } from '../../mol-geo/primitive/sphere'; import { ValueCell } from '../../mol-util'; import { Geometry } from '../../mol-geo/geometry/geometry'; export const DebugHelperParams = { sceneBoundingSpheres: PD.Boolean(false, { description: 'Show full scene bounding spheres.' }), visibleSceneBoundingSpheres: PD.Boolean(false, { description: 'Show visible scene bounding spheres.' }), objectBoundingSpheres: PD.Boolean(false, { description: 'Show bounding spheres of visible render objects.' }), instanceBoundingSpheres: PD.Boolean(false, { description: 'Show bounding spheres of visible instances.' }), }; export class BoundingSphereHelper { constructor(ctx, parent, props) { this.objectsData = new Map(); this.instancesData = new Map(); this.scene = Scene.create(ctx, 'blended'); this.parent = parent; this._props = { ...PD.getDefaultValues(DebugHelperParams), ...props }; } update() { const newSceneData = updateBoundingSphereData(this.scene, this.parent.boundingSphere, this.sceneData, ColorNames.lightgrey, sceneMaterialId); if (newSceneData) this.sceneData = newSceneData; const newVisibleSceneData = updateBoundingSphereData(this.scene, this.parent.boundingSphereVisible, this.visibleSceneData, ColorNames.black, visibleSceneMaterialId); if (newVisibleSceneData) this.visibleSceneData = newVisibleSceneData; this.parent.forEach((r, ro) => { const objectData = this.objectsData.get(ro); const newObjectData = updateBoundingSphereData(this.scene, r.values.boundingSphere.ref.value, objectData, ColorNames.tomato, objectMaterialId); if (newObjectData) this.objectsData.set(ro, newObjectData); const instanceData = this.instancesData.get(ro); const newInstanceData = updateBoundingSphereData(this.scene, r.values.invariantBoundingSphere.ref.value, instanceData, ColorNames.skyblue, instanceMaterialId, { aTransform: ro.values.aTransform, matrix: ro.values.matrix, transform: ro.values.transform, extraTransform: ro.values.extraTransform, uInstanceCount: ro.values.uInstanceCount, instanceCount: ro.values.instanceCount, aInstance: ro.values.aInstance, hasReflection: ro.values.hasReflection, instanceGrid: ro.values.instanceGrid, }); if (newInstanceData) this.instancesData.set(ro, newInstanceData); }); this.objectsData.forEach((objectData, ro) => { if (!this.parent.has(ro)) { this.scene.remove(objectData.renderObject); this.objectsData.delete(ro); } }); this.instancesData.forEach((instanceData, ro) => { if (!this.parent.has(ro)) { this.scene.remove(instanceData.renderObject); this.instancesData.delete(ro); } }); this.scene.update(void 0, false); this.scene.commit(); } syncVisibility() { if (this.sceneData) { this.sceneData.renderObject.state.visible = this._props.sceneBoundingSpheres; } if (this.visibleSceneData) { this.visibleSceneData.renderObject.state.visible = this._props.visibleSceneBoundingSpheres; } this.parent.forEach((_, ro) => { const objectData = this.objectsData.get(ro); if (objectData) objectData.renderObject.state.visible = ro.state.visible && this._props.objectBoundingSpheres; const instanceData = this.instancesData.get(ro); if (instanceData) instanceData.renderObject.state.visible = ro.state.visible && this._props.instanceBoundingSpheres; }); } clear() { this.sceneData = undefined; this.objectsData.clear(); this.scene.clear(); } get isEnabled() { return (this._props.sceneBoundingSpheres || this._props.visibleSceneBoundingSpheres || this._props.objectBoundingSpheres || this._props.instanceBoundingSpheres); } get props() { return this._props; } setProps(props) { Object.assign(this._props, props); if (this.isEnabled) this.update(); } } function updateBoundingSphereData(scene, boundingSphere, data, color, materialId, transform) { if (!data || !Sphere3D.equals(data.boundingSphere, boundingSphere)) { const mesh = createBoundingSphereMesh(boundingSphere, data && data.mesh); const renderObject = data ? data.renderObject : createBoundingSphereRenderObject(mesh, color, materialId, transform); if (data) { ValueCell.updateIfChanged(renderObject.values.drawCount, Geometry.getDrawCount(mesh)); } else { scene.add(renderObject); } return { boundingSphere: Sphere3D.clone(boundingSphere), renderObject, mesh }; } } function createBoundingSphereMesh(boundingSphere, mesh) { const detail = 2; const vertexCount = sphereVertexCount(detail); const builderState = MeshBuilder.createState(vertexCount, vertexCount / 2, mesh); if (boundingSphere.radius) { addSphere(builderState, boundingSphere.center, boundingSphere.radius, detail); if (Sphere3D.hasExtrema(boundingSphere)) { for (const e of boundingSphere.extrema) addSphere(builderState, e, 1.0, 0); } } return MeshBuilder.getMesh(builderState); } const sceneMaterialId = getNextMaterialId(); const visibleSceneMaterialId = getNextMaterialId(); const objectMaterialId = getNextMaterialId(); const instanceMaterialId = getNextMaterialId(); function createBoundingSphereRenderObject(mesh, color, materialId, transform) { const values = Mesh.Utils.createValuesSimple(mesh, { alpha: 0.1, doubleSided: false, cellSize: 0, batchSize: 0 }, color, 1, transform); return createRenderObject('mesh', values, { disposed: false, visible: true, alphaFactor: 1, pickable: false, colorOnly: false, opaque: false, writeDepth: false }, materialId); }