diff --git a/src/mol-repr/structure/complex-visual.ts b/src/mol-repr/structure/complex-visual.ts index e55ca11050db20e03c63e04a28ee80d004c623a9..756a98be0f3287bc28a2f917901a65fa2732d165 100644 --- a/src/mol-repr/structure/complex-visual.ts +++ b/src/mol-repr/structure/complex-visual.ts @@ -22,7 +22,7 @@ import { createColors } from 'mol-geo/geometry/color-data'; import { MarkerAction, applyMarkerAction } from 'mol-geo/geometry/marker-data'; import { Mesh } from 'mol-geo/geometry/mesh/mesh'; import { VisualUpdateState } from 'mol-repr/util'; -import { Theme } from 'mol-theme/theme'; +import { Theme, createEmptyTheme } from 'mol-theme/theme'; import { ColorTheme } from 'mol-theme/color'; import { SizeTheme } from 'mol-theme/size'; @@ -58,117 +58,109 @@ export function ComplexVisual<P extends ComplexParams>(builder: ComplexVisualGeo const updateState = VisualUpdateState.create() let renderObject: ComplexRenderObject | undefined + let newProps: PD.Values<P> let newTheme: Theme - let currentProps: PD.Values<P> - let currentTheme: Theme - let geometry: Geometry - let currentStructure: Structure - let locationIt: LocationIterator - let conformationHash: number + let newStructure: Structure - function create(newGeometry: Geometry, structure: Structure, theme: Theme, props: Partial<PD.Values<P>> = {}) { - currentProps = Object.assign({}, defaultProps, props) - currentTheme = theme - currentStructure = structure + let currentProps: PD.Values<P> = Object.assign({}, defaultProps) + let currentTheme: Theme = createEmptyTheme() + let currentStructure: Structure - conformationHash = Structure.conformationHash(currentStructure) - // geometry = createGeometry(ctx, currentStructure, theme, currentProps, geometry) + let geometry: Geometry + let locationIt: LocationIterator - locationIt = createLocationIterator(structure) - renderObject = createRenderObject(structure, newGeometry, locationIt, theme, currentProps) - } + function prepareUpdate(theme: Theme, props: Partial<PD.Values<P>>, structure: Structure) { + if (!structure && !currentStructure) { + throw new Error('missing structure') + } - function getUpdateState(theme: Theme, props: Partial<PD.Values<P>>) { - if (!renderObject) return - - newProps = Object.assign({}, currentProps, props, { structure: currentStructure }) + newProps = Object.assign({}, currentProps, props) newTheme = theme + newStructure = structure VisualUpdateState.reset(updateState) - setUpdateState(updateState, newProps, currentProps, theme, currentTheme) - if (!ColorTheme.areEqual(theme.color, currentTheme.color)) updateState.updateColor = true - if (!deepEqual(newProps.unitKinds, currentProps.unitKinds)) updateState.createGeometry = true + if (!renderObject) { + updateState.createNew = true + } else if (!currentStructure || !Structure.areEquivalent(newStructure, currentStructure)) { + updateState.createNew = true + } + + if (updateState.createNew) { + updateState.createGeometry = true + return + } + + setUpdateState(updateState, newProps, currentProps, newTheme, currentTheme) - const newConformationHash = Structure.conformationHash(currentStructure) - if (newConformationHash !== conformationHash) { - conformationHash = newConformationHash + if (Structure.conformationHash(newStructure) !== Structure.conformationHash(currentStructure)) { updateState.createGeometry = true } + if (!ColorTheme.areEqual(theme.color, currentTheme.color)) updateState.updateColor = true + if (!deepEqual(newProps.unitKinds, currentProps.unitKinds)) updateState.createGeometry = true + if (updateState.createGeometry) { updateState.updateColor = true } } function update(newGeometry?: Geometry) { - if (!renderObject) return - - locationIt.reset() - - if (updateState.createGeometry) { + if (updateState.createNew) { + locationIt = createLocationIterator(newStructure) if (newGeometry) { - ValueCell.update(renderObject.values.drawCount, Geometry.getDrawCount(newGeometry)) - updateBoundingSphere(renderObject.values, newGeometry) + renderObject = createRenderObject(newStructure, newGeometry, locationIt, newTheme, newProps) } else { throw new Error('expected geometry to be given') } - } + } else { + if (!renderObject) { + throw new Error('expected renderObject to be available') + } - if (updateState.updateSize) { - // not all geometries have size data, so check here - if ('uSize' in renderObject.values) { - createSizes(locationIt, newTheme.size, renderObject.values) + locationIt.reset() + + if (updateState.createGeometry) { + if (newGeometry) { + ValueCell.update(renderObject.values.drawCount, Geometry.getDrawCount(newGeometry)) + updateBoundingSphere(renderObject.values, newGeometry) + } else { + throw new Error('expected geometry to be given') + } } - } - if (updateState.updateColor) { - createColors(locationIt, newTheme.color, renderObject.values) - } + if (updateState.updateSize) { + // not all geometries have size data, so check here + if ('uSize' in renderObject.values) { + createSizes(locationIt, newTheme.size, renderObject.values) + } + } - updateValues(renderObject.values, newProps) - updateRenderableState(renderObject.state, newProps) + if (updateState.updateColor) { + createColors(locationIt, newTheme.color, renderObject.values) + } + + updateValues(renderObject.values, newProps) + updateRenderableState(renderObject.state, newProps) + } currentProps = newProps currentTheme = newTheme + currentStructure = newStructure + if (newGeometry) geometry = newGeometry } return { get groupCount() { return locationIt ? locationIt.count : 0 }, get renderObject () { return renderObject }, createOrUpdate(ctx: VisualContext, theme: Theme, props: Partial<PD.Values<P>> = {}, structure?: Structure) { - if (!structure && !currentStructure) { - throw new Error('missing structure') - } else if (structure && (!currentStructure || !renderObject)) { - const newGeometry = createGeometry(ctx, structure, theme, Object.assign({}, defaultProps, props), geometry) - if (newGeometry instanceof Promise) { - return newGeometry.then(geo => create(geo, structure, theme, props)) - } else { - create(newGeometry, structure, theme, props) - } - } else if (structure && !Structure.areEquivalent(structure, currentStructure)) { - const newGeometry = createGeometry(ctx, structure, theme, Object.assign({}, defaultProps, props), geometry) - if (newGeometry instanceof Promise) { - return newGeometry.then(geo => create(geo, structure, theme, props)) - } else { - create(newGeometry, structure, theme, props) - } + prepareUpdate(theme, props, structure || currentStructure) + if (updateState.createGeometry) { + const newGeometry = createGeometry(ctx, newStructure, newTheme, newProps, geometry) + return newGeometry instanceof Promise ? newGeometry.then(update) : update(newGeometry) } else { - if (structure && Structure.conformationHash(structure) !== Structure.conformationHash(currentStructure)) { - currentStructure = structure - } - getUpdateState(theme, props) - if (updateState.createGeometry) { - const newGeometry = createGeometry(ctx, currentStructure, newTheme, newProps, geometry) - if (newGeometry instanceof Promise) { - return newGeometry.then(update) - } else { - update(newGeometry) - } - } else { - update() - } + update() } }, getLoci(pickingId: PickingId) { diff --git a/src/mol-repr/structure/units-visual.ts b/src/mol-repr/structure/units-visual.ts index 52f61324f745b518a5d2a794173460fcb8d8174d..6363acbe6d8b70de2cc301e0c445ce311d55295a 100644 --- a/src/mol-repr/structure/units-visual.ts +++ b/src/mol-repr/structure/units-visual.ts @@ -10,7 +10,7 @@ import { StructureMeshParams, StructurePointsParams, StructureLinesParams, Struc import { Loci, isEveryLoci, EmptyLoci } from 'mol-model/loci'; import { MeshRenderObject, PointsRenderObject, LinesRenderObject, DirectVolumeRenderObject } from 'mol-gl/render-object'; import { createUnitsMeshRenderObject, createUnitsPointsRenderObject, createUnitsTransform, createUnitsLinesRenderObject, createUnitsDirectVolumeRenderObject, includesUnitKind } from './visual/util/common'; -import { deepEqual, ValueCell, UUID } from 'mol-util'; +import { deepEqual, ValueCell } from 'mol-util'; import { Interval } from 'mol-data/int'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import { RenderableValues } from 'mol-gl/renderable/schema'; @@ -25,7 +25,7 @@ import { Points } from 'mol-geo/geometry/points/points'; import { Lines } from 'mol-geo/geometry/lines/lines'; import { DirectVolume } from 'mol-geo/geometry/direct-volume/direct-volume'; import { VisualUpdateState } from 'mol-repr/util'; -import { Theme } from 'mol-theme/theme'; +import { Theme, createEmptyTheme } from 'mol-theme/theme'; import { ColorTheme } from 'mol-theme/color'; import { SizeTheme } from 'mol-theme/size'; import { UnitsParams } from './units-representation'; @@ -58,35 +58,40 @@ export function UnitsVisual<P extends UnitsParams>(builder: UnitsVisualGeometryB const updateState = VisualUpdateState.create() let renderObject: UnitsRenderObject | undefined - let newProps: PD.Values<P> - let newTheme: Theme + + let newProps: PD.Values<P> = Object.assign({}, defaultProps) + let newTheme: Theme = createEmptyTheme() + let newStructureGroup: StructureGroup + let currentProps: PD.Values<P> let currentTheme: Theme + let currentStructureGroup: StructureGroup + let geometry: Geometry - let currentGroup: Unit.SymmetryGroup - let currentStructure: Structure let locationIt: LocationIterator - let currentConformationId: UUID - function create(newGeometry: Geometry, group: Unit.SymmetryGroup, theme: Theme, props: Partial<PD.Values<P>> = {}) { - currentProps = Object.assign({}, defaultProps, props, { structure: currentStructure }) - currentTheme = theme - currentGroup = group + function prepareUpdate(theme: Theme, props: Partial<PD.Values<P>> = {}, structureGroup: StructureGroup) { + if (!structureGroup && !currentStructureGroup) { + throw new Error('missing structureGroup') + } - currentConformationId = Unit.conformationId(group.units[0]) + newProps = Object.assign({}, currentProps, props) + newTheme = theme + newStructureGroup = structureGroup - // TODO create empty location iterator when not in unitKinds - locationIt = createLocationIterator(group) - renderObject = createRenderObject(group, newGeometry, locationIt, theme, currentProps) - } + VisualUpdateState.reset(updateState) - function getUpdateState(group: Unit.SymmetryGroup, theme: Theme, props: Partial<PD.Values<P>> = {}) { - if (!renderObject) return + if (!renderObject) { + updateState.createNew = true + } else if (!currentStructureGroup || newStructureGroup.group.hashCode !== currentStructureGroup.group.hashCode) { + updateState.createNew = true + } - newProps = Object.assign({}, currentProps, props, { structure: currentStructure }) - newTheme = theme + if (updateState.createNew) { + updateState.createGeometry = true + return + } - VisualUpdateState.reset(updateState) setUpdateState(updateState, newProps, currentProps, theme, currentTheme) if (!ColorTheme.areEqual(theme.color, currentTheme.color)) { @@ -98,9 +103,9 @@ export function UnitsVisual<P extends UnitsParams>(builder: UnitsVisualGeometryB updateState.createGeometry = true } - if (group.transformHash !== currentGroup.transformHash) { + if (newStructureGroup.group.transformHash !== currentStructureGroup.group.transformHash) { // console.log('new transformHash') - if (group.units.length !== currentGroup.units.length || updateState.updateColor) { + if (newStructureGroup.group.units.length !== currentStructureGroup.group.units.length || updateState.updateColor) { updateState.updateTransform = true } else { updateState.updateMatrix = true @@ -108,10 +113,8 @@ export function UnitsVisual<P extends UnitsParams>(builder: UnitsVisualGeometryB } // check if the conformation of unit.model has changed - const newConformationId = Unit.conformationId(group.units[0]) - if (newConformationId !== currentConformationId) { + if (Unit.conformationId(newStructureGroup.group.units[0]) !== Unit.conformationId(currentStructureGroup.group.units[0])) { // console.log('new conformation') - currentConformationId = newConformationId updateState.createGeometry = true } @@ -125,52 +128,64 @@ export function UnitsVisual<P extends UnitsParams>(builder: UnitsVisualGeometryB } } - function update(group: Unit.SymmetryGroup, newGeometry?: Geometry) { - if (!renderObject) return + function update(newGeometry?: Geometry) { + if (updateState.createNew) { + locationIt = createLocationIterator(newStructureGroup.group) + if (newGeometry) { + renderObject = createRenderObject(newStructureGroup.group, newGeometry, locationIt, newTheme, newProps) + } else { + throw new Error('expected geometry to be given') + } + } else { + if (!renderObject) { + throw new Error('expected renderObject to be available') + } - locationIt.reset() + locationIt.reset() - if (updateState.updateTransform) { - // console.log('update transform') - locationIt = createLocationIterator(group) - const { instanceCount, groupCount } = locationIt - createMarkers(instanceCount * groupCount, renderObject.values) - } + if (updateState.updateTransform) { + // console.log('update transform') + locationIt = createLocationIterator(newStructureGroup.group) + const { instanceCount, groupCount } = locationIt + createMarkers(instanceCount * groupCount, renderObject.values) + } - if (updateState.updateMatrix) { - // console.log('update matrix') - createUnitsTransform(group, renderObject.values) - } + if (updateState.updateMatrix) { + // console.log('update matrix') + createUnitsTransform(newStructureGroup.group, renderObject.values) + } - if (updateState.createGeometry) { - // console.log('update geometry') - if (newGeometry) { - ValueCell.update(renderObject.values.drawCount, Geometry.getDrawCount(newGeometry)) - updateBoundingSphere(renderObject.values, newGeometry) - } else { - throw new Error('expected geometry to be given') + if (updateState.createGeometry) { + // console.log('update geometry') + if (newGeometry) { + ValueCell.update(renderObject.values.drawCount, Geometry.getDrawCount(newGeometry)) + updateBoundingSphere(renderObject.values, newGeometry) + } else { + throw new Error('expected geometry to be given') + } + } + + if (updateState.updateSize) { + // not all geometries have size data, so check here + if ('uSize' in renderObject.values) { + // console.log('update size') + createSizes(locationIt, newTheme.size, renderObject.values) + } } - } - if (updateState.updateSize) { - // not all geometries have size data, so check here - if ('uSize' in renderObject.values) { - // console.log('update size') - createSizes(locationIt, newTheme.size, renderObject.values) + if (updateState.updateColor) { + // console.log('update color') + createColors(locationIt, newTheme.color, renderObject.values) } - } - if (updateState.updateColor) { - // console.log('update color') - createColors(locationIt, newTheme.color, renderObject.values) + updateValues(renderObject.values, newProps) + updateRenderableState(renderObject.state, newProps) } - updateValues(renderObject.values, newProps) - updateRenderableState(renderObject.state, newProps) - currentProps = newProps currentTheme = newTheme - currentGroup = group + currentStructureGroup = newStructureGroup + if (newGeometry) geometry = newGeometry } function _createGeometry(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.Values<P>, geometry?: Geometry) { @@ -183,45 +198,16 @@ export function UnitsVisual<P extends UnitsParams>(builder: UnitsVisualGeometryB get groupCount() { return locationIt ? locationIt.count : 0 }, get renderObject () { return renderObject }, createOrUpdate(ctx: VisualContext, theme: Theme, props: Partial<PD.Values<P>> = {}, structureGroup?: StructureGroup) { - if (structureGroup) currentStructure = structureGroup.structure - const group = structureGroup ? structureGroup.group : undefined - if (!group && !currentGroup) { - throw new Error('missing group') - } else if (group && (!currentGroup || !renderObject)) { - // console.log('unit-visual first create') - const newGeometry = _createGeometry(ctx, group.units[0], currentStructure, theme, Object.assign({}, defaultProps, props), geometry) - if (newGeometry instanceof Promise) { - return newGeometry.then(geo => create(geo, group, theme, props)) - } else { - create(newGeometry, group, theme, props) - } - } else if (group && group.hashCode !== currentGroup.hashCode) { - // console.log('unit-visual group.hashCode !== currentGroup.hashCode') - const newGeometry = _createGeometry(ctx, group.units[0], currentStructure, theme, Object.assign({}, defaultProps, props), geometry) - if (newGeometry instanceof Promise) { - return newGeometry.then(geo => create(geo, group, theme, props)) - } else { - create(newGeometry, group, theme, props) - } + prepareUpdate(theme, props, structureGroup || currentStructureGroup) + if (updateState.createGeometry) { + const newGeometry = _createGeometry(ctx, newStructureGroup.group.units[0], newStructureGroup.structure, newTheme, newProps, geometry) + return newGeometry instanceof Promise ? newGeometry.then(update) : update(newGeometry) } else { - // console.log('unit-visual update') - // update(ctx, group || currentGroup, theme, props) - - getUpdateState(group || currentGroup, theme, props) - if (updateState.createGeometry) { - const newGeometry = _createGeometry(ctx, (group || currentGroup).units[0], currentStructure, newTheme, newProps, geometry) - if (newGeometry instanceof Promise) { - return newGeometry.then(geo => update(group || currentGroup, geo)) - } else { - update(group || currentGroup, newGeometry) - } - } else { - update(group || currentGroup) - } + update() } }, getLoci(pickingId: PickingId) { - return renderObject ? getLoci(pickingId, { structure: currentStructure, group: currentGroup }, renderObject.id) : EmptyLoci + return renderObject ? getLoci(pickingId, currentStructureGroup, renderObject.id) : EmptyLoci }, mark(loci: Loci, action: MarkerAction) { if (!renderObject) return false @@ -235,10 +221,10 @@ export function UnitsVisual<P extends UnitsParams>(builder: UnitsVisualGeometryB } let changed = false - if (isEveryLoci(loci) || (Structure.isLoci(loci) && loci.structure === currentStructure)) { + if (isEveryLoci(loci) || (Structure.isLoci(loci) && loci.structure === currentStructureGroup.structure)) { changed = apply(Interval.ofBounds(0, groupCount * instanceCount)) } else { - changed = mark(loci, { structure: currentStructure, group: currentGroup }, apply) + changed = mark(loci, currentStructureGroup, apply) } if (changed) { ValueCell.update(tMarker, tMarker.ref.value) diff --git a/src/mol-repr/util.ts b/src/mol-repr/util.ts index 44a2efe762a90888044a90d647bfdf8bf80f1e1a..b5f27195f85950c7cd400b78cdd71efe5e374b00 100644 --- a/src/mol-repr/util.ts +++ b/src/mol-repr/util.ts @@ -14,6 +14,7 @@ export interface VisualUpdateState { updateColor: boolean updateSize: boolean createGeometry: boolean + createNew: boolean } export namespace VisualUpdateState { export function create(): VisualUpdateState { @@ -22,7 +23,8 @@ export namespace VisualUpdateState { updateMatrix: false, updateColor: false, updateSize: false, - createGeometry: false + createGeometry: false, + createNew: false, } } export function reset(state: VisualUpdateState) { @@ -31,6 +33,7 @@ export namespace VisualUpdateState { state.updateColor = false state.updateSize = false state.createGeometry = false + state.createNew = false } }