Bladeren bron

volume refactoring

- renamed VolumeData to Grid
- renamed VolumeData.data to cells
- renamed VolumeData.dataStats to stats
- added grid to Volume
- added label to Volume
- added custom props to volume
- use Volume instead of VolumeData/Grid as main object
Alexander Rose 5 jaren geleden
bovenliggende
commit
50d95ccf6a
58 gewijzigde bestanden met toevoegingen van 489 en 420 verwijderingen
  1. 10 12
      src/apps/structure-info/volume.ts
  2. 2 1
      src/extensions/cellpack/property.ts
  3. 2 1
      src/extensions/pdbe/preferred-assembly.ts
  4. 2 1
      src/extensions/pdbe/struct-ref-domain.ts
  5. 2 1
      src/extensions/pdbe/structure-quality-report/prop.ts
  6. 2 1
      src/extensions/rcsb/assembly-symmetry/prop.ts
  7. 2 1
      src/extensions/rcsb/validation-report/prop.ts
  8. 1 1
      src/mol-model-formats/structure/basic/parser.ts
  9. 2 1
      src/mol-model-formats/structure/common/property.ts
  10. 1 1
      src/mol-model-formats/structure/property/anisotropic.ts
  11. 1 1
      src/mol-model-formats/structure/property/bonds/comp.ts
  12. 1 1
      src/mol-model-formats/structure/property/bonds/index-pair.ts
  13. 1 1
      src/mol-model-formats/structure/property/bonds/struct_conn.ts
  14. 1 1
      src/mol-model-formats/structure/property/secondary-structure.ts
  15. 1 1
      src/mol-model-formats/structure/property/symmetry.ts
  16. 16 11
      src/mol-model-formats/volume/ccp4.ts
  17. 16 11
      src/mol-model-formats/volume/cube.ts
  18. 16 11
      src/mol-model-formats/volume/density-server.ts
  19. 16 11
      src/mol-model-formats/volume/dsn6.ts
  20. 16 11
      src/mol-model-formats/volume/dx.ts
  21. 2 1
      src/mol-model-props/common/custom-element-property.ts
  22. 2 1
      src/mol-model-props/common/custom-model-property.ts
  23. 1 1
      src/mol-model-props/common/custom-property.ts
  24. 2 1
      src/mol-model-props/common/custom-structure-property.ts
  25. 2 1
      src/mol-model-props/computed/accessible-surface-area.ts
  26. 2 1
      src/mol-model-props/computed/interactions.ts
  27. 1 1
      src/mol-model-props/computed/secondary-structure.ts
  28. 2 1
      src/mol-model-props/computed/valence-model.ts
  29. 2 1
      src/mol-model-props/integrative/cross-link-restraint/format.ts
  30. 2 1
      src/mol-model-props/integrative/cross-link-restraint/property.ts
  31. 5 5
      src/mol-model/custom-property.ts
  32. 1 2
      src/mol-model/structure.ts
  33. 1 1
      src/mol-model/structure/export/mmcif.ts
  34. 1 1
      src/mol-model/structure/model/model.ts
  35. 1 1
      src/mol-model/structure/structure/structure.ts
  36. 4 2
      src/mol-model/volume.ts
  37. 0 96
      src/mol-model/volume/data.ts
  38. 57 0
      src/mol-model/volume/grid.ts
  39. 91 18
      src/mol-model/volume/volume.ts
  40. 7 8
      src/mol-plugin-state/formats/volume.ts
  41. 18 18
      src/mol-plugin-state/helpers/volume-representation-params.ts
  42. 2 2
      src/mol-plugin-state/objects.ts
  43. 6 6
      src/mol-plugin-state/transforms/representation.ts
  44. 3 3
      src/mol-plugin-state/transforms/volume.ts
  45. 7 7
      src/mol-plugin-ui/custom/volume.tsx
  46. 2 2
      src/mol-plugin-ui/structure/volume.tsx
  47. 18 18
      src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts
  48. 3 3
      src/mol-plugin/behavior/dynamic/volume-streaming/model.ts
  49. 4 4
      src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts
  50. 18 21
      src/mol-repr/volume/direct-volume.ts
  51. 48 49
      src/mol-repr/volume/isosurface.ts
  52. 2 2
      src/mol-repr/volume/registry.ts
  53. 18 18
      src/mol-repr/volume/representation.ts
  54. 32 33
      src/mol-repr/volume/slice.ts
  55. 2 1
      src/mol-script/runtime/query/base.ts
  56. 5 5
      src/mol-theme/label.ts
  57. 2 2
      src/mol-theme/theme.ts
  58. 2 1
      src/perf-tests/mol-script.ts

+ 10 - 12
src/apps/structure-info/volume.ts

@@ -8,38 +8,36 @@ import * as fs from 'fs';
 import * as argparse from 'argparse';
 import * as util from 'util';
 
-import { VolumeData, VolumeIsoValue } from '../../mol-model/volume';
+import { Volume } from '../../mol-model/volume';
 import { downloadCif } from './helpers';
 import { CIF } from '../../mol-io/reader/cif';
-import { DensityServer_Data_Database } from '../../mol-io/reader/cif/schema/density-server';
 import { Table } from '../../mol-data/db';
 import { StringBuilder } from '../../mol-util';
 import { Task } from '../../mol-task';
 import { createVolumeIsosurfaceMesh } from '../../mol-repr/volume/isosurface';
 import { Theme } from '../../mol-theme/theme';
-import { volumeFromDensityServerData } from '../../mol-model-formats/volume/density-server';
+import { volumeFromDensityServerData, DscifFormat } from '../../mol-model-formats/volume/density-server';
 
 require('util.promisify').shim();
 const writeFileAsync = util.promisify(fs.writeFile);
 
-type Volume = { source: DensityServer_Data_Database, volume: VolumeData }
-
 async function getVolume(url: string): Promise<Volume> {
     const cif = await downloadCif(url, true);
     const data = CIF.schema.densityServer(cif.blocks[1]);
-    return { source: data, volume: await volumeFromDensityServerData(data).run() };
+    return await volumeFromDensityServerData(data).run();
 }
 
-function print(data: Volume) {
-    const { volume_data_3d_info } = data.source;
+function print(volume: Volume) {
+    if (!DscifFormat.is(volume.sourceData)) return;
+    const { volume_data_3d_info } = volume.sourceData.data;
     const row = Table.getRow(volume_data_3d_info, 0);
     console.log(row);
-    if (data.volume.transform) console.log(data.volume.transform);
-    console.log(data.volume.dataStats);
+    console.log(volume.grid.transform);
+    console.log(volume.grid.stats);
 }
 
-async function doMesh(data: Volume, filename: string) {
-    const mesh = await Task.create('', runtime => createVolumeIsosurfaceMesh({ runtime }, data.volume, Theme.createEmpty(), { isoValue: VolumeIsoValue.absolute(1.5) } )).run();
+async function doMesh(volume: Volume, filename: string) {
+    const mesh = await Task.create('', runtime => createVolumeIsosurfaceMesh({ runtime }, volume, Theme.createEmpty(), { isoValue: Volume.IsoValue.absolute(1.5) } )).run();
     console.log({ vc: mesh.vertexCount, tc: mesh.triangleCount });
 
     // Export the mesh in OBJ format.

+ 2 - 1
src/extensions/cellpack/property.ts

@@ -5,9 +5,10 @@
  */
 
 import { CustomStructureProperty } from '../../mol-model-props/common/custom-structure-property';
-import { Structure, CustomPropertyDescriptor } from '../../mol-model/structure';
+import { Structure } from '../../mol-model/structure';
 import { CustomProperty } from '../../mol-model-props/common/custom-property';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
+import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
 
 export type CellPackInfoValue = {
     packingsCount: number

+ 2 - 1
src/extensions/pdbe/preferred-assembly.ts

@@ -7,9 +7,10 @@
 import { Column, Table } from '../../mol-data/db';
 import { toTable } from '../../mol-io/reader/cif/schema';
 import { CifWriter } from '../../mol-io/writer/cif';
-import { Model, CustomPropertyDescriptor } from '../../mol-model/structure';
+import { Model } from '../../mol-model/structure';
 import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry';
 import { MmcifFormat } from '../../mol-model-formats/structure/mmcif';
+import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
 
 export namespace PDBePreferredAssembly {
     export type Property = string

+ 2 - 1
src/extensions/pdbe/struct-ref-domain.ts

@@ -7,9 +7,10 @@
 import { Column, Table } from '../../mol-data/db';
 import { toTable } from '../../mol-io/reader/cif/schema';
 import { CifWriter } from '../../mol-io/writer/cif';
-import { Model, CustomPropertyDescriptor } from '../../mol-model/structure';
+import { Model } from '../../mol-model/structure';
 import { PropertyWrapper } from '../../mol-model-props/common/wrapper';
 import { MmcifFormat } from '../../mol-model-formats/structure/mmcif';
+import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
 
 export namespace PDBeStructRefDomain {
     export type Property = PropertyWrapper<Table<Schema['pdbe_struct_ref_domain']> | undefined>

+ 2 - 1
src/extensions/pdbe/structure-quality-report/prop.ts

@@ -9,7 +9,7 @@ import { Column, Table } from '../../../mol-data/db';
 import { toTable } from '../../../mol-io/reader/cif/schema';
 import { mmCIF_residueId_schema } from '../../../mol-io/reader/cif/schema/mmcif-extras';
 import { CifWriter } from '../../../mol-io/writer/cif';
-import { Model, CustomPropertyDescriptor, ResidueIndex, Unit, IndexedCustomProperty } from '../../../mol-model/structure';
+import { Model, ResidueIndex, Unit, IndexedCustomProperty } from '../../../mol-model/structure';
 import { residueIdFields } from '../../../mol-model/structure/export/categories/atom_site';
 import { StructureElement, CifExportContext, Structure } from '../../../mol-model/structure/structure';
 import { CustomPropSymbol } from '../../../mol-script/language/symbol';
@@ -22,6 +22,7 @@ import { PropertyWrapper } from '../../../mol-model-props/common/wrapper';
 import { CustomProperty } from '../../../mol-model-props/common/custom-property';
 import { CustomModelProperty } from '../../../mol-model-props/common/custom-model-property';
 import { Asset } from '../../../mol-util/assets';
+import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
 
 export { StructureQualityReport };
 

+ 2 - 1
src/extensions/rcsb/assembly-symmetry/prop.ts

@@ -8,7 +8,7 @@ import { AssemblySymmetryQuery, AssemblySymmetryQueryVariables } from '../graphq
 import query from '../graphql/symmetry.gql';
 
 import { ParamDefinition as PD } from '../../../mol-util/param-definition';
-import { CustomPropertyDescriptor, Structure, Model, StructureSelection, QueryContext } from '../../../mol-model/structure';
+import { Structure, Model, StructureSelection, QueryContext } from '../../../mol-model/structure';
 import { Database as _Database, Column } from '../../../mol-data/db';
 import { GraphQLClient } from '../../../mol-util/graphql-client';
 import { CustomProperty } from '../../../mol-model-props/common/custom-property';
@@ -19,6 +19,7 @@ import { ReadonlyVec3 } from '../../../mol-math/linear-algebra/3d/vec3';
 import { SetUtils } from '../../../mol-util/set';
 import { MolScriptBuilder as MS } from '../../../mol-script/language/builder';
 import { compile } from '../../../mol-script/runtime/query/compiler';
+import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
 
 const BiologicalAssemblyNames = new Set([
     'author_and_software_defined_assembly',

+ 2 - 1
src/extensions/rcsb/validation-report/prop.ts

@@ -5,7 +5,7 @@
  */
 
 import { ParamDefinition as PD } from '../../../mol-util/param-definition';
-import { CustomPropertyDescriptor, Structure, Unit } from '../../../mol-model/structure';
+import { Structure, Unit } from '../../../mol-model/structure';
 import { CustomProperty } from '../../../mol-model-props/common/custom-property';
 import { CustomModelProperty } from '../../../mol-model-props/common/custom-model-property';
 import { Model, ElementIndex, ResidueIndex } from '../../../mol-model/structure/model';
@@ -21,6 +21,7 @@ import { QuerySymbolRuntime } from '../../../mol-script/runtime/query/compiler';
 import { CustomPropSymbol } from '../../../mol-script/language/symbol';
 import Type from '../../../mol-script/language/type';
 import { Asset } from '../../../mol-util/assets';
+import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
 
 export { ValidationReport };
 

+ 1 - 1
src/mol-model-formats/structure/basic/parser.ts

@@ -10,7 +10,7 @@ import { RuntimeContext } from '../../../mol-task';
 import UUID from '../../../mol-util/uuid';
 import { Model } from '../../../mol-model/structure/model/model';
 import { Entities } from '../../../mol-model/structure/model/properties/common';
-import { CustomProperties } from '../../../mol-model/structure';
+import { CustomProperties } from '../../../mol-model/custom-property';
 import { getAtomicHierarchyAndConformation } from './atomic';
 import { getCoarse, EmptyCoarse, CoarseData } from './coarse';
 import { getSequence } from './sequence';

+ 2 - 1
src/mol-model-formats/structure/common/property.ts

@@ -4,8 +4,9 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { CustomPropertyDescriptor, Model } from '../../../mol-model/structure';
+import { Model } from '../../../mol-model/structure';
 import { ModelFormat } from '../../format';
+import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
 
 class FormatRegistry<T> {
     private map = new Map<ModelFormat['kind'], (model: Model) => T | undefined>()

+ 1 - 1
src/mol-model-formats/structure/property/anisotropic.ts

@@ -5,7 +5,7 @@
  */
 
 import { Table, Column } from '../../../mol-data/db';
-import { CustomPropertyDescriptor } from '../../../mol-model/structure';
+import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
 import { mmCIF_Schema } from '../../../mol-io/reader/cif/schema/mmcif';
 import { CifWriter } from '../../../mol-io/writer/cif';
 import { FormatPropertyProvider } from '../common/property';

+ 1 - 1
src/mol-model-formats/structure/property/bonds/comp.ts

@@ -7,7 +7,7 @@
 
 import { Model } from '../../../../mol-model/structure/model/model';
 import { BondType } from '../../../../mol-model/structure/model/types';
-import { CustomPropertyDescriptor } from '../../../../mol-model/structure';
+import { CustomPropertyDescriptor } from '../../../../mol-model/custom-property';
 import { mmCIF_Schema } from '../../../../mol-io/reader/cif/schema/mmcif';
 import { CifWriter } from '../../../../mol-io/writer/cif';
 import { Table } from '../../../../mol-data/db';

+ 1 - 1
src/mol-model-formats/structure/property/bonds/index-pair.ts

@@ -4,7 +4,7 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { CustomPropertyDescriptor } from '../../../../mol-model/structure';
+import { CustomPropertyDescriptor } from '../../../../mol-model/custom-property';
 import { IntAdjacencyGraph } from '../../../../mol-math/graph';
 import { Column } from '../../../../mol-data/db';
 import { FormatPropertyProvider } from '../../common/property';

+ 1 - 1
src/mol-model-formats/structure/property/bonds/struct_conn.ts

@@ -9,7 +9,7 @@ import { Model } from '../../../../mol-model/structure/model/model';
 import { Structure } from '../../../../mol-model/structure';
 import { BondType } from '../../../../mol-model/structure/model/types';
 import { Column, Table } from '../../../../mol-data/db';
-import { CustomPropertyDescriptor } from '../../../../mol-model/structure';
+import { CustomPropertyDescriptor } from '../../../../mol-model/custom-property';
 import { mmCIF_Schema } from '../../../../mol-io/reader/cif/schema/mmcif';
 import { SortedArray } from '../../../../mol-data/int';
 import { CifWriter } from '../../../../mol-io/writer/cif';

+ 1 - 1
src/mol-model-formats/structure/property/secondary-structure.ts

@@ -13,7 +13,7 @@ import { SecondaryStructure } from '../../../mol-model/structure/model/propertie
 import { Column, Table } from '../../../mol-data/db';
 import { ChainIndex, ResidueIndex } from '../../../mol-model/structure/model/indexing';
 import { FormatPropertyProvider } from '../common/property';
-import { CustomPropertyDescriptor } from '../../../mol-model/structure';
+import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
 
 export { ModelSecondaryStructure };
 

+ 1 - 1
src/mol-model-formats/structure/property/symmetry.ts

@@ -10,7 +10,7 @@ import { Spacegroup, SpacegroupCell, SymmetryOperator } from '../../../mol-math/
 import { Tensor, Vec3, Mat3 } from '../../../mol-math/linear-algebra';
 import { Symmetry } from '../../../mol-model/structure/model/properties/symmetry';
 import { createAssemblies } from './assembly';
-import { CustomPropertyDescriptor } from '../../../mol-model/structure';
+import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
 import { FormatPropertyProvider } from '../common/property';
 import { Table } from '../../../mol-data/db';
 

+ 16 - 11
src/mol-model-formats/volume/ccp4.ts

@@ -4,7 +4,7 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { VolumeData } from '../../mol-model/volume/data';
+import { Volume } from '../../mol-model/volume';
 import { Task } from '../../mol-task';
 import { SpacegroupCell, Box3D } from '../../mol-math/geometry';
 import { Tensor, Vec3 } from '../../mol-math/linear-algebra';
@@ -14,6 +14,7 @@ import { getCcp4ValueType } from '../../mol-io/reader/ccp4/parser';
 import { TypedArrayValueType } from '../../mol-io/common/typed-array';
 import { arrayMin, arrayRms, arrayMean, arrayMax } from '../../mol-util/array';
 import { ModelFormat } from '../format';
+import { CustomProperties } from '../../mol-model/custom-property';
 
 /** When available (e.g. in MRC files) use ORIGIN records instead of N[CRS]START */
 export function getCcp4Origin(header: Ccp4Header): Vec3 {
@@ -39,8 +40,8 @@ function getTypedArrayCtor(header: Ccp4Header) {
     throw Error(`${valueType} is not a supported value format.`);
 }
 
-export function volumeFromCcp4(source: Ccp4File, params?: { voxelSize?: Vec3, offset?: Vec3, label?: string }): Task<VolumeData> {
-    return Task.create<VolumeData>('Create Volume Data', async ctx => {
+export function volumeFromCcp4(source: Ccp4File, params?: { voxelSize?: Vec3, offset?: Vec3, label?: string }): Task<Volume> {
+    return Task.create<Volume>('Create Volume', async ctx => {
         const { header, values } = source;
         const size = Vec3.create(header.xLength, header.yLength, header.zLength);
         if (params && params.voxelSize) Vec3.mul(size, size, params.voxelSize);
@@ -69,15 +70,19 @@ export function volumeFromCcp4(source: Ccp4File, params?: { voxelSize?: Vec3, of
 
         return {
             label: params?.label,
-            transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin_frac, Vec3.add(Vec3.zero(), origin_frac, dimensions_frac)) },
-            data,
-            dataStats: {
-                min: isNaN(header.AMIN) ? arrayMin(values) : header.AMIN,
-                max: isNaN(header.AMAX) ? arrayMax(values) : header.AMAX,
-                mean: isNaN(header.AMEAN) ? arrayMean(values) : header.AMEAN,
-                sigma: (isNaN(header.ARMS) || header.ARMS === 0) ? arrayRms(values) : header.ARMS
+            grid: {
+                transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin_frac, Vec3.add(Vec3.zero(), origin_frac, dimensions_frac)) },
+                cells: data,
+                stats: {
+                    min: isNaN(header.AMIN) ? arrayMin(values) : header.AMIN,
+                    max: isNaN(header.AMAX) ? arrayMax(values) : header.AMAX,
+                    mean: isNaN(header.AMEAN) ? arrayMean(values) : header.AMEAN,
+                    sigma: (isNaN(header.ARMS) || header.ARMS === 0) ? arrayRms(values) : header.ARMS
+                },
             },
-            sourceData: Ccp4Format.create(source)
+            sourceData: Ccp4Format.create(source),
+            customProperties: new CustomProperties(),
+            _propertyData: Object.create(null),
         };
     });
 }

+ 16 - 11
src/mol-model-formats/volume/cube.ts

@@ -6,13 +6,14 @@
 
 import { CubeFile } from '../../mol-io/reader/cube/parser';
 import { Mat4, Tensor } from '../../mol-math/linear-algebra';
-import { VolumeData } from '../../mol-model/volume/data';
+import { Volume } from '../../mol-model/volume';
 import { Task } from '../../mol-task';
 import { arrayMax, arrayMean, arrayMin, arrayRms } from '../../mol-util/array';
 import { ModelFormat } from '../format';
+import { CustomProperties } from '../../mol-model/custom-property';
 
-export function volumeFromCube(source: CubeFile, params?: { dataIndex?: number, label?: string }): Task<VolumeData> {
-    return Task.create<VolumeData>('Create Volume Data', async () => {
+export function volumeFromCube(source: CubeFile, params?: { dataIndex?: number, label?: string }): Task<Volume> {
+    return Task.create<Volume>('Create Volume', async () => {
         const { header, values: sourceValues } = source;
         const space = Tensor.Space(header.dim, [0, 1, 2], Float64Array);
 
@@ -45,15 +46,19 @@ export function volumeFromCube(source: CubeFile, params?: { dataIndex?: number,
 
         return {
             label: params?.label,
-            transform: { kind: 'matrix', matrix },
-            data,
-            dataStats: {
-                min: arrayMin(values),
-                max: arrayMax(values),
-                mean: arrayMean(values),
-                sigma: arrayRms(values)
+            grid: {
+                transform: { kind: 'matrix', matrix },
+                cells: data,
+                stats: {
+                    min: arrayMin(values),
+                    max: arrayMax(values),
+                    mean: arrayMean(values),
+                    sigma: arrayRms(values)
+                },
             },
-            sourceData: CubeFormat.create(source)
+            sourceData: CubeFormat.create(source),
+            customProperties: new CustomProperties(),
+            _propertyData: Object.create(null),
         };
     });
 }

+ 16 - 11
src/mol-model-formats/volume/density-server.ts

@@ -5,14 +5,15 @@
  */
 
 import { DensityServer_Data_Database } from '../../mol-io/reader/cif/schema/density-server';
-import { VolumeData } from '../../mol-model/volume/data';
+import { Volume } from '../../mol-model/volume';
 import { Task } from '../../mol-task';
 import { SpacegroupCell, Box3D } from '../../mol-math/geometry';
 import { Tensor, Vec3 } from '../../mol-math/linear-algebra';
 import { ModelFormat } from '../format';
+import { CustomProperties } from '../../mol-model/custom-property';
 
-export function volumeFromDensityServerData(source: DensityServer_Data_Database): Task<VolumeData> {
-    return Task.create<VolumeData>('Create Volume Data', async ctx => {
+export function volumeFromDensityServerData(source: DensityServer_Data_Database): Task<Volume> {
+    return Task.create<Volume>('Create Volume', async ctx => {
         const { volume_data_3d_info: info, volume_data_3d: values } = source;
         const cell = SpacegroupCell.create(
             info.spacegroup_number.value(0),
@@ -35,15 +36,19 @@ export function volumeFromDensityServerData(source: DensityServer_Data_Database)
         const dimensions = Vec3.ofArray(normalizeOrder(info.dimensions.value(0)));
 
         return {
-            transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin, Vec3.add(Vec3.zero(), origin, dimensions)) },
-            data,
-            dataStats: {
-                min: info.min_sampled.value(0),
-                max: info.max_sampled.value(0),
-                mean: info.mean_sampled.value(0),
-                sigma: info.sigma_sampled.value(0)
+            grid: {
+                transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin, Vec3.add(Vec3.zero(), origin, dimensions)) },
+                cells: data,
+                stats: {
+                    min: info.min_sampled.value(0),
+                    max: info.max_sampled.value(0),
+                    mean: info.mean_sampled.value(0),
+                    sigma: info.sigma_sampled.value(0)
+                },
             },
-            sourceData: DscifFormat.create(source)
+            sourceData: DscifFormat.create(source),
+            customProperties: new CustomProperties(),
+            _propertyData: Object.create(null),
         };
     });
 }

+ 16 - 11
src/mol-model-formats/volume/dsn6.ts

@@ -4,7 +4,7 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { VolumeData } from '../../mol-model/volume/data';
+import { Volume } from '../../mol-model/volume';
 import { Task } from '../../mol-task';
 import { SpacegroupCell, Box3D } from '../../mol-math/geometry';
 import { Tensor, Vec3 } from '../../mol-math/linear-algebra';
@@ -12,9 +12,10 @@ import { degToRad } from '../../mol-math/misc';
 import { Dsn6File } from '../../mol-io/reader/dsn6/schema';
 import { arrayMin, arrayMax, arrayMean, arrayRms } from '../../mol-util/array';
 import { ModelFormat } from '../format';
+import { CustomProperties } from '../../mol-model/custom-property';
 
-export function volumeFromDsn6(source: Dsn6File, params?: { voxelSize?: Vec3, label?: string }): Task<VolumeData> {
-    return Task.create<VolumeData>('Create Volume Data', async ctx => {
+export function volumeFromDsn6(source: Dsn6File, params?: { voxelSize?: Vec3, label?: string }): Task<Volume> {
+    return Task.create<Volume>('Create Volume', async ctx => {
         const { header, values } = source;
         const size = Vec3.create(header.xlen, header.ylen, header.zlen);
         if (params && params.voxelSize) Vec3.mul(size, size, params.voxelSize);
@@ -34,15 +35,19 @@ export function volumeFromDsn6(source: Dsn6File, params?: { voxelSize?: Vec3, la
 
         return {
             label: params?.label,
-            transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin_frac, Vec3.add(Vec3.zero(), origin_frac, dimensions_frac)) },
-            data,
-            dataStats: {
-                min: arrayMin(values),
-                max: arrayMax(values),
-                mean: arrayMean(values),
-                sigma: header.sigma !== undefined ? header.sigma : arrayRms(values)
+            grid: {
+                transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin_frac, Vec3.add(Vec3.zero(), origin_frac, dimensions_frac)) },
+                cells: data,
+                stats: {
+                    min: arrayMin(values),
+                    max: arrayMax(values),
+                    mean: arrayMean(values),
+                    sigma: header.sigma !== undefined ? header.sigma : arrayRms(values)
+                },
             },
-            sourceData: Dsn6Format.create(source)
+            sourceData: Dsn6Format.create(source),
+            customProperties: new CustomProperties(),
+            _propertyData: Object.create(null),
         };
     });
 }

+ 16 - 11
src/mol-model-formats/volume/dx.ts

@@ -6,13 +6,14 @@
 
 import { DxFile } from '../../mol-io/reader/dx/parser';
 import { Mat4, Tensor } from '../../mol-math/linear-algebra';
-import { VolumeData } from '../../mol-model/volume/data';
+import { Volume } from '../../mol-model/volume';
 import { Task } from '../../mol-task';
 import { arrayMax, arrayMean, arrayMin, arrayRms } from '../../mol-util/array';
 import { ModelFormat } from '../format';
+import { CustomProperties } from '../../mol-model/custom-property';
 
-export function volumeFromDx(source: DxFile, params?: { label?: string }): Task<VolumeData> {
-    return Task.create<VolumeData>('Create Volume Data', async () => {
+export function volumeFromDx(source: DxFile, params?: { label?: string }): Task<Volume> {
+    return Task.create<Volume>('Create Volume', async () => {
         const { header, values } = source;
         const space = Tensor.Space(header.dim, [0, 1, 2], Float64Array);
         const data = Tensor.create(space, Tensor.Data1(values));
@@ -22,15 +23,19 @@ export function volumeFromDx(source: DxFile, params?: { label?: string }): Task<
 
         return {
             label: params?.label,
-            transform: { kind: 'matrix', matrix },
-            data,
-            dataStats: {
-                min: arrayMin(values),
-                max: arrayMax(values),
-                mean: arrayMean(values),
-                sigma: arrayRms(values)
+            grid: {
+                transform: { kind: 'matrix', matrix },
+                cells: data,
+                stats: {
+                    min: arrayMin(values),
+                    max: arrayMax(values),
+                    mean: arrayMean(values),
+                    sigma: arrayRms(values)
+                },
             },
-            sourceData: DxFormat.create(source)
+            sourceData: DxFormat.create(source),
+            customProperties: new CustomProperties(),
+            _propertyData: Object.create(null),
         };
     });
 }

+ 2 - 1
src/mol-model-props/common/custom-element-property.ts

@@ -5,7 +5,7 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { ElementIndex, Model, CustomPropertyDescriptor } from '../../mol-model/structure';
+import { ElementIndex, Model } from '../../mol-model/structure';
 import { StructureElement } from '../../mol-model/structure/structure';
 import { Location } from '../../mol-model/location';
 import { ThemeDataContext } from '../../mol-theme/theme';
@@ -16,6 +16,7 @@ import { OrderedSet } from '../../mol-data/int';
 import { CustomModelProperty } from './custom-model-property';
 import { CustomProperty } from './custom-property';
 import { LociLabelProvider } from '../../mol-plugin-state/manager/loci-label';
+import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
 
 export { CustomElementProperty };
 

+ 2 - 1
src/mol-model-props/common/custom-model-property.ts

@@ -4,10 +4,11 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { CustomPropertyDescriptor, Model } from '../../mol-model/structure';
+import { Model } from '../../mol-model/structure';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { ValueBox } from '../../mol-util';
 import { CustomProperty } from './custom-property';
+import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
 
 export { CustomModelProperty };
 

+ 1 - 1
src/mol-model-props/common/custom-property.ts

@@ -5,7 +5,7 @@
  */
 
 import { RuntimeContext } from '../../mol-task';
-import { CustomPropertyDescriptor } from '../../mol-model/structure';
+import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { ValueBox } from '../../mol-util';
 import { OrderedMap } from 'immutable';

+ 2 - 1
src/mol-model-props/common/custom-structure-property.ts

@@ -4,10 +4,11 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { CustomPropertyDescriptor, Structure } from '../../mol-model/structure';
+import { Structure } from '../../mol-model/structure';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { ValueBox } from '../../mol-util';
 import { CustomProperty } from './custom-property';
+import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
 
 export { CustomStructureProperty };
 

+ 2 - 1
src/mol-model-props/computed/accessible-surface-area.ts

@@ -7,12 +7,13 @@
 
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { ShrakeRupleyComputationParams, AccessibleSurfaceArea } from './accessible-surface-area/shrake-rupley';
-import { Structure, CustomPropertyDescriptor, Unit } from '../../mol-model/structure';
+import { Structure, Unit } from '../../mol-model/structure';
 import { CustomStructureProperty } from '../common/custom-structure-property';
 import { CustomProperty } from '../common/custom-property';
 import { QuerySymbolRuntime } from '../../mol-script/runtime/query/compiler';
 import { CustomPropSymbol } from '../../mol-script/language/symbol';
 import Type from '../../mol-script/language/type';
+import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
 
 export const AccessibleSurfaceAreaParams = {
     ...ShrakeRupleyComputationParams

+ 2 - 1
src/mol-model-props/computed/interactions.ts

@@ -4,11 +4,12 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { CustomPropertyDescriptor, Structure } from '../../mol-model/structure';
+import { Structure } from '../../mol-model/structure';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { computeInteractions, Interactions, InteractionsParams as _InteractionsParams } from './interactions/interactions';
 import { CustomStructureProperty } from '../common/custom-structure-property';
 import { CustomProperty } from '../common/custom-property';
+import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
 
 export const InteractionsParams = {
     ..._InteractionsParams

+ 1 - 1
src/mol-model-props/computed/secondary-structure.ts

@@ -12,7 +12,7 @@ import { Unit } from '../../mol-model/structure/structure';
 import { CustomStructureProperty } from '../common/custom-structure-property';
 import { CustomProperty } from '../common/custom-property';
 import { ModelSecondaryStructure } from '../../mol-model-formats/structure/property/secondary-structure';
-import { CustomPropertyDescriptor } from '../../mol-model/structure/common/custom-property';
+import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
 import { Model } from '../../mol-model/structure/model';
 
 function getSecondaryStructureParams(data?: Structure) {

+ 2 - 1
src/mol-model-props/computed/valence-model.ts

@@ -4,11 +4,12 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { CustomPropertyDescriptor, Structure } from '../../mol-model/structure';
+import { Structure } from '../../mol-model/structure';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { calcValenceModel, ValenceModel, ValenceModelParams as _ValenceModelParams } from './chemistry/valence-model';
 import { CustomStructureProperty } from '../common/custom-structure-property';
 import { CustomProperty } from '../common/custom-property';
+import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
 
 export const ValenceModelParams = {
     ..._ValenceModelParams

+ 2 - 1
src/mol-model-props/integrative/cross-link-restraint/format.ts

@@ -7,9 +7,10 @@
 import { Model } from '../../../mol-model/structure/model/model';
 import { Table } from '../../../mol-data/db';
 import { mmCIF_Schema } from '../../../mol-io/reader/cif/schema/mmcif';
-import { Unit, CustomPropertyDescriptor } from '../../../mol-model/structure';
+import { Unit } from '../../../mol-model/structure';
 import { ElementIndex } from '../../../mol-model/structure/model/indexing';
 import { FormatPropertyProvider } from '../../../mol-model-formats/structure/common/property';
+import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
 
 export { ModelCrossLinkRestraint };
 

+ 2 - 1
src/mol-model-props/integrative/cross-link-restraint/property.ts

@@ -5,7 +5,7 @@
  */
 
 import { ModelCrossLinkRestraint } from './format';
-import { Unit, StructureElement, Structure, CustomPropertyDescriptor, Bond} from '../../../mol-model/structure';
+import { Unit, StructureElement, Structure, Bond} from '../../../mol-model/structure';
 import { PairRestraints, PairRestraint } from '../pair-restraints';
 import { CustomStructureProperty } from '../../common/custom-structure-property';
 import { CustomProperty } from '../../common/custom-property';
@@ -15,6 +15,7 @@ import { Sphere3D } from '../../../mol-math/geometry';
 import { CentroidHelper } from '../../../mol-math/geometry/centroid-helper';
 import { bondLabel } from '../../../mol-theme/label';
 import { Vec3 } from '../../../mol-math/linear-algebra';
+import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
 
 export type CrossLinkRestraintValue = PairRestraints<CrossLinkRestraint>
 

+ 5 - 5
src/mol-model/structure/common/custom-property.ts → src/mol-model/custom-property.ts

@@ -5,11 +5,11 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { CifWriter } from '../../../mol-io/writer/cif';
-import { CifExportContext } from '../export/mmcif';
-import { QuerySymbolRuntime } from '../../../mol-script/runtime/query/compiler';
-import { UUID } from '../../../mol-util';
-import { Asset } from '../../../mol-util/assets';
+import { CifWriter } from '../mol-io/writer/cif';
+import { CifExportContext } from './structure/export/mmcif';
+import { QuerySymbolRuntime } from '../mol-script/runtime/query/compiler';
+import { UUID } from '../mol-util';
+import { Asset } from '../mol-util/assets';
 
 export { CustomPropertyDescriptor, CustomProperties };
 

+ 1 - 2
src/mol-model/structure.ts

@@ -9,5 +9,4 @@ export * from './structure/coordinates';
 export * from './structure/topology';
 export * from './structure/model';
 export * from './structure/structure';
-export * from './structure/query';
-export * from './structure/common/custom-property';
+export * from './structure/query';

+ 1 - 1
src/mol-model/structure/export/mmcif.ts

@@ -15,7 +15,7 @@ import { _chem_comp, _pdbx_chem_comp_identifier, _pdbx_nonpoly_scheme } from './
 import { Model } from '../model';
 import { getUniqueEntityIndicesFromStructures, copy_mmCif_category, copy_source_mmCifCategory } from './categories/utils';
 import { _struct_asym, _entity_poly, _entity_poly_seq } from './categories/sequence';
-import { CustomPropertyDescriptor } from '../common/custom-property';
+import { CustomPropertyDescriptor } from '../../custom-property';
 import { atom_site_operator_mapping } from './categories/atom_site_operator_mapping';
 import { MmcifFormat } from '../../../mol-model-formats/structure/mmcif';
 

+ 1 - 1
src/mol-model/structure/model/model.ts

@@ -10,7 +10,7 @@ import StructureSequence from './properties/sequence';
 import { AtomicHierarchy, AtomicConformation, AtomicRanges } from './properties/atomic';
 import { CoarseHierarchy, CoarseConformation } from './properties/coarse';
 import { Entities, ChemicalComponentMap, MissingResidues, StructAsymMap } from './properties/common';
-import { CustomProperties } from '../common/custom-property';
+import { CustomProperties } from '../../custom-property';
 import { SaccharideComponentMap } from '../structure/carbohydrates/constants';
 import { ModelFormat } from '../../../mol-model-formats/format';
 import { calcModelCenter } from './util';

+ 1 - 1
src/mol-model/structure/structure/structure.ts

@@ -25,7 +25,7 @@ import { Vec3, Mat4 } from '../../../mol-math/linear-algebra';
 import { idFactory } from '../../../mol-util/id-factory';
 import { GridLookup3D } from '../../../mol-math/geometry';
 import { UUID } from '../../../mol-util';
-import { CustomProperties } from '../common/custom-property';
+import { CustomProperties } from '../../custom-property';
 import { AtomicHierarchy } from '../model/properties/atomic';
 import { StructureSelection } from '../query/selection';
 import { getBoundary } from '../../../mol-math/geometry/boundary';

+ 4 - 2
src/mol-model/volume.ts

@@ -1,7 +1,9 @@
 /**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-export * from './volume/data';
+export * from './volume/volume';
+export * from './volume/grid';

+ 0 - 96
src/mol-model/volume/data.ts

@@ -1,96 +0,0 @@
-/**
- * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- */
-
-import { SpacegroupCell, Box3D } from '../../mol-math/geometry';
-import { Tensor, Mat4, Vec3 } from '../../mol-math/linear-algebra';
-import { equalEps } from '../../mol-math/linear-algebra/3d/common';
-import { ModelFormat } from '../../mol-model-formats/format';
-
-/** The basic unit cell that contains the data. */
-interface VolumeDataBase {
-    readonly label?: string,
-    readonly transform: { kind: 'spacegroup', cell: SpacegroupCell, fractionalBox: Box3D } | { kind: 'matrix', matrix: Mat4 },
-    readonly data: Tensor,
-    readonly dataStats: Readonly<{
-        min: number,
-        max: number,
-        mean: number,
-        sigma: number
-    }>
-    readonly sourceData: ModelFormat,
-}
-
-interface VolumeData extends VolumeDataBase {
-    readonly colorVolume?: VolumeDataBase
-}
-
-namespace VolumeData {
-    export const One: VolumeData = {
-        transform: { kind: 'matrix', matrix: Mat4.identity() },
-        data: Tensor.create(Tensor.Space([1, 1, 1], [0, 1, 2]), Tensor.Data1([0])),
-        dataStats: { min: 0, max: 0, mean: 0, sigma: 0 },
-        sourceData: { kind: '', data: '', name: '' }
-    };
-
-    const _scale = Mat4.zero(), _translate = Mat4.zero();
-    export function getGridToCartesianTransform(volume: VolumeData) {
-        if (volume.transform.kind === 'matrix') {
-            return Mat4.copy(Mat4(), volume.transform.matrix);
-        }
-
-        if (volume.transform.kind === 'spacegroup') {
-            const { data: { space } } = volume;
-            const scale = Mat4.fromScaling(_scale, Vec3.div(Vec3.zero(), Box3D.size(Vec3.zero(), volume.transform.fractionalBox), Vec3.ofArray(space.dimensions)));
-            const translate = Mat4.fromTranslation(_translate, volume.transform.fractionalBox.min);
-            return Mat4.mul3(Mat4.zero(), volume.transform.cell.fromFractional, translate, scale);
-        }
-
-        return Mat4.identity();
-    }
-
-    export function areEquivalent(volA: VolumeData, volB: VolumeData) {
-        return volA === volB;
-    }
-}
-
-type VolumeIsoValue = VolumeIsoValue.Absolute | VolumeIsoValue.Relative
-
-namespace VolumeIsoValue {
-    export type Relative = Readonly<{ kind: 'relative', relativeValue: number }>
-    export type Absolute = Readonly<{ kind: 'absolute', absoluteValue: number }>
-
-    export function areSame(a: VolumeIsoValue, b: VolumeIsoValue, stats: VolumeData['dataStats']) {
-        return equalEps(toAbsolute(a, stats).absoluteValue, toAbsolute(b, stats).absoluteValue, stats.sigma / 100);
-    }
-
-    export function absolute(value: number): Absolute { return { kind: 'absolute', absoluteValue: value }; }
-    export function relative(value: number): Relative { return { kind: 'relative', relativeValue: value }; }
-
-    export function calcAbsolute(stats: VolumeData['dataStats'], relativeValue: number): number {
-        return relativeValue * stats.sigma + stats.mean;
-    }
-
-    export function calcRelative(stats: VolumeData['dataStats'], absoluteValue: number): number {
-        return stats.sigma === 0 ? 0 : ((absoluteValue - stats.mean) / stats.sigma);
-    }
-
-    export function toAbsolute(value: VolumeIsoValue, stats: VolumeData['dataStats']): Absolute {
-        return value.kind === 'absolute' ? value : { kind: 'absolute', absoluteValue: VolumeIsoValue.calcAbsolute(stats, value.relativeValue) };
-    }
-
-    export function toRelative(value: VolumeIsoValue, stats: VolumeData['dataStats']): Relative {
-        return value.kind === 'relative' ? value : { kind: 'relative', relativeValue: VolumeIsoValue.calcRelative(stats, value.absoluteValue) };
-    }
-
-    export function toString(value: VolumeIsoValue) {
-        return value.kind === 'relative'
-            ? `${value.relativeValue.toFixed(2)} σ`
-            : `${value.absoluteValue.toPrecision(4)}`;
-    }
-}
-
-export { VolumeData, VolumeIsoValue };

+ 57 - 0
src/mol-model/volume/grid.ts

@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { SpacegroupCell, Box3D } from '../../mol-math/geometry';
+import { Tensor, Mat4, Vec3 } from '../../mol-math/linear-algebra';
+
+/** The basic unit cell that contains the grid data. */
+interface Grid {
+    readonly transform: Grid.Transform,
+    readonly cells: Tensor,
+    readonly stats: Readonly<{
+        min: number,
+        max: number,
+        mean: number,
+        sigma: number
+    }>
+}
+
+namespace Grid {
+    export const One: Grid = {
+        transform: { kind: 'matrix', matrix: Mat4.identity() },
+        cells: Tensor.create(Tensor.Space([1, 1, 1], [0, 1, 2]), Tensor.Data1([0])),
+        stats: { min: 0, max: 0, mean: 0, sigma: 0 },
+    };
+
+    export type Transform = { kind: 'spacegroup', cell: SpacegroupCell, fractionalBox: Box3D } | { kind: 'matrix', matrix: Mat4 }
+
+    const _scale = Mat4.zero(), _translate = Mat4.zero();
+    export function getGridToCartesianTransform(grid: Grid) {
+        if (grid.transform.kind === 'matrix') {
+            return Mat4.copy(Mat4(), grid.transform.matrix);
+        }
+
+        if (grid.transform.kind === 'spacegroup') {
+            const { cells: { space } } = grid;
+            const scale = Mat4.fromScaling(_scale, Vec3.div(Vec3.zero(), Box3D.size(Vec3.zero(), grid.transform.fractionalBox), Vec3.ofArray(space.dimensions)));
+            const translate = Mat4.fromTranslation(_translate, grid.transform.fractionalBox.min);
+            return Mat4.mul3(Mat4.zero(), grid.transform.cell.fromFractional, translate, scale);
+        }
+
+        return Mat4.identity();
+    }
+
+    export function areEquivalent(gridA: Grid, gridB: Grid) {
+        return gridA === gridB;
+    }
+
+    export function isEmpty(grid: Grid) {
+        return grid.cells.data.length === 0;
+    }
+}
+
+export { Grid };

+ 91 - 18
src/mol-model/volume/volume.ts

@@ -4,32 +4,105 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { VolumeData, VolumeIsoValue } from './data';
+import { Grid } from './grid';
 import { OrderedSet } from '../../mol-data/int';
 import { Sphere3D } from '../../mol-math/geometry';
 import { Vec3, Mat4 } from '../../mol-math/linear-algebra';
 import { BoundaryHelper } from '../../mol-math/geometry/boundary-helper';
 import { CubeFormat } from '../../mol-model-formats/volume/cube';
+import { equalEps } from '../../mol-math/linear-algebra/3d/common';
+import { ModelFormat } from '../../mol-model-formats/format';
+import { CustomProperties } from '../custom-property';
+
+export interface Volume {
+    readonly label?: string
+    readonly grid: Grid
+    readonly sourceData: ModelFormat
+
+    // TODO use...
+    customProperties: CustomProperties
+
+    /**
+     * Not to be accessed directly, each custom property descriptor
+     * defines property accessors that use this field to store the data.
+     */
+    _propertyData: { [name: string]: any }
+
+    // TODO add as customProperty?
+    readonly colorVolume?: Volume
+}
 
 export namespace Volume {
     export type CellIndex = { readonly '@type': 'cell-index' } & number
 
-    export function isOrbitals(volume: VolumeData) {
+    export type IsoValue = IsoValue.Absolute | IsoValue.Relative
+
+    export namespace IsoValue {
+        export type Relative = Readonly<{ kind: 'relative', relativeValue: number }>
+        export type Absolute = Readonly<{ kind: 'absolute', absoluteValue: number }>
+
+        export function areSame(a: IsoValue, b: IsoValue, stats: Grid['stats']) {
+            return equalEps(toAbsolute(a, stats).absoluteValue, toAbsolute(b, stats).absoluteValue, stats.sigma / 100);
+        }
+
+        export function absolute(value: number): Absolute { return { kind: 'absolute', absoluteValue: value }; }
+        export function relative(value: number): Relative { return { kind: 'relative', relativeValue: value }; }
+
+        export function calcAbsolute(stats: Grid['stats'], relativeValue: number): number {
+            return relativeValue * stats.sigma + stats.mean;
+        }
+
+        export function calcRelative(stats: Grid['stats'], absoluteValue: number): number {
+            return stats.sigma === 0 ? 0 : ((absoluteValue - stats.mean) / stats.sigma);
+        }
+
+        export function toAbsolute(value: IsoValue, stats: Grid['stats']): Absolute {
+            return value.kind === 'absolute' ? value : { kind: 'absolute', absoluteValue: IsoValue.calcAbsolute(stats, value.relativeValue) };
+        }
+
+        export function toRelative(value: IsoValue, stats: Grid['stats']): Relative {
+            return value.kind === 'relative' ? value : { kind: 'relative', relativeValue: IsoValue.calcRelative(stats, value.absoluteValue) };
+        }
+
+        export function toString(value: IsoValue) {
+            return value.kind === 'relative'
+                ? `${value.relativeValue.toFixed(2)} σ`
+                : `${value.absoluteValue.toPrecision(4)}`;
+        }
+    }
+
+    export const One: Volume = {
+        label: '',
+        grid: Grid.One,
+        sourceData: { kind: '', name: '', data: {} },
+        customProperties: new CustomProperties(),
+        _propertyData: Object.create(null),
+    };
+
+    export function areEquivalent(volA: Volume, volB: Volume) {
+        return Grid.areEquivalent(volA.grid, volB.grid);
+    }
+
+    export function isEmpty(vol: Volume) {
+        return Grid.isEmpty(vol.grid);
+    }
+
+    export function isOrbitals(volume: Volume) {
         if (!CubeFormat.is(volume.sourceData)) return false;
         return volume.sourceData.data.header.orbitals;
     }
 
-    export interface Loci { readonly kind: 'volume-loci', readonly volume: VolumeData }
-    export function Loci(volume: VolumeData): Loci { return { kind: 'volume-loci', volume }; }
+    export interface Loci { readonly kind: 'volume-loci', readonly volume: Volume }
+    export function Loci(volume: Volume): Loci { return { kind: 'volume-loci', volume }; }
     export function isLoci(x: any): x is Loci { return !!x && x.kind === 'volume-loci'; }
     export function areLociEqual(a: Loci, b: Loci) { return a.volume === b.volume; }
-    export function isLociEmpty(loci: Loci) { return loci.volume.data.data.length === 0; }
+    export function isLociEmpty(loci: Loci) { return Grid.isEmpty(loci.volume.grid); }
 
-    export function getBoundingSphere(volume: VolumeData, boundingSphere?: Sphere3D) {
+    export function getBoundingSphere(volume: Volume, boundingSphere?: Sphere3D) {
         if (!boundingSphere) boundingSphere = Sphere3D();
 
-        const transform = VolumeData.getGridToCartesianTransform(volume);
-        const [x, y, z] = volume.data.space.dimensions;
+        const transform = Grid.getGridToCartesianTransform(volume.grid);
+        const [x, y, z] = volume.grid.cells.space.dimensions;
 
         const cpA = Vec3.create(0, 0, 0); Vec3.transformMat4(cpA, cpA, transform);
         const cpB = Vec3.create(x, y, z); Vec3.transformMat4(cpB, cpB, transform);
@@ -52,31 +125,31 @@ export namespace Volume {
     }
 
     export namespace Isosurface {
-        export interface Loci { readonly kind: 'isosurface-loci', readonly volume: VolumeData, readonly isoValue: VolumeIsoValue }
-        export function Loci(volume: VolumeData, isoValue: VolumeIsoValue): Loci { return { kind: 'isosurface-loci', volume, isoValue }; }
+        export interface Loci { readonly kind: 'isosurface-loci', readonly volume: Volume, readonly isoValue: Volume.IsoValue }
+        export function Loci(volume: Volume, isoValue: Volume.IsoValue): Loci { return { kind: 'isosurface-loci', volume, isoValue }; }
         export function isLoci(x: any): x is Loci { return !!x && x.kind === 'isosurface-loci'; }
-        export function areLociEqual(a: Loci, b: Loci) { return a.volume === b.volume && VolumeIsoValue.areSame(a.isoValue, b.isoValue, a.volume.dataStats); }
-        export function isLociEmpty(loci: Loci) { return loci.volume.data.data.length === 0; }
+        export function areLociEqual(a: Loci, b: Loci) { return a.volume === b.volume && Volume.IsoValue.areSame(a.isoValue, b.isoValue, a.volume.grid.stats); }
+        export function isLociEmpty(loci: Loci) { return loci.volume.grid.cells.data.length === 0; }
 
-        export function getBoundingSphere(volume: VolumeData, isoValue: VolumeIsoValue, boundingSphere?: Sphere3D) {
+        export function getBoundingSphere(volume: Volume, isoValue: Volume.IsoValue, boundingSphere?: Sphere3D) {
             // TODO get bounding sphere for subgrid with values >= isoValue
             return Volume.getBoundingSphere(volume, boundingSphere);
         }
     }
 
     export namespace Cell {
-        export interface Loci { readonly kind: 'cell-loci', readonly volume: VolumeData, readonly indices: OrderedSet<CellIndex> }
-        export function Loci(volume: VolumeData, indices: OrderedSet<CellIndex>): Loci { return { kind: 'cell-loci', volume, indices }; }
+        export interface Loci { readonly kind: 'cell-loci', readonly volume: Volume, readonly indices: OrderedSet<CellIndex> }
+        export function Loci(volume: Volume, indices: OrderedSet<CellIndex>): Loci { return { kind: 'cell-loci', volume, indices }; }
         export function isLoci(x: any): x is Loci { return !!x && x.kind === 'cell-loci'; }
         export function areLociEqual(a: Loci, b: Loci) { return a.volume === b.volume && OrderedSet.areEqual(a.indices, b.indices); }
         export function isLociEmpty(loci: Loci) { return OrderedSet.size(loci.indices) === 0; }
 
         const boundaryHelper = new BoundaryHelper('98');
         const tmpBoundaryPos = Vec3();
-        export function getBoundingSphere(volume: VolumeData, indices: OrderedSet<CellIndex>, boundingSphere?: Sphere3D) {
+        export function getBoundingSphere(volume: Volume, indices: OrderedSet<CellIndex>, boundingSphere?: Sphere3D) {
             boundaryHelper.reset();
-            const transform = VolumeData.getGridToCartesianTransform(volume);
-            const { getCoords } = volume.data.space;
+            const transform = Grid.getGridToCartesianTransform(volume.grid);
+            const { getCoords } = volume.grid.cells.space;
 
             for (let i = 0, _i = OrderedSet.size(indices); i < _i; i++) {
                 const o = OrderedSet.getAt(indices, i);

+ 7 - 8
src/mol-plugin-state/formats/volume.ts

@@ -12,10 +12,9 @@ import { StateObjectSelector } from '../../mol-state';
 import { PluginStateObject } from '../objects';
 import { VolumeRepresentation3DHelpers } from '../transforms/representation';
 import { ColorNames } from '../../mol-util/color/names';
-import { VolumeIsoValue } from '../../mol-model/volume';
+import { Volume } from '../../mol-model/volume';
 import { createVolumeRepresentationParams } from '../helpers/volume-representation-params';
 import { objectForEach } from '../../mol-util/object';
-import { Volume } from '../../mol-model/volume/volume';
 
 const Category = 'Volume';
 
@@ -110,13 +109,13 @@ export const CubeProvider = DataFormatProvider({
         if (volumeData && Volume.isOrbitals(volumeData)) {
             const volumePos = surfaces.to(data.volume).apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(plugin, volumeData, {
                 type: 'isosurface',
-                typeParams: { isoValue: VolumeIsoValue.relative(1), alpha: 0.4 },
+                typeParams: { isoValue: Volume.IsoValue.relative(1), alpha: 0.4 },
                 color: 'uniform',
                 colorParams: { value: ColorNames.blue }
             }));
             const volumeNeg = surfaces.to(data.volume).apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(plugin, volumeData, {
                 type: 'isosurface',
-                typeParams: { isoValue: VolumeIsoValue.relative(-1), alpha: 0.4 },
+                typeParams: { isoValue: Volume.IsoValue.relative(-1), alpha: 0.4 },
                 color: 'uniform',
                 colorParams: { value: ColorNames.red }
             }));
@@ -124,7 +123,7 @@ export const CubeProvider = DataFormatProvider({
         } else {
             const volume = surfaces.to(data.volume).apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(plugin, volumeData, {
                 type: 'isosurface',
-                typeParams: { isoValue: VolumeIsoValue.relative(2), alpha: 0.4 },
+                typeParams: { isoValue: Volume.IsoValue.relative(2), alpha: 0.4 },
                 color: 'uniform',
                 colorParams: { value: ColorNames.grey }
             }));
@@ -176,13 +175,13 @@ export const DscifProvider = DataFormatProvider({
         if (volumes.length > 0) {
             visuals[0] = tree
                 .to(volumes[0])
-                .apply(StateTransforms.Representation.VolumeRepresentation3D, VolumeRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'isosurface', { isoValue: VolumeIsoValue.relative(1.5), alpha: 1 }, 'uniform', { value: ColorNames.teal }))
+                .apply(StateTransforms.Representation.VolumeRepresentation3D, VolumeRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'isosurface', { isoValue: Volume.IsoValue.relative(1.5), alpha: 1 }, 'uniform', { value: ColorNames.teal }))
                 .selector;
         }
 
         if (volumes.length > 1) {
-            const posParams = VolumeRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'isosurface', { isoValue: VolumeIsoValue.relative(3), alpha: 0.3 }, 'uniform', { value: ColorNames.green });
-            const negParams = VolumeRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'isosurface', { isoValue: VolumeIsoValue.relative(-3), alpha: 0.3 }, 'uniform', { value: ColorNames.red });
+            const posParams = VolumeRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'isosurface', { isoValue: Volume.IsoValue.relative(3), alpha: 0.3 }, 'uniform', { value: ColorNames.green });
+            const negParams = VolumeRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'isosurface', { isoValue: Volume.IsoValue.relative(-3), alpha: 0.3 }, 'uniform', { value: ColorNames.red });
             visuals[visuals.length] = tree.to(volumes[1]).apply(StateTransforms.Representation.VolumeRepresentation3D, posParams).selector;
             visuals[visuals.length] = tree.to(volumes[1]).apply(StateTransforms.Representation.VolumeRepresentation3D, negParams).selector;
         }

+ 18 - 18
src/mol-plugin-state/helpers/volume-representation-params.ts

@@ -4,7 +4,7 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { VolumeData } from '../../mol-model/volume';
+import { Volume } from '../../mol-model/volume';
 import { PluginContext } from '../../mol-plugin/context';
 import { RepresentationProvider } from '../../mol-repr/representation';
 import { VolumeRepresentationRegistry } from '../../mol-repr/volume/registry';
@@ -30,7 +30,7 @@ export interface VolumeRepresentationBuiltInProps<
 }
 
 export interface VolumeRepresentationProps<
-    R extends RepresentationProvider<VolumeData> = RepresentationProvider<VolumeData>,
+    R extends RepresentationProvider<Volume> = RepresentationProvider<Volume>,
     C extends ColorTheme.Provider = ColorTheme.Provider,
     S extends SizeTheme.Provider = SizeTheme.Provider> {
     type?: R,
@@ -41,43 +41,43 @@ export interface VolumeRepresentationProps<
     sizeParams?: Partial<SizeTheme.ParamValues<S>>
 }
 
-export function createVolumeRepresentationParams<R extends VolumeRepresentationRegistry.BuiltIn, C extends ColorTheme.BuiltIn, S extends SizeTheme.BuiltIn>(ctx: PluginContext, volume?: VolumeData, props?: VolumeRepresentationBuiltInProps<R, C, S>): StateTransformer.Params<VolumeRepresentation3D>
-export function createVolumeRepresentationParams<R extends RepresentationProvider<VolumeData>, C extends ColorTheme.Provider, S extends SizeTheme.Provider>(ctx: PluginContext, volume?: VolumeData, props?: VolumeRepresentationProps<R, C, S>): StateTransformer.Params<VolumeRepresentation3D>
-export function createVolumeRepresentationParams(ctx: PluginContext, volume?: VolumeData, props: any = {}): StateTransformer.Params<VolumeRepresentation3D>  {
+export function createVolumeRepresentationParams<R extends VolumeRepresentationRegistry.BuiltIn, C extends ColorTheme.BuiltIn, S extends SizeTheme.BuiltIn>(ctx: PluginContext, volume?: Volume, props?: VolumeRepresentationBuiltInProps<R, C, S>): StateTransformer.Params<VolumeRepresentation3D>
+export function createVolumeRepresentationParams<R extends RepresentationProvider<Volume>, C extends ColorTheme.Provider, S extends SizeTheme.Provider>(ctx: PluginContext, volume?: Volume, props?: VolumeRepresentationProps<R, C, S>): StateTransformer.Params<VolumeRepresentation3D>
+export function createVolumeRepresentationParams(ctx: PluginContext, volume?: Volume, props: any = {}): StateTransformer.Params<VolumeRepresentation3D>  {
     const p = props as VolumeRepresentationBuiltInProps;
-    if (typeof p.type === 'string' || typeof p.color === 'string' || typeof p.size === 'string') return createParamsByName(ctx, volume || VolumeData.One, props);
-    return createParamsProvider(ctx, volume || VolumeData.One, props);
+    if (typeof p.type === 'string' || typeof p.color === 'string' || typeof p.size === 'string') return createParamsByName(ctx, volume || Volume.One, props);
+    return createParamsProvider(ctx, volume || Volume.One, props);
 }
 
-export function getVolumeThemeTypes(ctx: PluginContext, volume?: VolumeData) {
+export function getVolumeThemeTypes(ctx: PluginContext, volume?: Volume) {
     const { themes: themeCtx } = ctx.representation.volume;
     if (!volume) return themeCtx.colorThemeRegistry.types;
     return themeCtx.colorThemeRegistry.getApplicableTypes({ volume });
 }
 
-export function createVolumeColorThemeParams<T extends ColorTheme.BuiltIn>(ctx: PluginContext, volume: VolumeData | undefined, typeName: string | undefined, themeName: T, params?: ColorTheme.BuiltInParams<T>): StateTransformer.Params<VolumeRepresentation3D>['colorTheme']
-export function createVolumeColorThemeParams(ctx: PluginContext, volume: VolumeData | undefined, typeName: string | undefined, themeName?: string, params?: any): StateTransformer.Params<VolumeRepresentation3D>['colorTheme']
-export function createVolumeColorThemeParams(ctx: PluginContext, volume: VolumeData | undefined, typeName: string | undefined, themeName?: string, params?: any): StateTransformer.Params<VolumeRepresentation3D>['colorTheme'] {
+export function createVolumeColorThemeParams<T extends ColorTheme.BuiltIn>(ctx: PluginContext, volume: Volume | undefined, typeName: string | undefined, themeName: T, params?: ColorTheme.BuiltInParams<T>): StateTransformer.Params<VolumeRepresentation3D>['colorTheme']
+export function createVolumeColorThemeParams(ctx: PluginContext, volume: Volume | undefined, typeName: string | undefined, themeName?: string, params?: any): StateTransformer.Params<VolumeRepresentation3D>['colorTheme']
+export function createVolumeColorThemeParams(ctx: PluginContext, volume: Volume | undefined, typeName: string | undefined, themeName?: string, params?: any): StateTransformer.Params<VolumeRepresentation3D>['colorTheme'] {
     const { registry, themes } = ctx.representation.volume;
     const repr = registry.get(typeName || registry.default.name);
     const color = themes.colorThemeRegistry.get(themeName || repr.defaultColorTheme.name);
-    const colorDefaultParams = PD.getDefaultValues(color.getParams({ volume: volume || VolumeData.One }));
+    const colorDefaultParams = PD.getDefaultValues(color.getParams({ volume: volume || Volume.One }));
     if (color.name === repr.defaultColorTheme.name) Object.assign(colorDefaultParams, repr.defaultColorTheme.props);
     return { name: color.name, params: Object.assign(colorDefaultParams, params) };
 }
 
-export function createVolumeSizeThemeParams<T extends SizeTheme.BuiltIn>(ctx: PluginContext, volume: VolumeData | undefined, typeName: string | undefined, themeName: T, params?: SizeTheme.BuiltInParams<T>): StateTransformer.Params<VolumeRepresentation3D>['sizeTheme']
-export function createVolumeSizeThemeParams(ctx: PluginContext, volume: VolumeData | undefined, typeName: string | undefined, themeName?: string, params?: any): StateTransformer.Params<VolumeRepresentation3D>['sizeTheme']
-export function createVolumeSizeThemeParams(ctx: PluginContext, volume: VolumeData | undefined, typeName: string | undefined, themeName?: string, params?: any): StateTransformer.Params<VolumeRepresentation3D>['sizeTheme'] {
+export function createVolumeSizeThemeParams<T extends SizeTheme.BuiltIn>(ctx: PluginContext, volume: Volume | undefined, typeName: string | undefined, themeName: T, params?: SizeTheme.BuiltInParams<T>): StateTransformer.Params<VolumeRepresentation3D>['sizeTheme']
+export function createVolumeSizeThemeParams(ctx: PluginContext, volume: Volume | undefined, typeName: string | undefined, themeName?: string, params?: any): StateTransformer.Params<VolumeRepresentation3D>['sizeTheme']
+export function createVolumeSizeThemeParams(ctx: PluginContext, volume: Volume | undefined, typeName: string | undefined, themeName?: string, params?: any): StateTransformer.Params<VolumeRepresentation3D>['sizeTheme'] {
     const { registry, themes } = ctx.representation.volume;
     const repr = registry.get(typeName || registry.default.name);
     const size = themes.sizeThemeRegistry.get(themeName || repr.defaultSizeTheme.name);
-    const sizeDefaultParams = PD.getDefaultValues(size.getParams({ volume: volume || VolumeData.One }));
+    const sizeDefaultParams = PD.getDefaultValues(size.getParams({ volume: volume || Volume.One }));
     if (size.name === repr.defaultSizeTheme.name) Object.assign(sizeDefaultParams, repr.defaultSizeTheme.props);
     return { name: size.name, params: Object.assign(sizeDefaultParams, params) };
 }
 
-function createParamsByName(ctx: PluginContext, volume: VolumeData, props: VolumeRepresentationBuiltInProps): StateTransformer.Params<VolumeRepresentation3D> {
+function createParamsByName(ctx: PluginContext, volume: Volume, props: VolumeRepresentationBuiltInProps): StateTransformer.Params<VolumeRepresentation3D> {
     const typeProvider = (props.type && ctx.representation.volume.registry.get(props.type))
         || ctx.representation.volume.registry.default.provider;
     const colorProvider = (props.color && ctx.representation.volume.themes.colorThemeRegistry.get(props.color))
@@ -95,7 +95,7 @@ function createParamsByName(ctx: PluginContext, volume: VolumeData, props: Volum
     });
 }
 
-function createParamsProvider(ctx: PluginContext, volume: VolumeData, props: VolumeRepresentationProps = {}): StateTransformer.Params<VolumeRepresentation3D> {
+function createParamsProvider(ctx: PluginContext, volume: Volume, props: VolumeRepresentationProps = {}): StateTransformer.Params<VolumeRepresentation3D> {
     const { themes: themeCtx } = ctx.representation.volume;
     const themeDataCtx = { volume };
 

+ 2 - 2
src/mol-plugin-state/objects.ts

@@ -14,7 +14,7 @@ import { PlyFile } from '../mol-io/reader/ply/schema';
 import { PsfFile } from '../mol-io/reader/psf/parser';
 import { ShapeProvider } from '../mol-model/shape/provider';
 import { Coordinates as _Coordinates, Model as _Model, Structure as _Structure, StructureElement, Topology as _Topology } from '../mol-model/structure';
-import { VolumeData } from '../mol-model/volume';
+import { Volume as _Volume } from '../mol-model/volume';
 import { PluginBehavior } from '../mol-plugin/behavior/behavior';
 import { Representation } from '../mol-repr/representation';
 import { ShapeRepresentation } from '../mol-repr/shape/representation';
@@ -121,7 +121,7 @@ export namespace PluginStateObject {
     }
 
     export namespace Volume {
-        export class Data extends Create<VolumeData>({ name: 'Volume Data', typeClass: 'Object' }) { }
+        export class Data extends Create<_Volume>({ name: 'Volume', typeClass: 'Object' }) { }
         export class Representation3D extends CreateRepresentation3D<VolumeRepresentation<any>>({ name: 'Volume 3D' }) { }
     }
 

+ 6 - 6
src/mol-plugin-state/transforms/representation.ts

@@ -6,7 +6,7 @@
  */
 
 import { Structure, StructureElement } from '../../mol-model/structure';
-import { VolumeData, VolumeIsoValue } from '../../mol-model/volume';
+import { Volume } from '../../mol-model/volume';
 import { PluginContext } from '../../mol-plugin/context';
 import { VolumeRepresentationRegistry } from '../../mol-repr/volume/registry';
 import { VolumeParams } from '../../mol-repr/volume/representation';
@@ -441,7 +441,7 @@ const TransparencyStructureRepresentation3DFromBundle = PluginStateTransform.Bui
 //
 
 export namespace VolumeRepresentation3DHelpers {
-    export function getDefaultParams(ctx: PluginContext, name: VolumeRepresentationRegistry.BuiltIn, volume: VolumeData, volumeParams?: Partial<PD.Values<VolumeParams>>): StateTransformer.Params<VolumeRepresentation3D> {
+    export function getDefaultParams(ctx: PluginContext, name: VolumeRepresentationRegistry.BuiltIn, volume: Volume, volumeParams?: Partial<PD.Values<VolumeParams>>): StateTransformer.Params<VolumeRepresentation3D> {
         const type = ctx.representation.volume.registry.get(name);
 
         const themeDataCtx = { volume };
@@ -467,7 +467,7 @@ export namespace VolumeRepresentation3DHelpers {
     }
 
     export function getDescription(props: any) {
-        return props.isoValue && VolumeIsoValue.toString(props.isoValue);
+        return props.isoValue && Volume.IsoValue.toString(props.isoValue);
     }
 }
 type VolumeRepresentation3D = typeof VolumeRepresentation3D
@@ -485,16 +485,16 @@ const VolumeRepresentation3D = PluginStateTransform.BuiltIn({
                 type: PD.Mapped<any>(
                     registry.default.name,
                     registry.types,
-                    name => PD.Group<any>(registry.get(name).getParams(themeCtx, VolumeData.One))),
+                    name => PD.Group<any>(registry.get(name).getParams(themeCtx, Volume.One))),
                 colorTheme: PD.Mapped<any>(
                     type.defaultColorTheme.name,
                     themeCtx.colorThemeRegistry.types,
-                    name => PD.Group<any>(themeCtx.colorThemeRegistry.get(name).getParams({ volume: VolumeData.One }))
+                    name => PD.Group<any>(themeCtx.colorThemeRegistry.get(name).getParams({ volume: Volume.One }))
                 ),
                 sizeTheme: PD.Mapped<any>(
                     type.defaultSizeTheme.name,
                     themeCtx.sizeThemeRegistry.types,
-                    name => PD.Group<any>(themeCtx.sizeThemeRegistry.get(name).getParams({ volume: VolumeData.One }))
+                    name => PD.Group<any>(themeCtx.sizeThemeRegistry.get(name).getParams({ volume: Volume.One }))
                 )
             };
         }

+ 3 - 3
src/mol-plugin-state/transforms/volume.ts

@@ -15,7 +15,7 @@ import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { PluginStateObject as SO, PluginStateTransform } from '../objects';
 import { volumeFromCube } from '../../mol-model-formats/volume/cube';
 import { volumeFromDx } from '../../mol-model-formats/volume/dx';
-import { VolumeData } from '../../mol-model/volume';
+import {  Volume } from '../../mol-model/volume';
 import { PluginContext } from '../../mol-plugin/context';
 import { StateSelection } from '../../mol-state';
 
@@ -160,8 +160,8 @@ const AssignColorVolume = PluginStateTransform.BuiltIn({
             if (!dependencies || !dependencies[params.ref]) {
                 throw new Error('Dependency not available.');
             }
-            const colorVolume = dependencies[params.ref].data as VolumeData;
-            const volume: VolumeData = {
+            const colorVolume = dependencies[params.ref].data as Volume;
+            const volume: Volume = {
                 ...a.data,
                 colorVolume
             };

+ 7 - 7
src/mol-plugin-ui/custom/volume.tsx

@@ -12,7 +12,7 @@ import { ExpandableControlRow, IconButton } from '../controls/common';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { ParameterControls, ParamOnChange } from '../controls/parameters';
 import { Slider } from '../controls/slider';
-import { VolumeIsoValue, VolumeData } from '../../mol-model/volume';
+import { Volume, Grid } from '../../mol-model/volume';
 import { Vec3 } from '../../mol-math/linear-algebra';
 import { ColorNames } from '../../mol-util/color/names';
 import { toPrecision } from '../../mol-util/number';
@@ -34,7 +34,7 @@ class Channel extends PluginUIComponent<{
     channels: { [k: string]: VolumeStreaming.ChannelParams },
     isRelative: boolean,
     params: StateTransformParameters.Props,
-    stats: VolumeData['dataStats'],
+    stats: Grid['stats'],
     changeIso: (name: string, value: number, isRelative: boolean) => void,
     changeParams: (name: string, param: string, value: any) => void,
     bCell: StateObjectCell,
@@ -111,7 +111,7 @@ export class VolumeStreamingCustomControls extends PluginUIComponent<StateTransf
                         ...old.entry.params.channels,
                         [name]: {
                             ...(old.entry.params.channels as any)[name],
-                            isoValue: isRelative ? VolumeIsoValue.relative(value) : VolumeIsoValue.absolute(value)
+                            isoValue: isRelative ? Volume.IsoValue.relative(value) : Volume.IsoValue.absolute(value)
                         }
                     }
                 }
@@ -139,11 +139,11 @@ export class VolumeStreamingCustomControls extends PluginUIComponent<StateTransf
         });
     };
 
-    convert(channel: any, stats: VolumeData['dataStats'], isRelative: boolean) {
+    convert(channel: any, stats: Grid['stats'], isRelative: boolean) {
         return {
             ...channel, isoValue: isRelative
-                ? VolumeIsoValue.toRelative(channel.isoValue, stats)
-                : VolumeIsoValue.toAbsolute(channel.isoValue, stats)
+                ? Volume.IsoValue.toRelative(channel.isoValue, stats)
+                : Volume.IsoValue.toAbsolute(channel.isoValue, stats)
         };
     }
 
@@ -215,7 +215,7 @@ export class VolumeStreamingCustomControls extends PluginUIComponent<StateTransf
         const params = this.props.params as VolumeStreaming.Params;
         const detailLevel = ((this.props.info.params as VolumeStreaming.ParamDefinition)
             .entry.map(params.entry.name) as PD.Group<VolumeStreaming.EntryParamDefinition>).params.detailLevel;
-        const isRelative = ((params.entry.params.channels as any)[pivot].isoValue as VolumeIsoValue).kind === 'relative';
+        const isRelative = ((params.entry.params.channels as any)[pivot].isoValue as Volume.IsoValue).kind === 'relative';
 
         const sampling = b.info.header.sampling[0];
 

+ 2 - 2
src/mol-plugin-ui/structure/volume.tsx

@@ -137,7 +137,7 @@ export class VolumeSourceControls extends CollapsableControls<{}, VolumeSourceCo
     private item = (ref: VolumeRef) => {
         const selected = this.plugin.managers.volume.hierarchy.selection;
 
-        const label = ref.cell.obj?.data.label || 'Volume';
+        const label = ref.cell.obj?.label || 'Volume';
         const item: ActionMenu.Item = { kind: 'item', label: label || ref.kind, selected: selected === ref, value: ref };
         return item;
     }
@@ -172,7 +172,7 @@ export class VolumeSourceControls extends CollapsableControls<{}, VolumeSourceCo
     get label() {
         const selected = this.plugin.managers.volume.hierarchy.selection;
         if (!selected) return 'Nothing Selected';
-        return selected?.cell.obj?.data.label || 'Volume';
+        return selected?.cell.obj?.label || 'Volume';
     }
 
     selectCurrent: ActionMenu.OnSelect = (item) => {

+ 18 - 18
src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts

@@ -7,7 +7,7 @@
 
 import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
 import { PluginStateObject } from '../../../../mol-plugin-state/objects';
-import { VolumeIsoValue, VolumeData } from '../../../../mol-model/volume';
+import { Volume, Grid } from '../../../../mol-model/volume';
 import { createIsoValueParam } from '../../../../mol-repr/volume/isosurface';
 import { VolumeServerHeader, VolumeServerInfo } from './model';
 import { Box3D } from '../../../../mol-math/geometry';
@@ -31,13 +31,13 @@ export namespace VolumeStreaming {
     export const RootTag = 'volume-streaming-info';
 
     export interface ChannelParams {
-        isoValue: VolumeIsoValue,
+        isoValue: Volume.IsoValue,
         color: Color,
         wireframe: boolean,
         opacity: number
     }
 
-    function channelParam(label: string, color: Color, defaultValue: VolumeIsoValue, stats: VolumeData['dataStats'], defaults: Partial<ChannelParams> = {}) {
+    function channelParam(label: string, color: Color, defaultValue: Volume.IsoValue, stats: Grid['stats'], defaults: Partial<ChannelParams> = {}) {
         return PD.Group<ChannelParams>({
             isoValue: createIsoValueParam(typeof defaults.isoValue !== 'undefined' ? defaults.isoValue : defaultValue, stats),
             color: PD.Color(typeof defaults.color !== 'undefined' ? defaults.color : color),
@@ -71,7 +71,7 @@ export namespace VolumeStreaming {
         const { entryData, defaultView, structure, channelParams = { } } = options;
 
         // fake the info
-        const info = entryData || { kind: 'em', header: { sampling: [fakeSampling], availablePrecisions: [{ precision: 0, maxVoxels: 0 }] }, emDefaultContourLevel: VolumeIsoValue.relative(0) };
+        const info = entryData || { kind: 'em', header: { sampling: [fakeSampling], availablePrecisions: [{ precision: 0, maxVoxels: 0 }] }, emDefaultContourLevel: Volume.IsoValue.relative(0) };
         const box = (structure && structure.boundary.box) || Box3D.empty();
 
         return {
@@ -93,12 +93,12 @@ export namespace VolumeStreaming {
                 info.header.availablePrecisions.map((p, i) => [i, `${i + 1} [ ${Math.pow(p.maxVoxels, 1 / 3) | 0}^3 cells ]`] as [number, string]), { description: 'Determines the maximum number of voxels. Depending on the size of the volume options are in the range from 0 (0.52M voxels) to 6 (25.17M voxels).' }),
             channels: info.kind === 'em'
                 ? PD.Group({
-                    'em': channelParam('EM', Color(0x638F8F), info.emDefaultContourLevel || VolumeIsoValue.relative(1), info.header.sampling[0].valuesInfo[0], channelParams['em'])
+                    'em': channelParam('EM', Color(0x638F8F), info.emDefaultContourLevel || Volume.IsoValue.relative(1), info.header.sampling[0].valuesInfo[0], channelParams['em'])
                 }, { isFlat: true })
                 : PD.Group({
-                    '2fo-fc': channelParam('2Fo-Fc', Color(0x3362B2), VolumeIsoValue.relative(1.5), info.header.sampling[0].valuesInfo[0], channelParams['2fo-fc']),
-                    'fo-fc(+ve)': channelParam('Fo-Fc(+ve)', Color(0x33BB33), VolumeIsoValue.relative(3), info.header.sampling[0].valuesInfo[1], channelParams['fo-fc(+ve)']),
-                    'fo-fc(-ve)': channelParam('Fo-Fc(-ve)', Color(0xBB3333), VolumeIsoValue.relative(-3), info.header.sampling[0].valuesInfo[1], channelParams['fo-fc(-ve)']),
+                    '2fo-fc': channelParam('2Fo-Fc', Color(0x3362B2), Volume.IsoValue.relative(1.5), info.header.sampling[0].valuesInfo[0], channelParams['2fo-fc']),
+                    'fo-fc(+ve)': channelParam('Fo-Fc(+ve)', Color(0x33BB33), Volume.IsoValue.relative(3), info.header.sampling[0].valuesInfo[1], channelParams['fo-fc(+ve)']),
+                    'fo-fc(-ve)': channelParam('Fo-Fc(-ve)', Color(0xBB3333), Volume.IsoValue.relative(-3), info.header.sampling[0].valuesInfo[1], channelParams['fo-fc(-ve)']),
                 }, { isFlat: true }),
         };
     }
@@ -110,16 +110,16 @@ export namespace VolumeStreaming {
     export type ParamDefinition = typeof createParams extends (...args: any[]) => (infer T) ? T : never
     export type Params = ParamDefinition extends PD.Params ? PD.Values<ParamDefinition> : {}
 
-    type ChannelsInfo = { [name in ChannelType]?: { isoValue: VolumeIsoValue, color: Color, wireframe: boolean, opacity: number } }
-    type ChannelsData = { [name in 'EM' | '2FO-FC' | 'FO-FC']?: VolumeData }
+    type ChannelsInfo = { [name in ChannelType]?: { isoValue: Volume.IsoValue, color: Color, wireframe: boolean, opacity: number } }
+    type ChannelsData = { [name in 'EM' | '2FO-FC' | 'FO-FC']?: Volume }
 
     export type ChannelType = 'em' | '2fo-fc' | 'fo-fc(+ve)' | 'fo-fc(-ve)'
     export const ChannelTypeOptions: [ChannelType, string][] = [['em', 'em'], ['2fo-fc', '2fo-fc'], ['fo-fc(+ve)', 'fo-fc(+ve)'], ['fo-fc(-ve)', 'fo-fc(-ve)']];
     export interface ChannelInfo {
-        data: VolumeData,
+        data: Volume,
         color: Color,
         wireframe: boolean,
-        isoValue: VolumeIsoValue.Relative,
+        isoValue: Volume.IsoValue.Relative,
         opacity: number
     }
     export type Channels = { [name in ChannelType]?: ChannelInfo }
@@ -329,24 +329,24 @@ export namespace VolumeStreaming {
             const info = params.entry.params.channels as ChannelsInfo;
 
             if (this.info.kind === 'x-ray') {
-                this.channels['2fo-fc'] = this.createChannel(data['2FO-FC'] || VolumeData.One, info['2fo-fc'], this.info.header.sampling[0].valuesInfo[0]);
-                this.channels['fo-fc(+ve)'] = this.createChannel(data['FO-FC'] || VolumeData.One, info['fo-fc(+ve)'], this.info.header.sampling[0].valuesInfo[1]);
-                this.channels['fo-fc(-ve)'] = this.createChannel(data['FO-FC'] || VolumeData.One, info['fo-fc(-ve)'], this.info.header.sampling[0].valuesInfo[1]);
+                this.channels['2fo-fc'] = this.createChannel(data['2FO-FC'] || Volume.One, info['2fo-fc'], this.info.header.sampling[0].valuesInfo[0]);
+                this.channels['fo-fc(+ve)'] = this.createChannel(data['FO-FC'] || Volume.One, info['fo-fc(+ve)'], this.info.header.sampling[0].valuesInfo[1]);
+                this.channels['fo-fc(-ve)'] = this.createChannel(data['FO-FC'] || Volume.One, info['fo-fc(-ve)'], this.info.header.sampling[0].valuesInfo[1]);
             } else {
-                this.channels['em'] = this.createChannel(data['EM'] || VolumeData.One, info['em'], this.info.header.sampling[0].valuesInfo[0]);
+                this.channels['em'] = this.createChannel(data['EM'] || Volume.One, info['em'], this.info.header.sampling[0].valuesInfo[0]);
             }
 
             return true;
         }
 
-        private createChannel(data: VolumeData, info: ChannelsInfo['em'], stats: VolumeData['dataStats']): ChannelInfo {
+        private createChannel(data: Volume, info: ChannelsInfo['em'], stats: Grid['stats']): ChannelInfo {
             const i = info!;
             return {
                 data,
                 color: i.color,
                 wireframe: i.wireframe,
                 opacity: i.opacity,
-                isoValue: i.isoValue.kind === 'relative' ? i.isoValue : VolumeIsoValue.toRelative(i.isoValue, stats)
+                isoValue: i.isoValue.kind === 'relative' ? i.isoValue : Volume.IsoValue.toRelative(i.isoValue, stats)
             };
         }
 

+ 3 - 3
src/mol-plugin/behavior/dynamic/volume-streaming/model.ts

@@ -1,12 +1,12 @@
 /**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
 import { PluginStateObject } from '../../../../mol-plugin-state/objects';
-import { VolumeIsoValue } from '../../../../mol-model/volume';
+import { Volume } from '../../../../mol-model/volume';
 import { Structure } from '../../../../mol-model/structure';
 
 export class VolumeServerInfo extends PluginStateObject.Create<VolumeServerInfo.Data>({ name: 'Volume Streaming', typeClass: 'Object' }) { }
@@ -18,7 +18,7 @@ export namespace VolumeServerInfo {
         // for em, the EMDB access code, for x-ray, the PDB id
         dataId: string,
         header: VolumeServerHeader,
-        emDefaultContourLevel?: VolumeIsoValue,
+        emDefaultContourLevel?: Volume.IsoValue,
     }
     export interface Data {
         serverUrl: string,

+ 4 - 4
src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts

@@ -12,7 +12,7 @@ import { Task } from '../../../../mol-task';
 import { PluginContext } from '../../../../mol-plugin/context';
 import { urlCombine } from '../../../../mol-util/url';
 import { createIsoValueParam } from '../../../../mol-repr/volume/isosurface';
-import { VolumeIsoValue } from '../../../../mol-model/volume';
+import { Volume } from '../../../../mol-model/volume';
 import { StateAction, StateObject, StateTransformer } from '../../../../mol-state';
 import { getStreamingMethod, getIds, getContourLevel, getEmdbIds } from './util';
 import { VolumeStreaming } from './behavior';
@@ -27,7 +27,7 @@ import { Model } from '../../../../mol-model/structure';
 function addEntry(entries: InfoEntryProps[], method: VolumeServerInfo.Kind, dataId: string, emDefaultContourLevel: number) {
     entries.push({
         source: method === 'em'
-            ? { name: 'em', params: { isoValue: VolumeIsoValue.absolute(emDefaultContourLevel || 0) } }
+            ? { name: 'em', params: { isoValue: Volume.IsoValue.absolute(emDefaultContourLevel || 0) } }
             : { name: 'x-ray', params: { } },
         dataId
     });
@@ -157,7 +157,7 @@ const InfoEntryParams = {
     dataId: PD.Text(''),
     source: PD.MappedStatic('x-ray', {
         'em': PD.Group({
-            isoValue: createIsoValueParam(VolumeIsoValue.relative(1))
+            isoValue: createIsoValueParam(Volume.IsoValue.relative(1))
         }),
         'x-ray': PD.Group({ })
     })
@@ -185,7 +185,7 @@ const CreateVolumeStreamingInfo = PluginStateTransform.BuiltIn({
         for (let i = 0, il = params.entries.length; i < il; ++i) {
             const e = params.entries[i];
             const dataId = e.dataId;
-            const emDefaultContourLevel = e.source.name === 'em' ? e.source.params.isoValue : VolumeIsoValue.relative(1);
+            const emDefaultContourLevel = e.source.name === 'em' ? e.source.params.isoValue : Volume.IsoValue.relative(1);
             await taskCtx.update('Getting server header...');
             const header = await plugin.fetch({ url: urlCombine(params.serverUrl, `${e.source.name}/${dataId.toLocaleLowerCase()}`), type: 'json' }).runInContext(taskCtx) as VolumeServerHeader;
             entries.push({

+ 18 - 21
src/mol-repr/volume/direct-volume.ts

@@ -7,7 +7,7 @@
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { Vec3, Mat4 } from '../../mol-math/linear-algebra';
 import { Box3D } from '../../mol-math/geometry';
-import { VolumeData } from '../../mol-model/volume';
+import { Grid, Volume } from '../../mol-model/volume';
 import { RuntimeContext } from '../../mol-task';
 import { WebGLContext } from '../../mol-gl/webgl/context';
 import { DirectVolume } from '../../mol-geo/geometry/direct-volume/direct-volume';
@@ -20,7 +20,6 @@ import { NullLocation } from '../../mol-model/location';
 import { EmptyLoci } from '../../mol-model/loci';
 import { VisualUpdateState } from '../util';
 import { RepresentationContext, RepresentationParamsGetter } from '../representation';
-import { Volume } from '../../mol-model/volume/volume';
 
 function getBoundingBox(gridDimension: Vec3, transform: Mat4) {
     const bbox = Box3D.empty();
@@ -49,9 +48,8 @@ function getVolumeTexture2dLayout(dim: Vec3, maxTextureSize: number) {
     return { width, height, columns, rows };
 }
 
-function createVolumeTexture2d(volume: VolumeData, maxTextureSize: number) {
-    const { data: tensor, dataStats: stats } = volume;
-    const { space, data } = tensor;
+function createVolumeTexture2d(volume: Volume, maxTextureSize: number) {
+    const { cells: { space, data }, stats } = volume.grid;
     const dim = space.dimensions as Vec3;
     const { get } = space;
     const { width, height, columns, rows } = getVolumeTexture2dLayout(dim, maxTextureSize);
@@ -85,11 +83,11 @@ function createVolumeTexture2d(volume: VolumeData, maxTextureSize: number) {
     return textureImage;
 }
 
-export function createDirectVolume2d(ctx: RuntimeContext, webgl: WebGLContext, volume: VolumeData, directVolume?: DirectVolume) {
-    const gridDimension = volume.data.space.dimensions as Vec3;
+export function createDirectVolume2d(ctx: RuntimeContext, webgl: WebGLContext, volume: Volume, directVolume?: DirectVolume) {
+    const gridDimension = volume.grid.cells.space.dimensions as Vec3;
     const textureImage = createVolumeTexture2d(volume, webgl.maxTextureSize);
     // debugTexture(createImageData(textureImage.array, textureImage.width, textureImage.height), 1/3)
-    const transform = VolumeData.getGridToCartesianTransform(volume);
+    const transform = Grid.getGridToCartesianTransform(volume.grid);
     const bbox = getBoundingBox(gridDimension, transform);
     const dim = Vec3.create(gridDimension[0], gridDimension[1], gridDimension[2]);
     dim[0] += 1; // horizontal padding
@@ -103,9 +101,8 @@ export function createDirectVolume2d(ctx: RuntimeContext, webgl: WebGLContext, v
 
 // 3d volume texture
 
-function createVolumeTexture3d(volume: VolumeData) {
-    const { data: tensor, dataStats: stats } = volume;
-    const { space, data } = tensor;
+function createVolumeTexture3d(volume: Volume) {
+    const { cells: { space, data }, stats } = volume.grid;
     const [ width, height, depth ] = space.dimensions as Vec3;
     const { get } = space;
 
@@ -128,10 +125,10 @@ function createVolumeTexture3d(volume: VolumeData) {
     return textureVolume;
 }
 
-export function createDirectVolume3d(ctx: RuntimeContext, webgl: WebGLContext, volume: VolumeData, directVolume?: DirectVolume) {
-    const gridDimension = volume.data.space.dimensions as Vec3;
+export function createDirectVolume3d(ctx: RuntimeContext, webgl: WebGLContext, volume: Volume, directVolume?: DirectVolume) {
+    const gridDimension = volume.grid.cells.space.dimensions as Vec3;
     const textureVolume = createVolumeTexture3d(volume);
-    const transform = VolumeData.getGridToCartesianTransform(volume);
+    const transform = Grid.getGridToCartesianTransform(volume.grid);
     // Mat4.invert(transform, transform)
     const bbox = getBoundingBox(gridDimension, transform);
 
@@ -143,7 +140,7 @@ export function createDirectVolume3d(ctx: RuntimeContext, webgl: WebGLContext, v
 
 //
 
-export async function createDirectVolume(ctx: VisualContext, volume: VolumeData, theme: Theme, props: PD.Values<DirectVolumeParams>, directVolume?: DirectVolume) {
+export async function createDirectVolume(ctx: VisualContext, volume: Volume, theme: Theme, props: PD.Values<DirectVolumeParams>, directVolume?: DirectVolume) {
     const { runtime, webgl } = ctx;
     if (webgl === undefined) throw new Error('DirectVolumeVisual requires `webgl` in props');
 
@@ -152,7 +149,7 @@ export async function createDirectVolume(ctx: VisualContext, volume: VolumeData,
         createDirectVolume2d(runtime, webgl, volume, directVolume);
 }
 
-function getLoci(volume: VolumeData, props: PD.Values<DirectVolumeParams>) {
+function getLoci(volume: Volume, props: PD.Values<DirectVolumeParams>) {
     return Volume.Loci(volume);
 }
 
@@ -163,7 +160,7 @@ export const DirectVolumeParams = {
     ...DirectVolume.Params
 };
 export type DirectVolumeParams = typeof DirectVolumeParams
-export function getDirectVolumeParams(ctx: ThemeRegistryContext, volume: VolumeData) {
+export function getDirectVolumeParams(ctx: ThemeRegistryContext, volume: Volume) {
     return PD.clone(DirectVolumeParams);
 }
 
@@ -171,16 +168,16 @@ export function DirectVolumeVisual(materialId: number): VolumeVisual<DirectVolum
     return VolumeVisual<DirectVolume, DirectVolumeParams>({
         defaultProps: PD.getDefaultValues(DirectVolumeParams),
         createGeometry: createDirectVolume,
-        createLocationIterator: (volume: VolumeData) => LocationIterator(1, 1, () => NullLocation),
+        createLocationIterator: (volume: Volume) => LocationIterator(1, 1, () => NullLocation),
         getLoci: () => EmptyLoci,
         eachLocation: () => false,
-        setUpdateState: (state: VisualUpdateState, volume: VolumeData, newProps: PD.Values<DirectVolumeParams>, currentProps: PD.Values<DirectVolumeParams>) => {
+        setUpdateState: (state: VisualUpdateState, volume: Volume, newProps: PD.Values<DirectVolumeParams>, currentProps: PD.Values<DirectVolumeParams>) => {
         },
         geometryUtils: DirectVolume.Utils
     }, materialId);
 }
 
-export function DirectVolumeRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<VolumeData, DirectVolumeParams>): VolumeRepresentation<DirectVolumeParams> {
+export function DirectVolumeRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<Volume, DirectVolumeParams>): VolumeRepresentation<DirectVolumeParams> {
     return VolumeRepresentation('Direct Volume', ctx, getParams, DirectVolumeVisual, getLoci);
 }
 
@@ -193,5 +190,5 @@ export const DirectVolumeRepresentationProvider = VolumeRepresentationProvider({
     defaultValues: PD.getDefaultValues(DirectVolumeParams),
     defaultColorTheme: { name: 'uniform' },
     defaultSizeTheme: { name: 'uniform' },
-    isApplicable: (volume: VolumeData) => volume.data.data.length > 0
+    isApplicable: (volume: Volume) => !Volume.isEmpty(volume)
 });

+ 48 - 49
src/mol-repr/volume/isosurface.ts

@@ -6,7 +6,7 @@
  */
 
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
-import { VolumeData, VolumeIsoValue } from '../../mol-model/volume';
+import { Grid, Volume } from '../../mol-model/volume';
 import { VisualContext } from '../visual';
 import { Theme, ThemeRegistryContext } from '../../mol-theme/theme';
 import { Mesh } from '../../mol-geo/geometry/mesh/mesh';
@@ -18,15 +18,14 @@ import { VisualUpdateState } from '../util';
 import { Lines } from '../../mol-geo/geometry/lines/lines';
 import { RepresentationContext, RepresentationParamsGetter, Representation } from '../representation';
 import { toPrecision } from '../../mol-util/number';
-import { Volume } from '../../mol-model/volume/volume';
 import { PickingId } from '../../mol-geo/geometry/picking';
 import { EmptyLoci, Loci } from '../../mol-model/loci';
 import { Interval, OrderedSet } from '../../mol-data/int';
 import { Tensor } from '../../mol-math/linear-algebra';
 import { fillSerial } from '../../mol-util/array';
 
-const defaultStats: VolumeData['dataStats'] = { min: -1, max: 1, mean: 0, sigma: 0.1  };
-export function createIsoValueParam(defaultValue: VolumeIsoValue, stats?: VolumeData['dataStats']) {
+const defaultStats: Grid['stats'] = { min: -1, max: 1, mean: 0, sigma: 0.1  };
+export function createIsoValueParam(defaultValue: Volume.IsoValue, stats?: Grid['stats']) {
     const sts = stats || defaultStats;
     const { min, max, mean, sigma } = sts;
 
@@ -36,34 +35,34 @@ export function createIsoValueParam(defaultValue: VolumeIsoValue, stats?: Volume
 
     let def = defaultValue;
     if (defaultValue.kind === 'absolute') {
-        if (defaultValue.absoluteValue < min) def = VolumeIsoValue.absolute(min);
-        else if (defaultValue.absoluteValue > max) def = VolumeIsoValue.absolute(max);
+        if (defaultValue.absoluteValue < min) def = Volume.IsoValue.absolute(min);
+        else if (defaultValue.absoluteValue > max) def = Volume.IsoValue.absolute(max);
     } else {
-        if (defaultValue.relativeValue < relMin) def = VolumeIsoValue.relative(relMin);
-        else if (defaultValue.relativeValue > relMax) def = VolumeIsoValue.relative(relMax);
+        if (defaultValue.relativeValue < relMin) def = Volume.IsoValue.relative(relMin);
+        else if (defaultValue.relativeValue > relMax) def = Volume.IsoValue.relative(relMax);
     }
 
     return PD.Conditioned(
         def,
         {
             'absolute': PD.Converted(
-                (v: VolumeIsoValue) => VolumeIsoValue.toAbsolute(v, VolumeData.One.dataStats).absoluteValue,
-                (v: number) => VolumeIsoValue.absolute(v),
+                (v: Volume.IsoValue) => Volume.IsoValue.toAbsolute(v, Grid.One.stats).absoluteValue,
+                (v: number) => Volume.IsoValue.absolute(v),
                 PD.Numeric(mean, { min, max, step: toPrecision(sigma / 100, 2) })
             ),
             'relative': PD.Converted(
-                (v: VolumeIsoValue) => VolumeIsoValue.toRelative(v, VolumeData.One.dataStats).relativeValue,
-                (v: number) => VolumeIsoValue.relative(v),
+                (v: Volume.IsoValue) => Volume.IsoValue.toRelative(v, Grid.One.stats).relativeValue,
+                (v: number) => Volume.IsoValue.relative(v),
                 PD.Numeric(Math.min(1, relMax), { min: relMin, max: relMax, step: toPrecision(Math.round(((max - min) / sigma)) / 100, 2) })
             )
         },
-        (v: VolumeIsoValue) => v.kind === 'absolute' ? 'absolute' : 'relative',
-        (v: VolumeIsoValue, c: 'absolute' | 'relative') => c === 'absolute' ? VolumeIsoValue.toAbsolute(v, sts) : VolumeIsoValue.toRelative(v, sts),
+        (v: Volume.IsoValue) => v.kind === 'absolute' ? 'absolute' : 'relative',
+        (v: Volume.IsoValue, c: 'absolute' | 'relative') => c === 'absolute' ? Volume.IsoValue.toAbsolute(v, sts) : Volume.IsoValue.toRelative(v, sts),
         { isEssential: true }
     );
 }
 
-export const IsoValueParam = createIsoValueParam(VolumeIsoValue.relative(2));
+export const IsoValueParam = createIsoValueParam(Volume.IsoValue.relative(2));
 type IsoValueParam = typeof IsoValueParam
 
 export const VolumeIsosurfaceParams = {
@@ -72,11 +71,11 @@ export const VolumeIsosurfaceParams = {
 export type VolumeIsosurfaceParams = typeof VolumeIsosurfaceParams
 export type VolumeIsosurfaceProps = PD.Values<VolumeIsosurfaceParams>
 
-function getLoci(volume: VolumeData, props: VolumeIsosurfaceProps) {
+function getLoci(volume: Volume, props: VolumeIsosurfaceProps) {
     return Volume.Isosurface.Loci(volume, props.isoValue);
 }
 
-function getIsosurfaceLoci(pickingId: PickingId, volume: VolumeData, props: VolumeIsosurfaceProps, id: number) {
+function getIsosurfaceLoci(pickingId: PickingId, volume: Volume, props: VolumeIsosurfaceProps, id: number) {
     const { objectId, groupId } = pickingId;
     if (id === objectId) {
         return Volume.Cell.Loci(volume, Interval.ofSingleton(groupId as Volume.CellIndex));
@@ -84,17 +83,17 @@ function getIsosurfaceLoci(pickingId: PickingId, volume: VolumeData, props: Volu
     return EmptyLoci;
 }
 
-function eachIsosurface(loci: Loci, volume: VolumeData, props: VolumeIsosurfaceProps, apply: (interval: Interval) => boolean) {
+function eachIsosurface(loci: Loci, volume: Volume, props: VolumeIsosurfaceProps, apply: (interval: Interval) => boolean) {
     let changed = false;
     if (Volume.isLoci(loci)) {
-        if (!VolumeData.areEquivalent(loci.volume, volume)) return false;
-        if (apply(Interval.ofLength(volume.data.data.length))) changed = true;
+        if (!Volume.areEquivalent(loci.volume, volume)) return false;
+        if (apply(Interval.ofLength(volume.grid.cells.data.length))) changed = true;
     } else if (Volume.Isosurface.isLoci(loci)) {
-        if (!VolumeData.areEquivalent(loci.volume, volume)) return false;
-        if (!VolumeIsoValue.areSame(loci.isoValue, props.isoValue, volume.dataStats)) return false;
-        if (apply(Interval.ofLength(volume.data.data.length))) changed = true;
+        if (!Volume.areEquivalent(loci.volume, volume)) return false;
+        if (!Volume.IsoValue.areSame(loci.isoValue, props.isoValue, volume.grid.stats)) return false;
+        if (apply(Interval.ofLength(volume.grid.cells.data.length))) changed = true;
     } else if (Volume.Cell.isLoci(loci)) {
-        if (!VolumeData.areEquivalent(loci.volume, volume)) return false;
+        if (!Volume.areEquivalent(loci.volume, volume)) return false;
         if (Interval.is(loci.indices)) {
             if (apply(loci.indices)) changed = true;
         } else {
@@ -108,18 +107,18 @@ function eachIsosurface(loci: Loci, volume: VolumeData, props: VolumeIsosurfaceP
 
 //
 
-export async function createVolumeIsosurfaceMesh(ctx: VisualContext, volume: VolumeData, theme: Theme, props: VolumeIsosurfaceProps, mesh?: Mesh) {
+export async function createVolumeIsosurfaceMesh(ctx: VisualContext, volume: Volume, theme: Theme, props: VolumeIsosurfaceProps, mesh?: Mesh) {
     ctx.runtime.update({ message: 'Marching cubes...' });
 
-    const ids = fillSerial(new Int32Array(volume.data.data.length));
+    const ids = fillSerial(new Int32Array(volume.grid.cells.data.length));
 
     const surface = await computeMarchingCubesMesh({
-        isoLevel: VolumeIsoValue.toAbsolute(props.isoValue, volume.dataStats).absoluteValue,
-        scalarField: volume.data,
-        idField: Tensor.create(volume.data.space, Tensor.Data1(ids))
+        isoLevel: Volume.IsoValue.toAbsolute(props.isoValue, volume.grid.stats).absoluteValue,
+        scalarField: volume.grid.cells,
+        idField: Tensor.create(volume.grid.cells.space, Tensor.Data1(ids))
     }, mesh).runAsChild(ctx.runtime);
 
-    const transform = VolumeData.getGridToCartesianTransform(volume);
+    const transform = Grid.getGridToCartesianTransform(volume.grid);
     ctx.runtime.update({ message: 'Transforming mesh...' });
     Mesh.transform(surface, transform);
     return surface;
@@ -136,11 +135,11 @@ export function IsosurfaceMeshVisual(materialId: number): VolumeVisual<Isosurfac
     return VolumeVisual<Mesh, IsosurfaceMeshParams>({
         defaultProps: PD.getDefaultValues(IsosurfaceMeshParams),
         createGeometry: createVolumeIsosurfaceMesh,
-        createLocationIterator: (volume: VolumeData) => LocationIterator(volume.data.data.length, 1, () => NullLocation),
+        createLocationIterator: (volume: Volume) => LocationIterator(volume.grid.cells.data.length, 1, () => NullLocation),
         getLoci: getIsosurfaceLoci,
         eachLocation: eachIsosurface,
-        setUpdateState: (state: VisualUpdateState, volume: VolumeData, newProps: PD.Values<IsosurfaceMeshParams>, currentProps: PD.Values<IsosurfaceMeshParams>) => {
-            if (!VolumeIsoValue.areSame(newProps.isoValue, currentProps.isoValue, volume.dataStats)) state.createGeometry = true;
+        setUpdateState: (state: VisualUpdateState, volume: Volume, newProps: PD.Values<IsosurfaceMeshParams>, currentProps: PD.Values<IsosurfaceMeshParams>) => {
+            if (!Volume.IsoValue.areSame(newProps.isoValue, currentProps.isoValue, volume.grid.stats)) state.createGeometry = true;
         },
         geometryUtils: Mesh.Utils
     }, materialId);
@@ -148,18 +147,18 @@ export function IsosurfaceMeshVisual(materialId: number): VolumeVisual<Isosurfac
 
 //
 
-export async function createVolumeIsosurfaceWireframe(ctx: VisualContext, volume: VolumeData, theme: Theme, props: VolumeIsosurfaceProps, lines?: Lines) {
+export async function createVolumeIsosurfaceWireframe(ctx: VisualContext, volume: Volume, theme: Theme, props: VolumeIsosurfaceProps, lines?: Lines) {
     ctx.runtime.update({ message: 'Marching cubes...' });
 
-    const ids = fillSerial(new Int32Array(volume.data.data.length));
+    const ids = fillSerial(new Int32Array(volume.grid.cells.data.length));
 
     const wireframe = await computeMarchingCubesLines({
-        isoLevel: VolumeIsoValue.toAbsolute(props.isoValue, volume.dataStats).absoluteValue,
-        scalarField: volume.data,
-        idField: Tensor.create(volume.data.space, Tensor.Data1(ids))
+        isoLevel: Volume.IsoValue.toAbsolute(props.isoValue, volume.grid.stats).absoluteValue,
+        scalarField: volume.grid.cells,
+        idField: Tensor.create(volume.grid.cells.space, Tensor.Data1(ids))
     }, lines).runAsChild(ctx.runtime);
 
-    const transform = VolumeData.getGridToCartesianTransform(volume);
+    const transform = Grid.getGridToCartesianTransform(volume.grid);
     Lines.transform(wireframe, transform);
 
     return wireframe;
@@ -177,11 +176,11 @@ export function IsosurfaceWireframeVisual(materialId: number): VolumeVisual<Isos
     return VolumeVisual<Lines, IsosurfaceWireframeParams>({
         defaultProps: PD.getDefaultValues(IsosurfaceWireframeParams),
         createGeometry: createVolumeIsosurfaceWireframe,
-        createLocationIterator: (volume: VolumeData) => LocationIterator(volume.data.data.length, 1, () => NullLocation),
+        createLocationIterator: (volume: Volume) => LocationIterator(volume.grid.cells.data.length, 1, () => NullLocation),
         getLoci: getIsosurfaceLoci,
         eachLocation: eachIsosurface,
-        setUpdateState: (state: VisualUpdateState, volume: VolumeData, newProps: PD.Values<IsosurfaceWireframeParams>, currentProps: PD.Values<IsosurfaceWireframeParams>) => {
-            if (!VolumeIsoValue.areSame(newProps.isoValue, currentProps.isoValue, volume.dataStats)) state.createGeometry = true;
+        setUpdateState: (state: VisualUpdateState, volume: Volume, newProps: PD.Values<IsosurfaceWireframeParams>, currentProps: PD.Values<IsosurfaceWireframeParams>) => {
+            if (!Volume.IsoValue.areSame(newProps.isoValue, currentProps.isoValue, volume.grid.stats)) state.createGeometry = true;
         },
         geometryUtils: Lines.Utils
     }, materialId);
@@ -190,8 +189,8 @@ export function IsosurfaceWireframeVisual(materialId: number): VolumeVisual<Isos
 //
 
 const IsosurfaceVisuals = {
-    'solid': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<VolumeData, IsosurfaceMeshParams>) => VolumeRepresentation('Isosurface mesh', ctx, getParams, IsosurfaceMeshVisual, getLoci),
-    'wireframe': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<VolumeData, IsosurfaceWireframeParams>) => VolumeRepresentation('Isosurface wireframe', ctx, getParams, IsosurfaceWireframeVisual, getLoci),
+    'solid': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Volume, IsosurfaceMeshParams>) => VolumeRepresentation('Isosurface mesh', ctx, getParams, IsosurfaceMeshVisual, getLoci),
+    'wireframe': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Volume, IsosurfaceWireframeParams>) => VolumeRepresentation('Isosurface wireframe', ctx, getParams, IsosurfaceWireframeVisual, getLoci),
 };
 
 export const IsosurfaceParams = {
@@ -200,15 +199,15 @@ export const IsosurfaceParams = {
     visuals: PD.MultiSelect(['solid'], PD.objectToOptions(IsosurfaceVisuals)),
 };
 export type IsosurfaceParams = typeof IsosurfaceParams
-export function getIsosurfaceParams(ctx: ThemeRegistryContext, volume: VolumeData) {
+export function getIsosurfaceParams(ctx: ThemeRegistryContext, volume: Volume) {
     const p = PD.clone(IsosurfaceParams);
-    p.isoValue = createIsoValueParam(VolumeIsoValue.relative(2), volume.dataStats);
+    p.isoValue = createIsoValueParam(Volume.IsoValue.relative(2), volume.grid.stats);
     return p;
 }
 
 export type IsosurfaceRepresentation = VolumeRepresentation<IsosurfaceParams>
-export function IsosurfaceRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<VolumeData, IsosurfaceParams>): IsosurfaceRepresentation {
-    return Representation.createMulti('Isosurface', ctx, getParams, Representation.StateBuilder, IsosurfaceVisuals as unknown as Representation.Def<VolumeData, IsosurfaceParams>);
+export function IsosurfaceRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<Volume, IsosurfaceParams>): IsosurfaceRepresentation {
+    return Representation.createMulti('Isosurface', ctx, getParams, Representation.StateBuilder, IsosurfaceVisuals as unknown as Representation.Def<Volume, IsosurfaceParams>);
 }
 
 export const IsosurfaceRepresentationProvider = VolumeRepresentationProvider({
@@ -220,5 +219,5 @@ export const IsosurfaceRepresentationProvider = VolumeRepresentationProvider({
     defaultValues: PD.getDefaultValues(IsosurfaceParams),
     defaultColorTheme: { name: 'uniform' },
     defaultSizeTheme: { name: 'uniform' },
-    isApplicable: (volume: VolumeData) => volume.data.data.length > 0
+    isApplicable: (volume: Volume) => !Volume.isEmpty(volume)
 });

+ 2 - 2
src/mol-repr/volume/registry.ts

@@ -5,12 +5,12 @@
  */
 
 import { RepresentationRegistry, Representation, RepresentationProvider } from '../representation';
-import { VolumeData } from '../../mol-model/volume';
+import { Volume } from '../../mol-model/volume';
 import { IsosurfaceRepresentationProvider } from './isosurface';
 import { objectForEach } from '../../mol-util/object';
 import { SliceRepresentationProvider } from './slice';
 
-export class VolumeRepresentationRegistry extends RepresentationRegistry<VolumeData, Representation.State> {
+export class VolumeRepresentationRegistry extends RepresentationRegistry<Volume, Representation.State> {
     constructor() {
         super();
         objectForEach(VolumeRepresentationRegistry.BuiltIn, (p, k) => {

+ 18 - 18
src/mol-repr/volume/representation.ts

@@ -6,7 +6,7 @@
 
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { Visual, VisualContext } from '../visual';
-import { VolumeData } from '../../mol-model/volume';
+import { Volume } from '../../mol-model/volume';
 import { Geometry, GeometryUtils } from '../../mol-geo/geometry/geometry';
 import { LocationIterator } from '../../mol-geo/util/location-iterator';
 import { Theme } from '../../mol-theme/theme';
@@ -30,9 +30,9 @@ import { Subject } from 'rxjs';
 import { Task } from '../../mol-task';
 import { SizeValues } from '../../mol-gl/renderable/schema';
 
-export interface VolumeVisual<P extends VolumeParams> extends Visual<VolumeData, P> { }
+export interface VolumeVisual<P extends VolumeParams> extends Visual<Volume, P> { }
 
-function createVolumeRenderObject<G extends Geometry>(volume: VolumeData, geometry: G, locationIt: LocationIterator, theme: Theme, props: PD.Values<Geometry.Params<G>>, materialId: number) {
+function createVolumeRenderObject<G extends Geometry>(volume: Volume, geometry: G, locationIt: LocationIterator, theme: Theme, props: PD.Values<Geometry.Params<G>>, materialId: number) {
     const { createValues, createRenderableState } = Geometry.getUtils(geometry);
     const transform = createIdentityTransform();
     const values = createValues(geometry, transform, locationIt, theme, props);
@@ -42,11 +42,11 @@ function createVolumeRenderObject<G extends Geometry>(volume: VolumeData, geomet
 
 interface VolumeVisualBuilder<P extends VolumeParams, G extends Geometry> {
     defaultProps: PD.Values<P>
-    createGeometry(ctx: VisualContext, volume: VolumeData, theme: Theme, props: PD.Values<P>, geometry?: G): Promise<G> | G
-    createLocationIterator(volume: VolumeData): LocationIterator
-    getLoci(pickingId: PickingId, volume: VolumeData, props: PD.Values<P>, id: number): Loci
-    eachLocation(loci: Loci, volume: VolumeData, props: PD.Values<P>, apply: (interval: Interval) => boolean): boolean
-    setUpdateState(state: VisualUpdateState, volume: VolumeData, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme): void
+    createGeometry(ctx: VisualContext, volume: Volume, theme: Theme, props: PD.Values<P>, geometry?: G): Promise<G> | G
+    createLocationIterator(volume: Volume): LocationIterator
+    getLoci(pickingId: PickingId, volume: Volume, props: PD.Values<P>, id: number): Loci
+    eachLocation(loci: Loci, volume: Volume, props: PD.Values<P>, apply: (interval: Interval) => boolean): boolean
+    setUpdateState(state: VisualUpdateState, volume: Volume, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme): void
 }
 
 interface VolumeVisualGeometryBuilder<P extends VolumeParams, G extends Geometry> extends VolumeVisualBuilder<P, G> {
@@ -62,16 +62,16 @@ export function VolumeVisual<G extends Geometry, P extends VolumeParams & Geomet
 
     let newProps: PD.Values<P>;
     let newTheme: Theme;
-    let newVolume: VolumeData;
+    let newVolume: Volume;
 
     let currentProps: PD.Values<P> = Object.assign({}, defaultProps);
     let currentTheme: Theme = Theme.createEmpty();
-    let currentVolume: VolumeData;
+    let currentVolume: Volume;
 
     let geometry: G;
     let locationIt: LocationIterator;
 
-    function prepareUpdate(theme: Theme, props: Partial<PD.Values<P>>, volume: VolumeData) {
+    function prepareUpdate(theme: Theme, props: Partial<PD.Values<P>>, volume: Volume) {
         if (!volume && !currentVolume) {
             throw new Error('missing volume');
         }
@@ -84,7 +84,7 @@ export function VolumeVisual<G extends Geometry, P extends VolumeParams & Geomet
 
         if (!renderObject) {
             updateState.createNew = true;
-        } else if (!currentVolume || !VolumeData.areEquivalent(newVolume, currentVolume)) {
+        } else if (!currentVolume || !Volume.areEquivalent(newVolume, currentVolume)) {
             updateState.createNew = true;
         }
 
@@ -158,7 +158,7 @@ export function VolumeVisual<G extends Geometry, P extends VolumeParams & Geomet
     return {
         get groupCount() { return locationIt ? locationIt.count : 0; },
         get renderObject () { return renderObject; },
-        async createOrUpdate(ctx: VisualContext, theme: Theme, props: Partial<PD.Values<P>> = {}, volume?: VolumeData) {
+        async createOrUpdate(ctx: VisualContext, theme: Theme, props: Partial<PD.Values<P>> = {}, volume?: Volume) {
             prepareUpdate(theme, props, volume || currentVolume);
             if (updateState.createGeometry) {
                 const newGeometry = createGeometry(ctx, newVolume, newTheme, newProps, geometry);
@@ -198,9 +198,9 @@ export function VolumeVisual<G extends Geometry, P extends VolumeParams & Geomet
     };
 }
 
-export interface VolumeRepresentation<P extends VolumeParams> extends Representation<VolumeData, P> { }
+export interface VolumeRepresentation<P extends VolumeParams> extends Representation<Volume, P> { }
 
-export type VolumeRepresentationProvider<P extends VolumeParams, Id extends string = string> = RepresentationProvider<VolumeData, P, Representation.State, Id>
+export type VolumeRepresentationProvider<P extends VolumeParams, Id extends string = string> = RepresentationProvider<Volume, P, Representation.State, Id>
 export function VolumeRepresentationProvider<P extends VolumeParams, Id extends string>(p: VolumeRepresentationProvider<P, Id>): VolumeRepresentationProvider<P, Id> { return p; }
 
 //
@@ -210,7 +210,7 @@ export const VolumeParams = {
 };
 export type VolumeParams = typeof VolumeParams
 
-export function VolumeRepresentation<P extends VolumeParams>(label: string, ctx: RepresentationContext, getParams: RepresentationParamsGetter<VolumeData, P>, visualCtor: (materialId: number) => VolumeVisual<P>, getLoci: (volume: VolumeData, props: PD.Values<P>) => Loci): VolumeRepresentation<P> {
+export function VolumeRepresentation<P extends VolumeParams>(label: string, ctx: RepresentationContext, getParams: RepresentationParamsGetter<Volume, P>, visualCtor: (materialId: number) => VolumeVisual<P>, getLoci: (volume: Volume, props: PD.Values<P>) => Loci): VolumeRepresentation<P> {
     let version = 0;
     const updated = new Subject<number>();
     const materialId = getNextMaterialId();
@@ -218,12 +218,12 @@ export function VolumeRepresentation<P extends VolumeParams>(label: string, ctx:
     const _state = Representation.createState();
     let visual: VolumeVisual<P> | undefined;
 
-    let _volume: VolumeData;
+    let _volume: Volume;
     let _params: P;
     let _props: PD.Values<P>;
     let _theme = Theme.createEmpty();
 
-    function createOrUpdate(props: Partial<PD.Values<P>> = {}, volume?: VolumeData) {
+    function createOrUpdate(props: Partial<PD.Values<P>> = {}, volume?: Volume) {
         if (volume && volume !== _volume) {
             _params = getParams(ctx, volume);
             _volume = volume;

+ 32 - 33
src/mol-repr/volume/slice.ts

@@ -7,14 +7,13 @@
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { Image } from '../../mol-geo/geometry/image/image';
 import { ThemeRegistryContext, Theme } from '../../mol-theme/theme';
-import { VolumeData, VolumeIsoValue } from '../../mol-model/volume';
+import { Grid, Volume } from '../../mol-model/volume';
 import { VolumeVisual, VolumeRepresentation, VolumeRepresentationProvider } from './representation';
 import { LocationIterator } from '../../mol-geo/util/location-iterator';
 import { VisualUpdateState } from '../util';
 import { NullLocation } from '../../mol-model/location';
 import { RepresentationContext, RepresentationParamsGetter } from '../representation';
 import { VisualContext } from '../visual';
-import { Volume } from '../../mol-model/volume/volume';
 import { PickingId } from '../../mol-geo/geometry/picking';
 import { EmptyLoci, Loci } from '../../mol-model/loci';
 import { Interval, OrderedSet, SortedArray } from '../../mol-data/int';
@@ -25,12 +24,12 @@ import { createIsoValueParam, IsoValueParam } from './isosurface';
 import { Color } from '../../mol-util/color';
 import { ColorTheme } from '../../mol-theme/color';
 
-export async function createImage(ctx: VisualContext, volume: VolumeData, theme: Theme, props: PD.Values<SliceParams>, image?: Image) {
+export async function createImage(ctx: VisualContext, volume: Volume, theme: Theme, props: PD.Values<SliceParams>, image?: Image) {
     const { dimension: { name: dim }, isoValue } = props;
 
-    const { space, data: data } = volume.data;
-    const { min, max } = volume.dataStats;
-    const isoVal = VolumeIsoValue.toAbsolute(isoValue, volume.dataStats).absoluteValue;
+    const { space, data } = volume.grid.cells;
+    const { min, max } = volume.grid.stats;
+    const isoVal = Volume.IsoValue.toAbsolute(isoValue, volume.grid.stats).absoluteValue;
 
     // TODO more color themes
     const color = theme.color.color(NullLocation, false);
@@ -41,7 +40,7 @@ export async function createImage(ctx: VisualContext, volume: VolumeData, theme:
         x, y, z,
         x0, y0, z0,
         nx, ny, nz
-    } = getSliceInfo(volume, props);
+    } = getSliceInfo(volume.grid, props);
 
     const corners = new Float32Array(
         dim === 'x' ? [x, 0, 0,  x, y, 0,  x, 0, z,  x, y, z] :
@@ -50,7 +49,7 @@ export async function createImage(ctx: VisualContext, volume: VolumeData, theme:
     );
 
     const imageArray = new Float32Array(width * height * 4);
-    const groupArray = getGroupArray(volume, props);
+    const groupArray = getGroupArray(volume.grid, props);
 
     let i = 0;
     for (let iy = y0; iy < ny; ++iy) {
@@ -72,15 +71,15 @@ export async function createImage(ctx: VisualContext, volume: VolumeData, theme:
     const imageTexture = { width, height, array: imageArray, flipY: true };
     const groupTexture = { width, height, array: groupArray, flipY: true };
 
-    const transform = VolumeData.getGridToCartesianTransform(volume);
+    const transform = Grid.getGridToCartesianTransform(volume.grid);
     transformPositionArray(transform, corners, 0, 4);
 
     return Image.create(imageTexture, corners, groupTexture, image);
 }
 
-function getSliceInfo(volume: VolumeData, props: PD.Values<SliceParams>) {
+function getSliceInfo(grid: Grid, props: PD.Values<SliceParams>) {
     const { dimension: { name: dim, params: index } } = props;
-    const { space } = volume.data;
+    const { space } = grid.cells;
 
     let width, height;
     let x, y, z;
@@ -108,9 +107,9 @@ function getSliceInfo(volume: VolumeData, props: PD.Values<SliceParams>) {
     };
 }
 
-function getGroupArray(volume: VolumeData, props: PD.Values<SliceParams>) {
-    const { space } = volume.data;
-    const { width, height, x0, y0, z0, nx, ny, nz } = getSliceInfo(volume, props);
+function getGroupArray(grid: Grid, props: PD.Values<SliceParams>) {
+    const { space } = grid.cells;
+    const { width, height, x0, y0, z0, nx, ny, nz } = getSliceInfo(grid, props);
     const groupArray = new Float32Array(width * height);
 
     let j = 0;
@@ -125,13 +124,13 @@ function getGroupArray(volume: VolumeData, props: PD.Values<SliceParams>) {
     return groupArray;
 }
 
-function getLoci(volume: VolumeData, props: PD.Values<SliceParams>) {
+function getLoci(volume: Volume, props: PD.Values<SliceParams>) {
     // TODO cache somehow?
-    const groupArray = getGroupArray(volume, props);
+    const groupArray = getGroupArray(volume.grid, props);
     return Volume.Cell.Loci(volume, SortedArray.ofUnsortedArray(groupArray));
 }
 
-function getSliceLoci(pickingId: PickingId, volume: VolumeData, props: PD.Values<SliceParams>, id: number) {
+function getSliceLoci(pickingId: PickingId, volume: Volume, props: PD.Values<SliceParams>, id: number) {
     const { objectId, groupId } = pickingId;
     if (id === objectId) {
         return Volume.Cell.Loci(volume, Interval.ofSingleton(groupId as Volume.CellIndex));
@@ -139,24 +138,24 @@ function getSliceLoci(pickingId: PickingId, volume: VolumeData, props: PD.Values
     return EmptyLoci;
 }
 
-function eachSlice(loci: Loci, volume: VolumeData, props: PD.Values<SliceParams>, apply: (interval: Interval) => boolean) {
+function eachSlice(loci: Loci, volume: Volume, props: PD.Values<SliceParams>, apply: (interval: Interval) => boolean) {
     let changed = false;
     if (Volume.isLoci(loci)) {
-        if (!VolumeData.areEquivalent(loci.volume, volume)) return false;
-        if (apply(Interval.ofLength(volume.data.data.length))) changed = true;
+        if (!Volume.areEquivalent(loci.volume, volume)) return false;
+        if (apply(Interval.ofLength(volume.grid.cells.data.length))) changed = true;
     } else if (Volume.Isosurface.isLoci(loci)) {
-        if (!VolumeData.areEquivalent(loci.volume, volume)) return false;
+        if (!Volume.areEquivalent(loci.volume, volume)) return false;
         // TODO find a cheaper way?
-        const { dataStats, data: { data } } = volume;
-        const eps = dataStats.sigma;
-        const v = VolumeIsoValue.toAbsolute(loci.isoValue, dataStats).absoluteValue;
+        const { stats, cells: { data } } = volume.grid;
+        const eps = stats.sigma;
+        const v = Volume.IsoValue.toAbsolute(loci.isoValue, stats).absoluteValue;
         for (let i = 0, il = data.length; i < il; ++i) {
             if (equalEps(v, data[i], eps)) {
                 if (apply(Interval.ofSingleton(i))) changed = true;
             }
         }
     } else if (Volume.Cell.isLoci(loci)) {
-        if (!VolumeData.areEquivalent(loci.volume, volume)) return false;
+        if (!Volume.areEquivalent(loci.volume, volume)) return false;
         if (Interval.is(loci.indices)) {
             if (apply(loci.indices)) changed = true;
         } else {
@@ -181,15 +180,15 @@ export const SliceParams = {
     isoValue: IsoValueParam,
 };
 export type SliceParams = typeof SliceParams
-export function getSliceParams(ctx: ThemeRegistryContext, volume: VolumeData) {
+export function getSliceParams(ctx: ThemeRegistryContext, volume: Volume) {
     const p = PD.clone(SliceParams);
-    const dim = volume.data.space.dimensions;
+    const dim = volume.grid.cells.space.dimensions;
     p.dimension = PD.MappedStatic('x', {
         x: PD.Numeric(0, { min: 0, max: dim[0] - 1, step: 1 }),
         y: PD.Numeric(0, { min: 0, max: dim[1] - 1, step: 1 }),
         z: PD.Numeric(0, { min: 0, max: dim[2] - 1, step: 1 }),
     }, { isEssential: true });
-    p.isoValue = createIsoValueParam(VolumeIsoValue.absolute(volume.dataStats.min), volume.dataStats);
+    p.isoValue = createIsoValueParam(Volume.IsoValue.absolute(volume.grid.stats.min), volume.grid.stats);
     return p;
 }
 
@@ -197,14 +196,14 @@ export function SliceVisual(materialId: number): VolumeVisual<SliceParams> {
     return VolumeVisual<Image, SliceParams>({
         defaultProps: PD.getDefaultValues(SliceParams),
         createGeometry: createImage,
-        createLocationIterator: (volume: VolumeData) => LocationIterator(volume.data.data.length, 1, () => NullLocation),
+        createLocationIterator: (volume: Volume) => LocationIterator(volume.grid.cells.data.length, 1, () => NullLocation),
         getLoci: getSliceLoci,
         eachLocation: eachSlice,
-        setUpdateState: (state: VisualUpdateState, volume: VolumeData, newProps: PD.Values<SliceParams>, currentProps: PD.Values<SliceParams>, newTheme: Theme, currentTheme: Theme) => {
+        setUpdateState: (state: VisualUpdateState, volume: Volume, newProps: PD.Values<SliceParams>, currentProps: PD.Values<SliceParams>, newTheme: Theme, currentTheme: Theme) => {
             state.createGeometry = (
                 newProps.dimension.name !== currentProps.dimension.name ||
                 newProps.dimension.params !== currentProps.dimension.params ||
-                !VolumeIsoValue.areSame(newProps.isoValue, currentProps.isoValue, volume.dataStats) ||
+                !Volume.IsoValue.areSame(newProps.isoValue, currentProps.isoValue, volume.grid.stats) ||
                 !ColorTheme.areEqual(newTheme.color, currentTheme.color)
             );
         },
@@ -226,7 +225,7 @@ function updateRenderableState(state: RenderableState, props: PD.Values<SlicePar
     state.writeDepth = true;
 }
 
-export function SliceRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<VolumeData, SliceParams>): VolumeRepresentation<SliceParams> {
+export function SliceRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<Volume, SliceParams>): VolumeRepresentation<SliceParams> {
     return VolumeRepresentation('Slice', ctx, getParams, SliceVisual, getLoci);
 }
 
@@ -239,5 +238,5 @@ export const SliceRepresentationProvider = VolumeRepresentationProvider({
     defaultValues: PD.getDefaultValues(SliceParams),
     defaultColorTheme: { name: 'uniform' },
     defaultSizeTheme: { name: 'uniform' },
-    isApplicable: (volume: VolumeData) => volume.data.data.length > 0
+    isApplicable: (volume: Volume) => !Volume.isEmpty(volume)
 });

+ 2 - 1
src/mol-script/runtime/query/base.ts

@@ -5,8 +5,9 @@
  */
 
 import Expression from '../../language/expression';
-import { QueryContext, QueryFn, Structure, CustomPropertyDescriptor } from '../../../mol-model/structure';
+import { QueryContext, QueryFn, Structure } from '../../../mol-model/structure';
 import { MSymbol } from '../../language/symbol';
+import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
 
 export class QueryRuntimeTable {
     private map = new Map<string, QuerySymbolRuntime>();

+ 5 - 5
src/mol-theme/label.ts

@@ -12,7 +12,7 @@ import { capitalize, stripTags } from '../mol-util/string';
 import { Column } from '../mol-data/db';
 import { Vec3 } from '../mol-math/linear-algebra';
 import { radToDeg } from '../mol-math/misc';
-import { VolumeIsoValue } from '../mol-model/volume';
+import { Volume } from '../mol-model/volume';
 
 export type LabelGranularity = 'element' | 'conformation' | 'residue' | 'chain' | 'structure'
 
@@ -51,19 +51,19 @@ export function lociLabel(loci: Loci, options: Partial<LabelOptions> = {}): stri
         case 'isosurface-loci':
             return [
                 `${loci.volume.label || 'Volume'}`,
-                `Isosurface at ${VolumeIsoValue.toString(loci.isoValue)}`
+                `Isosurface at ${Volume.IsoValue.toString(loci.isoValue)}`
             ].join(' | ');
         case 'cell-loci':
             const size = OrderedSet.size(loci.indices);
             const start = OrderedSet.start(loci.indices);
-            const absVal = VolumeIsoValue.absolute(loci.volume.data.data[start]);
-            const relVal = VolumeIsoValue.toRelative(absVal, loci.volume.dataStats);
+            const absVal = Volume.IsoValue.absolute(loci.volume.grid.cells.data[start]);
+            const relVal = Volume.IsoValue.toRelative(absVal, loci.volume.grid.stats);
             const label = [
                 `${loci.volume.label || 'Volume'}`,
                 `${size === 1 ? `Cell #${start}` : `${size} Cells`}`
             ];
             if (size === 1) {
-                label.push(`${VolumeIsoValue.toString(absVal)} (${VolumeIsoValue.toString(relVal)})`);
+                label.push(`${Volume.IsoValue.toString(absVal)} (${Volume.IsoValue.toString(relVal)})`);
             }
             return label.join(' | ');
     }

+ 2 - 2
src/mol-theme/theme.ts

@@ -7,7 +7,7 @@
 import { ColorTheme } from './color';
 import { SizeTheme } from './size';
 import { Structure } from '../mol-model/structure';
-import { VolumeData } from '../mol-model/volume';
+import { Volume } from '../mol-model/volume';
 import { ParamDefinition as PD } from '../mol-util/param-definition';
 import { Shape } from '../mol-model/shape';
 import { CustomProperty } from '../mol-model-props/common/custom-property';
@@ -21,7 +21,7 @@ export interface ThemeRegistryContext {
 export interface ThemeDataContext {
     [k: string]: any
     structure?: Structure
-    volume?: VolumeData
+    volume?: Volume
     shape?: Shape
 }
 

+ 2 - 1
src/perf-tests/mol-script.ts

@@ -1,6 +1,6 @@
 import { MolScriptBuilder } from '../mol-script/language/builder';
 import { compile, QuerySymbolRuntime, DefaultQueryRuntimeTable } from '../mol-script/runtime/query/compiler';
-import { QueryContext, Structure, StructureQuery, CustomPropertyDescriptor } from '../mol-model/structure';
+import { QueryContext, Structure, StructureQuery } from '../mol-model/structure';
 import { readCifFile, getModelsAndStructure } from '../apps/structure-info/model';
 import { CustomPropSymbol } from '../mol-script/language/symbol';
 import Type from '../mol-script/language/type';
@@ -10,6 +10,7 @@ import { transpileMolScript } from '../mol-script/script/mol-script/symbols';
 import { formatMolScript } from '../mol-script/language/expression-formatter';
 import { StructureQualityReport, StructureQualityReportProvider } from '../extensions/pdbe/structure-quality-report/prop';
 import fetch from 'node-fetch';
+import { CustomPropertyDescriptor } from '../mol-model/custom-property';
 
 // import Examples from 'mol-script/script/mol-script/examples'
 // import { parseMolScript } from 'mol-script/script/mol-script/parser'