Browse Source

primitive repr update in canvas3d

Alexander Rose 6 years ago
parent
commit
829ab7c5b2

+ 1 - 4
src/apps/canvas/component/representation.tsx

@@ -15,7 +15,6 @@ export interface RepresentationComponentProps<P extends PD.Params> {
     app: App
     canvas3d: Canvas3D
     repr: Representation<P>
-    params: P
 }
 
 export interface RepresentationComponentState {
@@ -29,7 +28,7 @@ export class RepresentationComponent<P extends PD.Params> extends React.Componen
     private stateFromRepr(repr: Representation<P>) {
         return {
             label: repr.label,
-            reprParams: this.props.params,
+            reprParams: repr.params,
             reprProps: repr.props
         }
     }
@@ -42,8 +41,6 @@ export class RepresentationComponent<P extends PD.Params> extends React.Componen
         await this.props.app.runTask(this.props.repr.createOrUpdate(this.props.app.reprCtx, { [k]: v }).run(
             progress => this.props.app.log(progress)
         ), 'Representation Update')
-        this.props.canvas3d.add(this.props.repr)
-        this.props.canvas3d.requestDraw(true)
         this.setState(this.stateFromRepr(this.props.repr))
     }
 

+ 1 - 4
src/apps/canvas/component/structure-view.tsx

@@ -85,7 +85,7 @@ export class StructureViewComponent extends React.Component<StructureViewCompone
     }
 
     render() {
-        const { structureView, label, structure, modelIds, assemblyIds, symmetryFeatureIds, active, structureRepresentations } = this.state
+        const { structureView, label, modelIds, assemblyIds, symmetryFeatureIds, active, structureRepresentations } = this.state
 
         const modelIdOptions = modelIds.map(m => {
             return <option key={m.id} value={m.id}>{m.label}</option>
@@ -175,9 +175,6 @@ export class StructureViewComponent extends React.Component<StructureViewCompone
                             return <div key={i}>
                                 <RepresentationComponent
                                     repr={structureRepresentations[k] as Representation<any>}
-                                    params={
-                                        structureView.app.structureRepresentationRegistry.get(k)!.getParams(structureView.app.reprCtx, structure!)
-                                    }
                                     canvas3d={structureView.canvas3d}
                                     app={structureView.app}
                                 />

+ 1 - 4
src/apps/canvas/component/volume-view.tsx

@@ -62,7 +62,7 @@ export class VolumeViewComponent extends React.Component<VolumeViewComponentProp
     // }
 
     render() {
-        const { volumeView, label, volume, active, volumeRepresentations } = this.state
+        const { volumeView, label, active, volumeRepresentations } = this.state
 
         return <div>
             <div>
@@ -90,9 +90,6 @@ export class VolumeViewComponent extends React.Component<VolumeViewComponentProp
                             return <div key={i}>
                                 <RepresentationComponent
                                     repr={volumeRepresentations[k] as Representation<any>}
-                                    params={
-                                        volumeView.app.volumeRepresentationRegistry.get(k)!.getParams(volumeView.app.reprCtx, volume!)
-                                    }
                                     canvas3d={volumeView.viewer}
                                     app={volumeView.app}
                                 />

+ 1 - 1
src/apps/canvas/structure-view.ts

@@ -199,11 +199,11 @@ export async function StructureView(app: App, canvas3d: Canvas3D, models: Readon
                     } else {
                         repr = app.structureRepresentationRegistry.create(k, app.reprCtx, structure)
                         structureRepresentations[k] = repr
+                        canvas3d.add(repr)
                     }
                     await app.runTask(repr.createOrUpdate(app.reprCtx, {}, {}, structure).run(
                         progress => app.log(progress)
                     ), 'Create/update representation')
-                    canvas3d.add(repr)
                 } else {
                     if (structureRepresentations[k]) {
                         canvas3d.remove(structureRepresentations[k])

+ 45 - 34
src/mol-canvas3d/canvas3d.ts

@@ -4,7 +4,7 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { BehaviorSubject } from 'rxjs';
+import { BehaviorSubject, Subscription } from 'rxjs';
 
 import { Vec3, Mat4, EPSILON } from 'mol-math/linear-algebra'
 import InputObserver from 'mol-util/input/input-observer'
@@ -38,11 +38,11 @@ interface Canvas3D {
 
     center: (p: Vec3) => void
 
-    hide: (repr: Representation<any>) => void
-    show: (repr: Representation<any>) => void
+    hide: (repr: Representation.Any) => void
+    show: (repr: Representation.Any) => void
 
-    add: (repr: Representation<any>) => void
-    remove: (repr: Representation<any>) => void
+    add: (repr: Representation.Any) => void
+    remove: (repr: Representation.Any) => void
     update: () => void
     clear: () => void
 
@@ -52,7 +52,7 @@ interface Canvas3D {
     pick: () => void
     identify: (x: number, y: number) => Promise<PickingId | undefined>
     mark: (loci: Loci, action: MarkerAction) => void
-    getLoci: (pickingId: PickingId) => { loci: Loci, repr?: Representation<any> }
+    getLoci: (pickingId: PickingId) => { loci: Loci, repr?: Representation.Any }
 
     readonly reprCount: BehaviorSubject<number>
     readonly identified: BehaviorSubject<string>
@@ -76,7 +76,8 @@ namespace Canvas3D {
     export function create(canvas: HTMLCanvasElement, container: Element, props: Partial<Canvas3DProps> = {}): Canvas3D {
         const p = { ...props, ...DefaultCanvas3DProps }
 
-        const reprMap = new Map<Representation<any>, Set<RenderObject>>()
+        const reprRenderObjects = new Map<Representation.Any, Set<RenderObject>>()
+        const reprUpdatedSubscriptions = new Map<Representation.Any, Subscription>()
         const reprCount = new BehaviorSubject(0)
         const identified = new BehaviorSubject('')
 
@@ -123,7 +124,7 @@ namespace Canvas3D {
         function getLoci(pickingId: PickingId) {
             let loci: Loci = EmptyLoci
             let repr: Representation.Any = Representation.Empty
-            reprMap.forEach((_, _repr) => {
+            reprRenderObjects.forEach((_, _repr) => {
                 const _loci = _repr.getLoci(pickingId)
                 if (!isEmptyLoci(_loci)) {
                     if (!isEmptyLoci(loci)) console.warn('found another loci')
@@ -136,7 +137,7 @@ namespace Canvas3D {
 
         function mark(loci: Loci, action: MarkerAction) {
             let changed = false
-            reprMap.forEach((roSet, repr) => {
+            reprRenderObjects.forEach((roSet, repr) => {
                 changed = repr.mark(loci, action) || changed
             })
             if (changed) {
@@ -268,6 +269,23 @@ namespace Canvas3D {
             }
         }
 
+        function add(repr: Representation.Any) {
+            const oldRO = reprRenderObjects.get(repr)
+            const newRO = new Set<RenderObject>()
+            repr.renderObjects.forEach(o => newRO.add(o))
+            if (oldRO) {
+                SetUtils.difference(newRO, oldRO).forEach(o => scene.add(o))
+                SetUtils.difference(oldRO, newRO).forEach(o => scene.remove(o))
+                scene.update()
+            } else {
+                repr.renderObjects.forEach(o => scene.add(o))
+            }
+            reprRenderObjects.set(repr, newRO)
+            reprCount.next(reprRenderObjects.size)
+            scene.update()
+            requestDraw(true)
+        }
+
         handleResize()
 
         return {
@@ -278,42 +296,35 @@ namespace Canvas3D {
                 Vec3.set(camera.target, p[0], p[1], p[2])
             },
 
-            hide: (repr: Representation<any>) => {
-                const renderObjectSet = reprMap.get(repr)
+            hide: (repr: Representation.Any) => {
+                const renderObjectSet = reprRenderObjects.get(repr)
                 if (renderObjectSet) renderObjectSet.forEach(o => o.state.visible = false)
             },
-            show: (repr: Representation<any>) => {
-                const renderObjectSet = reprMap.get(repr)
+            show: (repr: Representation.Any) => {
+                const renderObjectSet = reprRenderObjects.get(repr)
                 if (renderObjectSet) renderObjectSet.forEach(o => o.state.visible = true)
             },
 
-            add: (repr: Representation<any>) => {
-                const oldRO = reprMap.get(repr)
-                const newRO = new Set<RenderObject>()
-                repr.renderObjects.forEach(o => newRO.add(o))
-                if (oldRO) {
-                    SetUtils.difference(newRO, oldRO).forEach(o => scene.add(o))
-                    SetUtils.difference(oldRO, newRO).forEach(o => scene.remove(o))
-                    scene.update()
-                } else {
-                    repr.renderObjects.forEach(o => scene.add(o))
-                }
-                reprMap.set(repr, newRO)
-                reprCount.next(reprMap.size)
-                scene.update()
+            add: (repr: Representation.Any) => {
+                add(repr)
+                reprUpdatedSubscriptions.set(repr, repr.updated.subscribe(_ => add(repr)))
             },
-            remove: (repr: Representation<any>) => {
-                const renderObjectSet = reprMap.get(repr)
-                if (renderObjectSet) {
-                    renderObjectSet.forEach(o => scene.remove(o))
-                    reprMap.delete(repr)
-                    reprCount.next(reprMap.size)
+            remove: (repr: Representation.Any) => {
+                const updatedSubscription = reprUpdatedSubscriptions.get(repr)
+                if (updatedSubscription) {
+                    updatedSubscription.unsubscribe()
+                }
+                const renderObjects = reprRenderObjects.get(repr)
+                if (renderObjects) {
+                    renderObjects.forEach(o => scene.remove(o))
+                    reprRenderObjects.delete(repr)
+                    reprCount.next(reprRenderObjects.size)
                     scene.update()
                 }
             },
             update: () => scene.update(),
             clear: () => {
-                reprMap.clear()
+                reprRenderObjects.clear()
                 scene.clear()
             },
 

+ 14 - 8
src/mol-repr/representation.ts

@@ -15,6 +15,7 @@ import { getQualityProps } from './util';
 import { ColorTheme } from 'mol-theme/color';
 import { SizeTheme } from 'mol-theme/size';
 import { ThemeProps, Theme, ThemeRegistryContext } from 'mol-theme/theme';
+import { BehaviorSubject } from 'rxjs';
 
 // export interface RepresentationProps {
 //     visuals?: string[]
@@ -67,6 +68,7 @@ export interface RepresentationContext {
 export { Representation }
 interface Representation<D, P extends PD.Params = {}> {
     readonly label: string
+    readonly updated: BehaviorSubject<number>
     readonly renderObjects: ReadonlyArray<RenderObject>
     readonly props: Readonly<PD.DefaultValues<P>>
     readonly params: Readonly<P>
@@ -78,7 +80,7 @@ interface Representation<D, P extends PD.Params = {}> {
 namespace Representation {
     export type Any = Representation<any>
     export const Empty: Representation<any> = {
-        label: '', renderObjects: [], props: {}, params: {},
+        label: '', renderObjects: [], props: {}, params: {}, updated: new BehaviorSubject(0),
         createOrUpdate: () => Task.constant('', undefined),
         getLoci: () => EmptyLoci,
         mark: () => false,
@@ -88,6 +90,8 @@ namespace Representation {
     export type Def<D, P extends PD.Params = {}> = { [k: string]: (getParams: RepresentationParamsGetter<D, P>) => Representation<any, P> }
 
     export function createMulti<D, P extends PD.Params = {}>(label: string, getParams: RepresentationParamsGetter<D, P>, reprDefs: Def<D, P>): Representation<D, P> {
+        const updated = new BehaviorSubject(0)
+
         let currentParams: P
         let currentProps: PD.DefaultValues<P>
         let currentData: D
@@ -100,12 +104,15 @@ namespace Representation {
 
         return {
             label,
+            updated,
             get renderObjects() {
-                const { visuals } = currentProps
                 const renderObjects: RenderObject[] = []
-                for (let i = 0, il = reprList.length; i < il; ++i) {
-                    if (!visuals || visuals.includes(reprMap[i])) {
-                        renderObjects.push(...reprList[i].renderObjects)
+                if (currentProps) {
+                    const { visuals } = currentProps
+                    for (let i = 0, il = reprList.length; i < il; ++i) {
+                        if (!visuals || visuals.includes(reprMap[i])) {
+                            renderObjects.push(...reprList[i].renderObjects)
+                        }
                     }
                 }
                 return renderObjects
@@ -115,9 +122,7 @@ namespace Representation {
                 reprList.forEach(r => Object.assign(props, r.props))
                 return props as P
             },
-            get params() {
-                return currentParams
-            },
+            get params() { return currentParams },
             createOrUpdate: (ctx: RepresentationContext, props: Partial<P> = {}, themeProps: ThemeProps = {}, data?: D) => {
                 if (data && data !== currentData) {
                     currentParams = getParams(ctx, data)
@@ -134,6 +139,7 @@ namespace Representation {
                             await reprList[i].createOrUpdate(ctx, currentProps, themeProps, currentData).runInContext(runtime)
                         }
                     }
+                    updated.next(updated.getValue() + 1)
                 })
             },
             getLoci: (pickingId: PickingId) => {

+ 4 - 0
src/mol-repr/shape/representation.ts

@@ -19,6 +19,7 @@ import { PickingId } from 'mol-geo/geometry/picking';
 import { MarkerAction, applyMarkerAction } from 'mol-geo/geometry/marker-data';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { ThemeProps, createTheme } from 'mol-theme/theme';
+import { BehaviorSubject } from 'rxjs';
 
 export interface ShapeRepresentation<P extends ShapeParams> extends Representation<Shape, P> { }
 
@@ -30,6 +31,7 @@ export const ShapeParams = {
 export type ShapeParams = typeof ShapeParams
 
 export function ShapeRepresentation<P extends ShapeParams>(): ShapeRepresentation<P> {
+    const updated = new BehaviorSubject(0)
     const renderObjects: RenderObject[] = []
     let _renderObject: MeshRenderObject | undefined
     let _shape: Shape
@@ -55,11 +57,13 @@ export function ShapeRepresentation<P extends ShapeParams>(): ShapeRepresentatio
 
             _renderObject = createMeshRenderObject(values, state)
             renderObjects.push(_renderObject)
+            updated.next(updated.getValue() + 1)
         });
     }
 
     return {
         label: 'Shape mesh',
+        updated,
         get renderObjects () { return renderObjects },
         get params () { return currentParams },
         get props () { return currentProps },

+ 4 - 0
src/mol-repr/structure/complex-representation.ts

@@ -15,8 +15,10 @@ import { MarkerAction } from 'mol-geo/geometry/marker-data';
 import { RepresentationContext, RepresentationParamsGetter } from 'mol-repr/representation';
 import { Theme, ThemeProps, createTheme } from 'mol-theme/theme';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
+import { BehaviorSubject } from 'rxjs';
 
 export function ComplexRepresentation<P extends StructureParams>(label: string, getParams: RepresentationParamsGetter<Structure, P>, visualCtor: () => ComplexVisual<P>): StructureRepresentation<P> {
+    const updated = new BehaviorSubject(0)
     let visual: ComplexVisual<P> | undefined
 
     let _structure: Structure
@@ -36,6 +38,7 @@ export function ComplexRepresentation<P extends StructureParams>(label: string,
         return Task.create('Creating or updating ComplexRepresentation', async runtime => {
             if (!visual) visual = visualCtor()
             await visual.createOrUpdate({ ...ctx, runtime }, _theme, _props, structure)
+            updated.next(updated.getValue() + 1)
         });
     }
 
@@ -58,6 +61,7 @@ export function ComplexRepresentation<P extends StructureParams>(label: string,
         },
         get props() { return _props },
         get params() { return _params },
+        get updated() { return updated },
         createOrUpdate,
         getLoci,
         mark,

+ 7 - 3
src/mol-repr/structure/units-representation.ts

@@ -17,6 +17,7 @@ import { MarkerAction } from 'mol-geo/geometry/marker-data';
 import { Theme, ThemeProps, createTheme } from 'mol-theme/theme';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { UnitKind, UnitKindOptions } from './visual/util/common';
+import { BehaviorSubject } from 'rxjs';
 
 export const UnitsParams = {
     ...StructureParams,
@@ -27,6 +28,7 @@ export type UnitsParams = typeof UnitsParams
 export interface UnitsVisual<P extends UnitsParams> extends Visual<StructureGroup, P> { }
 
 export function UnitsRepresentation<P extends UnitsParams>(label: string, getParams: RepresentationParamsGetter<Structure, P>, visualCtor: () => UnitsVisual<P>): StructureRepresentation<P> {
+    const updated = new BehaviorSubject(0)
     let visuals = new Map<number, { group: Unit.SymmetryGroup, visual: UnitsVisual<P> }>()
 
     let _structure: Structure
@@ -91,7 +93,7 @@ export function UnitsRepresentation<P extends UnitsParams>(label: string, getPar
                 //     visuals.set(group.hashCode, { visual, group })
                 // })
                 // unusedVisuals.forEach(visual => visual.destroy())
-            } else if (structure && _structure.hashCode === structure.hashCode) {
+            } else if (structure && structure !== _structure && _structure.hashCode === structure.hashCode) {
                 // console.log('_structure.hashCode === structure.hashCode')
                 // Expects that for structures with the same hashCode,
                 // the unitSymmetryGroups are the same as well.
@@ -113,11 +115,12 @@ export function UnitsRepresentation<P extends UnitsParams>(label: string, getPar
                 const visualsList: [ UnitsVisual<P>, Unit.SymmetryGroup ][] = [] // TODO avoid allocation
                 visuals.forEach(({ visual, group }) => visualsList.push([ visual, group ]))
                 for (let i = 0, il = visualsList.length; i < il; ++i) {
-                    const [ visual, group ] = visualsList[i]
-                    await visual.createOrUpdate({ ...ctx, runtime }, _theme, _props, { group, structure: _structure })
+                    const [ visual ] = visualsList[i]
+                    await visual.createOrUpdate({ ...ctx, runtime }, _theme, _props)
                 }
             }
             if (structure) _structure = structure
+            updated.next(updated.getValue() + 1)
         });
     }
 
@@ -154,6 +157,7 @@ export function UnitsRepresentation<P extends UnitsParams>(label: string, getPar
         },
         get props() { return _props },
         get params() { return _params },
+        get updated() { return updated },
         createOrUpdate,
         getLoci,
         mark,

+ 4 - 0
src/mol-repr/volume/representation.ts

@@ -20,6 +20,7 @@ import { NullLocation } from 'mol-model/location';
 import { VisualUpdateState } from 'mol-repr/util';
 import { ValueCell } from 'mol-util';
 import { ThemeProps, Theme, createTheme } from 'mol-theme/theme';
+import { BehaviorSubject } from 'rxjs';
 
 export interface VolumeVisual<P extends VolumeParams> extends Visual<VolumeData, P> { }
 
@@ -147,6 +148,7 @@ export const VolumeParams = {
 export type VolumeParams = typeof VolumeParams
 
 export function VolumeRepresentation<P extends VolumeParams>(label: string, getParams: RepresentationParamsGetter<VolumeData, P>, visualCtor: (volume: VolumeData) => VolumeVisual<P>): VolumeRepresentation<P> {
+    const updated = new BehaviorSubject(0)
     let visual: VolumeVisual<P>
 
     let _volume: VolumeData
@@ -180,6 +182,7 @@ export function VolumeRepresentation<P extends VolumeParams>(label: string, getP
                 await visual.createOrUpdate({ ...ctx, runtime }, _theme, _props, volume)
                 busy = false
             }
+            updated.next(updated.getValue() + 1)
         });
     }
 
@@ -202,6 +205,7 @@ export function VolumeRepresentation<P extends VolumeParams>(label: string, getP
         },
         get props () { return _props },
         get params() { return _params },
+        get updated() { return updated },
         createOrUpdate,
         getLoci,
         mark,