Browse Source

cleanup, sync/async visual create/update

Alexander Rose 6 years ago
parent
commit
c9ae4a1017
3 changed files with 153 additions and 172 deletions
  1. 66 74
      src/mol-repr/structure/complex-visual.ts
  2. 83 97
      src/mol-repr/structure/units-visual.ts
  3. 4 1
      src/mol-repr/util.ts

+ 66 - 74
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) {

+ 83 - 97
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)

+ 4 - 1
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
     }
 }