UNPKG

molstar

Version:

A comprehensive macromolecular library.

301 lines (300 loc) 13.8 kB
"use strict"; /** * Copyright (c) 2018-2024 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ Object.defineProperty(exports, "__esModule", { value: true }); exports.ShapeRepresentation = ShapeRepresentation; const geometry_1 = require("../../mol-geo/geometry/geometry"); const representation_1 = require("../representation"); const shape_1 = require("../../mol-model/shape"); const rxjs_1 = require("rxjs"); const render_object_1 = require("../../mol-gl/render-object"); const theme_1 = require("../../mol-theme/theme"); const util_1 = require("../util"); const marker_data_1 = require("../../mol-geo/geometry/marker-data"); const marker_action_1 = require("../../mol-util/marker-action"); const mol_util_1 = require("../../mol-util"); const color_data_1 = require("../../mol-geo/geometry/color-data"); const size_data_1 = require("../../mol-geo/geometry/size-data"); const loci_1 = require("../../mol-model/loci"); const int_1 = require("../../mol-data/int"); const visual_1 = require("../visual"); const mol_task_1 = require("../../mol-task"); const param_definition_1 = require("../../mol-util/param-definition"); const debug_1 = require("../../mol-util/debug"); function ShapeRepresentation(getShape, geometryUtils, builder = {}) { let version = 0; const updated = new rxjs_1.Subject(); const _state = representation_1.Representation.createState(); const materialId = (0, render_object_1.getNextMaterialId)(); const renderObjects = []; let _renderObject; let _shape; let geometryVersion = -1; const _theme = theme_1.Theme.createEmpty(); const currentParams = geometryUtils.Params; // TODO avoid casting let currentProps = param_definition_1.ParamDefinition.getDefaultValues(currentParams); let locationIt; let positionIt; if (builder.modifyState) representation_1.Representation.updateState(_state, builder.modifyState(_state)); const updateState = util_1.VisualUpdateState.create(); function prepareUpdate(props = {}, shape) { util_1.VisualUpdateState.reset(updateState); if (!shape && !_shape) { // console.error('no shape given') return; } else if (shape && !_shape) { // console.log('first shape') updateState.createNew = true; } else if (shape && _shape && shape.id === _shape.id) { // console.log('same shape') // nothing to set } else if (shape && _shape && shape.id !== _shape.id) { // console.log('new shape') updateState.updateTransform = true; updateState.createGeometry = true; } else if (!shape) { // console.log('only props') // nothing to set } else { console.warn('unexpected state'); } if (props.instanceGranularity !== currentProps.instanceGranularity) { updateState.updateTransform = true; } if (updateState.updateTransform) { updateState.updateColor = true; updateState.updateSize = true; updateState.updateMatrix = true; } if (updateState.createGeometry) { updateState.updateColor = true; updateState.updateSize = true; } } function createOrUpdate(props = {}, data) { if (builder.modifyProps) props = builder.modifyProps(props); return mol_task_1.Task.create('ShapeRepresentation.create', async (runtime) => { const newProps = Object.assign(currentProps, props); const shape = data ? await getShape(runtime, data, newProps, _shape) : undefined; prepareUpdate(props, shape); if (shape) { _shape = shape; Object.assign(_theme, shape_1.Shape.getTheme(_shape)); } if (updateState.createNew) { renderObjects.length = 0; // clear list o renderObjects locationIt = shape_1.Shape.groupIterator(_shape); const transform = shape_1.Shape.createTransform(_shape.transforms, _shape.geometry.boundingSphere, newProps.cellSize, newProps.batchSize); const values = geometryUtils.createValues(_shape.geometry, transform, locationIt, _theme, newProps); const state = geometryUtils.createRenderableState(newProps); if (builder.modifyState) Object.assign(state, builder.modifyState(state)); representation_1.Representation.updateState(_state, state); _renderObject = (0, render_object_1.createRenderObject)(_shape.geometry.kind, values, state, materialId); if (_renderObject) renderObjects.push(_renderObject); // add new renderObject to list positionIt = geometryUtils.createPositionIterator(_shape.geometry, _renderObject.values); } else { if (!_renderObject) { throw new Error('expected renderObject to be available'); } if (updateState.updateTransform) { // console.log('update transform') locationIt = shape_1.Shape.groupIterator(_shape); const { instanceCount, groupCount } = locationIt; if (props.instanceGranularity) { (0, marker_data_1.createMarkers)(instanceCount, 'instance', _renderObject.values); } else { (0, marker_data_1.createMarkers)(instanceCount * groupCount, 'groupInstance', _renderObject.values); } } if (updateState.updateMatrix) { // console.log('update matrix'); shape_1.Shape.createTransform(_shape.transforms, _shape.geometry.boundingSphere, newProps.cellSize, newProps.batchSize, _renderObject.values); if ('lodLevels' in _renderObject.values) { // to trigger `uLod` update in `renderable.cull` mol_util_1.ValueCell.update(_renderObject.values.lodLevels, _renderObject.values.lodLevels.ref.value); } } if (updateState.createGeometry) { // console.log('update geometry') mol_util_1.ValueCell.updateIfChanged(_renderObject.values.drawCount, geometry_1.Geometry.getDrawCount(_shape.geometry)); mol_util_1.ValueCell.updateIfChanged(_renderObject.values.uVertexCount, geometry_1.Geometry.getVertexCount(_shape.geometry)); mol_util_1.ValueCell.updateIfChanged(_renderObject.values.uGroupCount, geometry_1.Geometry.getGroupCount(_shape.geometry)); } if (updateState.updateTransform || updateState.createGeometry) { // console.log('updateBoundingSphere') geometryUtils.updateBoundingSphere(_renderObject.values, _shape.geometry); positionIt = geometryUtils.createPositionIterator(_shape.geometry, _renderObject.values); } if (updateState.updateColor) { // console.log('update color') (0, color_data_1.createColors)(locationIt, positionIt, _theme.color, _renderObject.values); } if (updateState.updateSize) { // not all geometries have size data, so check here if ('uSize' in _renderObject.values) { // console.log('update size') (0, size_data_1.createSizes)(locationIt, _theme.size, _renderObject.values); } } geometryUtils.updateValues(_renderObject.values, newProps); geometryUtils.updateRenderableState(_renderObject.state, newProps); } currentProps = newProps; if (updateState.createGeometry || updateState.createNew) { geometryVersion += 1; } // increment version updated.next(version++); }); } function eachInstance(loci, shape, apply) { let changed = false; if (!shape_1.ShapeGroup.isLoci(loci)) return false; if (shape_1.ShapeGroup.isLociEmpty(loci)) return false; if (loci.shape !== shape) return false; for (const g of loci.groups) { if (apply(int_1.Interval.ofSingleton(g.instance))) changed = true; } return changed; } function lociApply(loci, apply) { if ((0, loci_1.isEveryLoci)(loci) || (shape_1.Shape.isLoci(loci) && loci.shape === _shape)) { if (currentProps.instanceGranularity) { return apply(int_1.Interval.ofBounds(0, _shape.transforms.length)); } else { return apply(int_1.Interval.ofBounds(0, _shape.groupCount * _shape.transforms.length)); } } else { if (currentProps.instanceGranularity) { return eachInstance(loci, _shape, apply); } else { return eachShapeGroup(loci, _shape, apply); } } } return { label: 'Shape geometry', get groupCount() { return locationIt ? locationIt.count : 0; }, get props() { return currentProps; }, get params() { return currentParams; }, get state() { return _state; }, get theme() { return _theme; }, renderObjects, get geometryVersion() { return geometryVersion; }, updated, createOrUpdate, getLoci(pickingId) { const { objectId, groupId, instanceId } = pickingId; if (_renderObject && _renderObject.id === objectId) { return shape_1.ShapeGroup.Loci(_shape, [{ ids: int_1.OrderedSet.ofSingleton(groupId), instance: instanceId }]); } return loci_1.EmptyLoci; }, getAllLoci() { return [shape_1.Shape.Loci(_shape)]; }, eachLocation: (cb) => { locationIt.reset(); while (locationIt.hasNext) { const { location, isSecondary } = locationIt.move(); cb(location, isSecondary); } }, mark(loci, action) { if (!marker_action_1.MarkerActions.is(_state.markerActions, action)) return false; if (shape_1.ShapeGroup.isLoci(loci) || shape_1.Shape.isLoci(loci)) { if (loci.shape !== _shape) return false; } else if (!(0, loci_1.isEveryLoci)(loci)) { return false; } return visual_1.Visual.mark(_renderObject, loci, action, lociApply); }, setState(state) { if (builder.modifyState) state = builder.modifyState(state); if (_renderObject) { if (state.visible !== undefined) visual_1.Visual.setVisibility(_renderObject, state.visible); if (state.alphaFactor !== undefined) visual_1.Visual.setAlphaFactor(_renderObject, state.alphaFactor); if (state.pickable !== undefined) visual_1.Visual.setPickable(_renderObject, state.pickable); if (state.colorOnly !== undefined) visual_1.Visual.setColorOnly(_renderObject, state.colorOnly); if (state.overpaint !== undefined) { visual_1.Visual.setOverpaint(_renderObject, state.overpaint, lociApply, true); } if (state.transparency !== undefined) { visual_1.Visual.setTransparency(_renderObject, state.transparency, lociApply, true); } if (state.substance !== undefined) { visual_1.Visual.setSubstance(_renderObject, state.substance, lociApply, true); } if (state.transform !== undefined) visual_1.Visual.setTransform(_renderObject, state.transform); } representation_1.Representation.updateState(_state, state); }, setTheme(theme) { if (debug_1.isDebugMode) { console.warn('The `ShapeRepresentation` theme is fixed to `ShapeGroupColorTheme` and `ShapeGroupSizeTheme`. Colors are taken from `Shape.getColor` and sizes from `Shape.getSize`'); } }, destroy() { renderObjects.length = 0; if (_renderObject) { _renderObject.state.disposed = true; _renderObject = undefined; } } }; } function eachShapeGroup(loci, shape, apply) { if (!shape_1.ShapeGroup.isLoci(loci)) return false; if (loci.shape !== shape) return false; let changed = false; const { groupCount } = shape; const { groups } = loci; for (const { ids, instance } of groups) { if (int_1.Interval.is(ids)) { const start = instance * groupCount + int_1.Interval.start(ids); const end = instance * groupCount + int_1.Interval.end(ids); if (apply(int_1.Interval.ofBounds(start, end))) changed = true; } else { for (let i = 0, _i = ids.length; i < _i; i++) { const idx = instance * groupCount + ids[i]; if (apply(int_1.Interval.ofSingleton(idx))) changed = true; } } } return changed; }