Browse Source

representation refactoring

Alexander Rose 6 years ago
parent
commit
9a21737b3d

+ 78 - 4
src/mol-repr/index.ts

@@ -7,11 +7,14 @@
 import { Task, RuntimeContext } from 'mol-task'
 import { RenderObject } from 'mol-gl/render-object'
 import { PickingId } from '../mol-geo/geometry/picking';
-import { Loci } from 'mol-model/loci';
+import { Loci, isEmptyLoci, EmptyLoci } from 'mol-model/loci';
 import { MarkerAction } from '../mol-geo/geometry/marker-data';
-import { Params } from 'mol-util/parameter';
+import { Params, MultiSelectParam } from 'mol-util/parameter';
 
-export interface RepresentationProps {}
+// export interface RepresentationProps {
+//     visuals?: string[]
+// }
+export type RepresentationProps = { [k: string]: any }
 
 export interface Representation<D, P extends RepresentationProps = {}> {
     readonly label: string
@@ -24,7 +27,78 @@ export interface Representation<D, P extends RepresentationProps = {}> {
     destroy: () => void
 }
 
-export interface Visual<D, P extends RepresentationProps = {}> {
+export namespace Representation {
+    export function createMulti<D, P extends RepresentationProps = {}>(label: string, params: Params, defaultProps: P, reprList: Representation<D, P>[]): Representation<D, P> {
+        let currentProps: P
+        let currentData: D
+
+        const visualsOptions: [string, string][] = []
+        for (let i = 0, il = reprList.length; i < il; ++i) {
+            visualsOptions.push([ i.toString(), reprList[i].label ])
+        }
+        params['visuals'] = MultiSelectParam<string>('Visuals', '', ['surface'], visualsOptions)
+
+        if (!defaultProps.visuals) {
+            defaultProps.visuals = reprList.map((r, i) => i.toString())
+        }
+
+        return {
+            label,
+            params,
+            get renderObjects() {
+                const { visuals } = currentProps
+                const renderObjects: RenderObject[] = []
+                for (let i = 0, il = reprList.length; i < il; ++i) {
+                    if (!visuals || visuals.includes(i.toString())) {
+                        renderObjects.push(...reprList[i].renderObjects)
+                    }
+                }
+                return renderObjects
+            },
+            get props() {
+                const props = {}
+                reprList.forEach(r => Object.assign(props, r.props))
+                return props as P
+            },
+            createOrUpdate: (props: Partial<P> = {}, data?: D) => {
+                if (data) currentData = data
+                // const qualityProps = getQualityProps(Object.assign({}, currentProps, props), structure)
+                // currentProps = Object.assign({}, DefaultCartoonProps, currentProps, props, qualityProps)
+                currentProps = Object.assign({}, defaultProps, currentProps, props)
+
+                const { visuals } = currentProps
+                return Task.create(`Creating '${label}' representation`, async ctx => {
+                    for (let i = 0, il = reprList.length; i < il; ++i) {
+                        if (!visuals || visuals.includes(i.toString())) {
+                            await reprList[i].createOrUpdate(currentProps, currentData).runInContext(ctx)
+                        }
+                    }
+                })
+            },
+            getLoci: (pickingId: PickingId) => {
+                for (let i = 0, il = reprList.length; i < il; ++i) {
+                    const loci = reprList[i].getLoci(pickingId)
+                    if (!isEmptyLoci(loci)) return loci
+                }
+                return EmptyLoci
+            },
+            mark: (loci: Loci, action: MarkerAction) => {
+                let marked = false
+                for (let i = 0, il = reprList.length; i < il; ++i) {
+                    marked = reprList[i].mark(loci, action) || marked
+                }
+                return marked
+            },
+            destroy() {
+                for (let i = 0, il = reprList.length; i < il; ++i) {
+                    reprList[i].destroy()
+                }
+            }
+        }
+    }
+}
+
+export interface Visual<D, P extends RepresentationProps> {
     readonly renderObject: RenderObject | undefined
     createOrUpdate: (ctx: RuntimeContext, props?: Partial<P>, data?: D) => Promise<void>
     getLoci: (pickingId: PickingId) => Loci

+ 0 - 2
src/mol-repr/structure/index.ts

@@ -17,10 +17,8 @@ import { Lines } from 'mol-geo/geometry/lines/lines';
 import { DirectVolume } from 'mol-geo/geometry/direct-volume/direct-volume';
 
 export interface StructureRepresentation<P extends RepresentationProps = {}> extends Representation<Structure, P> { }
-
 // export interface  StructureVisual<P extends RepresentationProps = {}> extends Visual<Structure, P> { }
 
-
 export const StructureParams = {
     ...Geometry.Params,
     colorTheme: SelectParam<ColorThemeName>('Color Theme', '', 'polymer-index', ColorThemeOptions),

+ 4 - 35
src/mol-repr/structure/representation/backbone.ts

@@ -4,16 +4,11 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Structure } from 'mol-model/structure';
-import { Task } from 'mol-task';
-import { Loci } from 'mol-model/loci';
 import { PolymerBackboneVisual, PolymerBackboneParams } from '../visual/polymer-backbone-cylinder';
-import { getQualityProps } from '../../util';
 import { paramDefaultValues } from 'mol-util/parameter';
 import { UnitsRepresentation } from '../units-representation';
-import { PickingId } from 'mol-geo/geometry/picking';
-import { MarkerAction } from 'mol-geo/geometry/marker-data';
 import { StructureRepresentation } from '../index';
+import { Representation } from 'mol-repr';
 
 export const BackboneParams = {
     ...PolymerBackboneParams
@@ -24,33 +19,7 @@ export type BackboneProps = typeof DefaultBackboneProps
 export type BackboneRepresentation = StructureRepresentation<BackboneProps>
 
 export function BackboneRepresentation(): BackboneRepresentation {
-    const traceRepr = UnitsRepresentation('Polymer backbone cylinder', PolymerBackboneVisual)
-
-    let currentProps: BackboneProps
-    return {
-        label: 'Backbone',
-        params: BackboneParams,
-        get renderObjects() {
-            return [ ...traceRepr.renderObjects ]
-        },
-        get props() {
-            return { ...traceRepr.props }
-        },
-        createOrUpdate: (props: Partial<BackboneProps> = {}, structure?: Structure) => {
-            const qualityProps = getQualityProps(Object.assign({}, currentProps, props), structure)
-            currentProps = Object.assign({}, DefaultBackboneProps, currentProps, props, qualityProps)
-            return Task.create('BackboneRepresentation', async ctx => {
-                await traceRepr.createOrUpdate(currentProps, structure).runInContext(ctx)
-            })
-        },
-        getLoci: (pickingId: PickingId) => {
-            return traceRepr.getLoci(pickingId)
-        },
-        mark: (loci: Loci, action: MarkerAction) => {
-            return traceRepr.mark(loci, action)
-        },
-        destroy() {
-            traceRepr.destroy()
-        }
-    }
+    return Representation.createMulti('Backbone', BackboneParams, DefaultBackboneProps, [
+        UnitsRepresentation('Polymer backbone cylinder', PolymerBackboneVisual)
+    ] as StructureRepresentation<BackboneProps>[])
 }

+ 8 - 56
src/mol-repr/structure/representation/ball-and-stick.ts

@@ -6,23 +6,19 @@
 
 import { ElementSphereVisual, ElementSphereParams } from '../visual/element-sphere';
 import { IntraUnitLinkVisual, IntraUnitLinkParams } from '../visual/intra-unit-link-cylinder';
-import { Structure } from 'mol-model/structure';
-import { Task } from 'mol-task';
-import { Loci, isEmptyLoci } from 'mol-model/loci';
-import { InterUnitLinkVisual } from '../visual/inter-unit-link-cylinder';
+import { InterUnitLinkVisual, InterUnitLinkParams } from '../visual/inter-unit-link-cylinder';
 import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
-import { getQualityProps } from '../../util';
 import { paramDefaultValues, SelectParam, NumberParam, MultiSelectParam } from 'mol-util/parameter';
 import { UnitKind, UnitKindOptions } from '../visual/util/common';
 import { UnitsRepresentation } from '../units-representation';
-import { PickingId } from 'mol-geo/geometry/picking';
-import { MarkerAction } from 'mol-geo/geometry/marker-data';
 import { ComplexRepresentation } from '../complex-representation';
 import { StructureRepresentation } from '../index';
+import { Representation } from 'mol-repr';
 
 export const BallAndStickParams = {
     ...ElementSphereParams,
     ...IntraUnitLinkParams,
+    ...InterUnitLinkParams,
     sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions),
     sizeValue: NumberParam('Size Value', '', 0.2, 0, 10, 0.1),
     sizeFactor: NumberParam('Size Factor', '', 1, 0, 10, 0.1),
@@ -34,53 +30,9 @@ export type BallAndStickProps = typeof DefaultBallAndStickProps
 export type BallAndStickRepresentation = StructureRepresentation<BallAndStickProps>
 
 export function BallAndStickRepresentation(): BallAndStickRepresentation {
-    const elmementRepr = UnitsRepresentation('Element sphere mesh', ElementSphereVisual)
-    const intraLinkRepr = UnitsRepresentation('Intra-unit link cylinder', IntraUnitLinkVisual)
-    const interLinkRepr = ComplexRepresentation('Inter-unit link cylinder', InterUnitLinkVisual)
-
-    let currentProps: BallAndStickProps
-    return {
-        label: 'Ball & Stick',
-        params: BallAndStickParams,
-        get renderObjects() {
-            return [ ...elmementRepr.renderObjects, ...intraLinkRepr.renderObjects, ...interLinkRepr.renderObjects ]
-        },
-        get props() {
-            return { ...elmementRepr.props, ...intraLinkRepr.props, ...interLinkRepr.props }
-        },
-        createOrUpdate: (props: Partial<BallAndStickProps> = {}, structure?: Structure) => {
-            const qualityProps = getQualityProps(Object.assign({}, currentProps, props), structure)
-            currentProps = Object.assign({}, DefaultBallAndStickProps, currentProps, props, qualityProps)
-            return Task.create('BallAndStickRepresentation', async ctx => {
-                await elmementRepr.createOrUpdate(currentProps, structure).runInContext(ctx)
-                await intraLinkRepr.createOrUpdate(currentProps, structure).runInContext(ctx)
-                await interLinkRepr.createOrUpdate(currentProps, structure).runInContext(ctx)
-            })
-        },
-        getLoci: (pickingId: PickingId) => {
-            const sphereLoci = elmementRepr.getLoci(pickingId)
-            const intraLinkLoci = intraLinkRepr.getLoci(pickingId)
-            const interLinkLoci = interLinkRepr.getLoci(pickingId)
-            if (isEmptyLoci(sphereLoci)) {
-                if (isEmptyLoci(intraLinkLoci)) {
-                    return interLinkLoci
-                } else {
-                    return intraLinkLoci
-                }
-            } else {
-                return sphereLoci
-            }
-        },
-        mark: (loci: Loci, action: MarkerAction) => {
-            const markElement = elmementRepr.mark(loci, action)
-            const markIntraLink = intraLinkRepr.mark(loci, action)
-            const markInterLink = interLinkRepr.mark(loci, action)
-            return markElement || markIntraLink || markInterLink
-        },
-        destroy() {
-            elmementRepr.destroy()
-            intraLinkRepr.destroy()
-            interLinkRepr.destroy()
-        }
-    }
+    return Representation.createMulti('Ball & Stick', BallAndStickParams, DefaultBallAndStickProps, [
+        UnitsRepresentation('Element sphere mesh', ElementSphereVisual),
+        UnitsRepresentation('Intra-unit link cylinder', IntraUnitLinkVisual),
+        ComplexRepresentation('Inter-unit link cylinder', InterUnitLinkVisual)
+    ] as unknown as StructureRepresentation<BallAndStickProps>[]) // TODO avoid cast to unknown
 }

+ 5 - 43
src/mol-repr/structure/representation/carbohydrate.ts

@@ -4,18 +4,13 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Structure } from 'mol-model/structure';
-import { Task } from 'mol-task';
-import { Loci, isEmptyLoci } from 'mol-model/loci';
 import { CarbohydrateSymbolVisual, CarbohydrateSymbolParams } from '../visual/carbohydrate-symbol-mesh';
 import { CarbohydrateLinkVisual, CarbohydrateLinkParams } from '../visual/carbohydrate-link-cylinder';
 import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
-import { getQualityProps } from '../../util';
 import { paramDefaultValues, SelectParam, NumberParam } from 'mol-util/parameter';
-import { PickingId } from 'mol-geo/geometry/picking';
-import { MarkerAction } from 'mol-geo/geometry/marker-data';
 import { ComplexRepresentation } from '../complex-representation';
 import { StructureRepresentation } from '../index';
+import { Representation } from 'mol-repr';
 
 export const CarbohydrateParams = {
     ...CarbohydrateSymbolParams,
@@ -30,41 +25,8 @@ export type CarbohydrateProps = typeof DefaultCarbohydrateProps
 export type CarbohydrateRepresentation = StructureRepresentation<CarbohydrateProps>
 
 export function CarbohydrateRepresentation(): CarbohydrateRepresentation {
-    const carbohydrateSymbolRepr = ComplexRepresentation('Carbohydrate symbol mesh', CarbohydrateSymbolVisual)
-    const carbohydrateLinkRepr = ComplexRepresentation('Carbohydrate link cylinder', CarbohydrateLinkVisual)
-
-    let currentProps: CarbohydrateProps
-    return {
-        label: 'Carbohydrate',
-        params: CarbohydrateParams,
-        get renderObjects() {
-            return [ ...carbohydrateSymbolRepr.renderObjects, ...carbohydrateLinkRepr.renderObjects ]
-        },
-        get props() {
-            return { ...carbohydrateSymbolRepr.props, ...carbohydrateLinkRepr.props }
-        },
-        createOrUpdate: (props: Partial<CarbohydrateProps> = {}, structure?: Structure) => {
-            const qualityProps = getQualityProps(Object.assign({}, currentProps, props), structure)
-            currentProps = Object.assign({}, DefaultCarbohydrateProps, currentProps, props, qualityProps)
-            return Task.create('Creating CarbohydrateRepresentation', async ctx => {
-                await carbohydrateSymbolRepr.createOrUpdate(currentProps, structure).runInContext(ctx)
-                await carbohydrateLinkRepr.createOrUpdate(currentProps, structure).runInContext(ctx)
-            })
-        },
-        getLoci: (pickingId: PickingId) => {
-            const carbohydrateSymbolLoci = carbohydrateSymbolRepr.getLoci(pickingId)
-            const carbohydrateLinkLoci = carbohydrateLinkRepr.getLoci(pickingId)
-            return !isEmptyLoci(carbohydrateSymbolLoci) ? carbohydrateSymbolLoci
-                : carbohydrateLinkLoci
-        },
-        mark: (loci: Loci, action: MarkerAction) => {
-            const markSymbol = carbohydrateSymbolRepr.mark(loci, action)
-            const markLink = carbohydrateLinkRepr.mark(loci, action)
-            return markSymbol || markLink
-        },
-        destroy() {
-            carbohydrateSymbolRepr.destroy()
-            carbohydrateLinkRepr.destroy()
-        }
-    }
+    return Representation.createMulti('Carbohydrate', CarbohydrateParams, DefaultCarbohydrateProps, [
+        ComplexRepresentation('Carbohydrate symbol mesh', CarbohydrateSymbolVisual),
+        ComplexRepresentation('Carbohydrate link cylinder', CarbohydrateLinkVisual)
+    ] as StructureRepresentation<CarbohydrateProps>[])
 }

+ 7 - 58
src/mol-repr/structure/representation/cartoon.ts

@@ -4,19 +4,14 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Structure } from 'mol-model/structure';
-import { Task } from 'mol-task';
-import { Loci, isEmptyLoci } from 'mol-model/loci';
 import { PolymerTraceVisual,  PolymerTraceParams } from '../visual/polymer-trace-mesh';
 import { PolymerGapVisual, PolymerGapParams } from '../visual/polymer-gap-cylinder';
 import { NucleotideBlockVisual, NucleotideBlockParams } from '../visual/nucleotide-block-mesh';
 import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
-import { getQualityProps } from '../../util';
 import { paramDefaultValues, SelectParam, NumberParam } from 'mol-util/parameter';
 import { UnitsRepresentation } from '../units-representation';
-import { PickingId } from 'mol-geo/geometry/picking';
-import { MarkerAction } from 'mol-geo/geometry/marker-data';
 import { StructureRepresentation } from '../index';
+import { Representation } from 'mol-repr';
 // import { PolymerDirectionVisual, DefaultPolymerDirectionProps } from '../visual/polymer-direction-wedge';
 
 export const CartoonParams = {
@@ -33,56 +28,10 @@ export type CartoonProps = typeof DefaultCartoonProps
 export type CartoonRepresentation = StructureRepresentation<CartoonProps>
 
 export function CartoonRepresentation(): CartoonRepresentation {
-    const traceRepr = UnitsRepresentation('Polymer trace mesh', PolymerTraceVisual)
-    const gapRepr = UnitsRepresentation('Polymer gap cylinder', PolymerGapVisual)
-    const blockRepr = UnitsRepresentation('Nucleotide block mesh', NucleotideBlockVisual)
-    // const directionRepr = UnitsRepresentation('Polymer direction wedge', PolymerDirectionVisual)
-
-    let currentProps: CartoonProps
-    return {
-        label: 'Cartoon',
-        params: CartoonParams,
-        get renderObjects() {
-            return [ ...traceRepr.renderObjects, ...gapRepr.renderObjects,
-                ...blockRepr.renderObjects // , ...directionRepr.renderObjects
-            ]
-        },
-        get props() {
-            return { ...traceRepr.props, ...gapRepr.props, ...blockRepr.props }
-        },
-        createOrUpdate: (props: Partial<CartoonProps> = {}, structure?: Structure) => {
-            const qualityProps = getQualityProps(Object.assign({}, currentProps, props), structure)
-            currentProps = Object.assign({}, DefaultCartoonProps, currentProps, props, qualityProps)
-            return Task.create('Creating CartoonRepresentation', async ctx => {
-                await traceRepr.createOrUpdate(currentProps, structure).runInContext(ctx)
-                await gapRepr.createOrUpdate(currentProps, structure).runInContext(ctx)
-                await blockRepr.createOrUpdate(currentProps, structure).runInContext(ctx)
-                // await directionRepr.createOrUpdate(currentProps, structure).runInContext(ctx)
-            })
-        },
-        getLoci: (pickingId: PickingId) => {
-            const traceLoci = traceRepr.getLoci(pickingId)
-            const gapLoci = gapRepr.getLoci(pickingId)
-            const blockLoci = blockRepr.getLoci(pickingId)
-            // const directionLoci = directionRepr.getLoci(pickingId)
-            return !isEmptyLoci(traceLoci) ? traceLoci
-                : !isEmptyLoci(gapLoci) ? gapLoci
-                : blockLoci
-                // : !isEmptyLoci(blockLoci) ? blockLoci
-                // : directionLoci
-        },
-        mark: (loci: Loci, action: MarkerAction) => {
-            const markTrace = traceRepr.mark(loci, action)
-            const markGap = gapRepr.mark(loci, action)
-            const markBlock = blockRepr.mark(loci, action)
-            // const markDirection = directionRepr.mark(loci, action)
-            return markTrace || markGap || markBlock // \\ markDirection
-        },
-        destroy() {
-            traceRepr.destroy()
-            gapRepr.destroy()
-            blockRepr.destroy()
-            // directionRepr.destroy()
-        }
-    }
+    return Representation.createMulti('Cartoon', CartoonParams, DefaultCartoonProps, [
+        UnitsRepresentation('Polymer trace mesh', PolymerTraceVisual),
+        UnitsRepresentation('Polymer gap cylinder', PolymerGapVisual),
+        UnitsRepresentation('Nucleotide block mesh', NucleotideBlockVisual),
+        // UnitsRepresentation('Polymer direction wedge', PolymerDirectionVisual)
+    ] as StructureRepresentation<CartoonProps>[])
 }

+ 4 - 35
src/mol-repr/structure/representation/distance-restraint.ts

@@ -4,17 +4,12 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Structure } from 'mol-model/structure';
-import { Task } from 'mol-task';
-import { Loci } from 'mol-model/loci';
 import { CrossLinkRestraintVisual, CrossLinkRestraintParams } from '../visual/cross-link-restraint-cylinder';
 import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
-import { getQualityProps } from '../../util';
 import { paramDefaultValues, SelectParam, NumberParam } from 'mol-util/parameter';
 import { ComplexRepresentation } from '../complex-representation';
-import { PickingId } from 'mol-geo/geometry/picking';
-import { MarkerAction } from 'mol-geo/geometry/marker-data';
 import { StructureRepresentation } from '../index';
+import { Representation } from 'mol-repr';
 
 export const DistanceRestraintParams = {
     ...CrossLinkRestraintParams,
@@ -27,33 +22,7 @@ export type DistanceRestraintProps = typeof DefaultDistanceRestraintProps
 export type DistanceRestraintRepresentation = StructureRepresentation<DistanceRestraintProps>
 
 export function DistanceRestraintRepresentation(): DistanceRestraintRepresentation {
-    const crossLinkRepr = ComplexRepresentation('Cross-link restraint', CrossLinkRestraintVisual)
-
-    let currentProps: DistanceRestraintProps
-    return {
-        label: 'Distance restraint',
-        params: DistanceRestraintParams,
-        get renderObjects() {
-            return [ ...crossLinkRepr.renderObjects ]
-        },
-        get props() {
-            return { ...crossLinkRepr.props }
-        },
-        createOrUpdate: (props: Partial<DistanceRestraintProps> = {}, structure?: Structure) => {
-            const qualityProps = getQualityProps(Object.assign({}, currentProps, props), structure)
-            currentProps = Object.assign({}, DefaultDistanceRestraintProps, currentProps, props, qualityProps)
-            return Task.create('DistanceRestraintRepresentation', async ctx => {
-                await crossLinkRepr.createOrUpdate(currentProps, structure).runInContext(ctx)
-            })
-        },
-        getLoci: (pickingId: PickingId) => {
-            return crossLinkRepr.getLoci(pickingId)
-        },
-        mark: (loci: Loci, action: MarkerAction) => {
-            return crossLinkRepr.mark(loci, action)
-        },
-        destroy() {
-            crossLinkRepr.destroy()
-        }
-    }
+    return Representation.createMulti('Distance restraint', DistanceRestraintParams, DefaultDistanceRestraintProps, [
+        ComplexRepresentation('Cross-link restraint', CrossLinkRestraintVisual)
+    ] as StructureRepresentation<DistanceRestraintProps>[])
 }

+ 8 - 66
src/mol-repr/structure/representation/molecular-surface.ts

@@ -6,86 +6,28 @@
 
 import { GaussianSurfaceVisual, GaussianSurfaceParams } from '../visual/gaussian-surface-mesh';
 import { UnitsRepresentation } from '../units-representation';
-import { Structure } from 'mol-model/structure';
-import { Loci, isEmptyLoci } from 'mol-model/loci';
-import { Task } from 'mol-task';
 import { GaussianWireframeVisual, GaussianWireframeParams } from '../visual/gaussian-surface-wireframe';
-import { getQualityProps } from '../../util';
-import { paramDefaultValues, MultiSelectParam, SelectParam } from 'mol-util/parameter';
+import { paramDefaultValues, SelectParam } from 'mol-util/parameter';
 import { GaussianDensityVolumeParams, GaussianDensityVolumeVisual } from '../visual/gaussian-density-volume';
 import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
-import { PickingId } from 'mol-geo/geometry/picking';
-import { MarkerAction } from 'mol-geo/geometry/marker-data';
 import { StructureRepresentation } from '../index';
-
-const VisualOptions = [['surface', 'Surface'], ['wireframe', 'Wireframe'], ['volume', 'Volume']] as [string, string][]
+import { Representation } from 'mol-repr';
 
 export const MolecularSurfaceParams = {
     ...GaussianSurfaceParams,
     ...GaussianWireframeParams,
     ...GaussianDensityVolumeParams,
-
     sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions),
-    visuals: MultiSelectParam<string>('Visuals', '', ['surface'], VisualOptions)
 }
-export const DefaultMolecularSurfaceProps = paramDefaultValues(MolecularSurfaceParams)
+export const DefaultMolecularSurfaceProps = { ...paramDefaultValues(MolecularSurfaceParams), visuals: [ '0' ] }
 export type MolecularSurfaceProps = typeof DefaultMolecularSurfaceProps
 
 export type MolecularSurfaceRepresentation = StructureRepresentation<MolecularSurfaceProps>
 
 export function MolecularSurfaceRepresentation(): MolecularSurfaceRepresentation {
-    let currentProps: MolecularSurfaceProps
-    let currentStructure: Structure
-    const gaussianSurfaceRepr = UnitsRepresentation('Gaussian surface', GaussianSurfaceVisual)
-    const gaussianWireframeRepr = UnitsRepresentation('Gaussian wireframe', GaussianWireframeVisual)
-    const gaussianVolumeRepr = UnitsRepresentation('Gaussian volume', GaussianDensityVolumeVisual)
-    return {
-        label: 'Molecular Surface',
-        params: MolecularSurfaceParams,
-        get renderObjects() {
-            const renderObjects = []
-            if (currentProps.visuals.includes('surface')) renderObjects.push(...gaussianSurfaceRepr.renderObjects)
-            if (currentProps.visuals.includes('wireframe')) renderObjects.push(...gaussianWireframeRepr.renderObjects)
-            if (currentProps.visuals.includes('volume')) renderObjects.push(...gaussianVolumeRepr.renderObjects)
-            return renderObjects
-        },
-        get props() {
-            return { ...gaussianSurfaceRepr.props, ...gaussianWireframeRepr.props, ...gaussianVolumeRepr.props, visuals: currentProps.visuals }
-        },
-        createOrUpdate: (props: Partial<MolecularSurfaceProps> = {}, structure?: Structure) => {
-            if (structure) currentStructure = structure
-            const qualityProps = getQualityProps(Object.assign({}, currentProps, props), currentStructure)
-            currentProps = Object.assign({}, DefaultMolecularSurfaceProps, currentProps, props, qualityProps)
-            return Task.create('Creating MolecularSurfaceRepresentation', async ctx => {
-                if (currentProps.visuals.includes('surface')) await gaussianSurfaceRepr.createOrUpdate(currentProps, currentStructure).runInContext(ctx)
-                if (currentProps.visuals.includes('wireframe')) await gaussianWireframeRepr.createOrUpdate(currentProps, currentStructure).runInContext(ctx)
-                if (currentProps.visuals.includes('volume')) await gaussianVolumeRepr.createOrUpdate(currentProps, currentStructure).runInContext(ctx)
-            })
-        },
-        getLoci: (pickingId: PickingId) => {
-            const surfaceLoci = gaussianSurfaceRepr.getLoci(pickingId)
-            const wireframeLoci = gaussianWireframeRepr.getLoci(pickingId)
-            const volumeLoci = gaussianVolumeRepr.getLoci(pickingId)
-            if (isEmptyLoci(surfaceLoci)) {
-                if (isEmptyLoci(wireframeLoci)) {
-                    return volumeLoci
-                } else {
-                    return wireframeLoci
-                }
-            } else {
-                return surfaceLoci
-            }
-        },
-        mark: (loci: Loci, action: MarkerAction) => {
-            const markSurfaceElement = gaussianSurfaceRepr.mark(loci, action)
-            const markWireframeElement = gaussianWireframeRepr.mark(loci, action)
-            const markVolumeElement = gaussianVolumeRepr.mark(loci, action)
-            return markSurfaceElement || markWireframeElement || markVolumeElement
-        },
-        destroy() {
-            gaussianSurfaceRepr.destroy()
-            gaussianWireframeRepr.destroy()
-            gaussianVolumeRepr.destroy()
-        }
-    }
+    return Representation.createMulti('Molecular Surface', MolecularSurfaceParams, DefaultMolecularSurfaceProps, [
+        UnitsRepresentation('Gaussian surface', GaussianSurfaceVisual),
+        UnitsRepresentation('Gaussian wireframe', GaussianWireframeVisual),
+        UnitsRepresentation('Gaussian volume', GaussianDensityVolumeVisual)
+    ] as unknown as StructureRepresentation<MolecularSurfaceProps>[]) // TODO avoid cast to unknown
 }

+ 4 - 29
src/mol-repr/structure/representation/point.ts

@@ -6,12 +6,9 @@
 
 import { ElementPointVisual, ElementPointParams } from '../visual/element-point';
 import { UnitsRepresentation } from '../units-representation';
-import { Structure } from 'mol-model/structure';
-import { Loci } from 'mol-model/loci';
 import { paramDefaultValues } from 'mol-util/parameter';
-import { PickingId } from 'mol-geo/geometry/picking';
-import { MarkerAction } from 'mol-geo/geometry/marker-data';
 import { StructureRepresentation } from '../index';
+import { Representation } from 'mol-repr';
 
 export const PointParams = {
     ...ElementPointParams,
@@ -22,29 +19,7 @@ export type PointProps = typeof DefaultPointProps
 export type PointRepresentation = StructureRepresentation<PointProps>
 
 export function PointRepresentation(): PointRepresentation {
-    let currentProps: PointProps
-    const pointRepr = UnitsRepresentation('Point', ElementPointVisual)
-    return {
-        label: 'Point',
-        params: PointParams,
-        get renderObjects() {
-            return [ ...pointRepr.renderObjects ]
-        },
-        get props() {
-            return { ...pointRepr.props }
-        },
-        createOrUpdate: (props: Partial<PointProps> = {}, structure?: Structure) => {
-            currentProps = Object.assign({}, DefaultPointProps, currentProps, props)
-            return pointRepr.createOrUpdate(currentProps, structure)
-        },
-        getLoci: (pickingId: PickingId) => {
-            return pointRepr.getLoci(pickingId)
-        },
-        mark: (loci: Loci, action: MarkerAction) => {
-            return pointRepr.mark(loci, action)
-        },
-        destroy() {
-            pointRepr.destroy()
-        }
-    }
+    return Representation.createMulti('Point', PointParams, DefaultPointProps, [
+        UnitsRepresentation('Point', ElementPointVisual)
+    ] as StructureRepresentation<PointProps>[])
 }

+ 4 - 31
src/mol-repr/structure/representation/spacefill.ts

@@ -6,13 +6,9 @@
 
 import { ElementSphereVisual, ElementSphereParams } from '../visual/element-sphere';
 import { UnitsRepresentation } from '../units-representation';
-import { Structure } from 'mol-model/structure';
-import { Loci } from 'mol-model/loci';
-import { getQualityProps } from '../../util';
 import { paramDefaultValues } from 'mol-util/parameter';
-import { PickingId } from 'mol-geo/geometry/picking';
-import { MarkerAction } from 'mol-geo/geometry/marker-data';
 import { StructureRepresentation } from '../index';
+import { Representation } from 'mol-repr';
 
 export const SpacefillParams = {
     ...ElementSphereParams
@@ -23,30 +19,7 @@ export type SpacefillProps = typeof DefaultSpacefillProps
 export type SpacefillRepresentation = StructureRepresentation<SpacefillProps>
 
 export function SpacefillRepresentation(): SpacefillRepresentation {
-    let currentProps: SpacefillProps
-    const sphereRepr = UnitsRepresentation('Sphere mesh', ElementSphereVisual)
-    return {
-        label: 'Spacefill',
-        params: SpacefillParams,
-        get renderObjects() {
-            return [ ...sphereRepr.renderObjects ]
-        },
-        get props() {
-            return { ...sphereRepr.props }
-        },
-        createOrUpdate: (props: Partial<SpacefillProps> = {}, structure?: Structure) => {
-            const qualityProps = getQualityProps(Object.assign({}, currentProps, props), structure)
-            currentProps = Object.assign({}, DefaultSpacefillProps, currentProps, props, qualityProps)
-            return sphereRepr.createOrUpdate(currentProps, structure)
-        },
-        getLoci: (pickingId: PickingId) => {
-            return sphereRepr.getLoci(pickingId)
-        },
-        mark: (loci: Loci, action: MarkerAction) => {
-            return sphereRepr.mark(loci, action)
-        },
-        destroy() {
-            sphereRepr.destroy()
-        }
-    }
+    return Representation.createMulti('Spacefill', SpacefillParams, DefaultSpacefillProps, [
+        UnitsRepresentation('Sphere mesh', ElementSphereVisual)
+    ] as StructureRepresentation<SpacefillProps>[])
 }