Bladeren bron

geometry refactoring

Alexander Rose 6 jaren geleden
bovenliggende
commit
bcec3f8c21

+ 3 - 3
src/mol-canvas3d/helper/bounding-sphere-helper.ts

@@ -4,7 +4,7 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { createMeshRenderObject, RenderObject } from 'mol-gl/render-object'
+import { createRenderObject, RenderObject } from 'mol-gl/render-object'
 import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
 import { addSphere } from 'mol-geo/geometry/mesh/builder/sphere';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
@@ -135,6 +135,6 @@ function createBoundingSphereMesh(boundingSphere: Sphere3D, mesh?: Mesh) {
 }
 
 function createBoundingSphereRenderObject(mesh: Mesh, color: Color, transform?: TransformData) {
-    const values = Mesh.createValuesSimple(mesh, { alpha: 0.1, doubleSided: false }, color, transform)
-    return createMeshRenderObject(values, { visible: true, pickable: false, opaque: false })
+    const values = Mesh.Utils.createValuesSimple(mesh, { alpha: 0.1, doubleSided: false }, color, 1, transform)
+    return createRenderObject('mesh', values, { visible: true, pickable: false, opaque: false })
 }

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

@@ -11,7 +11,7 @@ import { Vec2, Vec3 } from 'mol-math/linear-algebra';
 import { LocationIterator } from '../util/location-iterator';
 import { NullLocation } from 'mol-model/location';
 import { LocationColor, ColorTheme } from 'mol-theme/color';
-import { getGranularity } from './geometry';
+import { Geometry } from './geometry';
 
 export type ColorType = 'uniform' | 'instance' | 'group' | 'groupInstance'
 
@@ -23,7 +23,7 @@ export type ColorData = {
 }
 
 export function createColors(locationIt: LocationIterator, colorTheme: ColorTheme<any>, colorData?: ColorData): ColorData {
-    switch (getGranularity(locationIt, colorTheme.granularity)) {
+    switch (Geometry.getGranularity(locationIt, colorTheme.granularity)) {
         case 'uniform': return createUniformColor(locationIt, colorTheme.color, colorData)
         case 'group': return createGroupColor(locationIt, colorTheme.color, colorData)
         case 'groupInstance': return createGroupInstanceColor(locationIt, colorTheme.color, colorData)

+ 29 - 10
src/mol-geo/geometry/direct-volume/direct-volume.ts

@@ -16,12 +16,14 @@ import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { TransformData } from '../transform-data';
 import { createColors } from '../color-data';
 import { createMarkers } from '../marker-data';
-import { Geometry } from '../geometry';
+import { GeometryUtils } from '../geometry';
 import { transformPositionArray } from 'mol-geo/util';
 import { calculateBoundingSphere } from 'mol-gl/renderable/util';
 import { Theme } from 'mol-theme/theme';
 import { RenderableState } from 'mol-gl/renderable';
 import { ColorListOptions, ColorListName } from 'mol-util/color/scale';
+import { Color } from 'mol-util/color';
+import { BaseGeometry } from '../base';
 
 const VolumeBox = Box()
 const RenderModeOptions = [['isosurface', 'Isosurface'], ['volume', 'Volume']] as [string, string][]
@@ -70,7 +72,7 @@ export namespace DirectVolume {
     }
 
     export const Params = {
-        ...Geometry.Params,
+        ...BaseGeometry.Params,
         isoValue: PD.Numeric(0.22, { min: -1, max: 1, step: 0.01 }),
         renderMode: PD.Select('volume', RenderModeOptions),
         controlPoints: PD.LineGraph([
@@ -81,7 +83,18 @@ export namespace DirectVolume {
     }
     export type Params = typeof Params
 
-    export function createValues(directVolume: DirectVolume, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: PD.Values<Params>): DirectVolumeValues {
+    export const Utils: GeometryUtils<DirectVolume, Params> = {
+        Params,
+        createEmpty,
+        createValues,
+        createValuesSimple,
+        updateValues,
+        updateBoundingSphere,
+        createRenderableState,
+        updateRenderableState
+    }
+
+    function createValues(directVolume: DirectVolume, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: PD.Values<Params>): DirectVolumeValues {
         const { gridTexture, gridTextureDim } = directVolume
         const { bboxSize, bboxMin, bboxMax, gridDimension, transform: gridTransform } = directVolume
 
@@ -102,7 +115,7 @@ export namespace DirectVolume {
             ...color,
             ...marker,
             ...transform,
-            ...Geometry.createValues(props, counts),
+            ...BaseGeometry.createValues(props, counts),
 
             aPosition: ValueCell.create(VolumeBox.vertices as Float32Array),
             elements: ValueCell.create(VolumeBox.indices as Uint32Array),
@@ -125,7 +138,13 @@ export namespace DirectVolume {
         }
     }
 
-    export function updateValues(values: DirectVolumeValues, props: PD.Values<Params>) {
+    function createValuesSimple(directVolume: DirectVolume, props: Partial<PD.Values<Params>>, colorValue: Color, sizeValue: number, transform?: TransformData) {
+        const s = BaseGeometry.createSimple(colorValue, sizeValue, transform)
+        const p = { ...PD.getDefaultValues(Params), props }
+        return createValues(directVolume, s.transform, s.locationIterator, s.theme, p)
+    }
+
+    function updateValues(values: DirectVolumeValues, props: PD.Values<Params>) {
         ValueCell.updateIfChanged(values.uIsoValue, props.isoValue)
         ValueCell.updateIfChanged(values.uAlpha, props.alpha)
         ValueCell.updateIfChanged(values.dUseFog, props.useFog)
@@ -135,7 +154,7 @@ export namespace DirectVolume {
         createTransferFunctionTexture(controlPoints, props.list, values.tTransferTex)
     }
 
-    export function updateBoundingSphere(values: DirectVolumeValues, directVolume: DirectVolume) {
+    function updateBoundingSphere(values: DirectVolumeValues, directVolume: DirectVolume) {
         const { boundingSphere, invariantBoundingSphere } = getBoundingSphere(values.uGridDim.ref.value, values.uTransform.ref.value, values.aTransform.ref.value, values.instanceCount.ref.value)
         if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
             ValueCell.update(values.boundingSphere, boundingSphere)
@@ -145,14 +164,14 @@ export namespace DirectVolume {
         }
     }
 
-    export function createRenderableState(props: PD.Values<Params>): RenderableState {
-        const state = Geometry.createRenderableState(props)
+    function createRenderableState(props: PD.Values<Params>): RenderableState {
+        const state = BaseGeometry.createRenderableState(props)
         state.opaque = false
         return state
     }
 
-    export function updateRenderableState(state: RenderableState, props: PD.Values<Params>) {
-        Geometry.updateRenderableState(state, props)
+    function updateRenderableState(state: RenderableState, props: PD.Values<Params>) {
+        BaseGeometry.updateRenderableState(state, props)
         state.opaque = false
     }
 }

+ 52 - 71
src/mol-geo/geometry/geometry.ts

@@ -6,9 +6,8 @@
 
 import { Mesh } from './mesh/mesh';
 import { Points } from './points/points';
+import { Text } from './text/text';
 import { RenderableState } from 'mol-gl/renderable';
-import { ValueCell } from 'mol-util';
-import { BaseValues } from 'mol-gl/renderable/schema';
 import { LocationIterator } from '../util/location-iterator';
 import { ColorType } from './color-data';
 import { SizeType } from './size-data';
@@ -16,102 +15,84 @@ import { Lines } from './lines/lines';
 import { ParamDefinition as PD } from 'mol-util/param-definition'
 import { DirectVolume } from './direct-volume/direct-volume';
 import { Color } from 'mol-util/color';
-import { Vec3 } from 'mol-math/linear-algebra';
 import { Spheres } from './spheres/spheres';
-
-//
-
-export const VisualQualityInfo = {
-    'custom': {},
-    'auto': {},
-    'highest': {},
-    'higher': {},
-    'high': {},
-    'medium': {},
-    'low': {},
-    'lower': {},
-    'lowest': {},
-}
-export type VisualQuality = keyof typeof VisualQualityInfo
-export const VisualQualityNames = Object.keys(VisualQualityInfo)
-export const VisualQualityOptions = VisualQualityNames.map(n => [n, n] as [VisualQuality, string])
-
-//
+import { arrayMax } from 'mol-util/array';
+import { TransformData } from './transform-data';
+import { Theme } from 'mol-theme/theme';
+import { RenderObjectValuesType } from 'mol-gl/render-object';
 
 export type GeometryKindType = {
     'mesh': Mesh,
     'points': Points,
     'spheres': Spheres,
+    'text': Text,
     'lines': Lines,
     'direct-volume': DirectVolume,
 }
+export type GeometryKindParams = {
+    'mesh': Mesh.Params,
+    'points': Points.Params,
+    'spheres': Spheres.Params,
+    'text': Text.Params,
+    'lines': Lines.Params,
+    'direct-volume': DirectVolume.Params,
+}
 export type GeometryKind = keyof GeometryKindType
 export type Geometry = Helpers.ValueOf<GeometryKindType>
 
+export interface GeometryUtils<G extends Geometry, P extends PD.Params = GeometryKindParams[G['kind']], V = RenderObjectValuesType[G['kind']]> {
+    Params: P
+    createEmpty(geometry?: G): G
+    createValues(geometry: G, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: PD.Values<P>): V
+    createValuesSimple(geometry: G, props: Partial<PD.Values<P>>, colorValue: Color, sizeValue: number, transform?: TransformData): V
+    updateValues(values: V, props: PD.Values<P>): void
+    updateBoundingSphere(values: V, geometry: G): void
+    createRenderableState(props: Partial<PD.Values<P>>): RenderableState
+    updateRenderableState(state: RenderableState, props: PD.Values<P>): void
+}
+
 export namespace Geometry {
-    export function getDrawCount(geometry: Geometry) {
+    export type Params<G extends Geometry> = GeometryKindParams[G['kind']]
+
+    export function getDrawCount(geometry: Geometry): number {
         switch (geometry.kind) {
             case 'mesh': return geometry.triangleCount * 3
             case 'points': return geometry.pointCount
             case 'spheres': return geometry.sphereCount * 2 * 3
+            case 'text': return geometry.charCount * 2 * 3
             case 'lines': return geometry.lineCount * 2 * 3
             case 'direct-volume': return 12 * 3
         }
     }
 
-    //
-
-    export const Params = {
-        alpha: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }, { label: 'Opacity' }),
-        useFog: PD.Boolean(true),
-        highlightColor: PD.Color(Color.fromNormalizedRgb(1.0, 0.4, 0.6)),
-        selectColor: PD.Color(Color.fromNormalizedRgb(0.2, 1.0, 0.1)),
-
-        quality: PD.Select<VisualQuality>('auto', VisualQualityOptions),
-    }
-    export type Params = typeof Params
-
-    export type Counts = { drawCount: number, groupCount: number, instanceCount: number }
-
-    export function createValues(props: PD.Values<Params>, counts: Counts) {
-        return {
-            uAlpha: ValueCell.create(props.alpha),
-            uHighlightColor: ValueCell.create(Color.toArrayNormalized(props.highlightColor, Vec3.zero(), 0)),
-            uSelectColor: ValueCell.create(Color.toArrayNormalized(props.selectColor, Vec3.zero(), 0)),
-            uGroupCount: ValueCell.create(counts.groupCount),
-            drawCount: ValueCell.create(counts.drawCount),
-            dUseFog: ValueCell.create(props.useFog),
-        }
-    }
-
-    export function updateValues(values: BaseValues, props: PD.Values<Params>) {
-        if (Color.fromNormalizedArray(values.uHighlightColor.ref.value, 0) !== props.highlightColor) {
-            ValueCell.update(values.uHighlightColor, Color.toArrayNormalized(props.highlightColor, values.uHighlightColor.ref.value, 0))
-        }
-        if (Color.fromNormalizedArray(values.uSelectColor.ref.value, 0) !== props.selectColor) {
-            ValueCell.update(values.uSelectColor, Color.toArrayNormalized(props.selectColor, values.uSelectColor.ref.value, 0))
+    export function getGroupCount(geometry: Geometry): number {
+        switch (geometry.kind) {
+            case 'mesh':
+            case 'points':
+            case 'spheres':
+            case 'text':
+            case 'lines':
+                return arrayMax(geometry.groupBuffer.ref.value) + 1
+            case 'direct-volume':
+                return 1
         }
-        ValueCell.updateIfChanged(values.uAlpha, props.alpha)
-        ValueCell.updateIfChanged(values.dUseFog, props.useFog)
     }
 
-    export function createRenderableState(props: Partial<PD.Values<Params>> = {}): RenderableState {
-        return {
-            visible: true,
-            pickable: true,
-            opaque: props.alpha === undefined ? true : props.alpha === 1
+    export function getUtils<K extends GeometryKind>(kind: K): GeometryUtils<GeometryKindType[K]> {
+        switch (kind) {
+            case 'mesh': return Mesh.Utils
+            case 'points': return Points.Utils
+            case 'spheres': return Spheres.Utils
+            case 'text': return Text.Utils
+            case 'lines': return Lines.Utils
+            case 'direct-volume': return DirectVolume.Utils
         }
+        throw new Error('unknown geometry kind')
     }
 
-    export function updateRenderableState(state: RenderableState, props: PD.Values<Params>) {
-        state.opaque = props.alpha === 1
+    export function getGranularity(locationIt: LocationIterator, granularity: ColorType | SizeType) {
+        // Always use 'group' granularity for 'complex' location iterators,
+        // i.e. for which an instance may include multiple units
+        return granularity === 'instance' && locationIt.isComplex ? 'group' : granularity
     }
-}
-
-//
-
-export function getGranularity(locationIt: LocationIterator, granularity: ColorType | SizeType) {
-    // Always use 'group' granularity for 'complex' location iterators,
-    // i.e. for which an instance may include multiple units
-    return granularity === 'instance' && locationIt.isComplex ? 'group' : granularity
 }

+ 26 - 7
src/mol-geo/geometry/lines/lines.ts

@@ -7,7 +7,7 @@
 import { ValueCell } from 'mol-util'
 import { Mat4 } from 'mol-math/linear-algebra'
 import { transformPositionArray/* , transformDirectionArray, getNormalMatrix */ } from '../../util';
-import { Geometry } from '../geometry';
+import { GeometryUtils } from '../geometry';
 import { createColors } from '../color-data';
 import { createMarkers } from '../marker-data';
 import { createSizes } from '../size-data';
@@ -20,6 +20,8 @@ import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { calculateBoundingSphere } from 'mol-gl/renderable/util';
 import { Sphere3D } from 'mol-math/geometry';
 import { Theme } from 'mol-theme/theme';
+import { Color } from 'mol-util/color';
+import { BaseGeometry } from '../base';
 
 /** Wide line */
 export interface Lines {
@@ -93,13 +95,24 @@ export namespace Lines {
     //
 
     export const Params = {
-        ...Geometry.Params,
+        ...BaseGeometry.Params,
         sizeFactor: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }),
         lineSizeAttenuation: PD.Boolean(false),
     }
     export type Params = typeof Params
 
-    export function createValues(lines: Lines, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: PD.Values<Params>): LinesValues {
+    export const Utils: GeometryUtils<Lines, Params> = {
+        Params,
+        createEmpty,
+        createValues,
+        createValuesSimple,
+        updateValues,
+        updateBoundingSphere,
+        createRenderableState: BaseGeometry.createRenderableState,
+        updateRenderableState: BaseGeometry.updateRenderableState
+    }
+
+    function createValues(lines: Lines, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: PD.Values<Params>): LinesValues {
         const { instanceCount, groupCount } = locationIt
         const color = createColors(locationIt, theme.color)
         const size = createSizes(locationIt, theme.size)
@@ -123,7 +136,7 @@ export namespace Lines {
             ...marker,
             ...transform,
 
-            ...Geometry.createValues(props, counts),
+            ...BaseGeometry.createValues(props, counts),
             uSizeFactor: ValueCell.create(props.sizeFactor),
             dLineSizeAttenuation: ValueCell.create(props.lineSizeAttenuation),
             dDoubleSided: ValueCell.create(true),
@@ -131,13 +144,19 @@ export namespace Lines {
         }
     }
 
-    export function updateValues(values: LinesValues, props: PD.Values<Params>) {
-        Geometry.updateValues(values, props)
+    function createValuesSimple(lines: Lines, props: Partial<PD.Values<Params>>, colorValue: Color, sizeValue: number, transform?: TransformData) {
+        const s = BaseGeometry.createSimple(colorValue, sizeValue, transform)
+        const p = { ...PD.getDefaultValues(Params), props }
+        return createValues(lines, s.transform, s.locationIterator, s.theme, p)
+    }
+
+    function updateValues(values: LinesValues, props: PD.Values<Params>) {
+        BaseGeometry.updateValues(values, props)
         ValueCell.updateIfChanged(values.uSizeFactor, props.sizeFactor)
         ValueCell.updateIfChanged(values.dLineSizeAttenuation, props.lineSizeAttenuation)
     }
 
-    export function updateBoundingSphere(values: LinesValues, lines: Lines) {
+    function updateBoundingSphere(values: LinesValues, lines: Lines) {
         const { boundingSphere, invariantBoundingSphere } = getBoundingSphere(values.aStart.ref.value, values.aEnd.ref.value, lines.lineCount,
             values.aTransform.ref.value, values.instanceCount.ref.value)
         if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {

+ 26 - 41
src/mol-geo/geometry/mesh/mesh.ts

@@ -9,17 +9,18 @@ import { ValueCell } from 'mol-util'
 import { Vec3, Mat4 } from 'mol-math/linear-algebra'
 import { Sphere3D } from 'mol-math/geometry'
 import { transformPositionArray/* , transformDirectionArray, getNormalMatrix */ } from '../../util';
-import { Geometry } from '../geometry';
+import { GeometryUtils } from '../geometry';
 import { createMarkers } from '../marker-data';
-import { TransformData, createIdentityTransform } from '../transform-data';
+import { TransformData } from '../transform-data';
 import { LocationIterator } from '../../util/location-iterator';
-import { createColors, createValueColor } from '../color-data';
+import { createColors } from '../color-data';
 import { ChunkedArray } from 'mol-data/util';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { calculateBoundingSphere } from 'mol-gl/renderable/util';
 import { Theme } from 'mol-theme/theme';
 import { MeshValues } from 'mol-gl/renderable/mesh';
-import { ColorNames } from 'mol-util/color/tables';
+import { Color } from 'mol-util/color';
+import { BaseGeometry } from '../base';
 
 export interface Mesh {
     readonly kind: 'mesh',
@@ -341,14 +342,25 @@ export namespace Mesh {
     //
 
     export const Params = {
-        ...Geometry.Params,
+        ...BaseGeometry.Params,
         doubleSided: PD.Boolean(false),
         flipSided: PD.Boolean(false),
         flatShaded: PD.Boolean(false),
     }
     export type Params = typeof Params
 
-    export function createValues(mesh: Mesh, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: PD.Values<Params>): MeshValues {
+    export const Utils: GeometryUtils<Mesh, Params> = {
+        Params,
+        createEmpty,
+        createValues,
+        createValuesSimple,
+        updateValues,
+        updateBoundingSphere,
+        createRenderableState: BaseGeometry.createRenderableState,
+        updateRenderableState: BaseGeometry.updateRenderableState
+    }
+
+    function createValues(mesh: Mesh, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: PD.Values<Params>): MeshValues {
         const { instanceCount, groupCount } = locationIt
         if (instanceCount !== transform.instanceCount.ref.value) {
             throw new Error('instanceCount values in TransformData and LocationIterator differ')
@@ -375,54 +387,27 @@ export namespace Mesh {
             ...marker,
             ...transform,
 
-            ...Geometry.createValues(props, counts),
+            ...BaseGeometry.createValues(props, counts),
             dDoubleSided: ValueCell.create(props.doubleSided),
             dFlatShaded: ValueCell.create(props.flatShaded),
             dFlipSided: ValueCell.create(props.flipSided),
         }
     }
 
-    export function createValuesSimple(mesh: Mesh, props: Partial<PD.Values<Params>>, colorValue = ColorNames.grey, transform?: TransformData): MeshValues {
-        const p = { ...PD.getDefaultValues(Params), ...props }
-        if (!transform) transform = createIdentityTransform()
-        const instanceCount = transform.instanceCount.ref.value
-        const groupCount = 1
-        const color = createValueColor(colorValue)
-        const marker = createMarkers(instanceCount * groupCount)
-
-        const counts = { drawCount: mesh.triangleCount * 3, groupCount, instanceCount }
-
-        const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
-            mesh.vertexBuffer.ref.value, mesh.vertexCount,
-            transform.aTransform.ref.value, instanceCount
-        )
-
-        return {
-            aPosition: mesh.vertexBuffer,
-            aNormal: mesh.normalBuffer,
-            aGroup: mesh.groupBuffer,
-            elements: mesh.indexBuffer,
-            boundingSphere: ValueCell.create(boundingSphere),
-            invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),
-            ...color,
-            ...marker,
-            ...transform,
-
-            ...Geometry.createValues(p, counts),
-            dDoubleSided: ValueCell.create(p.doubleSided),
-            dFlatShaded: ValueCell.create(p.flatShaded),
-            dFlipSided: ValueCell.create(p.flipSided),
-        }
+    function createValuesSimple(mesh: Mesh, props: Partial<PD.Values<Params>>, colorValue: Color, sizeValue: number, transform?: TransformData) {
+        const s = BaseGeometry.createSimple(colorValue, sizeValue, transform)
+        const p = { ...PD.getDefaultValues(Params), props }
+        return createValues(mesh, s.transform, s.locationIterator, s.theme, p)
     }
 
-    export function updateValues(values: MeshValues, props: PD.Values<Params>) {
-        Geometry.updateValues(values, props)
+    function updateValues(values: MeshValues, props: PD.Values<Params>) {
+        BaseGeometry.updateValues(values, props)
         ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided)
         ValueCell.updateIfChanged(values.dFlatShaded, props.flatShaded)
         ValueCell.updateIfChanged(values.dFlipSided, props.flipSided)
     }
 
-    export function updateBoundingSphere(values: MeshValues, mesh: Mesh) {
+    function updateBoundingSphere(values: MeshValues, mesh: Mesh) {
         const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
             values.aPosition.ref.value, mesh.vertexCount,
             values.aTransform.ref.value, values.instanceCount.ref.value

+ 30 - 11
src/mol-geo/geometry/points/points.ts

@@ -7,7 +7,7 @@
 import { ValueCell } from 'mol-util'
 import { Mat4 } from 'mol-math/linear-algebra'
 import { transformPositionArray/* , transformDirectionArray, getNormalMatrix */ } from '../../util';
-import { Geometry } from '../geometry';
+import { GeometryUtils } from '../geometry';
 import { createColors } from '../color-data';
 import { createMarkers } from '../marker-data';
 import { createSizes } from '../size-data';
@@ -19,6 +19,8 @@ import { Sphere3D } from 'mol-math/geometry';
 import { Theme } from 'mol-theme/theme';
 import { PointsValues } from 'mol-gl/renderable/points';
 import { RenderableState } from 'mol-gl/renderable';
+import { Color } from 'mol-util/color';
+import { BaseGeometry } from '../base';
 
 /** Point cloud */
 export interface Points {
@@ -56,7 +58,7 @@ export namespace Points {
     //
 
     export const Params = {
-        ...Geometry.Params,
+        ...BaseGeometry.Params,
         sizeFactor: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }),
         pointSizeAttenuation: PD.Boolean(false),
         pointFilledCircle: PD.Boolean(false),
@@ -64,7 +66,18 @@ export namespace Points {
     }
     export type Params = typeof Params
 
-    export function createValues(points: Points, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: PD.Values<Params>): PointsValues {
+    export const Utils: GeometryUtils<Points, Params> = {
+        Params,
+        createEmpty,
+        createValues,
+        createValuesSimple,
+        updateValues,
+        updateBoundingSphere,
+        createRenderableState,
+        updateRenderableState
+    }
+
+    function createValues(points: Points, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: PD.Values<Params>): PointsValues {
         const { instanceCount, groupCount } = locationIt
         const color = createColors(locationIt, theme.color)
         const size = createSizes(locationIt, theme.size)
@@ -87,7 +100,7 @@ export namespace Points {
             ...marker,
             ...transform,
 
-            ...Geometry.createValues(props, counts),
+            ...BaseGeometry.createValues(props, counts),
             uSizeFactor: ValueCell.create(props.sizeFactor),
             dPointSizeAttenuation: ValueCell.create(props.pointSizeAttenuation),
             dPointFilledCircle: ValueCell.create(props.pointFilledCircle),
@@ -95,15 +108,21 @@ export namespace Points {
         }
     }
 
-    export function updateValues(values: PointsValues, props: PD.Values<Params>) {
-        Geometry.updateValues(values, props)
+    function createValuesSimple(points: Points, props: Partial<PD.Values<Params>>, colorValue: Color, sizeValue: number, transform?: TransformData) {
+        const s = BaseGeometry.createSimple(colorValue, sizeValue, transform)
+        const p = { ...PD.getDefaultValues(Params), props }
+        return createValues(points, s.transform, s.locationIterator, s.theme, p)
+    }
+
+    function updateValues(values: PointsValues, props: PD.Values<Params>) {
+        BaseGeometry.updateValues(values, props)
         ValueCell.updateIfChanged(values.uSizeFactor, props.sizeFactor)
         ValueCell.updateIfChanged(values.dPointSizeAttenuation, props.pointSizeAttenuation)
         ValueCell.updateIfChanged(values.dPointFilledCircle, props.pointFilledCircle)
         ValueCell.updateIfChanged(values.uPointEdgeBleach, props.pointEdgeBleach)
     }
 
-    export function updateBoundingSphere(values: PointsValues, points: Points) {
+    function updateBoundingSphere(values: PointsValues, points: Points) {
         const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
             values.aPosition.ref.value, points.pointCount,
             values.aTransform.ref.value, values.instanceCount.ref.value
@@ -116,14 +135,14 @@ export namespace Points {
         }
     }
 
-    export function createRenderableState(props: PD.Values<Params>): RenderableState {
-        const state = Geometry.createRenderableState(props)
+    function createRenderableState(props: PD.Values<Params>): RenderableState {
+        const state = BaseGeometry.createRenderableState(props)
         updateRenderableState(state, props)
         return state
     }
 
-    export function updateRenderableState(state: RenderableState, props: PD.Values<Params>) {
-        Geometry.updateRenderableState(state, props)
+    function updateRenderableState(state: RenderableState, props: PD.Values<Params>) {
+        BaseGeometry.updateRenderableState(state, props)
         state.opaque = state.opaque && (
             !props.pointFilledCircle ||
             (props.pointFilledCircle && props.pointEdgeBleach === 0)

+ 2 - 2
src/mol-geo/geometry/size-data.ts

@@ -10,7 +10,7 @@ import { TextureImage, createTextureImage } from 'mol-gl/renderable/util';
 import { LocationIterator } from '../util/location-iterator';
 import { Location, NullLocation } from 'mol-model/location';
 import { SizeTheme } from 'mol-theme/size';
-import { getGranularity } from './geometry';
+import { Geometry } from './geometry';
 import { encodeFloatLog } from 'mol-util/float-packing';
 
 export type SizeType = 'uniform' | 'instance' | 'group' | 'groupInstance'
@@ -23,7 +23,7 @@ export type SizeData = {
 }
 
 export function createSizes(locationIt: LocationIterator, sizeTheme: SizeTheme<any>, sizeData?: SizeData): SizeData {
-    switch (getGranularity(locationIt, sizeTheme.granularity)) {
+    switch (Geometry.getGranularity(locationIt, sizeTheme.granularity)) {
         case 'uniform': return createUniformSize(locationIt, sizeTheme.size, sizeData)
         case 'group': return createGroupSize(locationIt, sizeTheme.size, sizeData)
         case 'groupInstance': return createGroupInstanceSize(locationIt, sizeTheme.size, sizeData)

+ 25 - 23
src/mol-geo/geometry/spheres/spheres.ts

@@ -5,21 +5,19 @@
  */
 
 import { ValueCell } from 'mol-util';
-import { Geometry } from '../geometry';
+import { GeometryUtils } from '../geometry';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
-import { TransformData, createIdentityTransform } from '../transform-data';
+import { TransformData } from '../transform-data';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { Theme } from 'mol-theme/theme';
 import { SpheresValues } from 'mol-gl/renderable/spheres';
 import { createColors } from '../color-data';
 import { createMarkers } from '../marker-data';
 import { calculateBoundingSphere } from 'mol-gl/renderable/util';
-import { ColorNames } from 'mol-util/color/tables';
 import { Sphere3D } from 'mol-math/geometry';
 import { createSizes, getMaxSize } from '../size-data';
-import { NullLocation } from 'mol-model/location';
-import { UniformColorTheme } from 'mol-theme/color/uniform';
-import { UniformSizeTheme } from 'mol-theme/size/uniform';
+import { Color } from 'mol-util/color';
+import { BaseGeometry } from '../base';
 
 /** Spheres */
 export interface Spheres {
@@ -55,13 +53,24 @@ export namespace Spheres {
     }
 
     export const Params = {
-        ...Geometry.Params,
+        ...BaseGeometry.Params,
         sizeFactor: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }),
         doubleSided: PD.Boolean(false),
     }
     export type Params = typeof Params
 
-    export function createValues(spheres: Spheres, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: PD.Values<Params>): SpheresValues {
+    export const Utils: GeometryUtils<Spheres, Params> = {
+        Params,
+        createEmpty,
+        createValues,
+        createValuesSimple,
+        updateValues,
+        updateBoundingSphere,
+        createRenderableState: BaseGeometry.createRenderableState,
+        updateRenderableState: BaseGeometry.updateRenderableState
+    }
+
+    function createValues(spheres: Spheres, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: PD.Values<Params>): SpheresValues {
         const { instanceCount, groupCount } = locationIt
         if (instanceCount !== transform.instanceCount.ref.value) {
             throw new Error('instanceCount values in TransformData and LocationIterator differ')
@@ -93,32 +102,25 @@ export namespace Spheres {
 
             padding: ValueCell.create(padding),
 
-            ...Geometry.createValues(props, counts),
+            ...BaseGeometry.createValues(props, counts),
             uSizeFactor: ValueCell.create(props.sizeFactor),
             dDoubleSided: ValueCell.create(props.doubleSided),
         }
     }
 
-    export function createValuesSimple(spheres: Spheres, props: Partial<PD.Values<Params>>, colorValue = ColorNames.grey, sizeValue = 1, transform?: TransformData): SpheresValues {
-
-        if (!transform) transform = createIdentityTransform()
-        const locationIterator = LocationIterator(1, transform.instanceCount.ref.value, () => NullLocation, false, () => false)
-        const theme: Theme = {
-            color: UniformColorTheme({}, { value: colorValue}),
-            size: UniformSizeTheme({}, { value: sizeValue})
-        }
-        const p = { ...PD.getDefaultValues(Params), ...props }
-
-        return createValues(spheres, transform, locationIterator, theme, p)
+    function createValuesSimple(spheres: Spheres, props: Partial<PD.Values<Params>>, colorValue: Color, sizeValue: number, transform?: TransformData) {
+        const s = BaseGeometry.createSimple(colorValue, sizeValue, transform)
+        const p = { ...PD.getDefaultValues(Params), props }
+        return createValues(spheres, s.transform, s.locationIterator, s.theme, p)
     }
 
-    export function updateValues(values: SpheresValues, props: PD.Values<Params>) {
-        Geometry.updateValues(values, props)
+    function updateValues(values: SpheresValues, props: PD.Values<Params>) {
+        BaseGeometry.updateValues(values, props)
         ValueCell.updateIfChanged(values.uSizeFactor, props.sizeFactor)
         ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided)
     }
 
-    export function updateBoundingSphere(values: SpheresValues, spheres: Spheres) {
+    function updateBoundingSphere(values: SpheresValues, spheres: Spheres) {
         const padding = getMaxSize(values)
         const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
             values.aPosition.ref.value, spheres.sphereCount * 4,

+ 34 - 26
src/mol-geo/geometry/text/text.ts

@@ -6,17 +6,14 @@
 
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { ValueCell } from 'mol-util';
-import { Geometry } from '../geometry';
+import { GeometryUtils } from '../geometry';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
-import { TransformData, createIdentityTransform } from '../transform-data';
+import { TransformData } from '../transform-data';
 import { Theme } from 'mol-theme/theme';
 import { createColors } from '../color-data';
 import { createSizes, getMaxSize } from '../size-data';
 import { createMarkers } from '../marker-data';
 import { ColorNames } from 'mol-util/color/tables';
-import { NullLocation } from 'mol-model/location';
-import { UniformColorTheme } from 'mol-theme/color/uniform';
-import { UniformSizeTheme } from 'mol-theme/size/uniform';
 import { Sphere3D } from 'mol-math/geometry';
 import { calculateBoundingSphere, TextureImage, createTextureImage } from 'mol-gl/renderable/util';
 import { TextValues } from 'mol-gl/renderable/text';
@@ -25,6 +22,8 @@ import { Vec3 } from 'mol-math/linear-algebra';
 import { FontAtlasParams } from './font-atlas';
 import { RenderableState } from 'mol-gl/renderable';
 import { clamp } from 'mol-math/interpolate';
+import { createRenderObject as _createRenderObject } from 'mol-gl/render-object';
+import { BaseGeometry } from '../base';
 
 type TextAttachment = 'bottom-left' | 'bottom-center' | 'bottom-right' | 'middle-left' | 'middle-center' | 'middle-right' | 'top-left' | 'top-center' | 'top-right'
 
@@ -70,7 +69,7 @@ export namespace Text {
     }
 
     export const Params = {
-        ...Geometry.Params,
+        ...BaseGeometry.Params,
         ...FontAtlasParams,
         sizeFactor: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }),
 
@@ -88,7 +87,19 @@ export namespace Text {
     }
     export type Params = typeof Params
 
-    export function createValues(text: Text, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: PD.Values<Params>): TextValues {
+    export const Utils: GeometryUtils<Text, Params> = {
+        Params,
+        createEmpty,
+        createValues,
+        createValuesSimple,
+        updateValues,
+        updateBoundingSphere,
+        createRenderableState,
+        updateRenderableState,
+        // createRenderObject
+    }
+
+    function createValues(text: Text, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: PD.Values<Params>): TextValues {
         const { instanceCount, groupCount } = locationIt
         if (instanceCount !== transform.instanceCount.ref.value) {
             throw new Error('instanceCount values in TransformData and LocationIterator differ')
@@ -122,7 +133,7 @@ export namespace Text {
             tFont: text.fontTexture,
             padding: ValueCell.create(padding),
 
-            ...Geometry.createValues(props, counts),
+            ...BaseGeometry.createValues(props, counts),
             uSizeFactor: ValueCell.create(props.sizeFactor),
 
             uBorderWidth: ValueCell.create(clamp(props.borderWidth / 2, 0, 0.5)),
@@ -135,25 +146,18 @@ export namespace Text {
         }
     }
 
-    export function createValuesSimple(text: Text, props: Partial<PD.Values<Params>>, colorValue = ColorNames.grey, sizeValue = 1, transform?: TransformData): TextValues {
-
-        if (!transform) transform = createIdentityTransform()
-        const locationIterator = LocationIterator(1, transform.instanceCount.ref.value, () => NullLocation, false, () => false)
-        const theme: Theme = {
-            color: UniformColorTheme({}, { value: colorValue}),
-            size: UniformSizeTheme({}, { value: sizeValue})
-        }
-        const p = { ...PD.getDefaultValues(Params), ...props }
-
-        return createValues(text, transform, locationIterator, theme, p)
+    function createValuesSimple(text: Text, props: Partial<PD.Values<Params>>, colorValue: Color, sizeValue: number, transform?: TransformData) {
+        const s = BaseGeometry.createSimple(colorValue, sizeValue, transform)
+        const p = { ...PD.getDefaultValues(Params), props }
+        return createValues(text, s.transform, s.locationIterator, s.theme, p)
     }
 
-    export function updateValues(values: TextValues, props: PD.Values<Params>) {
-        Geometry.updateValues(values, props)
+    function updateValues(values: TextValues, props: PD.Values<Params>) {
+        BaseGeometry.updateValues(values, props)
         ValueCell.updateIfChanged(values.uSizeFactor, props.sizeFactor)
     }
 
-    export function updateBoundingSphere(values: TextValues, text: Text) {
+    function updateBoundingSphere(values: TextValues, text: Text) {
         const padding = getMaxSize(values)
         const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
             values.aPosition.ref.value, text.charCount * 4,
@@ -167,14 +171,18 @@ export namespace Text {
         }
     }
 
-    export function createRenderableState(props: PD.Values<Params>): RenderableState {
-        const state = Geometry.createRenderableState(props)
+    function createRenderableState(props: PD.Values<Params>): RenderableState {
+        const state = BaseGeometry.createRenderableState(props)
         updateRenderableState(state, props)
         return state
     }
 
-    export function updateRenderableState(state: RenderableState, props: PD.Values<Params>) {
-        Geometry.updateRenderableState(state, props)
+    function updateRenderableState(state: RenderableState, props: PD.Values<Params>) {
+        BaseGeometry.updateRenderableState(state, props)
         state.opaque = false
     }
+
+    // function createRenderObject(values: TextValues, state: RenderableState) {
+    //     return _createRenderObject('text', values, state)
+    // }
 }

+ 2 - 2
src/mol-gl/_spec/renderer.spec.ts

@@ -15,7 +15,7 @@ import { createValueColor } from 'mol-geo/geometry/color-data';
 import { createValueSize } from 'mol-geo/geometry/size-data';
 import { createContext } from '../webgl/context';
 import { RenderableState } from '../renderable';
-import { createPointsRenderObject } from '../render-object';
+import { createRenderObject } from '../render-object';
 import { PointsValues } from '../renderable/points';
 import Scene from '../scene';
 import { createEmptyMarkers } from 'mol-geo/geometry/marker-data';
@@ -94,7 +94,7 @@ function createPoints() {
         opaque: true
     }
 
-    return createPointsRenderObject(values, state)
+    return createRenderObject('points', values, state)
 }
 
 describe('renderer', () => {

+ 22 - 19
src/mol-gl/render-object.ts

@@ -36,29 +36,32 @@ export type ComputeRenderObject = GaussianDensityRenderObject
 
 export type RenderObject = GraphicsRenderObject | ComputeRenderObject
 
-//
+export type RenderObjectKindType = {
+    'mesh': MeshRenderObject
+    'points': PointsRenderObject
+    'spheres': SpheresRenderObject
+    'text': TextRenderObject
+    'lines': LinesRenderObject
+    'direct-volume': DirectVolumeRenderObject
 
-export function createMeshRenderObject(values: MeshValues, state: RenderableState): MeshRenderObject {
-    return { id: getNextId(), type: 'mesh', values, state }
-}
-export function createPointsRenderObject(values: PointsValues, state: RenderableState): PointsRenderObject {
-    return { id: getNextId(), type: 'points', values, state }
-}
-export function createSpheresRenderObject(values: SpheresValues, state: RenderableState): SpheresRenderObject {
-    return { id: getNextId(), type: 'spheres', values, state }
-}
-export function createTextRenderObject(values: TextValues, state: RenderableState): TextRenderObject {
-    return { id: getNextId(), type: 'text', values, state }
+    'gaussian-density': GaussianDensityRenderObject
 }
-export function createLinesRenderObject(values: LinesValues, state: RenderableState): LinesRenderObject {
-    return { id: getNextId(), type: 'lines', values, state }
-}
-export function createDirectVolumeRenderObject(values: DirectVolumeValues, state: RenderableState): DirectVolumeRenderObject {
-    return { id: getNextId(), type: 'direct-volume', values, state }
+export type RenderObjectValuesType = {
+    'mesh': MeshValues
+    'points': PointsValues
+    'spheres': SpheresValues
+    'text': TextValues
+    'lines': LinesValues
+    'direct-volume': DirectVolumeValues
+
+    'gaussian-density': GaussianDensityValues
 }
+export type RenderObjectType = keyof RenderObjectKindType
+
+//
 
-export function createGaussianDensityRenderObject(values: GaussianDensityValues, state: RenderableState): GaussianDensityRenderObject {
-    return { id: getNextId(), type: 'gaussian-density', values, state }
+export function createRenderObject<T extends RenderObjectType>(type: T, values: RenderObjectValuesType[T], state: RenderableState): RenderObjectKindType[T] {
+    return { id: getNextId(), type, values, state } as RenderObjectKindType[T]
 }
 
 export function createRenderable(ctx: WebGLContext, o: RenderObject): Renderable<any> {

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

@@ -14,7 +14,7 @@ import { Vec3, Tensor, Mat4 } from '../../linear-algebra'
 import { GaussianDensityValues } from 'mol-gl/renderable/gaussian-density'
 import { ValueCell } from 'mol-util'
 import { RenderableState, Renderable } from 'mol-gl/renderable'
-import { createRenderable, createGaussianDensityRenderObject } from 'mol-gl/render-object'
+import { createRenderable, createRenderObject } from 'mol-gl/render-object'
 import { WebGLContext } from 'mol-gl/webgl/context';
 import { createTexture, Texture } from 'mol-gl/webgl/texture';
 import { GLRenderingContext } from 'mol-gl/webgl/compat';
@@ -236,7 +236,7 @@ function getGaussianDensityRenderObject(webgl: WebGLContext, drawCount: number,
         opaque: true
     }
 
-    const renderObject = createGaussianDensityRenderObject(values, state)
+    const renderObject = createRenderObject('gaussian-density', values, state)
 
     return renderObject
 }

+ 18 - 11
src/mol-model/loci.ts

@@ -27,6 +27,7 @@ export function isEmptyLoci(x: any): x is EmptyLoci {
     return !!x && x.kind === 'empty-loci';
 }
 
+/** A generic data loci */
 export interface DataLoci {
     readonly kind: 'data-loci',
     readonly data: any,
@@ -71,12 +72,14 @@ namespace Loci {
 
     const sphereHelper = new CentroidHelper(), tempPos = Vec3.zero();
 
-    export function getBoundingSphere(loci: Loci): Sphere3D | undefined {
+    export function getBoundingSphere(loci: Loci, boundingSphere?: Sphere3D): Sphere3D | undefined {
         if (loci.kind === 'every-loci' || loci.kind === 'empty-loci') return void 0;
 
+        if (!boundingSphere) boundingSphere = Sphere3D.zero()
         sphereHelper.reset();
+
         if (loci.kind === 'structure-loci') {
-            return Sphere3D.clone(loci.structure.boundary.sphere)
+            return Sphere3D.copy(boundingSphere, loci.structure.boundary.sphere)
         } else if (loci.kind === 'element-loci') {
             for (const e of loci.elements) {
                 const { indices } = e;
@@ -99,20 +102,16 @@ namespace Loci {
             }
         } else if (loci.kind === 'link-loci') {
             for (const e of loci.links) {
-                let pos = e.aUnit.conformation.position;
-                pos(e.aUnit.elements[e.aIndex], tempPos);
+                e.aUnit.conformation.position(e.aUnit.elements[e.aIndex], tempPos);
                 sphereHelper.includeStep(tempPos);
-                pos = e.bUnit.conformation.position;
-                pos(e.bUnit.elements[e.bIndex], tempPos);
+                e.bUnit.conformation.position(e.bUnit.elements[e.bIndex], tempPos);
                 sphereHelper.includeStep(tempPos);
             }
             sphereHelper.finishedIncludeStep();
             for (const e of loci.links) {
-                let pos = e.aUnit.conformation.position;
-                pos(e.aUnit.elements[e.aIndex], tempPos);
+                e.aUnit.conformation.position(e.aUnit.elements[e.aIndex], tempPos);
                 sphereHelper.radiusStep(tempPos);
-                pos = e.bUnit.conformation.position;
-                pos(e.bUnit.elements[e.bIndex], tempPos);
+                e.aUnit.conformation.position(e.bUnit.elements[e.bIndex], tempPos);
                 sphereHelper.radiusStep(tempPos);
             }
         } else if (loci.kind === 'group-loci') {
@@ -123,6 +122,14 @@ namespace Loci {
             return void 0;
         }
 
-        return Sphere3D.create(Vec3.clone(sphereHelper.center), Math.sqrt(sphereHelper.radiusSq));
+        Vec3.copy(boundingSphere.center, sphereHelper.center)
+        boundingSphere.radius = Math.sqrt(sphereHelper.radiusSq)
+        return boundingSphere
+    }
+
+    const tmpSphere3D = Sphere3D.zero()
+    export function getCenter(loci: Loci, center?: Vec3): Vec3 | undefined {
+        const boundingSphere = getBoundingSphere(loci, tmpSphere3D)
+        return boundingSphere ? Vec3.copy(center || Vec3.zero(), boundingSphere.center) : undefined
     }
 }

+ 5 - 14
src/mol-model/shape/shape.ts

@@ -4,36 +4,27 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { Color } from 'mol-util/color';
 import { UUID, ValueCell } from 'mol-util';
 import { OrderedSet } from 'mol-data/int';
-import { arrayMax } from 'mol-util/array';
+import { Geometry } from 'mol-geo/geometry/geometry';
 
 export interface Shape {
     readonly id: UUID
     readonly name: string
-    readonly mesh: Mesh
+    readonly geometry: Geometry
     readonly colors: ValueCell<Color[]>
     readonly labels: ValueCell<string[]>
     readonly groupCount: number
 }
 
 export namespace Shape {
-    export function create(name: string, mesh: Mesh, colors: Color[], labels: string[]): Shape {
-        let currentGroupBufferVersion = -1
-        let currentGroupCount = -1
-
+    export function create(name: string, geometry: Geometry, colors: Color[], labels: string[]): Shape {
         return {
             id: UUID.create22(),
             name,
-            mesh,
-            get groupCount() {
-                if (mesh.groupBuffer.ref.version !== currentGroupBufferVersion) {
-                    currentGroupCount = arrayMax(mesh.groupBuffer.ref.value) + 1
-                }
-                return currentGroupCount
-            },
+            geometry,
+            groupCount: Geometry.getGroupCount(geometry),
             colors: ValueCell.create(colors),
             labels: ValueCell.create(labels),
         }

+ 5 - 5
src/mol-repr/representation.ts

@@ -17,7 +17,7 @@ import { SizeTheme } from 'mol-theme/size';
 import { Theme, ThemeRegistryContext, createEmptyTheme } from 'mol-theme/theme';
 import { Subject } from 'rxjs';
 import { Mat4 } from 'mol-math/linear-algebra';
-import { Geometry } from 'mol-geo/geometry/geometry';
+import { BaseGeometry } from 'mol-geo/geometry/base';
 
 // export interface RepresentationProps {
 //     visuals?: string[]
@@ -242,14 +242,14 @@ namespace Representation {
         }
     }
 
-    export function fromRenderObject(label: string, renderObject: GraphicsRenderObject): Representation<GraphicsRenderObject, Geometry.Params> {
+    export function fromRenderObject(label: string, renderObject: GraphicsRenderObject): Representation<GraphicsRenderObject, BaseGeometry.Params> {
         let version = 0
         const updated = new Subject<number>()
         const currentState = Representation.createState()
         const currentTheme = createEmptyTheme()
 
-        const currentParams = PD.clone(Geometry.Params)
-        const currentProps = PD.getDefaultValues(Geometry.Params)
+        const currentParams = PD.clone(BaseGeometry.Params)
+        const currentProps = PD.getDefaultValues(BaseGeometry.Params)
 
         return {
             label,
@@ -258,7 +258,7 @@ namespace Representation {
             get renderObjects() { return [renderObject] },
             get props() { return currentProps },
             get params() { return currentParams },
-            createOrUpdate: (props: Partial<PD.Values<Geometry.Params>> = {}) => {
+            createOrUpdate: (props: Partial<PD.Values<BaseGeometry.Params>> = {}) => {
                 const qualityProps = getQualityProps(Object.assign({}, currentProps, props))
                 Object.assign(currentProps, props, qualityProps)
 

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

@@ -5,7 +5,7 @@
  */
 
 import { Task } from 'mol-task'
-import { RenderObject, createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object';
+import { RenderObject, createRenderObject, MeshRenderObject } from 'mol-gl/render-object';
 import { Representation, RepresentationContext } from '../representation';
 import { Loci, EmptyLoci, isEveryLoci } from 'mol-model/loci';
 import { ValueCell } from 'mol-util';
@@ -14,7 +14,6 @@ import { OrderedSet, Interval } from 'mol-data/int';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { createIdentityTransform } from 'mol-geo/geometry/transform-data';
-import { Geometry } from 'mol-geo/geometry/geometry';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { MarkerAction, applyMarkerAction } from 'mol-geo/geometry/marker-data';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
@@ -51,14 +50,13 @@ export function ShapeRepresentation<P extends ShapeParams>(ctx: RepresentationCo
 
             if (!_shape) return
 
-            const mesh = _shape.mesh
             locationIt = ShapeGroupIterator.fromShape(_shape)
             const transform = createIdentityTransform()
 
-            const values = Mesh.createValues(mesh, transform, locationIt, _theme, currentProps)
-            const state = Geometry.createRenderableState(currentProps)
+            const values = Mesh.Utils.createValues(_shape.mesh, transform, locationIt, _theme, currentProps)
+            const state = Mesh.Utils.createRenderableState(currentProps)
 
-            _renderObject = createMeshRenderObject(values, state)
+            _renderObject = createRenderObject('mesh', values, state)
             renderObjects.push(_renderObject)
             updated.next(version++)
         });

+ 24 - 31
src/mol-repr/structure/complex-visual.ts

@@ -6,16 +6,15 @@
 
 import { Structure } from 'mol-model/structure';
 import { Visual, VisualContext } from '../visual';
-import { MeshRenderObject, LinesRenderObject, PointsRenderObject, DirectVolumeRenderObject } from 'mol-gl/render-object';
-import { createComplexMeshRenderObject, UnitKind, UnitKindOptions, createComplexDirectVolumeRenderObject } from './visual/util/common';
+import { createRenderObject, GraphicsRenderObject } from 'mol-gl/render-object';
+import { UnitKind, UnitKindOptions } from './visual/util/common';
 import { StructureMeshParams, StructureParams, StructureDirectVolumeParams } from './representation';
 import { deepEqual, ValueCell } from 'mol-util';
 import { Loci, isEveryLoci, EmptyLoci } from 'mol-model/loci';
 import { Interval } from 'mol-data/int';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
-import { RenderableValues } from 'mol-gl/renderable/schema';
 import { createSizes } from 'mol-geo/geometry/size-data';
-import { Geometry } from 'mol-geo/geometry/geometry';
+import { Geometry, GeometryUtils } from 'mol-geo/geometry/geometry';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { createColors } from 'mol-geo/geometry/color-data';
@@ -25,21 +24,27 @@ import { VisualUpdateState } from 'mol-repr/util';
 import { Theme, createEmptyTheme } from 'mol-theme/theme';
 import { ColorTheme } from 'mol-theme/color';
 import { SizeTheme } from 'mol-theme/size';
-import { RenderableState } from 'mol-gl/renderable';
 import { UnitsParams } from './units-representation';
 import { DirectVolume } from 'mol-geo/geometry/direct-volume/direct-volume';
 import { Mat4 } from 'mol-math/linear-algebra';
+import { createIdentityTransform } from 'mol-geo/geometry/transform-data';
 
 export interface  ComplexVisual<P extends StructureParams> extends Visual<Structure, P> { }
 
+function createComplexRenderObject(structure: Structure, geometry: Geometry, locationIt: LocationIterator, theme: Theme, props: PD.Values<StructureParams>) {
+    const { createValues, createRenderableState } = Geometry.getUtils(geometry.kind)
+    const transform = createIdentityTransform()
+    const values = createValues(geometry, transform, locationIt, theme, props as any) // TODO
+    const state = createRenderableState(props)
+    return createRenderObject(geometry.kind, values, state)
+}
+
 const ComplexParams = {
     ...StructureParams,
     unitKinds: PD.MultiSelect<UnitKind>(['atomic', 'spheres'], UnitKindOptions),
 }
 type ComplexParams = typeof ComplexParams
 
-type ComplexRenderObject = MeshRenderObject | LinesRenderObject | PointsRenderObject | DirectVolumeRenderObject
-
 interface ComplexVisualBuilder<P extends ComplexParams, G extends Geometry> {
     defaultProps: PD.Values<P>
     createGeometry(ctx: VisualContext, structure: Structure, theme: Theme, props: PD.Values<P>, geometry?: G): Promise<G> | G
@@ -49,20 +54,16 @@ interface ComplexVisualBuilder<P extends ComplexParams, G extends Geometry> {
     setUpdateState(state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme): void
 }
 
-interface ComplexVisualGeometryBuilder<P extends ComplexParams, G extends Geometry> extends ComplexVisualBuilder<P, G> {
-    createEmptyGeometry(geometry?: G): G
-    createRenderObject(structure: Structure, geometry: Geometry, locationIt: LocationIterator, theme: Theme, currentProps: PD.Values<P>): ComplexRenderObject
-    updateValues(values: RenderableValues, newProps: PD.Values<P>): void,
-    updateBoundingSphere(values: RenderableValues, geometry: Geometry): void
-    updateRenderableState(state: RenderableState, props: PD.Values<P>): void
+interface ComplexVisualGeometryBuilder<P extends UnitsParams, G extends Geometry> extends ComplexVisualBuilder<P, G> {
+    geometryUtils: GeometryUtils<G>
 }
 
-export function ComplexVisual<P extends ComplexParams>(builder: ComplexVisualGeometryBuilder<P, Geometry>): ComplexVisual<P> {
+export function ComplexVisual<G extends Geometry, P extends ComplexParams & Geometry.Params<G>>(builder: ComplexVisualGeometryBuilder<P, G>): ComplexVisual<P> {
     const { defaultProps, createGeometry, createLocationIterator, getLoci, mark, setUpdateState } = builder
-    const { createRenderObject, updateValues, updateBoundingSphere, updateRenderableState } = builder
+    const { updateValues, updateBoundingSphere, updateRenderableState } = builder.geometryUtils
     const updateState = VisualUpdateState.create()
 
-    let renderObject: ComplexRenderObject | undefined
+    let renderObject: GraphicsRenderObject | undefined
 
     let newProps: PD.Values<P>
     let newTheme: Theme
@@ -72,7 +73,7 @@ export function ComplexVisual<P extends ComplexParams>(builder: ComplexVisualGeo
     let currentTheme: Theme = createEmptyTheme()
     let currentStructure: Structure
 
-    let geometry: Geometry
+    let geometry: G
     let locationIt: LocationIterator
 
     function prepareUpdate(theme: Theme, props: Partial<PD.Values<P>>, structure: Structure) {
@@ -111,11 +112,11 @@ export function ComplexVisual<P extends ComplexParams>(builder: ComplexVisualGeo
         }
     }
 
-    function update(newGeometry?: Geometry) {
+    function update(newGeometry?: G) {
         if (updateState.createNew) {
             locationIt = createLocationIterator(newStructure)
             if (newGeometry) {
-                renderObject = createRenderObject(newStructure, newGeometry, locationIt, newTheme, newProps)
+                renderObject = createComplexRenderObject(newStructure, newGeometry, locationIt, newTheme, newProps)
             } else {
                 throw new Error('expected geometry to be given')
             }
@@ -220,17 +221,13 @@ export type ComplexMeshParams = typeof ComplexMeshParams
 export interface ComplexMeshVisualBuilder<P extends ComplexMeshParams> extends ComplexVisualBuilder<P, Mesh> { }
 
 export function ComplexMeshVisual<P extends ComplexMeshParams>(builder: ComplexMeshVisualBuilder<P>): ComplexVisual<P> {
-    return ComplexVisual<StructureMeshParams & UnitsParams>({
+    return ComplexVisual<Mesh, StructureMeshParams & UnitsParams>({
         ...builder,
         setUpdateState: (state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme) => {
             builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme)
             if (!SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.createGeometry = true
         },
-        createEmptyGeometry: Mesh.createEmpty,
-        createRenderObject: createComplexMeshRenderObject,
-        updateValues: Mesh.updateValues,
-        updateBoundingSphere: Mesh.updateBoundingSphere,
-        updateRenderableState: Geometry.updateRenderableState
+        geometryUtils: Mesh.Utils
     })
 }
 
@@ -245,16 +242,12 @@ export type ComplexDirectVolumeParams = typeof ComplexDirectVolumeParams
 export interface ComplexDirectVolumeVisualBuilder<P extends ComplexDirectVolumeParams> extends ComplexVisualBuilder<P, DirectVolume> { }
 
 export function ComplexDirectVolumeVisual<P extends ComplexDirectVolumeParams>(builder: ComplexDirectVolumeVisualBuilder<P>): ComplexVisual<P> {
-    return ComplexVisual<StructureDirectVolumeParams & UnitsParams>({
+    return ComplexVisual<DirectVolume, StructureDirectVolumeParams & UnitsParams>({
         ...builder,
         setUpdateState: (state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme) => {
             builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme)
             if (!SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.createGeometry = true
         },
-        createEmptyGeometry: DirectVolume.createEmpty,
-        createRenderObject: createComplexDirectVolumeRenderObject,
-        updateValues: DirectVolume.updateValues,
-        updateBoundingSphere: DirectVolume.updateBoundingSphere,
-        updateRenderableState: DirectVolume.updateRenderableState
+        geometryUtils: DirectVolume.Utils
     })
 }

+ 2 - 2
src/mol-repr/structure/representation.ts

@@ -8,7 +8,7 @@
 import { Structure } from 'mol-model/structure';
 import { Representation, RepresentationProps, RepresentationProvider } from '../representation';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
-import { Geometry } from 'mol-geo/geometry/geometry';
+import { BaseGeometry } from 'mol-geo/geometry/base';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { Points } from 'mol-geo/geometry/points/points';
 import { Lines } from 'mol-geo/geometry/lines/lines';
@@ -21,7 +21,7 @@ export type StructureRepresentationProvider<P extends PD.Params> = Representatio
 
 //
 
-export const StructureParams = { ...Geometry.Params }
+export const StructureParams = { ...BaseGeometry.Params }
 export type StructureParams = typeof StructureParams
 
 export const StructureMeshParams = { ...Mesh.Params, ...StructureParams }

+ 30 - 50
src/mol-repr/structure/units-visual.ts

@@ -9,13 +9,11 @@ import { RepresentationProps } from '../representation';
 import { Visual, VisualContext } from '../visual';
 import { StructureMeshParams, StructurePointsParams, StructureLinesParams, StructureDirectVolumeParams, StructureSpheresParams } from './representation';
 import { Loci, isEveryLoci, EmptyLoci } from 'mol-model/loci';
-import { MeshRenderObject, PointsRenderObject, LinesRenderObject, DirectVolumeRenderObject, SpheresRenderObject } from 'mol-gl/render-object';
-import { createUnitsMeshRenderObject, createUnitsPointsRenderObject, createUnitsTransform, createUnitsLinesRenderObject, createUnitsDirectVolumeRenderObject, includesUnitKind, createUnitsSpheresRenderObject } from './visual/util/common';
+import { GraphicsRenderObject, createRenderObject } from 'mol-gl/render-object';
 import { deepEqual, ValueCell } from 'mol-util';
 import { Interval } from 'mol-data/int';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
-import { RenderableValues } from 'mol-gl/renderable/schema';
-import { Geometry } from 'mol-geo/geometry/geometry';
+import { Geometry, GeometryUtils } from 'mol-geo/geometry/geometry';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { createMarkers, MarkerAction, applyMarkerAction } from 'mol-geo/geometry/marker-data';
@@ -30,15 +28,21 @@ import { Theme, createEmptyTheme } from 'mol-theme/theme';
 import { ColorTheme } from 'mol-theme/color';
 import { SizeTheme } from 'mol-theme/size';
 import { UnitsParams } from './units-representation';
-import { RenderableState } from 'mol-gl/renderable';
 import { Mat4 } from 'mol-math/linear-algebra';
 import { Spheres } from 'mol-geo/geometry/spheres/spheres';
+import { createUnitsTransform, includesUnitKind } from './visual/util/common';
 
 export type StructureGroup = { structure: Structure, group: Unit.SymmetryGroup }
 
 export interface UnitsVisual<P extends RepresentationProps = {}> extends Visual<StructureGroup, P> { }
 
-type UnitsRenderObject = MeshRenderObject | LinesRenderObject | PointsRenderObject | SpheresRenderObject | DirectVolumeRenderObject
+function createUnitsRenderObject<G extends Geometry>(group: Unit.SymmetryGroup, geometry: G, locationIt: LocationIterator, theme: Theme, props: PD.Values<Geometry.Params<G>>) {
+    const { createValues, createRenderableState } = Geometry.getUtils(geometry.kind)
+    const transform = createUnitsTransform(group)
+    const values = createValues(geometry, transform, locationIt, theme, props as any) // TODO
+    const state = createRenderableState(props)
+    return createRenderObject(geometry.kind, values, state)
+}
 
 interface UnitsVisualBuilder<P extends UnitsParams, G extends Geometry> {
     defaultProps: PD.Values<P>
@@ -50,19 +54,15 @@ interface UnitsVisualBuilder<P extends UnitsParams, G extends Geometry> {
 }
 
 interface UnitsVisualGeometryBuilder<P extends UnitsParams, G extends Geometry> extends UnitsVisualBuilder<P, G> {
-    createEmptyGeometry(geometry?: G): G
-    createRenderObject(group: Unit.SymmetryGroup, geometry: Geometry, locationIt: LocationIterator, theme: Theme, currentProps: PD.Values<P>): UnitsRenderObject
-    updateValues(values: RenderableValues, newProps: Partial<PD.Values<P>>): void
-    updateBoundingSphere(values: RenderableValues, geometry: Geometry): void
-    updateRenderableState(state: RenderableState, props: Partial<PD.Values<P>>): void
+    geometryUtils: GeometryUtils<G>
 }
 
-export function UnitsVisual<P extends UnitsParams>(builder: UnitsVisualGeometryBuilder<P, Geometry>): UnitsVisual<P> {
+export function UnitsVisual<G extends Geometry, P extends UnitsParams & Geometry.Params<G>>(builder: UnitsVisualGeometryBuilder<P, G>): UnitsVisual<P> {
     const { defaultProps, createGeometry, createLocationIterator, getLoci, mark, setUpdateState } = builder
-    const { createEmptyGeometry, createRenderObject, updateValues, updateBoundingSphere, updateRenderableState } = builder
+    const { createEmpty: createEmptyGeometry, updateValues, updateBoundingSphere, updateRenderableState } = builder.geometryUtils
     const updateState = VisualUpdateState.create()
 
-    let renderObject: UnitsRenderObject | undefined
+    let renderObject: GraphicsRenderObject | undefined
 
     let newProps: PD.Values<P> = Object.assign({}, defaultProps)
     let newTheme: Theme = createEmptyTheme()
@@ -72,7 +72,7 @@ export function UnitsVisual<P extends UnitsParams>(builder: UnitsVisualGeometryB
     let currentTheme: Theme
     let currentStructureGroup: StructureGroup
 
-    let geometry: Geometry
+    let geometry: G
     let locationIt: LocationIterator
 
     function prepareUpdate(theme: Theme, props: Partial<PD.Values<P>> = {}, structureGroup: StructureGroup) {
@@ -133,11 +133,11 @@ export function UnitsVisual<P extends UnitsParams>(builder: UnitsVisualGeometryB
         }
     }
 
-    function update(newGeometry?: Geometry) {
+    function update(newGeometry?: G) {
         if (updateState.createNew) {
             locationIt = createLocationIterator(newStructureGroup.group)
             if (newGeometry) {
-                renderObject = createRenderObject(newStructureGroup.group, newGeometry, locationIt, newTheme, newProps)
+                renderObject = createUnitsRenderObject(newStructureGroup.group, newGeometry, locationIt, newTheme, newProps)
             } else {
                 throw new Error('expected geometry to be given')
             }
@@ -197,7 +197,7 @@ export function UnitsVisual<P extends UnitsParams>(builder: UnitsVisualGeometryB
         if (newGeometry) geometry = newGeometry
     }
 
-    function _createGeometry(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.Values<P>, geometry?: Geometry) {
+    function _createGeometry(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.Values<P>, geometry?: G) {
         return includesUnitKind(props.unitKinds, unit)
                 ? createGeometry(ctx, unit, structure, theme, props, geometry)
                 : createEmptyGeometry(geometry)
@@ -210,7 +210,7 @@ export function UnitsVisual<P extends UnitsParams>(builder: UnitsVisualGeometryB
             prepareUpdate(theme, props, structureGroup || currentStructureGroup)
             if (updateState.createGeometry) {
                 const newGeometry = _createGeometry(ctx, newStructureGroup.group.units[0], newStructureGroup.structure, newTheme, newProps, geometry)
-                return newGeometry instanceof Promise ? newGeometry.then(update) : update(newGeometry)
+                return newGeometry instanceof Promise ? newGeometry.then(update) : update(newGeometry as G)
             } else {
                 update()
             }
@@ -263,17 +263,13 @@ export type UnitsMeshParams = typeof UnitsMeshParams
 export interface UnitsMeshVisualBuilder<P extends UnitsMeshParams> extends UnitsVisualBuilder<P, Mesh> { }
 
 export function UnitsMeshVisual<P extends UnitsMeshParams>(builder: UnitsMeshVisualBuilder<P>): UnitsVisual<P> {
-    return UnitsVisual<StructureMeshParams & UnitsParams>({
+    return UnitsVisual<Mesh, StructureMeshParams & UnitsParams>({
         ...builder,
         setUpdateState: (state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme) => {
             builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme)
             if (!SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.createGeometry = true
         },
-        createEmptyGeometry: Mesh.createEmpty,
-        createRenderObject: createUnitsMeshRenderObject,
-        updateValues: Mesh.updateValues,
-        updateBoundingSphere: Mesh.updateBoundingSphere,
-        updateRenderableState: Geometry.updateRenderableState
+        geometryUtils: Mesh.Utils
     })
 }
 
@@ -284,17 +280,13 @@ export type UnitsSpheresParams = typeof UnitsSpheresParams
 export interface UnitsSpheresVisualBuilder<P extends UnitsSpheresParams> extends UnitsVisualBuilder<P, Spheres> { }
 
 export function UnitsSpheresVisual<P extends UnitsSpheresParams>(builder: UnitsSpheresVisualBuilder<P>): UnitsVisual<P> {
-    return UnitsVisual<StructureSpheresParams & UnitsParams>({
+    return UnitsVisual<Spheres, StructureSpheresParams & UnitsParams>({
         ...builder,
         setUpdateState: (state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme) => {
             builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme)
             if (!SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.updateSize = true
         },
-        createEmptyGeometry: Spheres.createEmpty,
-        createRenderObject: createUnitsSpheresRenderObject,
-        updateValues: Spheres.updateValues,
-        updateBoundingSphere: Spheres.updateBoundingSphere,
-        updateRenderableState: Geometry.updateRenderableState
+        geometryUtils: Spheres.Utils
     })
 }
 
@@ -305,17 +297,13 @@ export type UnitsPointsParams = typeof UnitsPointsParams
 export interface UnitsPointVisualBuilder<P extends UnitsPointsParams> extends UnitsVisualBuilder<P, Points> { }
 
 export function UnitsPointsVisual<P extends UnitsPointsParams>(builder: UnitsPointVisualBuilder<P>): UnitsVisual<P> {
-    return UnitsVisual<StructurePointsParams & UnitsParams>({
+    return UnitsVisual<Points, StructurePointsParams & UnitsParams>({
         ...builder,
-        createEmptyGeometry: Points.createEmpty,
-        createRenderObject: createUnitsPointsRenderObject,
         setUpdateState: (state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme) => {
             builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme)
             if (!SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.updateSize = true
         },
-        updateValues: Points.updateValues,
-        updateBoundingSphere: Points.updateBoundingSphere,
-        updateRenderableState: Points.updateRenderableState
+        geometryUtils: Points.Utils
     })
 }
 
@@ -326,17 +314,13 @@ export type UnitsLinesParams = typeof UnitsLinesParams
 export interface UnitsLinesVisualBuilder<P extends UnitsLinesParams> extends UnitsVisualBuilder<P, Lines> { }
 
 export function UnitsLinesVisual<P extends UnitsLinesParams>(builder: UnitsLinesVisualBuilder<P>): UnitsVisual<P> {
-    return UnitsVisual<StructureLinesParams & UnitsParams>({
+    return UnitsVisual<Lines, StructureLinesParams & UnitsParams>({
         ...builder,
-        createEmptyGeometry: Lines.createEmpty,
-        createRenderObject: createUnitsLinesRenderObject,
         setUpdateState: (state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme) => {
             builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme)
             if (!SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.updateSize = true
         },
-        updateValues: Lines.updateValues,
-        updateBoundingSphere: Lines.updateBoundingSphere,
-        updateRenderableState: Geometry.updateRenderableState
+        geometryUtils: Lines.Utils
     })
 }
 
@@ -344,19 +328,15 @@ export function UnitsLinesVisual<P extends UnitsLinesParams>(builder: UnitsLines
 
 export const UnitsDirectVolumeParams = { ...StructureDirectVolumeParams, ...UnitsParams }
 export type UnitsDirectVolumeParams = typeof UnitsDirectVolumeParams
-export interface UnitsDirectVolumeVisualBuilder<P extends UnitsDirectVolumeParams> extends UnitsVisualBuilder<P, DirectVolume> { }
+export interface UnitsDirectVolumeVisualBuilder<P extends UnitsDirectVolumeParams> extends UnitsVisualGeometryBuilder<P, DirectVolume> { }
 
 export function UnitsDirectVolumeVisual<P extends UnitsDirectVolumeParams>(builder: UnitsDirectVolumeVisualBuilder<P>): UnitsVisual<P> {
-    return UnitsVisual<StructureDirectVolumeParams & UnitsParams>({
+    return UnitsVisual<DirectVolume, StructureDirectVolumeParams & UnitsParams>({
         ...builder,
-        createEmptyGeometry: DirectVolume.createEmpty,
-        createRenderObject: createUnitsDirectVolumeRenderObject,
         setUpdateState: (state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme) => {
             builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme)
             if (!SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.createGeometry = true
         },
-        updateValues: DirectVolume.updateValues,
-        updateBoundingSphere: DirectVolume.updateBoundingSphere,
-        updateRenderableState: DirectVolume.updateRenderableState
+        geometryUtils: DirectVolume.Utils
     })
 }

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

@@ -5,21 +5,10 @@
  */
 
 import { Unit, Structure, ElementIndex, StructureElement } from 'mol-model/structure';
-import { createMeshRenderObject, createPointsRenderObject, createLinesRenderObject, createDirectVolumeRenderObject, createSpheresRenderObject } from 'mol-gl/render-object';
 import { Mat4 } from 'mol-math/linear-algebra';
-import { TransformData, createTransform, createIdentityTransform } from 'mol-geo/geometry/transform-data';
-import { Mesh } from 'mol-geo/geometry/mesh/mesh';
-import { LocationIterator } from 'mol-geo/util/location-iterator';
-import { Geometry } from 'mol-geo/geometry/geometry';
-import { Points } from 'mol-geo/geometry/points/points';
-import { Lines } from 'mol-geo/geometry/lines/lines';
-import { DirectVolume } from 'mol-geo/geometry/direct-volume/direct-volume';
-import { Theme } from 'mol-theme/theme';
-import { ParamDefinition as PD } from 'mol-util/param-definition';
-import { StructureMeshParams, StructurePointsParams, StructureLinesParams, StructureDirectVolumeParams, StructureSpheresParams } from 'mol-repr/structure/representation';
+import { TransformData, createTransform } from 'mol-geo/geometry/transform-data';
 import { OrderedSet, SortedArray } from 'mol-data/int';
 import { EmptyLoci, Loci } from 'mol-model/loci';
-import { Spheres } from 'mol-geo/geometry/spheres/spheres';
 
 /** Return a Loci for the elements of a whole residue the elementIndex belongs to. */
 export function getResidueLoci(structure: Structure, unit: Unit, elementIndex: ElementIndex): Loci {
@@ -66,63 +55,4 @@ export function includesUnitKind(unitKinds: UnitKind[], unit: Unit) {
         if (Unit.isGaussians(unit) && unitKinds[i] === 'gaussians') return true
     }
     return false
-}
-
-// mesh
-
-export function createComplexMeshRenderObject(structure: Structure, mesh: Mesh, locationIt: LocationIterator, theme: Theme, props: PD.Values<StructureMeshParams>) {
-    const transform = createIdentityTransform()
-    const values = Mesh.createValues(mesh, transform, locationIt, theme, props)
-    const state = Geometry.createRenderableState(props)
-    return createMeshRenderObject(values, state)
-}
-
-export function createUnitsMeshRenderObject(group: Unit.SymmetryGroup, mesh: Mesh, locationIt: LocationIterator, theme: Theme, props: PD.Values<StructureMeshParams>) {
-    const transform = createUnitsTransform(group)
-    const values = Mesh.createValues(mesh, transform, locationIt, theme, props)
-    const state = Geometry.createRenderableState(props)
-    return createMeshRenderObject(values, state)
-}
-
-// spheres
-
-export function createUnitsSpheresRenderObject(group: Unit.SymmetryGroup, spheres: Spheres, locationIt: LocationIterator, theme: Theme, props: PD.Values<StructureSpheresParams>) {
-    const transform = createUnitsTransform(group)
-    const values = Spheres.createValues(spheres, transform, locationIt, theme, props)
-    const state = Geometry.createRenderableState(props)
-    return createSpheresRenderObject(values, state)
-}
-
-// points
-
-export function createUnitsPointsRenderObject(group: Unit.SymmetryGroup, points: Points, locationIt: LocationIterator, theme: Theme, props: PD.Values<StructurePointsParams>) {
-    const transform = createUnitsTransform(group)
-    const values = Points.createValues(points, transform, locationIt, theme, props)
-    const state = Points.createRenderableState(props)
-    return createPointsRenderObject(values, state)
-}
-
-// lines
-
-export function createUnitsLinesRenderObject(group: Unit.SymmetryGroup, lines: Lines, locationIt: LocationIterator, theme: Theme, props: PD.Values<StructureLinesParams>) {
-    const transform = createUnitsTransform(group)
-    const values = Lines.createValues(lines, transform, locationIt, theme, props)
-    const state = Geometry.createRenderableState(props)
-    return createLinesRenderObject(values, state)
-}
-
-// direct-volume
-
-export function createComplexDirectVolumeRenderObject(structure: Structure, directVolume: DirectVolume, locationIt: LocationIterator, theme: Theme, props: PD.Values<StructureDirectVolumeParams>) {
-    const transform = createIdentityTransform()
-    const values = DirectVolume.createValues(directVolume, transform, locationIt, theme, props)
-    const state = DirectVolume.createRenderableState(props)
-    return createDirectVolumeRenderObject(values, state)
-}
-
-export function createUnitsDirectVolumeRenderObject(group: Unit.SymmetryGroup, directVolume: DirectVolume, locationIt: LocationIterator, theme: Theme, props: PD.Values<StructureDirectVolumeParams>) {
-    const transform = createUnitsTransform(group)
-    const values = DirectVolume.createValues(directVolume, transform, locationIt, theme, props)
-    const state = DirectVolume.createRenderableState(props)
-    return createDirectVolumeRenderObject(values, state)
 }

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

@@ -25,7 +25,7 @@ export interface ElementSphereMeshProps {
     sizeFactor: number
 }
 
-export function createElementSphereMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: ElementSphereMeshProps, mesh?: Mesh) {
+export function createElementSphereMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: ElementSphereMeshProps, mesh?: Mesh): Mesh {
     const { detail, sizeFactor } = props
 
     const { elements } = unit;
@@ -51,7 +51,7 @@ export function createElementSphereMesh(ctx: VisualContext, unit: Unit, structur
 
 export interface ElementSphereImpostorProps { }
 
-export function createElementSphereImpostor(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: ElementSphereImpostorProps, spheres?: Spheres) {
+export function createElementSphereImpostor(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: ElementSphereImpostorProps, spheres?: Spheres): Spheres {
 
     const { elements } = unit;
     const elementCount = elements.length;

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

@@ -6,7 +6,7 @@
 
 import { defaults } from 'mol-util';
 import { Structure } from 'mol-model/structure';
-import { VisualQuality } from 'mol-geo/geometry/geometry';
+import { VisualQuality } from 'mol-geo/geometry/base';
 
 export interface VisualUpdateState {
     updateTransform: boolean

+ 9 - 9
src/mol-repr/volume/direct-volume.ts

@@ -7,7 +7,7 @@
 import { VolumeData } from 'mol-model/volume'
 import { RuntimeContext } from 'mol-task'
 import { VolumeVisual, VolumeRepresentation, VolumeRepresentationProvider } from './representation';
-import { createDirectVolumeRenderObject } from 'mol-gl/render-object';
+import { createRenderObject } from 'mol-gl/render-object';
 import { EmptyLoci } from 'mol-model/loci';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { Vec3, Mat4 } from 'mol-math/linear-algebra';
@@ -17,7 +17,7 @@ import { createTexture } from 'mol-gl/webgl/texture';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { createIdentityTransform } from 'mol-geo/geometry/transform-data';
 import { DirectVolume } from 'mol-geo/geometry/direct-volume/direct-volume';
-import { Geometry } from 'mol-geo/geometry/geometry';
+import { BaseGeometry } from 'mol-geo/geometry/base';
 import { VisualUpdateState } from 'mol-repr/util';
 import { RepresentationContext, RepresentationParamsGetter } from 'mol-repr/representation';
 import { Theme, ThemeRegistryContext } from 'mol-theme/theme';
@@ -157,7 +157,7 @@ export async function createDirectVolume(ctx: VisualContext, volume: VolumeData,
 //
 
 export const DirectVolumeParams = {
-    ...Geometry.Params,
+    ...BaseGeometry.Params,
     ...DirectVolume.Params
 }
 export type DirectVolumeParams = typeof DirectVolumeParams
@@ -175,13 +175,13 @@ export function DirectVolumeVisual(): VolumeVisual<DirectVolumeParams> {
         },
         createRenderObject: (geometry: DirectVolume, locationIt: LocationIterator, theme: Theme, props: PD.Values<DirectVolumeParams>) => {
             const transform = createIdentityTransform()
-            const values = DirectVolume.createValues(geometry, transform, locationIt, theme, props)
-            const state = DirectVolume.createRenderableState(props)
-            return createDirectVolumeRenderObject(values, state)
+            const values = DirectVolume.Utils.createValues(geometry, transform, locationIt, theme, props)
+            const state = DirectVolume.Utils.createRenderableState(props)
+            return createRenderObject('direct-volume', values, state)
         },
-        updateValues: DirectVolume.updateValues,
-        updateBoundingSphere: DirectVolume.updateBoundingSphere,
-        updateRenderableState: DirectVolume.updateRenderableState
+        updateValues: DirectVolume.Utils.updateValues,
+        updateBoundingSphere: DirectVolume.Utils.updateBoundingSphere,
+        updateRenderableState: DirectVolume.Utils.updateRenderableState
     })
 }
 

+ 7 - 8
src/mol-repr/volume/isosurface-mesh.ts

@@ -7,14 +7,13 @@
 
 import { VolumeData } from 'mol-model/volume'
 import { VolumeVisual, VolumeRepresentation, VolumeRepresentationProvider } from './representation';
-import { createMeshRenderObject } from 'mol-gl/render-object';
+import { createRenderObject } from 'mol-gl/render-object';
 import { EmptyLoci } from 'mol-model/loci';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { computeMarchingCubesMesh } from 'mol-geo/util/marching-cubes/algorithm';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { createIdentityTransform } from 'mol-geo/geometry/transform-data';
-import { Geometry } from 'mol-geo/geometry/geometry';
 import { VisualUpdateState } from 'mol-repr/util';
 import { RepresentationContext, RepresentationParamsGetter } from 'mol-repr/representation';
 import { Theme, ThemeRegistryContext } from 'mol-theme/theme';
@@ -60,13 +59,13 @@ export function IsosurfaceVisual(): VolumeVisual<IsosurfaceParams> {
         },
         createRenderObject: (geometry: Mesh, locationIt: LocationIterator, theme: Theme, props: PD.Values<IsosurfaceParams>) => {
             const transform = createIdentityTransform()
-            const values = Mesh.createValues(geometry, transform, locationIt, theme, props)
-            const state = Geometry.createRenderableState(props)
-            return createMeshRenderObject(values, state)
+            const values = Mesh.Utils.createValues(geometry, transform, locationIt, theme, props)
+            const state = Mesh.Utils.createRenderableState(props)
+            return createRenderObject('mesh', values, state)
         },
-        updateValues: Mesh.updateValues,
-        updateBoundingSphere: Mesh.updateBoundingSphere,
-        updateRenderableState: Geometry.updateRenderableState
+        updateValues: Mesh.Utils.updateValues,
+        updateBoundingSphere: Mesh.Utils.updateBoundingSphere,
+        updateRenderableState: Mesh.Utils.updateRenderableState
     })
 }
 

+ 5 - 6
src/mol-repr/volume/representation.ts

@@ -13,7 +13,7 @@ import { Geometry } from 'mol-geo/geometry/geometry';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { MarkerAction, applyMarkerAction } from 'mol-geo/geometry/marker-data';
-import { DirectVolumeRenderObject, PointsRenderObject, LinesRenderObject, MeshRenderObject } from 'mol-gl/render-object';
+import { GraphicsRenderObject } from 'mol-gl/render-object';
 import { Interval } from 'mol-data/int';
 import { RenderableValues } from 'mol-gl/renderable/schema';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
@@ -24,11 +24,10 @@ import { Theme, createEmptyTheme } from 'mol-theme/theme';
 import { Subject } from 'rxjs';
 import { RenderableState } from 'mol-gl/renderable';
 import { Mat4 } from 'mol-math/linear-algebra';
+import { BaseGeometry } from 'mol-geo/geometry/base';
 
 export interface VolumeVisual<P extends VolumeParams> extends Visual<VolumeData, P> { }
 
-type VolumeRenderObject = MeshRenderObject | LinesRenderObject | PointsRenderObject | DirectVolumeRenderObject
-
 interface VolumeVisualBuilder<P extends VolumeParams, G extends Geometry> {
     defaultProps: PD.Values<P>
     createGeometry(ctx: VisualContext, volumeData: VolumeData, props: PD.Values<P>, geometry?: G): Promise<G>
@@ -38,7 +37,7 @@ interface VolumeVisualBuilder<P extends VolumeParams, G extends Geometry> {
 }
 
 interface VolumeVisualGeometryBuilder<P extends VolumeParams, G extends Geometry> extends VolumeVisualBuilder<P, G> {
-    createRenderObject(geometry: G, locationIt: LocationIterator, theme: Theme, currentProps: PD.Values<P>): VolumeRenderObject
+    createRenderObject(geometry: G, locationIt: LocationIterator, theme: Theme, currentProps: PD.Values<P>): GraphicsRenderObject
     updateValues(values: RenderableValues, newProps: PD.Values<P>): void,
     updateBoundingSphere(values: RenderableValues, geometry: G): void
     updateRenderableState(state: RenderableState, props: PD.Values<P>): void
@@ -50,7 +49,7 @@ export function VolumeVisual<P extends VolumeParams>(builder: VolumeVisualGeomet
     const updateState = VisualUpdateState.create()
 
     let currentProps: PD.Values<P>
-    let renderObject: VolumeRenderObject | undefined
+    let renderObject: GraphicsRenderObject | undefined
     let currentVolume: VolumeData
     let geometry: Geometry
     let locationIt: LocationIterator
@@ -147,7 +146,7 @@ export type VolumeRepresentationProvider<P extends VolumeParams> = Representatio
 //
 
 export const VolumeParams = {
-    ...Geometry.Params,
+    ...BaseGeometry.Params,
     isoValue: PD.Numeric(0.22, { min: -1, max: 1, step: 0.01 }),
 }
 export type VolumeParams = typeof VolumeParams

+ 4 - 5
src/tests/browser/render-mesh.ts

@@ -10,10 +10,9 @@ import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
 import { Sphere } from 'mol-geo/primitive/sphere';
 import { Mat4 } from 'mol-math/linear-algebra';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
-import { Geometry } from 'mol-geo/geometry/geometry';
-import { createMeshRenderObject } from 'mol-gl/render-object';
 import { Representation } from 'mol-repr/representation';
 import { Color } from 'mol-util/color';
+import { createRenderObject } from 'mol-gl/render-object';
 
 const parent = document.getElementById('app')!
 parent.style.width = '100%'
@@ -33,9 +32,9 @@ const sphere = Sphere(2)
 MeshBuilder.addPrimitive(builderState, t, sphere)
 const mesh = MeshBuilder.getMesh(builderState)
 
-const values = Mesh.createValuesSimple(mesh, {}, Color(0xFF0000))
-const state = Geometry.createRenderableState()
-const renderObject = createMeshRenderObject(values, state)
+const values = Mesh.Utils.createValuesSimple(mesh, {}, Color(0xFF0000), 1)
+const state = Mesh.Utils.createRenderableState({})
+const renderObject = createRenderObject('mesh', values, state)
 const repr = Representation.fromRenderObject('sphere-mesh', renderObject)
 
 canvas3d.add(repr)

+ 4 - 5
src/tests/browser/render-spheres.ts

@@ -7,11 +7,10 @@
 import './index.html'
 import { Canvas3D } from 'mol-canvas3d/canvas3d';
 import { SpheresBuilder } from 'mol-geo/geometry/spheres/spheres-builder';
-import { Geometry } from 'mol-geo/geometry/geometry';
-import { createSpheresRenderObject } from 'mol-gl/render-object';
 import { Representation } from 'mol-repr/representation';
 import { Spheres } from 'mol-geo/geometry/spheres/spheres';
 import { Color } from 'mol-util/color';
+import { createRenderObject } from 'mol-gl/render-object';
 
 const parent = document.getElementById('app')!
 parent.style.width = '100%'
@@ -32,9 +31,9 @@ function spheresRepr() {
     spheresBuilder.add(-4, 1, 0, 0)
     const spheres = spheresBuilder.getSpheres()
 
-    const values = Spheres.createValuesSimple(spheres, {}, Color(0xFF0000), 1)
-    const state = Geometry.createRenderableState()
-    const renderObject = createSpheresRenderObject(values, state)
+    const values = Spheres.Utils.createValuesSimple(spheres, {}, Color(0xFF0000), 1)
+    const state = Spheres.Utils.createRenderableState({})
+    const renderObject = createRenderObject('spheres', values, state)
     console.log(renderObject)
     const repr = Representation.fromRenderObject('spheres', renderObject)
     return repr

+ 7 - 8
src/tests/browser/render-text.ts

@@ -6,14 +6,13 @@
 
 import './index.html'
 import { Canvas3D } from 'mol-canvas3d/canvas3d';
-import { Geometry } from 'mol-geo/geometry/geometry';
 import { TextBuilder } from 'mol-geo/geometry/text/text-builder';
 import { Text } from 'mol-geo/geometry/text/text';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { Color } from 'mol-util/color';
-import { createTextRenderObject, createSpheresRenderObject } from 'mol-gl/render-object';
 import { Representation } from 'mol-repr/representation';
 import { SpheresBuilder } from 'mol-geo/geometry/spheres/spheres-builder';
+import { createRenderObject } from 'mol-gl/render-object';
 import { Spheres } from 'mol-geo/geometry/spheres/spheres';
 
 const parent = document.getElementById('app')!
@@ -45,9 +44,9 @@ function textRepr() {
     textBuilder.add('αβγ Å', 0, -2, 0, 0)
     const text = textBuilder.getText()
 
-    const values = Text.createValuesSimple(text, props, Color(0xFFDD00), 1)
-    const state = Text.createRenderableState(props)
-    const renderObject = createTextRenderObject(values, state)
+    const values = Text.Utils.createValuesSimple(text, props, Color(0xFFDD00), 1)
+    const state = Text.Utils.createRenderableState(props)
+    const renderObject = createRenderObject('text', values, state)
     console.log('text', renderObject)
     const repr = Representation.fromRenderObject('text', renderObject)
     return repr
@@ -60,9 +59,9 @@ function spheresRepr() {
     spheresBuilder.add(-4, 1, 0, 0)
     const spheres = spheresBuilder.getSpheres()
 
-    const values = Spheres.createValuesSimple(spheres, {}, Color(0xFF0000), 0.2)
-    const state = Geometry.createRenderableState()
-    const renderObject = createSpheresRenderObject(values, state)
+    const values = Spheres.Utils.createValuesSimple(spheres, {}, Color(0xFF0000), 0.2)
+    const state = Spheres.Utils.createRenderableState({})
+    const renderObject = createRenderObject('spheres', values, state)
     console.log('spheres', renderObject)
     const repr = Representation.fromRenderObject('spheres', renderObject)
     return repr