From 918fceeaa87f5667d674f9442f52c19f405b01e2 Mon Sep 17 00:00:00 2001
From: Alexander Rose <alex.rose@rcsb.org>
Date: Fri, 2 Nov 2018 17:46:05 -0700
Subject: [PATCH] volume repr refactoring

---
 src/apps/structure-info/volume.ts             |   4 +-
 src/mol-repr/structure/complex-visual.ts      |   5 +-
 src/mol-repr/structure/index.ts               |  23 ---
 src/mol-repr/structure/units-visual.ts        |   5 +-
 .../visual/carbohydrate-link-cylinder.ts      |   2 +-
 .../visual/carbohydrate-symbol-mesh.ts        |   3 +-
 .../visual/cross-link-restraint-cylinder.ts   |   3 +-
 .../structure/visual/element-point.ts         |   3 +-
 .../structure/visual/element-sphere.ts        |   3 +-
 .../visual/gaussian-density-point.ts          |   3 +-
 .../visual/gaussian-density-volume.ts         |   3 +-
 .../structure/visual/gaussian-surface-mesh.ts |   3 +-
 .../visual/gaussian-surface-wireframe.ts      |   3 +-
 .../visual/inter-unit-link-cylinder.ts        |   3 +-
 .../visual/intra-unit-link-cylinder.ts        |   3 +-
 .../visual/polymer-backbone-cylinder.ts       |   3 +-
 .../structure/visual/polymer-gap-cylinder.ts  |   3 +-
 .../structure/visual/polymer-trace-mesh.ts    |   3 +-
 src/mol-repr/structure/visual/util/common.ts  |  17 ---
 src/mol-repr/util.ts                          |  44 +++++-
 src/mol-repr/volume/index.ts                  | 131 +++++++++++++++++-
 src/mol-repr/volume/isosurface-mesh.ts        | 105 ++++----------
 22 files changed, 228 insertions(+), 147 deletions(-)

diff --git a/src/apps/structure-info/volume.ts b/src/apps/structure-info/volume.ts
index cef34b843..3b7d8cc3f 100644
--- a/src/apps/structure-info/volume.ts
+++ b/src/apps/structure-info/volume.ts
@@ -15,7 +15,7 @@ import { DensityServer_Data_Database } from 'mol-io/reader/cif/schema/density-se
 import { Table } from 'mol-data/db';
 import { StringBuilder } from 'mol-util';
 import { Task } from 'mol-task';
-import { createVolumeSurface } from 'mol-repr/volume/isosurface-mesh';
+import { createVolumeIsosurface } from 'mol-repr/volume/isosurface-mesh';
 
 require('util.promisify').shim();
 const writeFileAsync = util.promisify(fs.writeFile);
@@ -38,7 +38,7 @@ function print(data: Volume) {
 }
 
 async function doMesh(data: Volume, filename: string) {
-    const mesh = await Task.create('', ctx => createVolumeSurface(ctx, data.volume, VolumeIsoValue.calcAbsolute(data.volume.dataStats, 1.5))).run();
+    const mesh = await Task.create('', ctx => createVolumeIsosurface(ctx, 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.
diff --git a/src/mol-repr/structure/complex-visual.ts b/src/mol-repr/structure/complex-visual.ts
index a07cc0ded..4e7acbf75 100644
--- a/src/mol-repr/structure/complex-visual.ts
+++ b/src/mol-repr/structure/complex-visual.ts
@@ -8,8 +8,8 @@ import { Structure } from 'mol-model/structure';
 import { Visual } from '..';
 import { MeshRenderObject, LinesRenderObject, PointsRenderObject, DirectVolumeRenderObject } from 'mol-gl/render-object';
 import { RuntimeContext } from 'mol-task';
-import { createComplexMeshRenderObject, sizeChanged, colorChanged, UnitKind, UnitKindOptions } from './visual/util/common';
-import { StructureProps, VisualUpdateState, StructureMeshParams, StructureParams } from './index';
+import { createComplexMeshRenderObject, UnitKind, UnitKindOptions } from './visual/util/common';
+import { StructureProps, StructureMeshParams, StructureParams } from './index';
 import { deepEqual, ValueCell } from 'mol-util';
 import { Loci, isEveryLoci, EmptyLoci } from 'mol-model/loci';
 import { Interval } from 'mol-data/int';
@@ -22,6 +22,7 @@ import { PickingId } from 'mol-geo/geometry/picking';
 import { createColors } from 'mol-geo/geometry/color-data';
 import { MarkerAction, applyMarkerAction } from 'mol-geo/geometry/marker-data';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
+import { VisualUpdateState, colorChanged, sizeChanged } from 'mol-repr/util';
 
 export interface  ComplexVisual<P extends StructureProps> extends Visual<Structure, P> { }
 
diff --git a/src/mol-repr/structure/index.ts b/src/mol-repr/structure/index.ts
index 800c07203..03a58d51a 100644
--- a/src/mol-repr/structure/index.ts
+++ b/src/mol-repr/structure/index.ts
@@ -55,29 +55,6 @@ export const StructureDirectVolumeParams = {
 export const DefaultStructureDirectVolumeProps = paramDefaultValues(StructureDirectVolumeParams)
 export type StructureDirectVolumeProps = typeof DefaultStructureDirectVolumeProps
 
-export interface VisualUpdateState {
-    updateTransform: boolean
-    updateColor: boolean
-    updateSize: boolean
-    createGeometry: boolean
-}
-export namespace VisualUpdateState {
-    export function create(): VisualUpdateState {
-        return {
-            updateTransform: false,
-            updateColor: false,
-            updateSize: false,
-            createGeometry: false
-        }
-    }
-    export function reset(state: VisualUpdateState) {
-        state.updateTransform = false
-        state.updateColor = false
-        state.updateSize = false
-        state.createGeometry = false
-    }
-}
-
 export { ComplexRepresentation } from './complex-representation'
 export { UnitsRepresentation } from './units-representation'
 export { ComplexVisual } from './complex-visual'
diff --git a/src/mol-repr/structure/units-visual.ts b/src/mol-repr/structure/units-visual.ts
index b79db06aa..641d232f6 100644
--- a/src/mol-repr/structure/units-visual.ts
+++ b/src/mol-repr/structure/units-visual.ts
@@ -6,11 +6,11 @@
 
 import { Unit, Structure } from 'mol-model/structure';
 import { RepresentationProps, Visual } from '../';
-import { VisualUpdateState, StructureMeshParams, StructurePointsParams, StructureLinesParams, StructureDirectVolumeParams, StructureParams } from './index';
+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, colorChanged, sizeChanged } from './visual/util/common';
+import { createUnitsMeshRenderObject, createUnitsPointsRenderObject, createUnitsTransform, createUnitsLinesRenderObject, createUnitsDirectVolumeRenderObject, UnitKind, UnitKindOptions, includesUnitKind } from './visual/util/common';
 import { deepEqual, ValueCell, UUID } from 'mol-util';
 import { Interval } from 'mol-data/int';
 import { MultiSelectParam, paramDefaultValues } from 'mol-util/parameter';
@@ -25,6 +25,7 @@ import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { Points } from 'mol-geo/geometry/points/points';
 import { Lines } from 'mol-geo/geometry/lines/lines';
 import { DirectVolume } from 'mol-geo/geometry/direct-volume/direct-volume';
+import { VisualUpdateState, colorChanged, sizeChanged } from 'mol-repr/util';
 
 export type StructureGroup = { structure: Structure, group: Unit.SymmetryGroup }
 
diff --git a/src/mol-repr/structure/visual/carbohydrate-link-cylinder.ts b/src/mol-repr/structure/visual/carbohydrate-link-cylinder.ts
index fd904a280..8babf03f7 100644
--- a/src/mol-repr/structure/visual/carbohydrate-link-cylinder.ts
+++ b/src/mol-repr/structure/visual/carbohydrate-link-cylinder.ts
@@ -17,9 +17,9 @@ import { BitFlags } from 'mol-util';
 import { UnitsMeshParams } from '../units-visual';
 import { SelectParam, NumberParam, paramDefaultValues } from 'mol-util/parameter';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
-import { VisualUpdateState } from '../index';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { PickingId } from 'mol-geo/geometry/picking';
+import { VisualUpdateState } from '../../util';
 
 // TODO create seperate visual
 // for (let i = 0, il = carbohydrates.terminalLinks.length; i < il; ++i) {
diff --git a/src/mol-repr/structure/visual/carbohydrate-symbol-mesh.ts b/src/mol-repr/structure/visual/carbohydrate-symbol-mesh.ts
index 2e760c9a5..806fbd4dd 100644
--- a/src/mol-repr/structure/visual/carbohydrate-symbol-mesh.ts
+++ b/src/mol-repr/structure/visual/carbohydrate-symbol-mesh.ts
@@ -19,7 +19,8 @@ import { getSaccharideShape, SaccharideShapes } from 'mol-model/structure/struct
 import { addSphere } from 'mol-geo/geometry/mesh/builder/sphere';
 import { ComplexMeshParams, ComplexMeshVisual } from '../complex-visual';
 import { SelectParam, NumberParam, paramDefaultValues } from 'mol-util/parameter';
-import { ComplexVisual, VisualUpdateState } from '../index';
+import { ComplexVisual } from '../index';
+import { VisualUpdateState } from '../../util';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { OrderedSet, Interval } from 'mol-data/int';
diff --git a/src/mol-repr/structure/visual/cross-link-restraint-cylinder.ts b/src/mol-repr/structure/visual/cross-link-restraint-cylinder.ts
index c84541454..e71655c2a 100644
--- a/src/mol-repr/structure/visual/cross-link-restraint-cylinder.ts
+++ b/src/mol-repr/structure/visual/cross-link-restraint-cylinder.ts
@@ -5,7 +5,8 @@
  */
 
 import { Link, Structure, StructureElement } from 'mol-model/structure';
-import { ComplexVisual, VisualUpdateState } from '../index';
+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';
diff --git a/src/mol-repr/structure/visual/element-point.ts b/src/mol-repr/structure/visual/element-point.ts
index 09abfc3aa..d770bb0af 100644
--- a/src/mol-repr/structure/visual/element-point.ts
+++ b/src/mol-repr/structure/visual/element-point.ts
@@ -6,7 +6,8 @@
 
 import { Unit, Structure } from 'mol-model/structure';
 import { RuntimeContext } from 'mol-task'
-import { UnitsVisual, VisualUpdateState } from '../index';
+import { UnitsVisual } from '../index';
+import { VisualUpdateState } from '../../util';
 import { getElementLoci, StructureElementIterator, markElement } from './util/element';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { SizeThemeOptions, SizeThemeName } from 'mol-theme/size';
diff --git a/src/mol-repr/structure/visual/element-sphere.ts b/src/mol-repr/structure/visual/element-sphere.ts
index 8be9d0086..abb930830 100644
--- a/src/mol-repr/structure/visual/element-sphere.ts
+++ b/src/mol-repr/structure/visual/element-sphere.ts
@@ -5,7 +5,8 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { UnitsVisual, VisualUpdateState } from '../index';
+import { UnitsVisual } from '../index';
+import { VisualUpdateState } from '../../util';
 import { createElementSphereMesh, markElement, getElementLoci, StructureElementIterator } from './util/element';
 import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual';
 import { NumberParam, paramDefaultValues, SelectParam } from 'mol-util/parameter';
diff --git a/src/mol-repr/structure/visual/gaussian-density-point.ts b/src/mol-repr/structure/visual/gaussian-density-point.ts
index 807469abc..c4bfbc402 100644
--- a/src/mol-repr/structure/visual/gaussian-density-point.ts
+++ b/src/mol-repr/structure/visual/gaussian-density-point.ts
@@ -6,7 +6,8 @@
 
 import { Unit, Structure } from 'mol-model/structure';
 import { RuntimeContext } from 'mol-task'
-import { UnitsVisual, VisualUpdateState } from '../index';
+import { UnitsVisual } from '../index';
+import { VisualUpdateState } from '../../util';
 import { StructureElementIterator } from './util/element';
 import { EmptyLoci } from 'mol-model/loci';
 import { Vec3 } from 'mol-math/linear-algebra';
diff --git a/src/mol-repr/structure/visual/gaussian-density-volume.ts b/src/mol-repr/structure/visual/gaussian-density-volume.ts
index 00697e797..4179da8c1 100644
--- a/src/mol-repr/structure/visual/gaussian-density-volume.ts
+++ b/src/mol-repr/structure/visual/gaussian-density-volume.ts
@@ -5,7 +5,8 @@
  */
 
 import { Unit, Structure } from 'mol-model/structure';
-import { UnitsVisual, VisualUpdateState } from '../index';
+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';
diff --git a/src/mol-repr/structure/visual/gaussian-surface-mesh.ts b/src/mol-repr/structure/visual/gaussian-surface-mesh.ts
index ccbfab83e..7982008eb 100644
--- a/src/mol-repr/structure/visual/gaussian-surface-mesh.ts
+++ b/src/mol-repr/structure/visual/gaussian-surface-mesh.ts
@@ -5,7 +5,8 @@
  */
 
 import { Unit, Structure } from 'mol-model/structure';
-import { UnitsVisual, VisualUpdateState } from '../index';
+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';
diff --git a/src/mol-repr/structure/visual/gaussian-surface-wireframe.ts b/src/mol-repr/structure/visual/gaussian-surface-wireframe.ts
index 56cc5b6cb..beb212437 100644
--- a/src/mol-repr/structure/visual/gaussian-surface-wireframe.ts
+++ b/src/mol-repr/structure/visual/gaussian-surface-wireframe.ts
@@ -5,7 +5,8 @@
  */
 
 import { Unit, Structure } from 'mol-model/structure';
-import { UnitsVisual, VisualUpdateState } from '../index';
+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';
diff --git a/src/mol-repr/structure/visual/inter-unit-link-cylinder.ts b/src/mol-repr/structure/visual/inter-unit-link-cylinder.ts
index 2f4abc282..4f2c95708 100644
--- a/src/mol-repr/structure/visual/inter-unit-link-cylinder.ts
+++ b/src/mol-repr/structure/visual/inter-unit-link-cylinder.ts
@@ -5,7 +5,8 @@
  */
 
 import { Link, Structure, StructureElement } from 'mol-model/structure';
-import { ComplexVisual, VisualUpdateState } from '../index';
+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';
diff --git a/src/mol-repr/structure/visual/intra-unit-link-cylinder.ts b/src/mol-repr/structure/visual/intra-unit-link-cylinder.ts
index bb3c4898e..97179ec27 100644
--- a/src/mol-repr/structure/visual/intra-unit-link-cylinder.ts
+++ b/src/mol-repr/structure/visual/intra-unit-link-cylinder.ts
@@ -6,7 +6,8 @@
  */
 
 import { Unit, Link, StructureElement, Structure } from 'mol-model/structure';
-import { UnitsVisual, VisualUpdateState } from '../index';
+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';
diff --git a/src/mol-repr/structure/visual/polymer-backbone-cylinder.ts b/src/mol-repr/structure/visual/polymer-backbone-cylinder.ts
index ba556c24c..b43c0dfc2 100644
--- a/src/mol-repr/structure/visual/polymer-backbone-cylinder.ts
+++ b/src/mol-repr/structure/visual/polymer-backbone-cylinder.ts
@@ -5,7 +5,8 @@
  */
 
 import { Unit, Structure } from 'mol-model/structure';
-import { UnitsVisual, VisualUpdateState } from '../index';
+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';
diff --git a/src/mol-repr/structure/visual/polymer-gap-cylinder.ts b/src/mol-repr/structure/visual/polymer-gap-cylinder.ts
index 1e295ee14..389b494a2 100644
--- a/src/mol-repr/structure/visual/polymer-gap-cylinder.ts
+++ b/src/mol-repr/structure/visual/polymer-gap-cylinder.ts
@@ -5,7 +5,8 @@
  */
 
 import { Unit, Structure } from 'mol-model/structure';
-import { UnitsVisual, VisualUpdateState } from '../index';
+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';
diff --git a/src/mol-repr/structure/visual/polymer-trace-mesh.ts b/src/mol-repr/structure/visual/polymer-trace-mesh.ts
index 47d0ca82c..910f59b26 100644
--- a/src/mol-repr/structure/visual/polymer-trace-mesh.ts
+++ b/src/mol-repr/structure/visual/polymer-trace-mesh.ts
@@ -5,7 +5,8 @@
  */
 
 import { Unit, Structure } from 'mol-model/structure';
-import { UnitsVisual, VisualUpdateState } from '../index';
+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';
diff --git a/src/mol-repr/structure/visual/util/common.ts b/src/mol-repr/structure/visual/util/common.ts
index 56e9f0e8e..33f226f22 100644
--- a/src/mol-repr/structure/visual/util/common.ts
+++ b/src/mol-repr/structure/visual/util/common.ts
@@ -9,8 +9,6 @@ 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 { SizeProps } from 'mol-geo/geometry/size-data';
-import { ColorProps } from 'mol-geo/geometry/color-data';
 import { TransformData, createTransform, createIdentityTransform } from 'mol-geo/geometry/transform-data';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
@@ -47,21 +45,6 @@ export function includesUnitKind(unitKinds: UnitKind[], unit: Unit) {
     return false
 }
 
-export function sizeChanged(oldProps: SizeProps, newProps: SizeProps) {
-    return (
-        oldProps.sizeTheme !== newProps.sizeTheme ||
-        oldProps.sizeValue !== newProps.sizeValue ||
-        oldProps.sizeFactor !== newProps.sizeFactor
-    )
-}
-
-export function colorChanged(oldProps: ColorProps, newProps: ColorProps) {
-    return (
-        oldProps.colorTheme !== newProps.colorTheme ||
-        oldProps.colorValue !== newProps.colorValue
-    )
-}
-
 // mesh
 
 type StructureMeshProps = Mesh.Props & StructureProps
diff --git a/src/mol-repr/util.ts b/src/mol-repr/util.ts
index e227c24f8..792e9ff05 100644
--- a/src/mol-repr/util.ts
+++ b/src/mol-repr/util.ts
@@ -6,7 +6,49 @@
 
 import { defaults } from 'mol-util';
 import { Structure } from 'mol-model/structure';
-import { VisualQuality } from '../mol-geo/geometry/geometry';
+import { VisualQuality } from 'mol-geo/geometry/geometry';
+import { SizeProps } from 'mol-geo/geometry/size-data';
+import { ColorProps } from 'mol-geo/geometry/color-data';
+
+export interface VisualUpdateState {
+    updateTransform: boolean
+    updateColor: boolean
+    updateSize: boolean
+    createGeometry: boolean
+}
+export namespace VisualUpdateState {
+    export function create(): VisualUpdateState {
+        return {
+            updateTransform: false,
+            updateColor: false,
+            updateSize: false,
+            createGeometry: false
+        }
+    }
+    export function reset(state: VisualUpdateState) {
+        state.updateTransform = false
+        state.updateColor = false
+        state.updateSize = false
+        state.createGeometry = false
+    }
+}
+
+export function sizeChanged(oldProps: SizeProps, newProps: SizeProps) {
+    return (
+        oldProps.sizeTheme !== newProps.sizeTheme ||
+        oldProps.sizeValue !== newProps.sizeValue ||
+        oldProps.sizeFactor !== newProps.sizeFactor
+    )
+}
+
+export function colorChanged(oldProps: ColorProps, newProps: ColorProps) {
+    return (
+        oldProps.colorTheme !== newProps.colorTheme ||
+        oldProps.colorValue !== newProps.colorValue
+    )
+}
+
+//
 
 export interface QualityProps {
     quality: VisualQuality
diff --git a/src/mol-repr/volume/index.ts b/src/mol-repr/volume/index.ts
index 16192970f..49f6a31cb 100644
--- a/src/mol-repr/volume/index.ts
+++ b/src/mol-repr/volume/index.ts
@@ -4,21 +4,140 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Task } from 'mol-task'
+import { Task, RuntimeContext } from 'mol-task'
 import { RepresentationProps, Representation, Visual } from '..';
-import { VolumeData } from 'mol-model/volume';
-import { Loci, EmptyLoci } from 'mol-model/loci';
-import { paramDefaultValues } from 'mol-util/parameter';
-import { Geometry } from 'mol-geo/geometry/geometry';
+import { VolumeData, VolumeIsoValue } from 'mol-model/volume';
+import { Loci, EmptyLoci, isEveryLoci } from 'mol-model/loci';
+import { paramDefaultValues, RangeParam } from 'mol-util/parameter';
+import { Geometry, updateRenderableState } from 'mol-geo/geometry/geometry';
 import { PickingId } from 'mol-geo/geometry/picking';
-import { MarkerAction } from 'mol-geo/geometry/marker-data';
+import { MarkerAction, applyMarkerAction } from 'mol-geo/geometry/marker-data';
+import { DirectVolumeRenderObject, PointsRenderObject, LinesRenderObject, MeshRenderObject } from 'mol-gl/render-object';
+import { Interval } from 'mol-data/int';
+import { RenderableValues } from 'mol-gl/renderable/schema';
+import { LocationIterator } from 'mol-geo/util/location-iterator';
+import { NullLocation } from 'mol-model/location';
+import { VisualUpdateState } from 'mol-repr/util';
+import { ValueCell } from 'mol-util';
 
 export interface VolumeVisual<P extends RepresentationProps = {}> extends Visual<VolumeData, P> { }
 
+type VolumeRenderObject = MeshRenderObject | LinesRenderObject | PointsRenderObject | DirectVolumeRenderObject
+
+interface VolumeVisualBuilder<P extends VolumeProps, G extends Geometry> {
+    defaultProps: P
+    createGeometry(ctx: RuntimeContext, 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>
+    updateValues(values: RenderableValues, newProps: P): void
+}
+
+export function VolumeVisual<P extends VolumeProps>(builder: VolumeVisualGeometryBuilder<P, Geometry>) {
+    const { defaultProps, createGeometry, getLoci, mark, setUpdateState } = builder
+    const { createRenderObject, updateValues } = builder
+    const updateState = VisualUpdateState.create()
+
+    let currentProps: P
+    let renderObject: VolumeRenderObject | undefined
+    let currentVolume: VolumeData
+    let geometry: Geometry
+    let locationIt: LocationIterator
+
+    async function create(ctx: RuntimeContext, volume: VolumeData, props: Partial<VolumeProps> = {}) {
+        currentProps = Object.assign({}, defaultProps, props)
+        if (props.isoValueRelative) {
+            currentProps.isoValueAbsolute = VolumeIsoValue.calcAbsolute(currentVolume.dataStats, props.isoValueRelative)
+            console.log('create props.isoValueRelative', props.isoValueRelative, currentProps.isoValueAbsolute, currentVolume.dataStats)
+        }
+
+        geometry = await createGeometry(ctx, volume, currentProps, geometry)
+        locationIt = LocationIterator(1, 1, () => NullLocation)
+        renderObject = await createRenderObject(ctx, geometry, locationIt, currentProps)
+    }
+
+    async function update(ctx: RuntimeContext, props: Partial<VolumeProps> = {}) {
+        if (!renderObject) return
+        const newProps = Object.assign({}, currentProps, props)
+
+        if (props.isoValueRelative) {
+            newProps.isoValueAbsolute = VolumeIsoValue.calcAbsolute(currentVolume.dataStats, props.isoValueRelative)
+            console.log('update props.isoValueRelative', props.isoValueRelative, newProps.isoValueAbsolute, currentVolume.dataStats)
+        }
+
+        VisualUpdateState.reset(updateState)
+        setUpdateState(updateState, newProps, currentProps)
+
+        if (updateState.createGeometry) {
+            geometry = await createGeometry(ctx, currentVolume, currentProps, geometry)
+            ValueCell.update(renderObject.values.drawCount, Geometry.getDrawCount(geometry))
+        }
+
+        updateValues(renderObject.values, newProps)
+        updateRenderableState(renderObject.state, newProps)
+
+        currentProps = newProps
+    }
+
+    return {
+        get renderObject () { return renderObject },
+        async createOrUpdate(ctx: RuntimeContext, props: Partial<VolumeProps> = {}, 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 = Object.assign({}, defaultProps, props)
+        },
+        getLoci(pickingId: PickingId) {
+            return renderObject ? getLoci(pickingId, renderObject.id) : EmptyLoci
+        },
+        mark(loci: Loci, action: MarkerAction) {
+            if (!renderObject) return false
+            const { tMarker } = renderObject.values
+            const { groupCount, instanceCount } = locationIt
+
+            function apply(interval: Interval) {
+                const start = Interval.start(interval)
+                const end = Interval.end(interval)
+                return applyMarkerAction(tMarker.ref.value.array, start, end, action)
+            }
+
+            let changed = false
+            if (isEveryLoci(loci)) {
+                changed = apply(Interval.ofBounds(0, groupCount * instanceCount))
+            } else {
+                changed = mark(loci, apply)
+            }
+            if (changed) {
+                ValueCell.update(tMarker, tMarker.ref.value)
+            }
+            return changed
+        },
+        destroy() {
+            // TODO
+            renderObject = undefined
+        }
+    }
+}
+
 export interface VolumeRepresentation<P extends RepresentationProps = {}> extends Representation<VolumeData, P> { }
 
 export const VolumeParams = {
     ...Geometry.Params,
+    isoValueAbsolute: RangeParam('Iso Value Absolute', '', 0.22, -1, 1, 0.01),
+    isoValueRelative: RangeParam('Iso Value Relative', '', 2, -10, 10, 0.1),
 }
 export const DefaultVolumeProps = paramDefaultValues(VolumeParams)
 export type VolumeProps = typeof DefaultVolumeProps
diff --git a/src/mol-repr/volume/isosurface-mesh.ts b/src/mol-repr/volume/isosurface-mesh.ts
index 152cb3a8b..0dd1b600d 100644
--- a/src/mol-repr/volume/isosurface-mesh.ts
+++ b/src/mol-repr/volume/isosurface-mesh.ts
@@ -5,27 +5,30 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { VolumeData, VolumeIsoValue } from 'mol-model/volume'
+import { VolumeData } from 'mol-model/volume'
 import { RuntimeContext } from 'mol-task'
 import { VolumeVisual, VolumeRepresentation } from './index';
-import { createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object';
+import { createMeshRenderObject } from 'mol-gl/render-object';
 import { Loci, EmptyLoci } from 'mol-model/loci';
-import { NullLocation } from 'mol-model/location';
 import { paramDefaultValues, RangeParam } from 'mol-util/parameter';
-import { ValueCell } from 'mol-util';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { computeMarchingCubesMesh } from 'mol-geo/util/marching-cubes/algorithm';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { createIdentityTransform } from 'mol-geo/geometry/transform-data';
-import { createRenderableState, updateRenderableState } from 'mol-geo/geometry/geometry';
+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';
 
-export async function createVolumeSurface(ctx: RuntimeContext, volume: VolumeData, isoValueAbsolute: number, mesh?: Mesh) {
+interface VolumeIsosurfaceProps {
+    isoValueAbsolute: number
+}
+
+export async function createVolumeIsosurface(ctx: RuntimeContext, volume: VolumeData, props: VolumeIsosurfaceProps, mesh?: Mesh) {
     ctx.update({ message: 'Marching cubes...' });
 
     const surface = await computeMarchingCubesMesh({
-        isoLevel: isoValueAbsolute,
+        isoLevel: props.isoValueAbsolute,
         scalarField: volume.data
     }, mesh).runAsChild(ctx);
 
@@ -45,81 +48,23 @@ export const IsosurfaceParams = {
 export const DefaultIsosurfaceProps = paramDefaultValues(IsosurfaceParams)
 export type IsosurfaceProps = typeof DefaultIsosurfaceProps
 
-export function IsosurfaceVisual(): VolumeVisual<IsosurfaceProps> {
-    let currentProps = DefaultIsosurfaceProps
-    let renderObject: MeshRenderObject
-    let currentVolume: VolumeData
-    let mesh: Mesh
-
-    async function create(ctx: RuntimeContext, volume: VolumeData, props: Partial<IsosurfaceProps> = {}) {
-        currentProps = { ...DefaultIsosurfaceProps, ...props }
-        if (props.isoValueRelative) {
-            currentProps.isoValueAbsolute = VolumeIsoValue.calcAbsolute(currentVolume.dataStats, props.isoValueRelative)
-            console.log('create props.isoValueRelative', props.isoValueRelative, currentProps.isoValueAbsolute, currentVolume.dataStats)
-        }
-
-        mesh = await createVolumeSurface(ctx, volume, currentProps.isoValueAbsolute)
-
-        const locationIt = LocationIterator(1, 1, () => NullLocation)
-        const transform = createIdentityTransform()
-
-        const values = await Mesh.createValues(ctx, mesh, transform, locationIt, currentProps)
-        const state = createRenderableState(currentProps)
-
-        renderObject = createMeshRenderObject(values, state)
-    }
-
-    async function update(ctx: RuntimeContext, props: Partial<IsosurfaceProps> = {}) {
-        const newProps = { ...currentProps, ...props }
-        if (props.isoValueRelative) {
-            newProps.isoValueAbsolute = VolumeIsoValue.calcAbsolute(currentVolume.dataStats, props.isoValueRelative)
-            console.log('update props.isoValueRelative', props.isoValueRelative, newProps.isoValueAbsolute, currentVolume.dataStats)
-        }
-
-        let createMesh = false
-
-        if (newProps.isoValueAbsolute !== currentProps.isoValueAbsolute) createMesh = true
-
-        if (createMesh) {
-            mesh = await createVolumeSurface(ctx, currentVolume, newProps.isoValueAbsolute, mesh)
-            ValueCell.update(renderObject.values.drawCount, mesh.triangleCount * 3)
-        }
-
-        Mesh.updateValues(renderObject.values, newProps)
-        updateRenderableState(renderObject.state, newProps)
-
-        currentProps = newProps
-    }
-
-    return {
-        get renderObject () { return renderObject },
-        async createOrUpdate(ctx: RuntimeContext, props: Partial<IsosurfaceProps> = {}, 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 = { ...DefaultIsosurfaceProps, ...props }
-        },
-        getLoci(pickingId: PickingId) {
-            // TODO
-            return EmptyLoci
+export function IsosurfaceVisual():  VolumeVisual<IsosurfaceProps> {
+    return VolumeVisual<IsosurfaceProps>({
+        defaultProps: DefaultIsosurfaceProps,
+        createGeometry: createVolumeIsosurface,
+        getLoci: () => EmptyLoci,
+        mark: () => false,
+        setUpdateState: (state: VisualUpdateState, newProps: IsosurfaceProps, currentProps: IsosurfaceProps) => {
+            if (newProps.isoValueAbsolute !== currentProps.isoValueAbsolute) state.createGeometry = true
         },
-        mark(loci: Loci, action: MarkerAction) {
-            // TODO
-            return false
+        createRenderObject: async (ctx: RuntimeContext, geometry: Mesh, locationIt: LocationIterator, props: IsosurfaceProps) => {
+            const transform = createIdentityTransform()
+            const values = await Mesh.createValues(ctx, geometry, transform, locationIt, props)
+            const state = createRenderableState(props)
+            return createMeshRenderObject(values, state)
         },
-        destroy() {
-            // TODO
-        }
-    }
+        updateValues: Mesh.updateValues
+    })
 }
 
 export function IsosurfaceRepresentation(): VolumeRepresentation<IsosurfaceProps> {
-- 
GitLab