Browse Source

Merge branch 'master' of https://github.com/molstar/molstar-proto into state_representation

David Sehnal 6 years ago
parent
commit
2d00b5d284
63 changed files with 355 additions and 317 deletions
  1. 3 2
      src/apps/canvas/assembly-symmetry.ts
  2. 2 1
      src/apps/canvas/component/representation.tsx
  3. 2 2
      src/apps/canvas/index.ts
  4. 2 3
      src/apps/canvas/structure-view.ts
  5. 1 2
      src/apps/canvas/volume-view.ts
  6. 1 1
      src/apps/structure-info/volume.ts
  7. 43 30
      src/mol-canvas3d/canvas3d.ts
  8. 8 2
      src/mol-geo/geometry/color-data.ts
  9. 1 1
      src/mol-geo/geometry/direct-volume/transfer-function.ts
  10. 3 4
      src/mol-geo/geometry/geometry.ts
  11. 0 2
      src/mol-geo/util/marching-cubes/algorithm.ts
  12. 10 2
      src/mol-gl/renderer.ts
  13. 3 3
      src/mol-math/geometry/gaussian-density.ts
  14. 4 20
      src/mol-math/geometry/gaussian-density/gpu.ts
  15. 5 4
      src/mol-model/structure/structure/unit.ts
  16. 7 10
      src/mol-model/structure/structure/unit/gaussian-density.ts
  17. 2 2
      src/mol-plugin/state/transforms/visuals.ts
  18. 19 5
      src/mol-repr/index.ts
  19. 4 4
      src/mol-repr/shape/index.ts
  20. 4 3
      src/mol-repr/structure/complex-representation.ts
  21. 8 9
      src/mol-repr/structure/complex-visual.ts
  22. 9 9
      src/mol-repr/structure/units-representation.ts
  23. 8 9
      src/mol-repr/structure/units-visual.ts
  24. 2 2
      src/mol-repr/structure/visual/carbohydrate-link-cylinder.ts
  25. 4 4
      src/mol-repr/structure/visual/carbohydrate-symbol-mesh.ts
  26. 2 2
      src/mol-repr/structure/visual/cross-link-restraint-cylinder.ts
  27. 4 4
      src/mol-repr/structure/visual/element-point.ts
  28. 5 5
      src/mol-repr/structure/visual/gaussian-density-point.ts
  29. 5 5
      src/mol-repr/structure/visual/gaussian-density-volume.ts
  30. 4 4
      src/mol-repr/structure/visual/gaussian-surface-mesh.ts
  31. 4 4
      src/mol-repr/structure/visual/gaussian-surface-wireframe.ts
  32. 2 2
      src/mol-repr/structure/visual/inter-unit-link-cylinder.ts
  33. 2 2
      src/mol-repr/structure/visual/intra-unit-link-cylinder.ts
  34. 4 4
      src/mol-repr/structure/visual/nucleotide-block-mesh.ts
  35. 4 4
      src/mol-repr/structure/visual/polymer-backbone-cylinder.ts
  36. 4 4
      src/mol-repr/structure/visual/polymer-direction-wedge.ts
  37. 4 4
      src/mol-repr/structure/visual/polymer-gap-cylinder.ts
  38. 4 4
      src/mol-repr/structure/visual/polymer-trace-mesh.ts
  39. 11 11
      src/mol-repr/structure/visual/util/common.ts
  40. 4 4
      src/mol-repr/structure/visual/util/element.ts
  41. 4 4
      src/mol-repr/structure/visual/util/link.ts
  42. 4 1
      src/mol-repr/util.ts
  43. 31 78
      src/mol-repr/volume/direct-volume.ts
  44. 11 11
      src/mol-repr/volume/index.ts
  45. 10 10
      src/mol-repr/volume/isosurface-mesh.ts
  46. 43 1
      src/mol-theme/color.ts
  47. 1 0
      src/mol-theme/color/carbohydrate-symbol.ts
  48. 1 0
      src/mol-theme/color/chain-id.ts
  49. 8 2
      src/mol-theme/color/cross-link.ts
  50. 1 0
      src/mol-theme/color/custom.ts
  51. 2 1
      src/mol-theme/color/element-index.ts
  52. 1 0
      src/mol-theme/color/element-symbol.ts
  53. 1 0
      src/mol-theme/color/molecule-type.ts
  54. 2 1
      src/mol-theme/color/polymer-index.ts
  55. 1 0
      src/mol-theme/color/residue-name.ts
  56. 1 0
      src/mol-theme/color/secondary-structure.ts
  57. 3 1
      src/mol-theme/color/sequence-id.ts
  58. 1 0
      src/mol-theme/color/shape-group.ts
  59. 1 0
      src/mol-theme/color/uniform.ts
  60. 1 0
      src/mol-theme/color/unit-index.ts
  61. 2 2
      src/mol-util/color/color.ts
  62. 2 3
      src/mol-util/color/index.ts
  63. 5 3
      src/mol-util/color/scale.ts

+ 3 - 2
src/apps/canvas/assembly-symmetry.ts

@@ -66,10 +66,10 @@ export function getClusterColorTheme(symmetryId: number, assemblySymmetry: Assem
     const DefaultColor = Color(0xCCCCCC)
     const s = assemblySymmetry.db.rcsb_assembly_symmetry
     const symmetry = Table.pickRow(s, i => s.id.value(i) === symmetryId)
-    if (!symmetry) return { granularity: 'uniform', color: () => DefaultColor }
+    if (!symmetry) return { features: {}, granularity: 'uniform', color: () => DefaultColor }
 
     const clusters = assemblySymmetry.getClusters(symmetryId)
-    if (!clusters._rowCount) return { granularity: 'uniform', color: () => DefaultColor }
+    if (!clusters._rowCount) return { features: {}, granularity: 'uniform', color: () => DefaultColor }
 
     const clusterByMember = new Map<string, number>()
     for (let i = 0, il = clusters._rowCount; i < il; ++i) {
@@ -83,6 +83,7 @@ export function getClusterColorTheme(symmetryId: number, assemblySymmetry: Assem
     const scale = ColorScale.create({ domain: [ 0, clusters._rowCount - 1 ] })
 
     return {
+        features: {},
         granularity: 'instance',
         color: (location: Location): Color => {
             if (StructureElement.isLocation(location)) {

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

@@ -41,7 +41,8 @@ export class RepresentationComponent extends React.Component<RepresentationCompo
     }
 
     async onChange(k: string, v: any) {
-        await this.props.app.runTask(this.props.repr.createOrUpdate({ [k]: v }).run(
+        const ctx = { webgl: this.props.canvas3d.webgl }
+        await this.props.app.runTask(this.props.repr.createOrUpdate(ctx, { [k]: v }).run(
             progress => this.props.app.log(progress)
         ), 'Representation Update')
         this.props.canvas3d.add(this.props.repr)

+ 2 - 2
src/apps/canvas/index.ts

@@ -25,8 +25,8 @@ if (pdbId) app.loadPdbIdOrMmcifUrl(pdbId, { assemblyId })
 
 // app.loadPdbIdOrMmcifUrl('http://localhost:8091/ngl/data/1crn.cif')
 
-app.loadPdbIdOrMmcifUrl('3pqr')
-app.loadCcp4Url('http://localhost:8091/ngl/data/3pqr-mode0.ccp4')
+// app.loadPdbIdOrMmcifUrl('3pqr')
+// app.loadCcp4Url('http://localhost:8091/ngl/data/3pqr-mode0.ccp4')
 
 // app.loadPdbIdOrMmcifUrl('1lee')
 // app.loadCcp4Url('http://localhost:8091/ngl/data/1lee.ccp4')

+ 2 - 3
src/apps/canvas/structure-view.ts

@@ -208,8 +208,7 @@ export async function StructureView(app: App, canvas3d: Canvas3D, models: Readon
             console.log('createStructureRepr')
             for (const k in structureRepresentations) {
                 if (active[k]) {
-                    const p = { webgl: canvas3d.webgl }
-                    await app.runTask(structureRepresentations[k].createOrUpdate(p, structure).run(
+                    await app.runTask(structureRepresentations[k].createOrUpdate({ webgl: canvas3d.webgl }, {}, structure).run(
                         progress => app.log(progress)
                     ), 'Create/update representation')
                     canvas3d.add(structureRepresentations[k])
@@ -265,7 +264,7 @@ export async function StructureView(app: App, canvas3d: Canvas3D, models: Readon
                     //     colorFunction: colorTheme.color,
                     //     colorGranularity: colorTheme.granularity,
                     // }).run()
-                    await symmetryAxes.createOrUpdate({}, axesShape).run()
+                    await symmetryAxes.createOrUpdate({ webgl: canvas3d.webgl }, {}, axesShape).run()
                     canvas3d.add(symmetryAxes)
                 } else {
                     canvas3d.remove(symmetryAxes)

+ 1 - 2
src/apps/canvas/volume-view.ts

@@ -54,8 +54,7 @@ export async function VolumeView(app: App, viewer: Canvas3D, volume: VolumeData,
     async function createVolumeRepr() {
         for (const k in volumeRepresentations) {
             if (active[k]) {
-                const p = { webgl: viewer.webgl }
-                await app.runTask(volumeRepresentations[k].createOrUpdate(p, volume).run(
+                await app.runTask(volumeRepresentations[k].createOrUpdate({ webgl: viewer.webgl }, {}, volume).run(
                     progress => app.log(progress)
                 ), 'Create/update representation')
                 viewer.add(volumeRepresentations[k])

+ 1 - 1
src/apps/structure-info/volume.ts

@@ -38,7 +38,7 @@ function print(data: Volume) {
 }
 
 async function doMesh(data: Volume, filename: string) {
-    const mesh = await Task.create('', ctx => createVolumeIsosurface(ctx, data.volume, { isoValueAbsolute: VolumeIsoValue.calcAbsolute(data.volume.dataStats, 1.5) } )).run();
+    const mesh = await Task.create('', runtime => createVolumeIsosurface({ runtime }, data.volume, { isoValueAbsolute: VolumeIsoValue.calcAbsolute(data.volume.dataStats, 1.5) } )).run();
     console.log({ vc: mesh.vertexCount, tc: mesh.triangleCount });
 
     // Export the mesh in OBJ format.

+ 43 - 30
src/mol-canvas3d/canvas3d.ts

@@ -24,10 +24,17 @@ import { PickingId, decodeIdRGB } from 'mol-geo/geometry/picking';
 import { MarkerAction } from 'mol-geo/geometry/marker-data';
 import { Loci, EmptyLoci, isEmptyLoci } from 'mol-model/loci';
 import { Color } from 'mol-util/color';
-import { CombinedCamera } from './camera/combined';
+import { CombinedCamera, CombinedCameraMode } from './camera/combined';
+
+export const DefaultCanvas3DProps = {
+    cameraPosition: Vec3.create(0, 0, 50),
+    cameraMode: 'perspective' as CombinedCameraMode,
+    backgroundColor: Color(0x000000),
+}
+export type Canvas3DProps = typeof DefaultCanvas3DProps
 
 interface Canvas3D {
-    webgl: WebGLContext,
+    readonly webgl: WebGLContext,
 
     center: (p: Vec3) => void
 
@@ -47,23 +54,27 @@ interface Canvas3D {
     mark: (loci: Loci, action: MarkerAction) => void
     getLoci: (pickingId: PickingId) => Loci
 
-    reprCount: BehaviorSubject<number>
-    identified: BehaviorSubject<string>
-    didDraw: BehaviorSubject<number>
+    readonly reprCount: BehaviorSubject<number>
+    readonly identified: BehaviorSubject<string>
+    readonly didDraw: BehaviorSubject<number>
 
     handleResize: () => void
     resetCamera: () => void
-    camera: CombinedCamera
+    readonly camera: CombinedCamera
     downloadScreenshot: () => void
     getImageData: (variant: RenderVariant) => ImageData
 
-    input: InputObserver
-    stats: RendererStats
+    /** Returns a copy of the current Canvas3D instance props */
+    readonly props: Canvas3DProps
+    readonly input: InputObserver
+    readonly stats: RendererStats
     dispose: () => void
 }
 
 namespace Canvas3D {
-    export function create(canvas: HTMLCanvasElement, container: Element): 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 reprCount = new BehaviorSubject(0)
         const identified = new BehaviorSubject('')
@@ -75,14 +86,9 @@ namespace Canvas3D {
         const camera = CombinedCamera.create({
             near: 0.1,
             far: 10000,
-            position: Vec3.create(0, 0, 50),
-            mode: 'orthographic'
+            position: Vec3.clone(p.cameraPosition),
+            mode: p.cameraMode
         })
-        // const camera = OrthographicCamera.create({
-        //     zoom: 8,
-        //     position: Vec3.create(0, 0, 50)
-        // })
-        // camera.lookAt(Vec3.create(0, 0, 0))
 
         const gl = getGLContext(canvas, {
             alpha: false,
@@ -93,18 +99,18 @@ namespace Canvas3D {
         if (gl === null) {
             throw new Error('Could not create a WebGL rendering context')
         }
-        const ctx = createContext(gl)
+        const webgl = createContext(gl)
 
-        const scene = Scene.create(ctx)
+        const scene = Scene.create(webgl)
         const controls = TrackballControls.create(input, camera, {})
-        const renderer = Renderer.create(ctx, camera, { clearColor: Color(0x000000) })
+        const renderer = Renderer.create(webgl, camera, { clearColor: p.backgroundColor })
 
         const pickScale = 1
         const pickWidth = Math.round(canvas.width * pickScale)
         const pickHeight = Math.round(canvas.height * pickScale)
-        const objectPickTarget = createRenderTarget(ctx, pickWidth, pickHeight)
-        const instancePickTarget = createRenderTarget(ctx, pickWidth, pickHeight)
-        const groupPickTarget = createRenderTarget(ctx, pickWidth, pickHeight)
+        const objectPickTarget = createRenderTarget(webgl, pickWidth, pickHeight)
+        const instancePickTarget = createRenderTarget(webgl, pickWidth, pickHeight)
+        const groupPickTarget = createRenderTarget(webgl, pickWidth, pickHeight)
 
         let pickDirty = true
         let isPicking = false
@@ -173,7 +179,7 @@ namespace Canvas3D {
                 case 'pickInstance': instancePickTarget.bind(); break;
                 case 'pickGroup': groupPickTarget.bind(); break;
                 case 'draw':
-                    ctx.unbindFramebuffer();
+                    webgl.unbindFramebuffer();
                     renderer.setViewport(0, 0, canvas.width, canvas.height);
                     break;
             }
@@ -219,7 +225,7 @@ namespace Canvas3D {
             render('pickObject', pickDirty)
             render('pickInstance', pickDirty)
             render('pickGroup', pickDirty)
-            ctx.gl.finish()
+            webgl.gl.finish()
 
             pickDirty = false
         }
@@ -229,8 +235,8 @@ namespace Canvas3D {
 
             isPicking = true
 
-            x *= ctx.pixelRatio
-            y *= ctx.pixelRatio
+            x *= webgl.pixelRatio
+            y *= webgl.pixelRatio
             y = canvas.height - y // flip y
 
             const buffer = new Uint8Array(4)
@@ -238,15 +244,15 @@ namespace Canvas3D {
             const yp = Math.round(y * pickScale)
 
             objectPickTarget.bind()
-            await ctx.readPixelsAsync(xp, yp, 1, 1, buffer)
+            await webgl.readPixelsAsync(xp, yp, 1, 1, buffer)
             const objectId = decodeIdRGB(buffer[0], buffer[1], buffer[2])
 
             instancePickTarget.bind()
-            await ctx.readPixels(xp, yp, 1, 1, buffer)
+            await webgl.readPixels(xp, yp, 1, 1, buffer)
             const instanceId = decodeIdRGB(buffer[0], buffer[1], buffer[2])
 
             groupPickTarget.bind()
-            await ctx.readPixels(xp, yp, 1, 1, buffer)
+            await webgl.readPixels(xp, yp, 1, 1, buffer)
             const groupId = decodeIdRGB(buffer[0], buffer[1], buffer[2])
 
             isPicking = false
@@ -262,7 +268,7 @@ namespace Canvas3D {
         handleResize()
 
         return {
-            webgl: ctx,
+            webgl,
 
             center: (p: Vec3) => {
                 Vec3.set(controls.target, p[0], p[1], p[2])
@@ -336,6 +342,13 @@ namespace Canvas3D {
             identified,
             didDraw,
 
+            get props() {
+                return {
+                    cameraPosition: Vec3.clone(camera.position),
+                    cameraMode: camera.mode,
+                    backgroundColor: renderer.props.clearColor
+                }
+            },
             get input() {
                 return input
             },

+ 8 - 2
src/mol-geo/geometry/color-data.ts

@@ -6,11 +6,11 @@
 
 import { ValueCell } from 'mol-util';
 import { TextureImage, createTextureImage } from 'mol-gl/renderable/util';
-import { Color } from 'mol-util/color';
+import { Color, ColorMap } from 'mol-util/color';
 import { Vec2, Vec3 } from 'mol-math/linear-algebra';
 import { LocationIterator } from '../util/location-iterator';
 import { NullLocation } from 'mol-model/location';
-import { LocationColor, ColorThemeProps, ColorTheme, ColorThemeName, ScaleLegend, TableLegend } from 'mol-theme/color';
+import { LocationColor, ColorThemeProps, ColorTheme, ColorThemeName, ScaleLegend, TableLegend, ColorScaleName, getColorScaleFromName } from 'mol-theme/color';
 import { RuntimeContext } from 'mol-task';
 import { getGranularity } from './geometry';
 import { Structure } from 'mol-model/structure';
@@ -27,6 +27,8 @@ export type ColorData = {
 
 export interface ColorProps {
     colorTheme: ColorThemeName
+    colorList?: Color[] | ColorScaleName
+    colorMap?: ColorMap<any>
     colorDomain?: [number, number]
     colorValue?: Color
     colorFunction?: LocationColor,
@@ -41,6 +43,10 @@ export function getColorThemeProps(props: ColorProps): ColorThemeProps {
         name: props.colorTheme
     }
     if (props.colorDomain !== undefined) p.domain = props.colorDomain
+    if (props.colorList !== undefined) {
+        p.list = typeof props.colorList === 'string' ? getColorScaleFromName(props.colorList) : props.colorList
+    }
+    if (props.colorMap !== undefined) p.map = props.colorMap
     if (props.colorValue !== undefined) p.value = props.colorValue
     if (props.structure !== undefined) p.structure = props.structure
     if (props.colorFunction !== undefined) p.color = props.colorFunction

+ 1 - 1
src/mol-geo/geometry/direct-volume/transfer-function.ts

@@ -29,7 +29,7 @@ export function createTransferFunctionTexture(controlPoints: ControlPoint[], tex
     ]
     const scale = ColorScale.create({
         domain: [0, 1],
-        colors: ColorMatplotlib.viridis
+        list: ColorMatplotlib.viridis
     })
 
     const n = 256

+ 3 - 4
src/mol-geo/geometry/geometry.ts

@@ -10,14 +10,13 @@ import { RenderableState } from 'mol-gl/renderable';
 import { ValueCell } from 'mol-util';
 import { BaseValues } from 'mol-gl/renderable/schema';
 import { Color } from 'mol-util/color';
-import { ColorThemeOptions, ColorThemeName } from 'mol-theme/color';
+import { ColorThemeOptions, ColorThemeName, ColorScaleOptions, ColorScaleName } from 'mol-theme/color';
 import { LocationIterator } from '../util/location-iterator';
 import { ColorType } from './color-data';
 import { SizeType } from './size-data';
 import { Lines } from './lines/lines';
-import { paramDefaultValues, RangeParam, BooleanParam, SelectParam, ColorParam, ValueParam } from 'mol-util/parameter'
+import { paramDefaultValues, RangeParam, BooleanParam, SelectParam, ColorParam } from 'mol-util/parameter'
 import { DirectVolume } from './direct-volume/direct-volume';
-import { WebGLContext } from 'mol-gl/webgl/context';
 
 //
 
@@ -66,8 +65,8 @@ export namespace Geometry {
         useFog: BooleanParam('Use Fog', '', false),
         quality: SelectParam<VisualQuality>('Quality', '', 'auto', VisualQualityOptions),
         colorTheme: SelectParam<ColorThemeName>('Color Theme', '', 'uniform', ColorThemeOptions),
+        colorList: SelectParam<ColorScaleName>('Color Scale', '', 'default', ColorScaleOptions),
         colorValue: ColorParam('Color Value', '', Color(0xCCCCCC)),
-        webgl: ValueParam('WebGL Context', '', undefined as WebGLContext | undefined),
     }
     export const DefaultProps = paramDefaultValues(Params)
     export type Props = typeof DefaultProps

+ 0 - 2
src/mol-geo/util/marching-cubes/algorithm.ts

@@ -105,9 +105,7 @@ class MarchingCubesComputation {
     }
 
     async run() {
-        await this.ctx.update({ message: 'Computing surface...', current: 0, max: this.size });
         await this.doSlices();
-        await this.ctx.update('Finalizing...');
     }
 
     constructor(private ctx: RuntimeContext, builder: MarchinCubesBuilder<any>, params: MarchingCubesInputParams) {

+ 10 - 2
src/mol-gl/renderer.ts

@@ -34,6 +34,7 @@ export interface RendererStats {
 
 interface Renderer {
     readonly stats: RendererStats
+    readonly props: RendererProps
 
     render: (scene: Scene, variant: RenderVariant) => void
     setViewport: (x: number, y: number, width: number, height: number) => void
@@ -46,10 +47,10 @@ export const DefaultRendererProps = {
     clearColor: 0x000000 as Color,
     viewport: Viewport.create(0, 0, 0, 0)
 }
-export type RendererProps = Partial<typeof DefaultRendererProps>
+export type RendererProps = typeof DefaultRendererProps
 
 namespace Renderer {
-    export function create(ctx: WebGLContext, camera: Camera, props: RendererProps = {}): Renderer {
+    export function create(ctx: WebGLContext, camera: Camera, props: Partial<RendererProps> = {}): Renderer {
         const { gl } = ctx
         let { clearColor, viewport: _viewport } = { ...DefaultRendererProps, ...props }
 
@@ -64,6 +65,7 @@ namespace Renderer {
         const fogColor = Vec3.create(0.0, 0.0, 0.0)
 
         function setClearColor(color: Color) {
+            clearColor = color
             const [ r, g, b ] = Color.toRgbNormalized(color)
             gl.clearColor(r, g, b, 1.0)
         }
@@ -192,6 +194,12 @@ namespace Renderer {
                 return createImageData(buffer, width, height)
             },
 
+            get props() {
+                return {
+                    clearColor,
+                    viewport
+                }
+            },
             get stats(): RendererStats {
                 return {
                     programCount: ctx.programCache.count,

+ 3 - 3
src/mol-math/geometry/gaussian-density.ts

@@ -21,7 +21,6 @@ export const DefaultGaussianDensityProps = {
     radiusOffset: 0,
     smoothness: 1.5,
     useGpu: true,
-    webgl: undefined as WebGLContext | undefined
 }
 export type GaussianDensityProps = typeof DefaultGaussianDensityProps
 
@@ -39,10 +38,11 @@ export function computeGaussianDensity(position: PositionData, box: Box3D, radiu
     });
 }
 
-export async function GaussianDensity(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number,  props: GaussianDensityProps): Promise<DensityData> {
+export async function GaussianDensity(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number,  props: GaussianDensityProps, webgl?: WebGLContext): Promise<DensityData> {
     if (props.useGpu) {
         if (!GaussianDensityGPU) throw 'GPU computation not supported on this platform';
-        return await GaussianDensityGPU(ctx, position, box, radius, props)
+        if (!webgl) throw 'No WebGL context provided';
+        return await GaussianDensityGPU(ctx, position, box, radius, props, webgl)
     } else {
         return await GaussianDensityCPU(ctx, position, box, radius, props)
     }

+ 4 - 20
src/mol-math/geometry/gaussian-density/gpu.ts

@@ -12,10 +12,10 @@ import { GaussianDensityProps, getDelta } from '../gaussian-density'
 import { OrderedSet } from 'mol-data/int'
 import { Vec3, Tensor, Mat4 } from '../../linear-algebra'
 import { GaussianDensityValues } from 'mol-gl/renderable/gaussian-density'
-import { ValueCell, defaults } from 'mol-util'
+import { ValueCell } from 'mol-util'
 import { RenderableState, Renderable } from 'mol-gl/renderable'
 import { createRenderable, createGaussianDensityRenderObject } from 'mol-gl/render-object'
-import { WebGLContext, createContext, getGLContext } from 'mol-gl/webgl/context';
+import { WebGLContext } from 'mol-gl/webgl/context';
 import { createTexture, Texture } from 'mol-gl/webgl/texture';
 import { GLRenderingContext } from 'mol-gl/webgl/compat';
 import { decodeIdRGB } from 'mol-geo/geometry/picking';
@@ -23,8 +23,7 @@ import { decodeIdRGB } from 'mol-geo/geometry/picking';
 /** name for shared framebuffer used for gpu gaussian surface operations */
 const FramebufferName = 'gaussian-density-gpu'
 
-export async function GaussianDensityGPU(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps): Promise<DensityData> {
-    const webgl = defaults(props.webgl, getWebGLContext())
+export async function GaussianDensityGPU(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps, webgl: WebGLContext): Promise<DensityData> {
     // always use texture2d when the gaussian density needs to be downloaded from the GPU,
     // it's faster than texture3d
     // console.time('GaussianDensityTexture2d')
@@ -110,7 +109,7 @@ async function GaussianDensityTexture2d(ctx: RuntimeContext, webgl: WebGLContext
     setupGroupIdRendering(webgl, renderable)
     render(texture)
 
-    await ctx.update({ message: 'gpu gaussian density calculation' });
+    if (ctx.shouldUpdate) await ctx.update({ message: 'gpu gaussian density calculation' })
     await webgl.waitForGpuCommandsComplete()
 
     return { texture, scale: Vec3.inverse(Vec3.zero(), delta), bbox: expandedBox, dim }
@@ -165,21 +164,6 @@ async function GaussianDensityTexture3d(ctx: RuntimeContext, webgl: WebGLContext
 
 //
 
-let webglContext: WebGLContext
-function getWebGLContext() {
-    if (webglContext) return webglContext
-    const canvas = document.createElement('canvas')
-    const gl = getGLContext(canvas, {
-        alpha: true,
-        antialias: false,
-        depth: false,
-        preserveDrawingBuffer: true
-    })
-    if (!gl) throw new Error('Could not create a WebGL rendering context')
-    webglContext = createContext(gl)
-    return webglContext
-}
-
 async function prepareGaussianDensityData(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps) {
     const { resolution, radiusOffset } = props
 

+ 5 - 4
src/mol-model/structure/structure/unit.ts

@@ -20,6 +20,7 @@ import { getAtomicPolymerElements, getCoarsePolymerElements, getAtomicGapElement
 import { getNucleotideElements } from './util/nucleotide';
 import { GaussianDensityProps, computeUnitGaussianDensityCached } from './unit/gaussian-density';
 import { RuntimeContext } from 'mol-task';
+import { WebGLContext } from 'mol-gl/webgl/context';
 
 // A building block of a structure that corresponds to an atomic or a coarse grained representation
 // 'conveniently grouped together'.
@@ -182,8 +183,8 @@ namespace Unit {
             return this.model.atomicHierarchy.residueAtomSegments.index[this.elements[elementIndex]];
         }
 
-        async computeGaussianDensity(props: GaussianDensityProps, ctx?: RuntimeContext) {
-            return computeUnitGaussianDensityCached(this, props, this.props.gaussianDensities, ctx);
+        async computeGaussianDensity(props: GaussianDensityProps, ctx: RuntimeContext, webgl?: WebGLContext) {
+            return computeUnitGaussianDensityCached(this, props, this.props.gaussianDensities, ctx, webgl);
         }
 
         constructor(id: number, invariantId: number, model: Model, elements: StructureElement.Set, conformation: SymmetryOperator.ArrayMapping, props: AtomicProperties) {
@@ -271,8 +272,8 @@ namespace Unit {
             return this.kind === Kind.Spheres ? this.model.coarseConformation.spheres : this.model.coarseConformation.gaussians;
         }
 
-        async computeGaussianDensity(props: GaussianDensityProps, ctx?: RuntimeContext): Promise<DensityData> {
-            return computeUnitGaussianDensityCached(this as Unit.Spheres | Unit.Gaussians, props, this.props.gaussianDensities, ctx); // TODO get rid of casting
+        async computeGaussianDensity(props: GaussianDensityProps, ctx: RuntimeContext, webgl?: WebGLContext): Promise<DensityData> {
+            return computeUnitGaussianDensityCached(this as Unit.Spheres | Unit.Gaussians, props, this.props.gaussianDensities, ctx, webgl); // TODO get rid of casting
         }
 
         constructor(id: number, invariantId: number, model: Model, kind: K, elements: StructureElement.Set, conformation: SymmetryOperator.ArrayMapping, props: CoarseProperties) {

+ 7 - 10
src/mol-model/structure/structure/unit/gaussian-density.ts

@@ -9,10 +9,10 @@ import { SizeTheme } from 'mol-theme/size';
 import { GaussianDensity } from 'mol-math/geometry/gaussian-density';
 import { Task, RuntimeContext } from 'mol-task';
 import { DensityData } from 'mol-math/geometry';
-import { NumberParam, paramDefaultValues, BooleanParam, ValueParam } from 'mol-util/parameter';
-import { WebGLContext } from 'mol-gl/webgl/context';
+import { NumberParam, paramDefaultValues, BooleanParam } from 'mol-util/parameter';
 import { GaussianDensityTexture } from 'mol-math/geometry/gaussian-density/gpu';
 import { Texture } from 'mol-gl/webgl/texture';
+import { WebGLContext } from 'mol-gl/webgl/context';
 
 export const GaussianDensityParams = {
     resolution: NumberParam('Resolution', '', 1, 0.1, 10, 0.1),
@@ -20,7 +20,6 @@ export const GaussianDensityParams = {
     smoothness: NumberParam('Smoothness', '', 1.5, 0.5, 2.5, 0.1),
     useGpu: BooleanParam('Use GPU', '', true),
     ignoreCache: BooleanParam('Ignore Cache', '', false),
-    webgl: ValueParam('WebGL Context', '', undefined as WebGLContext | undefined),
 }
 export const DefaultGaussianDensityProps = paramDefaultValues(GaussianDensityParams)
 export type GaussianDensityProps = typeof DefaultGaussianDensityProps
@@ -53,27 +52,25 @@ function getConformationAndRadius(unit: Unit) {
     return { position, radius }
 }
 
-export function computeUnitGaussianDensity(unit: Unit, props: GaussianDensityProps) {
+export function computeUnitGaussianDensity(unit: Unit, props: GaussianDensityProps, webgl?: WebGLContext) {
     const { position, radius } = getConformationAndRadius(unit)
     return Task.create('Gaussian Density', async ctx => {
-        return await GaussianDensity(ctx, position, unit.lookup3d.boundary.box, radius, props);
+        return await GaussianDensity(ctx, position, unit.lookup3d.boundary.box, radius, props, webgl);
     });
 }
 
-export function computeUnitGaussianDensityTexture(unit: Unit, props: GaussianDensityProps, texture?: Texture) {
-    const webgl = props.webgl
-    if (!webgl) throw new Error('nned webgl context for computeUnitGaussianDensityTexture')
+export function computeUnitGaussianDensityTexture(unit: Unit, props: GaussianDensityProps, webgl: WebGLContext, texture?: Texture) {
     const { position, radius } = getConformationAndRadius(unit)
     return Task.create('Gaussian Density', async ctx => {
         return await GaussianDensityTexture(ctx, webgl, position, unit.lookup3d.boundary.box, radius, props, texture);
     });
 }
 
-export async function computeUnitGaussianDensityCached(unit: Unit, props: GaussianDensityProps, cache: Map<string, DensityData>, ctx?: RuntimeContext) {
+export async function computeUnitGaussianDensityCached(unit: Unit, props: GaussianDensityProps, cache: Map<string, DensityData>, ctx: RuntimeContext, webgl?: WebGLContext) {
     const key = `${props.radiusOffset}|${props.resolution}|${props.smoothness}`
     let density = cache.get(key)
     if (density && !props.ignoreCache) return density
-    density = ctx ? await computeUnitGaussianDensity(unit, props).runInContext(ctx) : await computeUnitGaussianDensity(unit, props).run()
+    density = await computeUnitGaussianDensity(unit, props, webgl).runInContext(ctx)
     if (!props.ignoreCache) cache.set(key, density)
     return density
 }

+ 2 - 2
src/mol-plugin/state/transforms/visuals.ts

@@ -18,13 +18,13 @@ export const CreateStructureRepresentation = PluginStateTransform.Create<SO.Stru
     apply({ a, params }) {
         return Task.create('Structure Representation', async ctx => {
             const repr = CartoonRepresentation();
-            await repr.createOrUpdate({ ...DefaultCartoonProps }, a.data).runInContext(ctx);
+            await repr.createOrUpdate({ /* TODO add `webgl: WebGLContext` */ }, { ...DefaultCartoonProps }, a.data).runInContext(ctx);
             return new SO.StructureRepresentation3D({ label: 'Cartoon' }, { repr });
         });
     },
     update({ a, b }) {
         return Task.create('Structure Representation', async ctx => {
-            await b.data.repr.createOrUpdate(b.data.repr.props, a.data).runInContext(ctx);
+            await b.data.repr.createOrUpdate({ /* TODO add `webgl: WebGLContext` */ }, b.data.repr.props, a.data).runInContext(ctx);
             return Transformer.UpdateResult.Updated;
         });
     }

+ 19 - 5
src/mol-repr/index.ts

@@ -10,18 +10,24 @@ import { PickingId } from '../mol-geo/geometry/picking';
 import { Loci, isEmptyLoci, EmptyLoci } from 'mol-model/loci';
 import { MarkerAction } from '../mol-geo/geometry/marker-data';
 import { Params, MultiSelectParam } from 'mol-util/parameter';
+import { WebGLContext } from 'mol-gl/webgl/context';
+// import { ColorTheme } from 'mol-theme/color';
 
 // export interface RepresentationProps {
 //     visuals?: string[]
 // }
 export type RepresentationProps = { [k: string]: any }
 
+export interface RepresentationContext {
+    webgl?: WebGLContext
+}
+
 export interface Representation<D, P extends RepresentationProps = {}> {
     readonly label: string
     readonly params: Params
     readonly renderObjects: ReadonlyArray<RenderObject>
     readonly props: Readonly<P>
-    createOrUpdate: (props?: Partial<P>, data?: D) => Task<void>
+    createOrUpdate: (ctx: RepresentationContext, props?: Partial<P>, data?: D) => Task<void>
     getLoci: (pickingId: PickingId) => Loci
     mark: (loci: Loci, action: MarkerAction) => boolean
     destroy: () => void
@@ -60,17 +66,17 @@ export namespace Representation {
                 reprList.forEach(r => Object.assign(props, r.props))
                 return props as P
             },
-            createOrUpdate: (props: Partial<P> = {}, data?: D) => {
+            createOrUpdate: (ctx: RepresentationContext, 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 => {
+                return Task.create(`Creating '${label}' representation`, async runtime => {
                     for (let i = 0, il = reprList.length; i < il; ++i) {
                         if (!visuals || visuals.includes(i.toString())) {
-                            await reprList[i].createOrUpdate(currentProps, currentData).runInContext(ctx)
+                            await reprList[i].createOrUpdate(ctx, currentProps, currentData).runInContext(runtime)
                         }
                     }
                 })
@@ -98,9 +104,17 @@ export namespace Representation {
     }
 }
 
+//
+
+export interface VisualContext extends RepresentationContext {
+    runtime: RuntimeContext,
+    // TODO
+    // colorTheme: ColorTheme,
+}
+
 export interface Visual<D, P extends RepresentationProps> {
     readonly renderObject: RenderObject | undefined
-    createOrUpdate: (ctx: RuntimeContext, props?: Partial<P>, data?: D) => Promise<void>
+    createOrUpdate: (ctx: VisualContext, props?: Partial<P>, data?: D) => Promise<void>
     getLoci: (pickingId: PickingId) => Loci
     mark: (loci: Loci, action: MarkerAction) => boolean
     destroy: () => void

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

@@ -6,7 +6,7 @@
 
 import { Task } from 'mol-task'
 import { RenderObject, createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object';
-import { RepresentationProps, Representation } from '..';
+import { RepresentationProps, Representation, RepresentationContext } from '..';
 import { Loci, EmptyLoci, isEveryLoci } from 'mol-model/loci';
 import { ValueCell } from 'mol-util';
 import { ColorThemeName, ColorThemeOptions } from 'mol-theme/color';
@@ -38,11 +38,11 @@ export function ShapeRepresentation<P extends ShapeProps>(): ShapeRepresentation
     let _shape: Shape
     let currentProps: P
 
-    function createOrUpdate(props: Partial<P> = {}, shape?: Shape) {
+    function createOrUpdate(ctx: RepresentationContext, props: Partial<P> = {}, shape?: Shape) {
         currentProps = Object.assign({}, DefaultShapeProps, currentProps, props)
         if (shape) _shape = shape
 
-        return Task.create('ShapeRepresentation.create', async ctx => {
+        return Task.create('ShapeRepresentation.create', async runtime => {
             renderObjects.length = 0
 
             if (!_shape) return
@@ -51,7 +51,7 @@ export function ShapeRepresentation<P extends ShapeProps>(): ShapeRepresentation
             const locationIt = ShapeGroupIterator.fromShape(_shape)
             const transform = createIdentityTransform()
 
-            const values = await Mesh.createValues(ctx, mesh, transform, locationIt, currentProps)
+            const values = await Mesh.createValues(runtime, mesh, transform, locationIt, currentProps)
             const state = createRenderableState(currentProps)
 
             _renderObject = createMeshRenderObject(values, state)

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

@@ -12,17 +12,18 @@ import { StructureProps, StructureRepresentation, StructureParams } from './inde
 import { ComplexVisual } from './complex-visual';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { MarkerAction } from 'mol-geo/geometry/marker-data';
+import { RepresentationContext } from 'mol-repr';
 
 export function ComplexRepresentation<P extends StructureProps>(label: string, visualCtor: () => ComplexVisual<P>): StructureRepresentation<P> {
     let visual: ComplexVisual<P> | undefined
     let _props: P
 
-    function createOrUpdate(props: Partial<P> = {}, structure?: Structure) {
+    function createOrUpdate(ctx: RepresentationContext, props: Partial<P> = {}, structure?: Structure) {
         _props = Object.assign({}, _props, props)
 
-        return Task.create('Creating or updating ComplexRepresentation', async ctx => {
+        return Task.create('Creating or updating ComplexRepresentation', async runtime => {
             if (!visual) visual = visualCtor()
-            await visual.createOrUpdate(ctx, _props, structure)
+            await visual.createOrUpdate({ ...ctx, runtime }, _props, structure)
         });
     }
 

+ 8 - 9
src/mol-repr/structure/complex-visual.ts

@@ -5,9 +5,8 @@
  */
 
 import { Structure } from 'mol-model/structure';
-import { Visual } from '..';
+import { Visual, VisualContext } from '..';
 import { MeshRenderObject, LinesRenderObject, PointsRenderObject, DirectVolumeRenderObject } from 'mol-gl/render-object';
-import { RuntimeContext } from 'mol-task';
 import { createComplexMeshRenderObject, UnitKind, UnitKindOptions } from './visual/util/common';
 import { StructureProps, StructureMeshParams, StructureParams } from './index';
 import { deepEqual, ValueCell } from 'mol-util';
@@ -37,7 +36,7 @@ type ComplexRenderObject = MeshRenderObject | LinesRenderObject | PointsRenderOb
 
 interface ComplexVisualBuilder<P extends ComplexProps, G extends Geometry> {
     defaultProps: P
-    createGeometry(ctx: RuntimeContext, structure: Structure, props: P, geometry?: G): Promise<G>
+    createGeometry(ctx: VisualContext, structure: Structure, props: P, geometry?: G): Promise<G>
     createLocationIterator(structure: Structure): LocationIterator
     getLoci(pickingId: PickingId, structure: Structure, id: number): Loci
     mark(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean): boolean,
@@ -46,7 +45,7 @@ interface ComplexVisualBuilder<P extends ComplexProps, G extends Geometry> {
 
 interface ComplexVisualGeometryBuilder<P extends ComplexProps, G extends Geometry> extends ComplexVisualBuilder<P, G> {
     createEmptyGeometry(geometry?: G): G
-    createRenderObject(ctx: RuntimeContext, structure: Structure, geometry: Geometry, locationIt: LocationIterator, currentProps: P): Promise<ComplexRenderObject>
+    createRenderObject(ctx: VisualContext, structure: Structure, geometry: Geometry, locationIt: LocationIterator, currentProps: P): Promise<ComplexRenderObject>
     updateValues(values: RenderableValues, newProps: P): void
 }
 
@@ -62,7 +61,7 @@ export function ComplexVisual<P extends ComplexMeshProps>(builder: ComplexVisual
     let locationIt: LocationIterator
     let conformationHash: number
 
-    async function create(ctx: RuntimeContext, structure: Structure, props: Partial<P> = {}) {
+    async function create(ctx: VisualContext, structure: Structure, props: Partial<P> = {}) {
         currentProps = Object.assign({}, defaultProps, props, { structure })
         currentStructure = structure
 
@@ -73,7 +72,7 @@ export function ComplexVisual<P extends ComplexMeshProps>(builder: ComplexVisual
         renderObject = await createRenderObject(ctx, structure, geometry, locationIt, currentProps)
     }
 
-    async function update(ctx: RuntimeContext, props: Partial<P>) {
+    async function update(ctx: VisualContext, props: Partial<P>) {
         const newProps = Object.assign({}, currentProps, props, { structure: currentStructure })
 
         if (!renderObject) return false
@@ -102,12 +101,12 @@ export function ComplexVisual<P extends ComplexMeshProps>(builder: ComplexVisual
         if (updateState.updateSize) {
             // not all geometries have size data, so check here
             if ('uSize' in renderObject.values) {
-                await createSizes(ctx, locationIt, newProps, renderObject.values)
+                await createSizes(ctx.runtime, locationIt, newProps, renderObject.values)
             }
         }
 
         if (updateState.updateColor) {
-            await createColors(ctx, locationIt, newProps, renderObject.values)
+            await createColors(ctx.runtime, locationIt, newProps, renderObject.values)
         }
 
         updateValues(renderObject.values, newProps)
@@ -119,7 +118,7 @@ export function ComplexVisual<P extends ComplexMeshProps>(builder: ComplexVisual
 
     return {
         get renderObject () { return renderObject },
-        async createOrUpdate(ctx: RuntimeContext, props: Partial<P> = {}, structure?: Structure) {
+        async createOrUpdate(ctx: VisualContext, props: Partial<P> = {}, structure?: Structure) {
             if (!structure && !currentStructure) {
                 throw new Error('missing structure')
             } else if (structure && (!currentStructure || !renderObject)) {

+ 9 - 9
src/mol-repr/structure/units-representation.ts

@@ -8,7 +8,7 @@
 import { Structure, Unit } from 'mol-model/structure';
 import { Task } from 'mol-task'
 import { RenderObject } from 'mol-gl/render-object';
-import { RepresentationProps, Visual } from '..';
+import { RepresentationProps, Visual, RepresentationContext } from '..';
 import { Loci, EmptyLoci, isEmptyLoci } from 'mol-model/loci';
 import { StructureGroup } from './units-visual';
 import { StructureProps, StructureParams, StructureRepresentation } from './index';
@@ -24,10 +24,10 @@ export function UnitsRepresentation<P extends StructureProps>(label: string, vis
     let _structure: Structure
     let _groups: ReadonlyArray<Unit.SymmetryGroup>
 
-    function createOrUpdate(props: Partial<P> = {}, structure?: Structure) {
+    function createOrUpdate(ctx: RepresentationContext, props: Partial<P> = {}, structure?: Structure) {
         _props = Object.assign({}, _props, props)
 
-        return Task.create('Creating or updating UnitsRepresentation', async ctx => {
+        return Task.create('Creating or updating UnitsRepresentation', async runtime => {
             if (!_structure && !structure) {
                 throw new Error('missing structure')
             } else if (structure && !_structure) {
@@ -37,7 +37,7 @@ export function UnitsRepresentation<P extends StructureProps>(label: string, vis
                 for (let i = 0; i < _groups.length; i++) {
                     const group = _groups[i];
                     const visual = visualCtor()
-                    await visual.createOrUpdate(ctx, _props, { group, structure })
+                    await visual.createOrUpdate({ ...ctx, runtime }, _props, { group, structure })
                     visuals.set(group.hashCode, { visual, group })
                 }
             } else if (structure && _structure.hashCode !== structure.hashCode) {
@@ -53,13 +53,13 @@ export function UnitsRepresentation<P extends StructureProps>(label: string, vis
                     const visualGroup = oldVisuals.get(group.hashCode)
                     if (visualGroup) {
                         const { visual } = visualGroup
-                        await visual.createOrUpdate(ctx, _props, { group, structure })
+                        await visual.createOrUpdate({ ...ctx, runtime }, _props, { group, structure })
                         visuals.set(group.hashCode, { visual, group })
                         oldVisuals.delete(group.hashCode)
                     } else {
                         // newGroups.push(group)
                         const visual = visualCtor()
-                        await visual.createOrUpdate(ctx, _props, { group, structure })
+                        await visual.createOrUpdate({ ...ctx, runtime }, _props, { group, structure })
                         visuals.set(group.hashCode, { visual, group })
                     }
                 }
@@ -71,7 +71,7 @@ export function UnitsRepresentation<P extends StructureProps>(label: string, vis
                 // oldVisuals.forEach(({ visual }) => unusedVisuals.push(visual))
                 // newGroups.forEach(async group => {
                 //     const visual = unusedVisuals.pop() || visualCtor()
-                //     await visual.createOrUpdate(ctx, _props, group)
+                //     await visual.createOrUpdate({ ...ctx, runtime }, _props, group)
                 //     visuals.set(group.hashCode, { visual, group })
                 // })
                 // unusedVisuals.forEach(visual => visual.destroy())
@@ -85,7 +85,7 @@ export function UnitsRepresentation<P extends StructureProps>(label: string, vis
                     const group = _groups[i];
                     const visualGroup = visuals.get(group.hashCode)
                     if (visualGroup) {
-                        await visualGroup.visual.createOrUpdate(ctx, _props, { group, structure })
+                        await visualGroup.visual.createOrUpdate({ ...ctx, runtime }, _props, { group, structure })
                         visualGroup.group = group
                     } else {
                         throw new Error(`expected to find visual for hashCode ${group.hashCode}`)
@@ -98,7 +98,7 @@ export function UnitsRepresentation<P extends StructureProps>(label: string, vis
                 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, _props, { group, structure: _structure })
+                    await visual.createOrUpdate({ ...ctx, runtime }, _props, { group, structure: _structure })
                 }
             }
             if (structure) _structure = structure

+ 8 - 9
src/mol-repr/structure/units-visual.ts

@@ -5,9 +5,8 @@
  */
 
 import { Unit, Structure } from 'mol-model/structure';
-import { RepresentationProps, Visual } from '../';
+import { RepresentationProps, Visual, VisualContext } from '../';
 import { StructureMeshParams, StructurePointsParams, StructureLinesParams, StructureDirectVolumeParams, StructureParams } from './index';
-import { RuntimeContext } from 'mol-task';
 import { Loci, isEveryLoci, EmptyLoci } from 'mol-model/loci';
 import { MeshRenderObject, PointsRenderObject, LinesRenderObject, DirectVolumeRenderObject } from 'mol-gl/render-object';
 import { createUnitsMeshRenderObject, createUnitsPointsRenderObject, createUnitsTransform, createUnitsLinesRenderObject, createUnitsDirectVolumeRenderObject, UnitKind, UnitKindOptions, includesUnitKind } from './visual/util/common';
@@ -49,7 +48,7 @@ type UnitsRenderObject = MeshRenderObject | LinesRenderObject | PointsRenderObje
 
 interface UnitsVisualBuilder<P extends UnitsProps, G extends Geometry> {
     defaultProps: P
-    createGeometry(ctx: RuntimeContext, unit: Unit, structure: Structure, props: P, geometry?: G): Promise<G>
+    createGeometry(ctx: VisualContext, unit: Unit, structure: Structure, props: P, geometry?: G): Promise<G>
     createLocationIterator(group: Unit.SymmetryGroup): LocationIterator
     getLoci(pickingId: PickingId, group: Unit.SymmetryGroup, id: number): Loci
     mark(loci: Loci, group: Unit.SymmetryGroup, apply: (interval: Interval) => boolean): boolean
@@ -58,7 +57,7 @@ interface UnitsVisualBuilder<P extends UnitsProps, G extends Geometry> {
 
 interface UnitsVisualGeometryBuilder<P extends UnitsProps, G extends Geometry> extends UnitsVisualBuilder<P, G> {
     createEmptyGeometry(geometry?: G): G
-    createRenderObject(ctx: RuntimeContext, group: Unit.SymmetryGroup, geometry: Geometry, locationIt: LocationIterator, currentProps: P): Promise<UnitsRenderObject>
+    createRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, geometry: Geometry, locationIt: LocationIterator, currentProps: P): Promise<UnitsRenderObject>
     updateValues(values: RenderableValues, newProps: P): void
 }
 
@@ -75,7 +74,7 @@ export function UnitsVisual<P extends UnitsProps>(builder: UnitsVisualGeometryBu
     let locationIt: LocationIterator
     let currentConformationId: UUID
 
-    async function create(ctx: RuntimeContext, group: Unit.SymmetryGroup, props: Partial<P> = {}) {
+    async function create(ctx: VisualContext, group: Unit.SymmetryGroup, props: Partial<P> = {}) {
         currentProps = Object.assign({}, defaultProps, props, { structure: currentStructure })
         currentGroup = group
 
@@ -90,7 +89,7 @@ export function UnitsVisual<P extends UnitsProps>(builder: UnitsVisualGeometryBu
         renderObject = await createRenderObject(ctx, group, geometry, locationIt, currentProps)
     }
 
-    async function update(ctx: RuntimeContext, props: Partial<P> = {}) {
+    async function update(ctx: VisualContext, props: Partial<P> = {}) {
         if (!renderObject) return
 
         const newProps = Object.assign({}, currentProps, props, { structure: currentStructure })
@@ -132,12 +131,12 @@ export function UnitsVisual<P extends UnitsProps>(builder: UnitsVisualGeometryBu
         if (updateState.updateSize) {
             // not all geometries have size data, so check here
             if ('uSize' in renderObject.values) {
-                await createSizes(ctx, locationIt, newProps, renderObject.values)
+                await createSizes(ctx.runtime, locationIt, newProps, renderObject.values)
             }
         }
 
         if (updateState.updateColor) {
-            await createColors(ctx, locationIt, newProps, renderObject.values)
+            await createColors(ctx.runtime, locationIt, newProps, renderObject.values)
         }
 
         updateValues(renderObject.values, newProps)
@@ -148,7 +147,7 @@ export function UnitsVisual<P extends UnitsProps>(builder: UnitsVisualGeometryBu
 
     return {
         get renderObject () { return renderObject },
-        async createOrUpdate(ctx: RuntimeContext, props: Partial<P> = {}, structureGroup?: StructureGroup) {
+        async createOrUpdate(ctx: VisualContext, props: Partial<P> = {}, structureGroup?: StructureGroup) {
             if (structureGroup) currentStructure = structureGroup.structure
             const group = structureGroup ? structureGroup.group : undefined
             if (!group && !currentGroup) {

+ 2 - 2
src/mol-repr/structure/visual/carbohydrate-link-cylinder.ts

@@ -5,7 +5,6 @@
  */
 
 import { Structure, Link, StructureElement } from 'mol-model/structure';
-import { RuntimeContext } from 'mol-task'
 import { Loci, EmptyLoci } from 'mol-model/loci';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { createLinkCylinderMesh, LinkCylinderProps, LinkCylinderParams } from './util/link';
@@ -20,6 +19,7 @@ import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { VisualUpdateState } from '../../util';
+import { VisualContext } from 'mol-repr';
 
 // TODO create seperate visual
 // for (let i = 0, il = carbohydrates.terminalLinks.length; i < il; ++i) {
@@ -35,7 +35,7 @@ import { VisualUpdateState } from '../../util';
 
 const radiusFactor = 0.3
 
-async function createCarbohydrateLinkCylinderMesh(ctx: RuntimeContext, structure: Structure, props: LinkCylinderProps, mesh?: Mesh) {
+async function createCarbohydrateLinkCylinderMesh(ctx: VisualContext, structure: Structure, props: LinkCylinderProps, mesh?: Mesh) {
     const { links, elements } = structure.carbohydrates
     const sizeTheme = SizeTheme({ name: props.sizeTheme, value: props.sizeValue })
     const location = StructureElement.create()

+ 4 - 4
src/mol-repr/structure/visual/carbohydrate-symbol-mesh.ts

@@ -10,7 +10,6 @@ import { OctagonalPyramid, PerforatedOctagonalPyramid } from 'mol-geo/primitive/
 import { Star } from 'mol-geo/primitive/star';
 import { Octahedron, PerforatedOctahedron } from 'mol-geo/primitive/octahedron';
 import { DiamondPrism, PentagonalPrism, HexagonalPrism } from 'mol-geo/primitive/prism';
-import { RuntimeContext } from 'mol-task';
 import { Structure, StructureElement } from 'mol-model/structure';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
@@ -25,6 +24,7 @@ import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { OrderedSet, Interval } from 'mol-data/int';
 import { EmptyLoci, Loci } from 'mol-model/loci';
+import { VisualContext } from 'mol-repr';
 
 const t = Mat4.identity()
 const sVec = Vec3.zero()
@@ -44,7 +44,7 @@ const diamondPrism = DiamondPrism()
 const pentagonalPrism = PentagonalPrism()
 const hexagonalPrism = HexagonalPrism()
 
-async function createCarbohydrateSymbolMesh(ctx: RuntimeContext, structure: Structure, props: CarbohydrateSymbolProps, mesh?: Mesh) {
+async function createCarbohydrateSymbolMesh(ctx: VisualContext, structure: Structure, props: CarbohydrateSymbolProps, mesh?: Mesh) {
     const builder = MeshBuilder.create(256, 128, mesh)
 
     const sizeTheme = SizeTheme({ name: props.sizeTheme, value: props.sizeValue })
@@ -138,8 +138,8 @@ async function createCarbohydrateSymbolMesh(ctx: RuntimeContext, structure: Stru
                 break
         }
 
-        if (i % 10000 === 0 && ctx.shouldUpdate) {
-            await ctx.update({ message: 'Carbohydrate symbols', current: i, max: n });
+        if (i % 10000 === 0 && ctx.runtime.shouldUpdate) {
+            await ctx.runtime.update({ message: 'Carbohydrate symbols', current: i, max: n });
         }
     }
 

+ 2 - 2
src/mol-repr/structure/visual/cross-link-restraint-cylinder.ts

@@ -7,7 +7,6 @@
 import { Link, Structure, StructureElement } from 'mol-model/structure';
 import { ComplexVisual } from '../index';
 import { VisualUpdateState } from '../../util';
-import { RuntimeContext } from 'mol-task'
 import { LinkCylinderProps, createLinkCylinderMesh, LinkCylinderParams } from './util/link';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { Loci, EmptyLoci } from 'mol-model/loci';
@@ -20,8 +19,9 @@ import { SelectParam, NumberParam, paramDefaultValues } from 'mol-util/parameter
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { PickingId } from 'mol-geo/geometry/picking';
+import { VisualContext } from 'mol-repr';
 
-async function createCrossLinkRestraintCylinderMesh(ctx: RuntimeContext, structure: Structure, props: LinkCylinderProps, mesh?: Mesh) {
+async function createCrossLinkRestraintCylinderMesh(ctx: VisualContext, structure: Structure, props: LinkCylinderProps, mesh?: Mesh) {
 
     const crossLinks = structure.crossLinkRestraints
     if (!crossLinks.count) return Mesh.createEmpty(mesh)

+ 4 - 4
src/mol-repr/structure/visual/element-point.ts

@@ -5,7 +5,6 @@
  */
 
 import { Unit, Structure } from 'mol-model/structure';
-import { RuntimeContext } from 'mol-task'
 import { UnitsVisual } from '../index';
 import { VisualUpdateState } from '../../util';
 import { getElementLoci, StructureElementIterator, markElement } from './util/element';
@@ -15,6 +14,7 @@ import { UnitsPointsVisual, UnitsPointsParams } from '../units-visual';
 import { SelectParam, NumberParam, BooleanParam, paramDefaultValues } from 'mol-util/parameter';
 import { Points } from 'mol-geo/geometry/points/points';
 import { PointsBuilder } from 'mol-geo/geometry/points/points-builder';
+import { VisualContext } from 'mol-repr';
 
 export const ElementPointParams = {
     ...UnitsPointsParams,
@@ -27,7 +27,7 @@ export type ElementPointProps = typeof DefaultElementPointProps
 
 // TODO size
 
-export async function createElementPoint(ctx: RuntimeContext, unit: Unit, structure: Structure, props: ElementPointProps, points: Points) {
+export async function createElementPoint(ctx: VisualContext, unit: Unit, structure: Structure, props: ElementPointProps, points: Points) {
     const elements = unit.elements
     const n = elements.length
     const builder = PointsBuilder.create(n, n / 10, points)
@@ -39,8 +39,8 @@ export async function createElementPoint(ctx: RuntimeContext, unit: Unit, struct
         pos(elements[i], p)
         builder.add(p[0], p[1], p[2], i)
 
-        if (i % 10000 === 0 && ctx.shouldUpdate) {
-            await ctx.update({ message: 'Creating points', current: i, max: n });
+        if (i % 10000 === 0 && ctx.runtime.shouldUpdate) {
+            await ctx.runtime.update({ message: 'Creating points', current: i, max: n });
         }
     }
     return builder.getPoints()

+ 5 - 5
src/mol-repr/structure/visual/gaussian-density-point.ts

@@ -5,7 +5,6 @@
  */
 
 import { Unit, Structure } from 'mol-model/structure';
-import { RuntimeContext } from 'mol-task'
 import { UnitsVisual } from '../index';
 import { VisualUpdateState } from '../../util';
 import { StructureElementIterator } from './util/element';
@@ -17,6 +16,7 @@ import { GaussianDensityProps, GaussianDensityParams } from 'mol-model/structure
 import { paramDefaultValues, SelectParam, NumberParam, BooleanParam } from 'mol-util/parameter';
 import { Points } from 'mol-geo/geometry/points/points';
 import { PointsBuilder } from 'mol-geo/geometry/points/points-builder';
+import { VisualContext } from 'mol-repr';
 
 export const GaussianDensityPointParams = {
     ...UnitsPointsParams,
@@ -28,8 +28,8 @@ export const GaussianDensityPointParams = {
 export const DefaultGaussianDensityPointProps = paramDefaultValues(GaussianDensityPointParams)
 export type GaussianDensityPointProps = typeof DefaultGaussianDensityPointProps
 
-export async function createGaussianDensityPoint(ctx: RuntimeContext, unit: Unit, structure: Structure, props: GaussianDensityProps, points?: Points) {
-    const { transform, field: { space, data } } = await unit.computeGaussianDensity(props, ctx)
+export async function createGaussianDensityPoint(ctx: VisualContext, unit: Unit, structure: Structure, props: GaussianDensityProps, points?: Points) {
+    const { transform, field: { space, data } } = await unit.computeGaussianDensity(props, ctx.runtime, ctx.webgl)
 
     const { dimensions, get } = space
     const [ xn, yn, zn ] = dimensions
@@ -48,8 +48,8 @@ export async function createGaussianDensityPoint(ctx: RuntimeContext, unit: Unit
                     Vec3.transformMat4(p, p, transform)
                     builder.add(p[0], p[1], p[2], i)
                 }
-                if (i % 100000 === 0 && ctx.shouldUpdate) {
-                    await ctx.update({ message: 'Creating density points', current: i, max: n });
+                if (i % 100000 === 0 && ctx.runtime.shouldUpdate) {
+                    await ctx.runtime.update({ message: 'Creating density points', current: i, max: n });
                 }
                 ++i
             }

+ 5 - 5
src/mol-repr/structure/visual/gaussian-density-volume.ts

@@ -7,20 +7,20 @@
 import { Unit, Structure } from 'mol-model/structure';
 import { UnitsVisual } from '../index';
 import { VisualUpdateState } from '../../util';
-import { RuntimeContext } from 'mol-task'
 import { UnitsDirectVolumeVisual, UnitsDirectVolumeParams } from '../units-visual';
 import { StructureElementIterator, getElementLoci, markElement } from './util/element';
 import { GaussianDensityProps, GaussianDensityParams, computeUnitGaussianDensityTexture } from 'mol-model/structure/structure/unit/gaussian-density';
 import { paramDefaultValues } from 'mol-util/parameter';
 import { DirectVolume } from 'mol-geo/geometry/direct-volume/direct-volume';
+import { VisualContext } from 'mol-repr';
 
-async function createGaussianDensityVolume(ctx: RuntimeContext, unit: Unit, structure: Structure, props: GaussianDensityProps, directVolume?: DirectVolume): Promise<DirectVolume> {
-    const { webgl } = props
-    if (webgl === undefined) throw new Error('createGaussianDensityVolume requires `webgl` in props')
+async function createGaussianDensityVolume(ctx: VisualContext, unit: Unit, structure: Structure, props: GaussianDensityProps, directVolume?: DirectVolume): Promise<DirectVolume> {
+    const { runtime, webgl } = ctx
+    if (webgl === undefined) throw new Error('createGaussianDensityVolume requires `webgl` object in VisualContext')
 
     const p = { ...props, useGpu: true }
     const oldTexture = directVolume ? directVolume.gridTexture.ref.value : undefined
-    const densityTextureData = await computeUnitGaussianDensityTexture(unit, p, oldTexture).runInContext(ctx)
+    const densityTextureData = await computeUnitGaussianDensityTexture(unit, p, webgl, oldTexture).runInContext(runtime)
     const { transform, texture, bbox, gridDimension } = densityTextureData
 
     return DirectVolume.create(bbox, gridDimension, transform, texture, directVolume)

+ 4 - 4
src/mol-repr/structure/visual/gaussian-surface-mesh.ts

@@ -7,24 +7,24 @@
 import { Unit, Structure } from 'mol-model/structure';
 import { UnitsVisual } from '../index';
 import { VisualUpdateState } from '../../util';
-import { RuntimeContext } from 'mol-task'
 import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual';
 import { StructureElementIterator, getElementLoci, markElement } from './util/element';
 import { GaussianDensityProps, GaussianDensityParams } from 'mol-model/structure/structure/unit/gaussian-density';
 import { paramDefaultValues } from 'mol-util/parameter';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { computeMarchingCubesMesh } from 'mol-geo/util/marching-cubes/algorithm';
+import { VisualContext } from 'mol-repr';
 
-async function createGaussianSurfaceMesh(ctx: RuntimeContext, unit: Unit, structure: Structure, props: GaussianDensityProps, mesh?: Mesh): Promise<Mesh> {
+async function createGaussianSurfaceMesh(ctx: VisualContext, unit: Unit, structure: Structure, props: GaussianDensityProps, mesh?: Mesh): Promise<Mesh> {
     const { smoothness } = props
-    const { transform, field, idField } = await unit.computeGaussianDensity(props, ctx)
+    const { transform, field, idField } = await unit.computeGaussianDensity(props, ctx.runtime, ctx.webgl)
 
     const params = {
         isoLevel: Math.exp(-smoothness),
         scalarField: field,
         idField
     }
-    const surface = await computeMarchingCubesMesh(params, mesh).runAsChild(ctx)
+    const surface = await computeMarchingCubesMesh(params, mesh).runAsChild(ctx.runtime)
 
     Mesh.transformImmediate(surface, transform)
     Mesh.computeNormalsImmediate(surface)

+ 4 - 4
src/mol-repr/structure/visual/gaussian-surface-wireframe.ts

@@ -7,7 +7,6 @@
 import { Unit, Structure } from 'mol-model/structure';
 import { UnitsVisual } from '../index';
 import { VisualUpdateState } from '../../util';
-import { RuntimeContext } from 'mol-task'
 import { UnitsLinesVisual, UnitsLinesParams } from '../units-visual';
 import { StructureElementIterator, getElementLoci, markElement } from './util/element';
 import { GaussianDensityProps, GaussianDensityParams } from 'mol-model/structure/structure/unit/gaussian-density';
@@ -15,17 +14,18 @@ import { paramDefaultValues, SelectParam, NumberParam, BooleanParam } from 'mol-
 import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
 import { Lines } from 'mol-geo/geometry/lines/lines';
 import { computeMarchingCubesLines } from 'mol-geo/util/marching-cubes/algorithm';
+import { VisualContext } from 'mol-repr';
 
-async function createGaussianWireframe(ctx: RuntimeContext, unit: Unit, structure: Structure, props: GaussianDensityProps, lines?: Lines): Promise<Lines> {
+async function createGaussianWireframe(ctx: VisualContext, unit: Unit, structure: Structure, props: GaussianDensityProps, lines?: Lines): Promise<Lines> {
     const { smoothness } = props
-    const { transform, field, idField } = await unit.computeGaussianDensity(props, ctx)
+    const { transform, field, idField } = await unit.computeGaussianDensity(props, ctx.runtime)
 
     const params = {
         isoLevel: Math.exp(-smoothness),
         scalarField: field,
         idField
     }
-    const wireframe = await computeMarchingCubesLines(params, lines).runAsChild(ctx)
+    const wireframe = await computeMarchingCubesLines(params, lines).runAsChild(ctx.runtime)
 
     Lines.transformImmediate(wireframe, transform)
 

+ 2 - 2
src/mol-repr/structure/visual/inter-unit-link-cylinder.ts

@@ -7,7 +7,6 @@
 import { Link, Structure, StructureElement } from 'mol-model/structure';
 import { ComplexVisual } from '../index';
 import { VisualUpdateState } from '../../util';
-import { RuntimeContext } from 'mol-task'
 import { LinkCylinderProps, createLinkCylinderMesh, LinkIterator, LinkCylinderParams } from './util/link';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { Loci, EmptyLoci } from 'mol-model/loci';
@@ -18,8 +17,9 @@ import { BitFlags } from 'mol-util';
 import { SelectParam, NumberParam, paramDefaultValues } from 'mol-util/parameter';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { PickingId } from 'mol-geo/geometry/picking';
+import { VisualContext } from 'mol-repr';
 
-async function createInterUnitLinkCylinderMesh(ctx: RuntimeContext, structure: Structure, props: LinkCylinderProps, mesh?: Mesh) {
+async function createInterUnitLinkCylinderMesh(ctx: VisualContext, structure: Structure, props: LinkCylinderProps, mesh?: Mesh) {
     const links = structure.links
     const { bondCount, bonds } = links
 

+ 2 - 2
src/mol-repr/structure/visual/intra-unit-link-cylinder.ts

@@ -8,7 +8,6 @@
 import { Unit, Link, StructureElement, Structure } from 'mol-model/structure';
 import { UnitsVisual } from '../index';
 import { VisualUpdateState } from '../../util';
-import { RuntimeContext } from 'mol-task'
 import { LinkCylinderProps, createLinkCylinderMesh, LinkIterator, LinkCylinderParams } from './util/link';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { Loci, EmptyLoci } from 'mol-model/loci';
@@ -19,8 +18,9 @@ import { BitFlags } from 'mol-util';
 import { SelectParam, NumberParam, paramDefaultValues } from 'mol-util/parameter';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { PickingId } from 'mol-geo/geometry/picking';
+import { VisualContext } from 'mol-repr';
 
-async function createIntraUnitLinkCylinderMesh(ctx: RuntimeContext, unit: Unit, structure: Structure, props: LinkCylinderProps, mesh?: Mesh) {
+async function createIntraUnitLinkCylinderMesh(ctx: VisualContext, unit: Unit, structure: Structure, props: LinkCylinderProps, mesh?: Mesh) {
     if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh)
 
     const sizeTheme = SizeTheme({ name: props.sizeTheme, value: props.sizeValue, factor: props.sizeFactor })

+ 4 - 4
src/mol-repr/structure/visual/nucleotide-block-mesh.ts

@@ -6,7 +6,6 @@
 
 import { Unit, Structure } from 'mol-model/structure';
 import { UnitsVisual } from '../index';
-import { RuntimeContext } from 'mol-task'
 import { Vec3, Mat4 } from 'mol-math/linear-algebra';
 import { Segmentation } from 'mol-data/int';
 import { MoleculeType, isNucleic, isPurinBase, isPyrimidineBase } from 'mol-model/structure/model/types';
@@ -18,6 +17,7 @@ import { Box } from 'mol-geo/primitive/box';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
 import { addCylinder } from 'mol-geo/geometry/mesh/builder/cylinder';
+import { VisualContext } from 'mol-repr';
 
 const p1 = Vec3.zero()
 const p2 = Vec3.zero()
@@ -34,7 +34,7 @@ const sVec = Vec3.zero()
 const box = Box()
 
 // TODO define props, should be scalable
-async function createNucleotideBlockMesh(ctx: RuntimeContext, unit: Unit, structure: Structure, props: {}, mesh?: Mesh) {
+async function createNucleotideBlockMesh(ctx: VisualContext, unit: Unit, structure: Structure, props: {}, mesh?: Mesh) {
     if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh)
 
     // TODO better vertex count estimate
@@ -100,8 +100,8 @@ async function createNucleotideBlockMesh(ctx: RuntimeContext, unit: Unit, struct
                     }
                 }
 
-                if (i % 10000 === 0 && ctx.shouldUpdate) {
-                    await ctx.update({ message: 'Nucleotide block mesh', current: i });
+                if (i % 10000 === 0 && ctx.runtime.shouldUpdate) {
+                    await ctx.runtime.update({ message: 'Nucleotide block mesh', current: i });
                 }
                 ++i
             }

+ 4 - 4
src/mol-repr/structure/visual/polymer-backbone-cylinder.ts

@@ -7,7 +7,6 @@
 import { Unit, Structure } from 'mol-model/structure';
 import { UnitsVisual } from '../index';
 import { VisualUpdateState } from '../../util';
-import { RuntimeContext } from 'mol-task'
 import { PolymerBackboneIterator } from './util/polymer';
 import { getElementLoci, markElement, StructureElementIterator } from './util/element';
 import { Vec3 } from 'mol-math/linear-algebra';
@@ -19,6 +18,7 @@ import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
 import { CylinderProps } from 'mol-geo/primitive/cylinder';
 import { addCylinder } from 'mol-geo/geometry/mesh/builder/cylinder';
+import { VisualContext } from 'mol-repr';
 
 export const PolymerBackboneCylinderParams = {
     sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions),
@@ -28,7 +28,7 @@ export const PolymerBackboneCylinderParams = {
 export const DefaultPolymerBackboneCylinderProps = paramDefaultValues(PolymerBackboneCylinderParams)
 export type PolymerBackboneCylinderProps = typeof DefaultPolymerBackboneCylinderProps
 
-async function createPolymerBackboneCylinderMesh(ctx: RuntimeContext, unit: Unit, structure: Structure, props: PolymerBackboneCylinderProps, mesh?: Mesh) {
+async function createPolymerBackboneCylinderMesh(ctx: VisualContext, unit: Unit, structure: Structure, props: PolymerBackboneCylinderProps, mesh?: Mesh) {
     const polymerElementCount = unit.polymerElements.length
     if (!polymerElementCount) return Mesh.createEmpty(mesh)
 
@@ -59,8 +59,8 @@ async function createPolymerBackboneCylinderMesh(ctx: RuntimeContext, unit: Unit
         builder.setGroup(OrderedSet.indexOf(elements, centerB.element))
         addCylinder(builder, pB, pA, 0.5, cylinderProps)
 
-        if (i % 10000 === 0 && ctx.shouldUpdate) {
-            await ctx.update({ message: 'Backbone mesh', current: i, max: polymerElementCount });
+        if (i % 10000 === 0 && ctx.runtime.shouldUpdate) {
+            await ctx.runtime.update({ message: 'Backbone mesh', current: i, max: polymerElementCount });
         }
         ++i
     }

+ 4 - 4
src/mol-repr/structure/visual/polymer-direction-wedge.ts

@@ -6,7 +6,6 @@
 
 import { Unit, Structure } from 'mol-model/structure';
 import { UnitsVisual } from '../index';
-import { RuntimeContext } from 'mol-task'
 import { PolymerTraceIterator, createCurveSegmentState, interpolateCurveSegment, PolymerLocationIterator, getPolymerElementLoci, markPolymerElement } from './util/polymer';
 import { Vec3, Mat4 } from 'mol-math/linear-algebra';
 import { SecondaryStructureType, isNucleic } from 'mol-model/structure/model/types';
@@ -16,6 +15,7 @@ import { SelectParam, NumberParam, paramDefaultValues } from 'mol-util/parameter
 import { Wedge } from 'mol-geo/primitive/wedge';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
+import { VisualContext } from 'mol-repr';
 
 const t = Mat4.identity()
 const sVec = Vec3.zero()
@@ -36,7 +36,7 @@ export const PolymerDirectionWedgeParams = {
 export const DefaultPolymerDirectionWedgeProps = paramDefaultValues(PolymerDirectionWedgeParams)
 export type PolymerDirectionWedgeProps = typeof DefaultPolymerDirectionWedgeProps
 
-async function createPolymerDirectionWedgeMesh(ctx: RuntimeContext, unit: Unit, structure: Structure, props: PolymerDirectionWedgeProps, mesh?: Mesh) {
+async function createPolymerDirectionWedgeMesh(ctx: VisualContext, unit: Unit, structure: Structure, props: PolymerDirectionWedgeProps, mesh?: Mesh) {
     const polymerElementCount = unit.polymerElements.length
     if (!polymerElementCount) return Mesh.createEmpty(mesh)
 
@@ -80,8 +80,8 @@ async function createPolymerDirectionWedgeMesh(ctx: RuntimeContext, unit: Unit,
             builder.add(t, wedge)
         }
 
-        if (i % 10000 === 0 && ctx.shouldUpdate) {
-            await ctx.update({ message: 'Polymer direction mesh', current: i, max: polymerElementCount });
+        if (i % 10000 === 0 && ctx.runtime.shouldUpdate) {
+            await ctx.runtime.update({ message: 'Polymer direction mesh', current: i, max: polymerElementCount });
         }
         ++i
     }

+ 4 - 4
src/mol-repr/structure/visual/polymer-gap-cylinder.ts

@@ -7,7 +7,6 @@
 import { Unit, Structure } from 'mol-model/structure';
 import { UnitsVisual } from '../index';
 import { VisualUpdateState } from '../../util';
-import { RuntimeContext } from 'mol-task'
 import { PolymerGapIterator, PolymerGapLocationIterator, markPolymerGapElement, getPolymerGapElementLoci } from './util/polymer';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual';
@@ -19,6 +18,7 @@ import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
 import { CylinderProps } from 'mol-geo/primitive/cylinder';
 import { addSphere } from 'mol-geo/geometry/mesh/builder/sphere';
 import { addFixedCountDashedCylinder } from 'mol-geo/geometry/mesh/builder/cylinder';
+import { VisualContext } from 'mol-repr';
 
 const segmentCount = 10
 
@@ -31,7 +31,7 @@ export const PolymerGapCylinderParams = {
 export const DefaultPolymerGapCylinderProps = paramDefaultValues(PolymerGapCylinderParams)
 export type PolymerGapCylinderProps = typeof DefaultPolymerGapCylinderProps
 
-async function createPolymerGapCylinderMesh(ctx: RuntimeContext, unit: Unit, structure: Structure, props: PolymerGapCylinderProps, mesh?: Mesh) {
+async function createPolymerGapCylinderMesh(ctx: VisualContext, unit: Unit, structure: Structure, props: PolymerGapCylinderProps, mesh?: Mesh) {
     const polymerGapCount = unit.gapElements.length
     if (!polymerGapCount) return Mesh.createEmpty(mesh)
 
@@ -69,8 +69,8 @@ async function createPolymerGapCylinderMesh(ctx: RuntimeContext, unit: Unit, str
             addFixedCountDashedCylinder(builder, pB, pA, 0.5, segmentCount, cylinderProps)
         }
 
-        if (i % 10000 === 0 && ctx.shouldUpdate) {
-            await ctx.update({ message: 'Gap mesh', current: i, max: polymerGapCount });
+        if (i % 10000 === 0 && ctx.runtime.shouldUpdate) {
+            await ctx.runtime.update({ message: 'Gap mesh', current: i, max: polymerGapCount });
         }
         i += 2
     }

+ 4 - 4
src/mol-repr/structure/visual/polymer-trace-mesh.ts

@@ -7,7 +7,6 @@
 import { Unit, Structure } from 'mol-model/structure';
 import { UnitsVisual } from '../index';
 import { VisualUpdateState } from '../../util';
-import { RuntimeContext } from 'mol-task'
 import { PolymerTraceIterator, createCurveSegmentState, interpolateCurveSegment, PolymerLocationIterator, getPolymerElementLoci, markPolymerElement } from './util/polymer';
 import { SecondaryStructureType, isNucleic } from 'mol-model/structure/model/types';
 import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual';
@@ -17,6 +16,7 @@ import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
 import { addSheet } from 'mol-geo/geometry/mesh/builder/sheet';
 import { addTube } from 'mol-geo/geometry/mesh/builder/tube';
+import { VisualContext } from 'mol-repr';
 
 export const PolymerTraceMeshParams = {
     sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions),
@@ -32,7 +32,7 @@ export type PolymerTraceMeshProps = typeof DefaultPolymerTraceMeshProps
 
 // TODO handle polymer ends properly
 
-async function createPolymerTraceMesh(ctx: RuntimeContext, unit: Unit, structure: Structure, props: PolymerTraceMeshProps, mesh?: Mesh) {
+async function createPolymerTraceMesh(ctx: VisualContext, unit: Unit, structure: Structure, props: PolymerTraceMeshProps, mesh?: Mesh) {
     const polymerElementCount = unit.polymerElements.length
 
     if (!polymerElementCount) return Mesh.createEmpty(mesh)
@@ -80,8 +80,8 @@ async function createPolymerTraceMesh(ctx: RuntimeContext, unit: Unit, structure
             addTube(builder, curvePoints, normalVectors, binormalVectors, linearSegments, radialSegments, width, height, 1, true, true)
         }
 
-        if (i % 10000 === 0 && ctx.shouldUpdate) {
-            await ctx.update({ message: 'Polymer trace mesh', current: i, max: polymerElementCount });
+        if (i % 10000 === 0 && ctx.runtime.shouldUpdate) {
+            await ctx.runtime.update({ message: 'Polymer trace mesh', current: i, max: polymerElementCount });
         }
         ++i
     }

+ 11 - 11
src/mol-repr/structure/visual/util/common.ts

@@ -7,7 +7,6 @@
 import { Unit, Structure } from 'mol-model/structure';
 import { StructureProps } from '../../index';
 import { createMeshRenderObject, createPointsRenderObject, createLinesRenderObject, createDirectVolumeRenderObject } from 'mol-gl/render-object';
-import { RuntimeContext } from 'mol-task';
 import { Mat4 } from 'mol-math/linear-algebra';
 import { TransformData, createTransform, createIdentityTransform } from 'mol-geo/geometry/transform-data';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
@@ -16,6 +15,7 @@ import { createRenderableState } from 'mol-geo/geometry/geometry';
 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 { VisualContext } from 'mol-repr';
 
 export function createUnitsTransform({ units }: Unit.SymmetryGroup, transformData?: TransformData) {
     const unitCount = units.length
@@ -49,16 +49,16 @@ export function includesUnitKind(unitKinds: UnitKind[], unit: Unit) {
 
 type StructureMeshProps = Mesh.Props & StructureProps
 
-export async function createComplexMeshRenderObject(ctx: RuntimeContext, structure: Structure, mesh: Mesh, locationIt: LocationIterator, props: StructureMeshProps) {
+export async function createComplexMeshRenderObject(ctx: VisualContext, structure: Structure, mesh: Mesh, locationIt: LocationIterator, props: StructureMeshProps) {
     const transform = createIdentityTransform()
-    const values = await Mesh.createValues(ctx, mesh, transform, locationIt, props)
+    const values = await Mesh.createValues(ctx.runtime, mesh, transform, locationIt, props)
     const state = createRenderableState(props)
     return createMeshRenderObject(values, state)
 }
 
-export async function createUnitsMeshRenderObject(ctx: RuntimeContext, group: Unit.SymmetryGroup, mesh: Mesh, locationIt: LocationIterator, props: StructureMeshProps) {
+export async function createUnitsMeshRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, mesh: Mesh, locationIt: LocationIterator, props: StructureMeshProps) {
     const transform = createUnitsTransform(group)
-    const values = await Mesh.createValues(ctx, mesh, transform, locationIt, props)
+    const values = await Mesh.createValues(ctx.runtime, mesh, transform, locationIt, props)
     const state = createRenderableState(props)
     return createMeshRenderObject(values, state)
 }
@@ -67,9 +67,9 @@ export async function createUnitsMeshRenderObject(ctx: RuntimeContext, group: Un
 
 type StructurePointsProps = Points.Props & StructureProps
 
-export async function createUnitsPointsRenderObject(ctx: RuntimeContext, group: Unit.SymmetryGroup, points: Points, locationIt: LocationIterator, props: StructurePointsProps) {
+export async function createUnitsPointsRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, points: Points, locationIt: LocationIterator, props: StructurePointsProps) {
     const transform = createUnitsTransform(group)
-    const values = await Points.createValues(ctx, points, transform, locationIt, props)
+    const values = await Points.createValues(ctx.runtime, points, transform, locationIt, props)
     const state = createRenderableState(props)
     return createPointsRenderObject(values, state)
 }
@@ -78,9 +78,9 @@ export async function createUnitsPointsRenderObject(ctx: RuntimeContext, group:
 
 type StructureLinesProps = Lines.Props & StructureProps
 
-export async function createUnitsLinesRenderObject(ctx: RuntimeContext, group: Unit.SymmetryGroup, lines: Lines, locationIt: LocationIterator, props: StructureLinesProps) {
+export async function createUnitsLinesRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, lines: Lines, locationIt: LocationIterator, props: StructureLinesProps) {
     const transform = createUnitsTransform(group)
-    const values = await Lines.createValues(ctx, lines, transform, locationIt, props)
+    const values = await Lines.createValues(ctx.runtime, lines, transform, locationIt, props)
     const state = createRenderableState(props)
     return createLinesRenderObject(values, state)
 }
@@ -89,9 +89,9 @@ export async function createUnitsLinesRenderObject(ctx: RuntimeContext, group: U
 
 type StructureDirectVolumeProps = DirectVolume.Props & StructureProps
 
-export async function createUnitsDirectVolumeRenderObject(ctx: RuntimeContext, group: Unit.SymmetryGroup, directVolume: DirectVolume, locationIt: LocationIterator, props: StructureDirectVolumeProps) {
+export async function createUnitsDirectVolumeRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, directVolume: DirectVolume, locationIt: LocationIterator, props: StructureDirectVolumeProps) {
     const transform = createUnitsTransform(group)
-    const values = await DirectVolume.createValues(ctx, directVolume, transform, locationIt, props)
+    const values = await DirectVolume.createValues(ctx.runtime, directVolume, transform, locationIt, props)
     const state = createRenderableState(props)
     return createDirectVolumeRenderObject(values, state)
 }

+ 4 - 4
src/mol-repr/structure/visual/util/element.ts

@@ -6,7 +6,6 @@
 
 import { Vec3 } from 'mol-math/linear-algebra';
 import { Unit, StructureElement, Structure } from 'mol-model/structure';
-import { RuntimeContext } from 'mol-task';
 import { Loci, EmptyLoci } from 'mol-model/loci';
 import { Interval, OrderedSet } from 'mol-data/int';
 import { SizeTheme, SizeThemeName } from 'mol-theme/size';
@@ -16,6 +15,7 @@ import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
 import { addSphere } from 'mol-geo/geometry/mesh/builder/sphere';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
+import { VisualContext } from 'mol-repr';
 
 export interface ElementSphereMeshProps {
     sizeTheme: SizeThemeName,
@@ -23,7 +23,7 @@ export interface ElementSphereMeshProps {
     detail: number,
 }
 
-export async function createElementSphereMesh(ctx: RuntimeContext, unit: Unit, structure: Structure, props: ElementSphereMeshProps, mesh?: Mesh) {
+export async function createElementSphereMesh(ctx: VisualContext, unit: Unit, structure: Structure, props: ElementSphereMeshProps, mesh?: Mesh) {
     const { detail } = props
 
     const { elements } = unit;
@@ -44,8 +44,8 @@ export async function createElementSphereMesh(ctx: RuntimeContext, unit: Unit, s
         meshBuilder.setGroup(i)
         addSphere(meshBuilder, v, sizeTheme.size(l), detail)
 
-        if (i % 10000 === 0 && ctx.shouldUpdate) {
-            await ctx.update({ message: 'Sphere mesh', current: i, max: elementCount });
+        if (i % 10000 === 0 && ctx.runtime.shouldUpdate) {
+            await ctx.runtime.update({ message: 'Sphere mesh', current: i, max: elementCount });
         }
     }
 

+ 4 - 4
src/mol-repr/structure/visual/util/link.ts

@@ -5,7 +5,6 @@
  */
 
 import { Vec3 } from 'mol-math/linear-algebra';
-import { RuntimeContext } from 'mol-task';
 import { LinkType } from 'mol-model/structure/model/types';
 import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
 import { Unit, StructureElement, Structure, Link } from 'mol-model/structure';
@@ -15,6 +14,7 @@ import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
 import { CylinderProps } from 'mol-geo/primitive/cylinder';
 import { addFixedCountDashedCylinder, addCylinder, addDoubleCylinder } from 'mol-geo/geometry/mesh/builder/cylinder';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
+import { VisualContext } from 'mol-repr';
 
 export const LinkCylinderParams = {
     sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions),
@@ -71,7 +71,7 @@ export interface LinkCylinderMeshBuilderProps {
  * Each edge is included twice to allow for coloring/picking
  * the half closer to the first vertex, i.e. vertex a.
  */
-export async function createLinkCylinderMesh(ctx: RuntimeContext, linkBuilder: LinkCylinderMeshBuilderProps, props: LinkCylinderProps, mesh?: Mesh) {
+export async function createLinkCylinderMesh(ctx: VisualContext, linkBuilder: LinkCylinderMeshBuilderProps, props: LinkCylinderProps, mesh?: Mesh) {
     const { linkCount, referencePosition, position, order, flags, radius } = linkBuilder
 
     if (!linkCount) return Mesh.createEmpty(mesh)
@@ -115,8 +115,8 @@ export async function createLinkCylinderMesh(ctx: RuntimeContext, linkBuilder: L
             addCylinder(meshBuilder, va, vb, 0.5, cylinderProps)
         }
 
-        if (edgeIndex % 10000 === 0 && ctx.shouldUpdate) {
-            await ctx.update({ message: 'Cylinder mesh', current: edgeIndex, max: linkCount });
+        if (edgeIndex % 10000 === 0 && ctx.runtime.shouldUpdate) {
+            await ctx.runtime.update({ message: 'Cylinder mesh', current: edgeIndex, max: linkCount });
         }
     }
 

+ 4 - 1
src/mol-repr/util.ts

@@ -44,7 +44,10 @@ export function sizeChanged(oldProps: SizeProps, newProps: SizeProps) {
 export function colorChanged(oldProps: ColorProps, newProps: ColorProps) {
     return (
         oldProps.colorTheme !== newProps.colorTheme ||
-        oldProps.colorValue !== newProps.colorValue
+        oldProps.colorValue !== newProps.colorValue ||
+        oldProps.colorDomain !== newProps.colorDomain ||
+        oldProps.colorList !== newProps.colorList ||
+        oldProps.colorMap !== newProps.colorMap
     )
 }
 

+ 31 - 78
src/mol-repr/volume/direct-volume.ts

@@ -7,7 +7,7 @@
 import { VolumeData } from 'mol-model/volume'
 import { RuntimeContext } from 'mol-task'
 import { VolumeVisual, VolumeRepresentation } from './index';
-import { DirectVolumeRenderObject, createDirectVolumeRenderObject } from 'mol-gl/render-object';
+import { createDirectVolumeRenderObject } from 'mol-gl/render-object';
 import { Loci, EmptyLoci } from 'mol-model/loci';
 import { paramDefaultValues } from 'mol-util/parameter';
 import { Vec3, Mat4 } from 'mol-math/linear-algebra';
@@ -15,12 +15,13 @@ import { Box3D } from 'mol-math/geometry';
 import { WebGLContext } from 'mol-gl/webgl/context';
 import { createTexture } from 'mol-gl/webgl/texture';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
-import { NullLocation } from 'mol-model/location';
 import { createIdentityTransform } from 'mol-geo/geometry/transform-data';
 import { DirectVolume } from 'mol-geo/geometry/direct-volume/direct-volume';
-import { Geometry, createRenderableState, updateRenderableState } from 'mol-geo/geometry/geometry';
+import { Geometry, createRenderableState } from 'mol-geo/geometry/geometry';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { MarkerAction } from 'mol-geo/geometry/marker-data';
+import { VisualUpdateState } from 'mol-repr/util';
+import { VisualContext, RepresentationContext } from 'mol-repr';
 
 function getBoundingBox(gridDimension: Vec3, transform: Mat4) {
     const bbox = Box3D.empty()
@@ -168,6 +169,18 @@ export function createDirectVolume3d(ctx: RuntimeContext, webgl: WebGLContext, v
     return DirectVolume.create(bbox, gridDimension, transform, texture, directVolume)
 }
 
+//
+
+export async function createDirectVolume(ctx: VisualContext, volume: VolumeData, props: DirectVolumeProps, directVolume?: DirectVolume) {
+    const { runtime, webgl } = ctx
+    if (webgl === undefined) throw new Error('DirectVolumeVisual requires `webgl` in props')
+
+    return webgl.isWebGL2 ?
+        await createDirectVolume3d(runtime, webgl, volume, directVolume) :
+        await createDirectVolume2d(runtime, webgl, volume, directVolume)
+}
+
+
 //
 
 export const DirectVolumeParams = {
@@ -178,81 +191,21 @@ export const DefaultDirectVolumeProps = paramDefaultValues(DirectVolumeParams)
 export type DirectVolumeProps = typeof DefaultDirectVolumeProps
 
 export function DirectVolumeVisual(): VolumeVisual<DirectVolumeProps> {
-    let currentProps = DefaultDirectVolumeProps
-    let renderObject: DirectVolumeRenderObject
-    let currentVolume: VolumeData
-    let directVolume: DirectVolume
-
-    async function create(ctx: RuntimeContext, volume: VolumeData, props: Partial<DirectVolumeProps> = {}) {
-        const { webgl } = props
-        if (webgl === undefined) throw new Error('DirectVolumeVisual requires `webgl` in props')
-
-        currentProps = { ...DefaultDirectVolumeProps, ...props }
-        if (props.isoValueRelative) {
-            // currentProps.isoValueAbsolute = VolumeIsoValue.calcAbsolute(currentVolume.dataStats, props.isoValueRelative)
-        }
-
-        const state = createRenderableState(currentProps)
-        const locationIt = LocationIterator(1, 1, () => NullLocation)
-        const transform = createIdentityTransform()
-
-        if (webgl.isWebGL2) {
-            directVolume = await createDirectVolume3d(ctx, webgl, volume, directVolume)
-            const values = await DirectVolume.createValues(ctx, directVolume, transform, locationIt, currentProps)
-            renderObject = createDirectVolumeRenderObject(values, state)
-        } else {
-            directVolume = await createDirectVolume2d(ctx, webgl, volume, directVolume)
-            const values = await DirectVolume.createValues(ctx, directVolume, transform, locationIt, currentProps)
-            renderObject = createDirectVolumeRenderObject(values, state)
-        }
-    }
-
-    async function update(ctx: RuntimeContext, props: Partial<DirectVolumeProps> = {}) {
-        const { webgl } = props
-        if (webgl === undefined) throw new Error('DirectVolumeVisual requires `webgl` in props')
-
-        const newProps = { ...currentProps, ...props }
-        if (props.isoValueRelative) {
-            // newProps.isoValueAbsolute = VolumeIsoValue.calcAbsolute(currentVolume.dataStats, props.isoValueRelative)
-        }
-
-        console.log('newProps.isoValueAbsolute', newProps.isoValueAbsolute)
-
-        DirectVolume.updateValues(renderObject.values, newProps)
-        updateRenderableState(renderObject.state, newProps)
-
-        currentProps = newProps
-    }
-
-    return {
-        get renderObject () { return renderObject },
-        async createOrUpdate(ctx: RuntimeContext, props: Partial<DirectVolumeProps> = {}, volume?: VolumeData) {
-            if (!volume && !currentVolume) {
-                throw new Error('missing volume')
-            } else if (volume && (!currentVolume || !renderObject)) {
-                currentVolume = volume
-                await create(ctx, volume, props)
-            } else if (volume && volume !== currentVolume) {
-                currentVolume = volume
-                await create(ctx, volume, props)
-            } else {
-                await update(ctx, props)
-            }
-
-            currentProps = { ...DefaultDirectVolumeProps, ...props }
-        },
-        getLoci(pickingId: PickingId) {
-            // TODO
-            return EmptyLoci
+    return VolumeVisual<DirectVolumeProps>({
+        defaultProps: DefaultDirectVolumeProps,
+        createGeometry: createDirectVolume,
+        getLoci: () => EmptyLoci,
+        mark: () => false,
+        setUpdateState: (state: VisualUpdateState, newProps: DirectVolumeProps, currentProps: DirectVolumeProps) => {
         },
-        mark(loci: Loci, action: MarkerAction) {
-            // TODO
-            return false
+        createRenderObject: async (ctx: VisualContext, geometry: DirectVolume, locationIt: LocationIterator, props: DirectVolumeProps) => {
+            const transform = createIdentityTransform()
+            const values = await DirectVolume.createValues(ctx.runtime, geometry, transform, locationIt, props)
+            const state = createRenderableState(props)
+            return createDirectVolumeRenderObject(values, state)
         },
-        destroy() {
-            // TODO
-        }
-    }
+        updateValues: DirectVolume.updateValues
+    })
 }
 
 export function DirectVolumeRepresentation(): VolumeRepresentation<DirectVolumeProps> {
@@ -267,9 +220,9 @@ export function DirectVolumeRepresentation(): VolumeRepresentation<DirectVolumeP
         get props() {
             return { ...volumeRepr.props }
         },
-        createOrUpdate: (props: Partial<DirectVolumeProps> = {}, volume?: VolumeData) => {
+        createOrUpdate: (ctx: RepresentationContext, props: Partial<DirectVolumeProps> = {}, volume?: VolumeData) => {
             currentProps = Object.assign({}, DefaultDirectVolumeProps, currentProps, props)
-            return volumeRepr.createOrUpdate(currentProps, volume)
+            return volumeRepr.createOrUpdate(ctx, currentProps, volume)
         },
         getLoci: (pickingId: PickingId) => {
             return volumeRepr.getLoci(pickingId)

+ 11 - 11
src/mol-repr/volume/index.ts

@@ -4,8 +4,8 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Task, RuntimeContext } from 'mol-task'
-import { RepresentationProps, Representation, Visual } from '..';
+import { Task } from 'mol-task'
+import { RepresentationProps, Representation, Visual, RepresentationContext, VisualContext } from '..';
 import { VolumeData, VolumeIsoValue } from 'mol-model/volume';
 import { Loci, EmptyLoci, isEveryLoci } from 'mol-model/loci';
 import { paramDefaultValues, RangeParam } from 'mol-util/parameter';
@@ -26,14 +26,14 @@ type VolumeRenderObject = MeshRenderObject | LinesRenderObject | PointsRenderObj
 
 interface VolumeVisualBuilder<P extends VolumeProps, G extends Geometry> {
     defaultProps: P
-    createGeometry(ctx: RuntimeContext, volumeData: VolumeData, props: P, geometry?: G): Promise<G>
+    createGeometry(ctx: VisualContext, volumeData: VolumeData, props: P, geometry?: G): Promise<G>
     getLoci(pickingId: PickingId, id: number): Loci
     mark(loci: Loci, apply: (interval: Interval) => boolean): boolean
     setUpdateState(state: VisualUpdateState, newProps: P, currentProps: P): void
 }
 
 interface VolumeVisualGeometryBuilder<P extends VolumeProps, G extends Geometry> extends VolumeVisualBuilder<P, G> {
-    createRenderObject(ctx: RuntimeContext, geometry: G, locationIt: LocationIterator, currentProps: P): Promise<VolumeRenderObject>
+    createRenderObject(ctx: VisualContext, geometry: G, locationIt: LocationIterator, currentProps: P): Promise<VolumeRenderObject>
     updateValues(values: RenderableValues, newProps: P): void
 }
 
@@ -48,7 +48,7 @@ export function VolumeVisual<P extends VolumeProps>(builder: VolumeVisualGeometr
     let geometry: Geometry
     let locationIt: LocationIterator
 
-    async function create(ctx: RuntimeContext, volume: VolumeData, props: Partial<VolumeProps> = {}) {
+    async function create(ctx: VisualContext, volume: VolumeData, props: Partial<VolumeProps> = {}) {
         currentProps = Object.assign({}, defaultProps, props)
         if (props.isoValueRelative) {
             currentProps.isoValueAbsolute = VolumeIsoValue.calcAbsolute(currentVolume.dataStats, props.isoValueRelative)
@@ -60,7 +60,7 @@ export function VolumeVisual<P extends VolumeProps>(builder: VolumeVisualGeometr
         renderObject = await createRenderObject(ctx, geometry, locationIt, currentProps)
     }
 
-    async function update(ctx: RuntimeContext, props: Partial<VolumeProps> = {}) {
+    async function update(ctx: VisualContext, props: Partial<VolumeProps> = {}) {
         if (!renderObject) return
         const newProps = Object.assign({}, currentProps, props)
 
@@ -85,7 +85,7 @@ export function VolumeVisual<P extends VolumeProps>(builder: VolumeVisualGeometr
 
     return {
         get renderObject () { return renderObject },
-        async createOrUpdate(ctx: RuntimeContext, props: Partial<VolumeProps> = {}, volume?: VolumeData) {
+        async createOrUpdate(ctx: VisualContext, props: Partial<VolumeProps> = {}, volume?: VolumeData) {
             if (!volume && !currentVolume) {
                 throw new Error('missing volume')
             } else if (volume && (!currentVolume || !renderObject)) {
@@ -147,9 +147,9 @@ export function VolumeRepresentation<P extends VolumeProps>(visualCtor: (volumeD
     let _props: P
     let busy = false
 
-    function createOrUpdate(props: Partial<P> = {}, volumeData?: VolumeData) {
+    function createOrUpdate(ctx: RepresentationContext, props: Partial<P> = {}, volumeData?: VolumeData) {
         _props = Object.assign({}, DefaultVolumeProps, _props, props)
-        return Task.create('VolumeRepresentation.create', async ctx => {
+        return Task.create('VolumeRepresentation.create', async runtime => {
             // TODO queue it somehow
             if (busy) return
 
@@ -158,11 +158,11 @@ export function VolumeRepresentation<P extends VolumeProps>(visualCtor: (volumeD
             } else if (volumeData && !visual) {
                 busy = true
                 visual = visualCtor(volumeData)
-                await visual.createOrUpdate(ctx, props, volumeData)
+                await visual.createOrUpdate({ ...ctx, runtime } , props, volumeData)
                 busy = false
             } else {
                 busy = true
-                await visual.createOrUpdate(ctx, props, volumeData)
+                await visual.createOrUpdate({ ...ctx, runtime }, props, volumeData)
                 busy = false
             }
         });

+ 10 - 10
src/mol-repr/volume/isosurface-mesh.ts

@@ -6,7 +6,6 @@
  */
 
 import { VolumeData } from 'mol-model/volume'
-import { RuntimeContext } from 'mol-task'
 import { VolumeVisual, VolumeRepresentation } from './index';
 import { createMeshRenderObject } from 'mol-gl/render-object';
 import { Loci, EmptyLoci } from 'mol-model/loci';
@@ -19,21 +18,22 @@ import { createRenderableState } from 'mol-geo/geometry/geometry';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { MarkerAction } from 'mol-geo/geometry/marker-data';
 import { VisualUpdateState } from 'mol-repr/util';
+import { RepresentationContext, VisualContext } from 'mol-repr';
 
 interface VolumeIsosurfaceProps {
     isoValueAbsolute: number
 }
 
-export async function createVolumeIsosurface(ctx: RuntimeContext, volume: VolumeData, props: VolumeIsosurfaceProps, mesh?: Mesh) {
-    ctx.update({ message: 'Marching cubes...' });
+export async function createVolumeIsosurface(ctx: VisualContext, volume: VolumeData, props: VolumeIsosurfaceProps, mesh?: Mesh) {
+    ctx.runtime.update({ message: 'Marching cubes...' });
 
     const surface = await computeMarchingCubesMesh({
         isoLevel: props.isoValueAbsolute,
         scalarField: volume.data
-    }, mesh).runAsChild(ctx);
+    }, mesh).runAsChild(ctx.runtime);
 
     const transform = VolumeData.getGridToCartesianTransform(volume);
-    ctx.update({ message: 'Transforming mesh...' });
+    ctx.runtime.update({ message: 'Transforming mesh...' });
     Mesh.transformImmediate(surface, transform);
     Mesh.computeNormalsImmediate(surface)
 
@@ -48,7 +48,7 @@ export const IsosurfaceParams = {
 export const DefaultIsosurfaceProps = paramDefaultValues(IsosurfaceParams)
 export type IsosurfaceProps = typeof DefaultIsosurfaceProps
 
-export function IsosurfaceVisual():  VolumeVisual<IsosurfaceProps> {
+export function IsosurfaceVisual(): VolumeVisual<IsosurfaceProps> {
     return VolumeVisual<IsosurfaceProps>({
         defaultProps: DefaultIsosurfaceProps,
         createGeometry: createVolumeIsosurface,
@@ -57,9 +57,9 @@ export function IsosurfaceVisual():  VolumeVisual<IsosurfaceProps> {
         setUpdateState: (state: VisualUpdateState, newProps: IsosurfaceProps, currentProps: IsosurfaceProps) => {
             if (newProps.isoValueAbsolute !== currentProps.isoValueAbsolute) state.createGeometry = true
         },
-        createRenderObject: async (ctx: RuntimeContext, geometry: Mesh, locationIt: LocationIterator, props: IsosurfaceProps) => {
+        createRenderObject: async (ctx: VisualContext, geometry: Mesh, locationIt: LocationIterator, props: IsosurfaceProps) => {
             const transform = createIdentityTransform()
-            const values = await Mesh.createValues(ctx, geometry, transform, locationIt, props)
+            const values = await Mesh.createValues(ctx.runtime, geometry, transform, locationIt, props)
             const state = createRenderableState(props)
             return createMeshRenderObject(values, state)
         },
@@ -79,9 +79,9 @@ export function IsosurfaceRepresentation(): VolumeRepresentation<IsosurfaceProps
         get props() {
             return { ...volumeRepr.props }
         },
-        createOrUpdate: (props: Partial<IsosurfaceProps> = {}, volume?: VolumeData) => {
+        createOrUpdate: (ctx: RepresentationContext, props: Partial<IsosurfaceProps> = {}, volume?: VolumeData) => {
             currentProps = Object.assign({}, DefaultIsosurfaceProps, currentProps, props)
-            return volumeRepr.createOrUpdate(currentProps, volume)
+            return volumeRepr.createOrUpdate(ctx, currentProps, volume)
         },
         getLoci: (pickingId: PickingId) => {
             return volumeRepr.getLoci(pickingId)

+ 43 - 1
src/mol-theme/color.ts

@@ -4,7 +4,7 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Color } from 'mol-util/color';
+import { Color, ColorMap } from 'mol-util/color';
 import { Structure } from 'mol-model/structure';
 import { Location } from 'mol-model/location';
 import { ColorType } from 'mol-geo/geometry/color-data';
@@ -23,6 +23,7 @@ import { SequenceIdColorTheme } from './color/sequence-id';
 import { SecondaryStructureColorTheme } from './color/secondary-structure';
 import { MoleculeTypeColorTheme } from './color/molecule-type';
 import { PolymerIndexColorTheme } from './color/polymer-index';
+import { ColorMatplotlib, ColorBrewer, ColorOther } from 'mol-util/color/tables';
 
 export type LocationColor = (location: Location, isSecondary: boolean) => Color
 
@@ -36,6 +37,29 @@ export function ScaleLegend(minLabel: string, maxLabel: string, colors: Color[])
     return { kind: 'scale-legend', minLabel, maxLabel, colors }
 }
 
+export type ColorScaleName = (
+    'default' |
+    keyof typeof ColorBrewer | keyof typeof ColorMatplotlib | keyof typeof ColorOther
+)
+export const ColorScaleNames = [
+    'default',
+    ...Object.keys(ColorBrewer), ...Object.keys(ColorMatplotlib), ...Object.keys(ColorOther)
+]
+export const ColorScaleOptions = ColorScaleNames.map(n => [n, n] as [ColorScaleName, string])
+
+export function getColorScaleFromName(name: string) {
+    if (name === 'default') {
+        return
+    } else if (name in ColorBrewer) {
+        return ColorBrewer[name as keyof typeof ColorBrewer]
+    } else if (name in ColorMatplotlib) {
+        return ColorMatplotlib[name as keyof typeof ColorMatplotlib]
+    } else if (name in ColorOther) {
+        return ColorOther[name as keyof typeof ColorOther]
+    }
+    console.warn(`unknwon color list named '${name}'`)
+}
+
 export interface TableLegend {
     kind: 'table-legend'
     table: [ string, Color ][]
@@ -44,7 +68,23 @@ export function TableLegend(table: [ string, Color ][]): TableLegend {
     return { kind: 'table-legend', table }
 }
 
+export interface ColorThemeFeatures {
+    /** Does allow providing a structure object */
+    structure?: boolean
+    /** Does allow providing a volume object */
+    volume?: boolean
+    /** Does allow providing a list of colors (for creating a scale) */
+    list?: boolean
+    /** Does allow providing a map of colors */
+    map?: boolean
+    /** Does allow providing the boundaries for the scale */
+    domain?: boolean
+    /** Does allow providing a single/special color value */
+    value?: boolean
+}
+
 export interface ColorTheme {
+    features: ColorThemeFeatures
     granularity: ColorType
     color: LocationColor
     description?: string
@@ -74,6 +114,8 @@ export interface ColorThemeProps {
     name: ColorThemeName
     domain?: [number, number]
     value?: Color
+    list?: Color[]
+    map?: ColorMap<any>
     structure?: Structure
     color?: LocationColor
     granularity?: ColorType,

+ 1 - 0
src/mol-theme/color/carbohydrate-symbol.ts

@@ -47,6 +47,7 @@ export function CarbohydrateSymbolColorTheme(props: ColorThemeProps): ColorTheme
     }
 
     return {
+        features: {},
         granularity: 'group',
         color: color,
         description: Description,

+ 1 - 0
src/mol-theme/color/chain-id.ts

@@ -63,6 +63,7 @@ export function ChainIdColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
+        features: {},
         granularity: 'group',
         color,
         description: Description,

+ 8 - 2
src/mol-theme/color/cross-link.ts

@@ -6,10 +6,12 @@
 
 import { Link } from 'mol-model/structure';
 
-import { Color, ColorScale, ColorBrewer } from 'mol-util/color';
+import { Color, ColorScale } from 'mol-util/color';
 import { Location } from 'mol-model/location';
 import { ColorThemeProps, ColorTheme, LocationColor } from '../color';
 import { Vec3 } from 'mol-math/linear-algebra';
+import { ColorBrewer } from 'mol-util/color/tables';
+import { defaults } from 'mol-util';
 
 const DefaultColor = Color(0xCCCCCC)
 const Description = 'Colors cross-links by the deviation of the observed distance versus the modeled distance (e.g. `ihm_cross_link_restraint.distance_threshold`).'
@@ -27,7 +29,10 @@ export function CrossLinkColorTheme(props: ColorThemeProps): ColorTheme {
 
     if (props.structure) {
         const crosslinks = props.structure.crossLinkRestraints
-        scale = ColorScale.create({ domain: [ -10, 10 ], colors: ColorBrewer.RdYlBu })
+        scale = ColorScale.create({
+            domain: defaults(props.domain, [ -10, 10 ]),
+            list: defaults(props.list, ColorBrewer.RdYlBu)
+        })
         const scaleColor = scale.color
 
         color = (location: Location): Color => {
@@ -44,6 +49,7 @@ export function CrossLinkColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
+        features: { list: true, domain: true },
         granularity: 'group',
         color,
         description: Description,

+ 1 - 0
src/mol-theme/color/custom.ts

@@ -13,6 +13,7 @@ const DefaultColor = Color(0xCCCCCC)
 export function CustomColorTheme(props: ColorThemeProps): ColorTheme {
     const value = defaults(props.value, DefaultColor)
     return {
+        features: {},
         granularity: defaults(props.granularity, 'uniform'),
         color: defaults(props.color, () => value),
         description: props.description,

+ 2 - 1
src/mol-theme/color/element-index.ts

@@ -29,7 +29,7 @@ export function ElementIndexColorTheme(props: ColorThemeProps): ColorTheme {
             elementCount += units[i].elements.length
             unitIdIndex.set(units[i].id, i)
         }
-        scale = ColorScale.create({ domain: [ 0, elementCount - 1 ] })
+        scale = ColorScale.create({ domain: [ 0, elementCount - 1 ], list: props.list })
         const scaleColor = scale.color
 
         color = (location: Location): Color => {
@@ -48,6 +48,7 @@ export function ElementIndexColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
+        features: { list: true },
         granularity: 'groupInstance',
         color,
         description: Description,

+ 1 - 0
src/mol-theme/color/element-symbol.ts

@@ -40,6 +40,7 @@ export function ElementSymbolColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
+        features: {},
         granularity: 'group',
         color,
         description: Description,

+ 1 - 0
src/mol-theme/color/molecule-type.ts

@@ -49,6 +49,7 @@ export function MoleculeTypeColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
+        features: {},
         granularity: 'group',
         color,
         description: Description,

+ 2 - 1
src/mol-theme/color/polymer-index.ts

@@ -22,7 +22,7 @@ export function PolymerIndexColorTheme(props: ColorThemeProps): ColorTheme {
         for (let i = 0, il = units.length; i <il; ++i) {
             if (units[i].polymerElements.length > 0) ++polymerCount
         }
-        scale = ColorScale.create({ domain: [ 0, polymerCount - 1 ] })
+        scale = ColorScale.create({ list: props.list, domain: [ 0, polymerCount - 1 ] })
         const unitIdColor = new Map<number, Color>()
         for (let i = 0, j = 0, il = units.length; i <il; ++i) {
             if (units[i].polymerElements.length > 0) {
@@ -45,6 +45,7 @@ export function PolymerIndexColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
+        features: { structure: true, list: true },
         granularity: 'instance',
         color,
         description: Description,

+ 1 - 0
src/mol-theme/color/residue-name.ts

@@ -105,6 +105,7 @@ export function ResidueNameColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
+        features: {},
         granularity: 'group',
         color,
         description: Description,

+ 1 - 0
src/mol-theme/color/secondary-structure.ts

@@ -72,6 +72,7 @@ export function SecondaryStructureColorTheme(props: ColorThemeProps): ColorTheme
     }
 
     return {
+        features: {},
         granularity: 'group',
         color,
         description: Description,

+ 3 - 1
src/mol-theme/color/sequence-id.ts

@@ -10,6 +10,7 @@ import { ColorScale, Color } from 'mol-util/color';
 import { Location } from 'mol-model/location';
 import { ColorThemeProps, ColorTheme } from '../color';
 import { ColorOther } from 'mol-util/color/tables';
+import { defaults } from 'mol-util';
 
 const DefaultColor = Color(0xCCCCCC)
 const Description = 'Gives every polymer residue a color based on its `seq_id` value.'
@@ -57,7 +58,7 @@ function getSequenceLength(unit: Unit, element: ElementIndex) {
 export function SequenceIdColorTheme(props: ColorThemeProps): ColorTheme {
     const p = {
         ...props,
-        colors: ColorOther.rainbow,
+        list: defaults(props.list, ColorOther.rainbow),
         minLabel: 'Start',
         maxLabel: 'End',
     }
@@ -83,6 +84,7 @@ export function SequenceIdColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
+        features: { list: true },
         granularity: 'group',
         color,
         description: Description,

+ 1 - 0
src/mol-theme/color/shape-group.ts

@@ -13,6 +13,7 @@ const DefaultColor = Color(0xCCCCCC)
 
 export function ShapeGroupColorTheme(props: ColorThemeProps): ColorTheme {
     return {
+        features: {},
         granularity: 'group',
         color: (location: Location): Color => {
             if (Shape.isLocation(location)) {

+ 1 - 0
src/mol-theme/color/uniform.ts

@@ -14,6 +14,7 @@ export function UniformColorTheme(props: ColorThemeProps): ColorTheme {
     const color = props.value || DefaultColor
 
     return {
+        features: {},
         granularity: 'uniform',
         color: () => color,
         description: Description,

+ 1 - 0
src/mol-theme/color/unit-index.ts

@@ -37,6 +37,7 @@ export function UnitIndexColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
+        features: {},
         granularity: 'instance',
         color,
         description: Description,

+ 2 - 2
src/mol-util/color/color.ts

@@ -65,8 +65,8 @@ export namespace Color {
     }
 }
 
-type ColorTable<T extends { [k: string]: number[] }> = { [k in keyof T]: Color[] }
+export type ColorTable<T extends { [k: string]: number[] }> = { [k in keyof T]: Color[] }
 export function ColorTable<T extends { [k: string]: number[] }>(o: T) { return o as ColorTable<T> }
 
-type ColorMap<T extends { [k: string]: number }> = { [k in keyof T]: Color }
+export type ColorMap<T extends { [k: string]: number }> = { [k in keyof T]: Color }
 export function ColorMap<T extends { [k: string]: number }>(o: T) { return o as ColorMap<T> }

+ 2 - 3
src/mol-util/color/index.ts

@@ -4,6 +4,5 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-export { Color, ColorMap, ColorTable } from './color'
-export { ColorScale } from './scale';
-export { ColorBrewer, ColorNames } from './tables'
+export { Color, ColorMap, ColorTable } from './color';
+export { ColorScale } from './scale';

+ 5 - 3
src/mol-util/color/scale.ts

@@ -25,7 +25,7 @@ export interface ColorScale {
 export const DefaultColorScale = {
     domain: [0, 1],
     reverse: false,
-    colors: ColorBrewer.RdYlBu,
+    list: ColorBrewer.RdYlBu,
     minLabel: '' as string | undefined,
     maxLabel: '' as string | undefined,
 }
@@ -33,8 +33,10 @@ export type ColorScaleProps = Partial<typeof DefaultColorScale>
 
 export namespace ColorScale {
     export function create(props: ColorScaleProps): ColorScale {
-        const { domain, reverse, colors: _colors } = { ...DefaultColorScale, ...props }
-        const colors = reverse ? _colors.slice().reverse() : _colors
+        // ensure that no undefined .list property exists so that the default assignment works
+        if (props.list === undefined) delete props.list
+        const { domain, reverse, list } = { ...DefaultColorScale, ...props }
+        const colors = reverse ? list.slice().reverse() : list
         const count1 = colors.length - 1
 
         let diff = 0, min = 0, max = 0