Selaa lähdekoodia

moved theme creation from geometry to representation level

Alexander Rose 6 vuotta sitten
vanhempi
commit
a3905bd470
41 muutettua tiedostoa jossa 202 lisäystä ja 199 poistoa
  1. 1 2
      src/mol-geo/geometry/color-data.ts
  2. 3 3
      src/mol-geo/geometry/direct-volume/direct-volume.ts
  3. 22 4
      src/mol-geo/geometry/geometry.ts
  4. 5 9
      src/mol-geo/geometry/lines/lines.ts
  5. 3 3
      src/mol-geo/geometry/mesh/mesh.ts
  6. 5 9
      src/mol-geo/geometry/points/points.ts
  7. 1 2
      src/mol-geo/geometry/size-data.ts
  8. 5 6
      src/mol-repr/index.ts
  9. 3 2
      src/mol-repr/shape/index.ts
  10. 7 2
      src/mol-repr/structure/complex-representation.ts
  11. 15 15
      src/mol-repr/structure/complex-visual.ts
  12. 6 6
      src/mol-repr/structure/representation/cartoon.ts
  13. 9 6
      src/mol-repr/structure/units-representation.ts
  14. 14 14
      src/mol-repr/structure/units-visual.ts
  15. 4 4
      src/mol-repr/structure/visual/carbohydrate-link-cylinder.ts
  16. 4 4
      src/mol-repr/structure/visual/carbohydrate-symbol-mesh.ts
  17. 4 4
      src/mol-repr/structure/visual/cross-link-restraint-cylinder.ts
  18. 2 1
      src/mol-repr/structure/visual/element-point.ts
  19. 2 1
      src/mol-repr/structure/visual/gaussian-density-point.ts
  20. 2 1
      src/mol-repr/structure/visual/gaussian-density-volume.ts
  21. 2 1
      src/mol-repr/structure/visual/gaussian-surface-mesh.ts
  22. 2 1
      src/mol-repr/structure/visual/gaussian-surface-wireframe.ts
  23. 4 8
      src/mol-repr/structure/visual/inter-unit-link-cylinder.ts
  24. 4 4
      src/mol-repr/structure/visual/intra-unit-link-cylinder.ts
  25. 2 1
      src/mol-repr/structure/visual/nucleotide-block-mesh.ts
  26. 5 8
      src/mol-repr/structure/visual/polymer-backbone-cylinder.ts
  27. 4 5
      src/mol-repr/structure/visual/polymer-direction-wedge.ts
  28. 5 8
      src/mol-repr/structure/visual/polymer-gap-cylinder.ts
  29. 4 8
      src/mol-repr/structure/visual/polymer-trace-mesh.ts
  30. 11 11
      src/mol-repr/structure/visual/util/common.ts
  31. 3 6
      src/mol-repr/structure/visual/util/element.ts
  32. 1 5
      src/mol-repr/structure/visual/util/link.ts
  33. 4 4
      src/mol-repr/util.ts
  34. 3 3
      src/mol-repr/volume/direct-volume.ts
  35. 16 13
      src/mol-repr/volume/index.ts
  36. 3 3
      src/mol-repr/volume/isosurface-mesh.ts
  37. 3 3
      src/mol-theme/color/chain-id.ts
  38. 1 1
      src/mol-theme/color/cross-link.ts
  39. 3 3
      src/mol-theme/color/element-index.ts
  40. 2 2
      src/mol-theme/color/polymer-index.ts
  41. 3 3
      src/mol-theme/color/unit-index.ts

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

@@ -56,8 +56,7 @@ export function getColorThemeProps(props: ColorProps): ColorThemeProps {
     return p
 }
 
-export function createColors(ctx: RuntimeContext, locationIt: LocationIterator, props: ColorProps, colorData?: ColorData): Promise<ColorData> {
-    const colorTheme = ColorTheme(getColorThemeProps(props))
+export function createColors(ctx: RuntimeContext, locationIt: LocationIterator, colorTheme: ColorTheme, colorData?: ColorData): Promise<ColorData> {
     switch (getGranularity(locationIt, colorTheme.granularity)) {
         case 'uniform': return createUniformColor(ctx, locationIt, colorTheme.color, colorData)
         case 'group': return createGroupColor(ctx, locationIt, colorTheme.color, colorData)

+ 3 - 3
src/mol-geo/geometry/direct-volume/direct-volume.ts

@@ -17,7 +17,7 @@ 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 { Geometry, Theme } from '../geometry';
 
 const VolumeBox = Box()
 const RenderModeOptions = [['isosurface', 'Isosurface'], ['volume', 'Volume']] as [string, string][]
@@ -75,11 +75,11 @@ export namespace DirectVolume {
     export const DefaultProps = paramDefaultValues(Params)
     export type Props = typeof DefaultProps
 
-    export async function createValues(ctx: RuntimeContext, directVolume: DirectVolume, transform: TransformData, locationIt: LocationIterator, props: Props): Promise<DirectVolumeValues> {
+    export async function createValues(ctx: RuntimeContext, directVolume: DirectVolume, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: Props): Promise<DirectVolumeValues> {
         const { gridTexture, gridTextureDim } = directVolume
 
         const { instanceCount, groupCount } = locationIt
-        const color = await createColors(ctx, locationIt, props)
+        const color = await createColors(ctx, locationIt, theme.color)
         const marker = createMarkers(instanceCount * groupCount)
 
         const counts = { drawCount: VolumeBox.indices.length, groupCount, instanceCount }

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

@@ -10,13 +10,14 @@ import { RenderableState } from 'mol-gl/renderable';
 import { ValueCell } from 'mol-util';
 import { BaseValues } from 'mol-gl/renderable/schema';
 import { Color } from 'mol-util/color';
-import { ColorThemeOptions, ColorThemeName, ColorScaleOptions, ColorScaleName } from 'mol-theme/color';
+import { ColorThemeOptions, ColorThemeName, ColorScaleOptions, ColorScaleName, ColorTheme } from 'mol-theme/color';
 import { LocationIterator } from '../util/location-iterator';
-import { ColorType } from './color-data';
-import { SizeType } from './size-data';
+import { ColorType, getColorThemeProps } from './color-data';
+import { SizeType, getSizeThemeProps } from './size-data';
 import { Lines } from './lines/lines';
-import { paramDefaultValues, RangeParam, BooleanParam, SelectParam, ColorParam } from 'mol-util/parameter'
+import { paramDefaultValues, RangeParam, BooleanParam, SelectParam, ColorParam, NumberParam } from 'mol-util/parameter'
 import { DirectVolume } from './direct-volume/direct-volume';
+import { SizeTheme, SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
 
 //
 
@@ -35,6 +36,18 @@ export type VisualQuality = keyof typeof VisualQualityInfo
 export const VisualQualityNames = Object.keys(VisualQualityInfo)
 export const VisualQualityOptions = VisualQualityNames.map(n => [n, n] as [VisualQuality, string])
 
+export interface Theme {
+    color: ColorTheme
+    size: SizeTheme
+}
+
+export function createTheme(props: Geometry.Props) {
+    return {
+        color: ColorTheme(getColorThemeProps(props)),
+        size: SizeTheme(getSizeThemeProps(props))
+    }
+}
+
 //
 
 export type GeometryKindType = {
@@ -64,9 +77,14 @@ export namespace Geometry {
         depthMask: BooleanParam('Depth Mask', '', true),
         useFog: BooleanParam('Use Fog', '', false),
         quality: SelectParam<VisualQuality>('Quality', '', 'auto', VisualQualityOptions),
+
         colorTheme: SelectParam<ColorThemeName>('Color Theme', '', 'uniform', ColorThemeOptions),
         colorList: SelectParam<ColorScaleName>('Color Scale', '', 'default', ColorScaleOptions),
         colorValue: ColorParam('Color Value', '', Color(0xCCCCCC)),
+
+        sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions),
+        sizeValue: NumberParam('Size Value', '', 1, 0, 20, 0.1),
+        sizeFactor: NumberParam('Size Factor', '', 1, 0, 10, 0.1),
     }
     export const DefaultProps = paramDefaultValues(Params)
     export type Props = typeof DefaultProps

+ 5 - 9
src/mol-geo/geometry/lines/lines.ts

@@ -7,18 +7,17 @@
 import { ValueCell } from 'mol-util'
 import { Mat4 } from 'mol-math/linear-algebra'
 import { transformPositionArray/* , transformDirectionArray, getNormalMatrix */ } from '../../util';
-import { Geometry } from '../geometry';
+import { Geometry, Theme } from '../geometry';
 import { RuntimeContext } from 'mol-task';
 import { createColors } from '../color-data';
 import { createMarkers } from '../marker-data';
 import { createSizes } from '../size-data';
 import { TransformData } from '../transform-data';
 import { LocationIterator } from '../../util/location-iterator';
-import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
 import { LinesValues } from 'mol-gl/renderable/lines';
 import { Mesh } from '../mesh/mesh';
 import { LinesBuilder } from './lines-builder';
-import { BooleanParam, SelectParam, NumberParam, paramDefaultValues } from 'mol-util/parameter';
+import { BooleanParam, paramDefaultValues } from 'mol-util/parameter';
 
 /** Wide line */
 export interface Lines {
@@ -94,17 +93,14 @@ export namespace Lines {
     export const Params = {
         ...Geometry.Params,
         lineSizeAttenuation: BooleanParam('Line Size Attenuation', '', false),
-        sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions),
-        sizeValue: NumberParam('Size Value', '', 1, 0, 10, 0.1),
-        sizeFactor: NumberParam('Size Factor', '', 1, 0, 10, 0.1),
     }
     export const DefaultProps = paramDefaultValues(Params)
     export type Props = typeof DefaultProps
 
-    export async function createValues(ctx: RuntimeContext, lines: Lines, transform: TransformData, locationIt: LocationIterator, props: Props): Promise<LinesValues> {
+    export async function createValues(ctx: RuntimeContext, lines: Lines, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: Props): Promise<LinesValues> {
         const { instanceCount, groupCount } = locationIt
-        const color = await createColors(ctx, locationIt, props)
-        const size = await createSizes(ctx, locationIt, props)
+        const color = await createColors(ctx, locationIt, theme.color)
+        const size = await createSizes(ctx, locationIt, theme.size)
         const marker = createMarkers(instanceCount * groupCount)
 
         const counts = { drawCount: lines.lineCount * 2 * 3, groupCount, instanceCount }

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

@@ -10,7 +10,7 @@ import { Vec3, Mat4 } from 'mol-math/linear-algebra'
 import { Sphere3D } from 'mol-math/geometry'
 import { transformPositionArray/* , transformDirectionArray, getNormalMatrix */ } from '../../util';
 import { MeshValues } from 'mol-gl/renderable';
-import { Geometry } from '../geometry';
+import { Geometry, Theme } from '../geometry';
 import { createMarkers } from '../marker-data';
 import { TransformData } from '../transform-data';
 import { LocationIterator } from '../../util/location-iterator';
@@ -346,9 +346,9 @@ export namespace Mesh {
     export const DefaultProps = paramDefaultValues(Params)
     export type Props = typeof DefaultProps
 
-    export async function createValues(ctx: RuntimeContext, mesh: Mesh, transform: TransformData, locationIt: LocationIterator, props: Props): Promise<MeshValues> {
+    export async function createValues(ctx: RuntimeContext, mesh: Mesh, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: Props): Promise<MeshValues> {
         const { instanceCount, groupCount } = locationIt
-        const color = await createColors(ctx, locationIt, props)
+        const color = await createColors(ctx, locationIt, theme.color)
         const marker = createMarkers(instanceCount * groupCount)
 
         const counts = { drawCount: mesh.triangleCount * 3, groupCount, instanceCount }

+ 5 - 9
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 { Geometry, Theme } from '../geometry';
 import { PointsValues } from 'mol-gl/renderable';
 import { RuntimeContext } from 'mol-task';
 import { createColors } from '../color-data';
@@ -15,8 +15,7 @@ import { createMarkers } from '../marker-data';
 import { createSizes } from '../size-data';
 import { TransformData } from '../transform-data';
 import { LocationIterator } from '../../util/location-iterator';
-import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
-import { BooleanParam, NumberParam, SelectParam, paramDefaultValues } from 'mol-util/parameter';
+import { BooleanParam, NumberParam, paramDefaultValues } from 'mol-util/parameter';
 
 /** Point cloud */
 export interface Points {
@@ -58,17 +57,14 @@ export namespace Points {
         pointSizeAttenuation: BooleanParam('Point Size Attenuation', '', false),
         pointFilledCircle: BooleanParam('Point Filled Circle', '', false),
         pointEdgeBleach: NumberParam('Point Edge Bleach', '', 0.2, 0, 1, 0.05),
-        sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions),
-        sizeValue: NumberParam('Size Value', '', 1, 0, 20, 0.1),
-        sizeFactor: NumberParam('Size Factor', '', 1, 0, 10, 0.1),
     }
     export const DefaultProps = paramDefaultValues(Params)
     export type Props = typeof DefaultProps
 
-    export async function createValues(ctx: RuntimeContext, points: Points, transform: TransformData, locationIt: LocationIterator, props: Props): Promise<PointsValues> {
+    export async function createValues(ctx: RuntimeContext, points: Points, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: Props): Promise<PointsValues> {
         const { instanceCount, groupCount } = locationIt
-        const color = await createColors(ctx, locationIt, props)
-        const size = await createSizes(ctx, locationIt, props)
+        const color = await createColors(ctx, locationIt, theme.color)
+        const size = await createSizes(ctx, locationIt, theme.size)
         const marker = createMarkers(instanceCount * groupCount)
 
         const counts = { drawCount: points.pointCount, groupCount, instanceCount }

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

@@ -40,8 +40,7 @@ export function getSizeThemeProps(props: SizeProps): SizeThemeProps {
     }
 }
 
-export async function createSizes(ctx: RuntimeContext, locationIt: LocationIterator, props: SizeProps, sizeData?: SizeData): Promise<SizeData> {
-    const sizeTheme = SizeTheme(getSizeThemeProps(props))
+export async function createSizes(ctx: RuntimeContext, locationIt: LocationIterator, sizeTheme: SizeTheme, sizeData?: SizeData): Promise<SizeData> {
     switch (getGranularity(locationIt, sizeTheme.granularity)) {
         case 'uniform': return createUniformSize(ctx, locationIt, sizeTheme.size, sizeData)
         case 'group': return createGroupSize(ctx, locationIt, sizeTheme.size, sizeData)

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

@@ -11,6 +11,8 @@ import { Loci, isEmptyLoci, EmptyLoci } from 'mol-model/loci';
 import { MarkerAction } from '../mol-geo/geometry/marker-data';
 import { Params, MultiSelectParam } from 'mol-util/parameter';
 import { WebGLContext } from 'mol-gl/webgl/context';
+import { getQualityProps } from './util';
+import { Theme } from 'mol-geo/geometry/geometry';
 // import { ColorTheme } from 'mol-theme/color';
 
 // export interface RepresentationProps {
@@ -68,9 +70,8 @@ export namespace Representation {
             },
             createOrUpdate: (ctx: RepresentationContext, props: Partial<P> = {}, data?: D) => {
                 if (data) currentData = data
-                // const qualityProps = getQualityProps(Object.assign({}, currentProps, props), structure)
-                // currentProps = Object.assign({}, DefaultCartoonProps, currentProps, props, qualityProps)
-                currentProps = Object.assign({}, defaultProps, currentProps, props)
+                const qualityProps = getQualityProps(Object.assign({}, currentProps, props), data)
+                currentProps = Object.assign({}, defaultProps, currentProps, props, qualityProps)
 
                 const { visuals } = currentProps
                 return Task.create(`Creating '${label}' representation`, async runtime => {
@@ -108,13 +109,11 @@ export namespace Representation {
 
 export interface VisualContext extends RepresentationContext {
     runtime: RuntimeContext,
-    // TODO
-    // colorTheme: ColorTheme,
 }
 
 export interface Visual<D, P extends RepresentationProps> {
     readonly renderObject: RenderObject | undefined
-    createOrUpdate: (ctx: VisualContext, props?: Partial<P>, data?: D) => Promise<void>
+    createOrUpdate: (ctx: VisualContext, theme: Theme, props?: Partial<P>, data?: D) => Promise<void>
     getLoci: (pickingId: PickingId) => Loci
     mark: (loci: Loci, action: MarkerAction) => boolean
     destroy: () => void

+ 3 - 2
src/mol-repr/shape/index.ts

@@ -15,7 +15,7 @@ import { OrderedSet, Interval } from 'mol-data/int';
 import { paramDefaultValues, SelectParam } from 'mol-util/parameter';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { createIdentityTransform } from 'mol-geo/geometry/transform-data';
-import { createRenderableState } from 'mol-geo/geometry/geometry';
+import { createRenderableState, createTheme } 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';
@@ -49,9 +49,10 @@ export function ShapeRepresentation<P extends ShapeProps>(): ShapeRepresentation
 
             const mesh = _shape.mesh
             const locationIt = ShapeGroupIterator.fromShape(_shape)
+            const theme = createTheme(currentProps)
             const transform = createIdentityTransform()
 
-            const values = await Mesh.createValues(runtime, mesh, transform, locationIt, currentProps)
+            const values = await Mesh.createValues(runtime, mesh, transform, locationIt, theme, currentProps)
             const state = createRenderableState(currentProps)
 
             _renderObject = createMeshRenderObject(values, state)

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

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

+ 15 - 15
src/mol-repr/structure/complex-visual.ts

@@ -15,7 +15,7 @@ import { Interval } from 'mol-data/int';
 import { MultiSelectParam, paramDefaultValues } from 'mol-util/parameter';
 import { RenderableValues } from 'mol-gl/renderable/schema';
 import { createSizes } from 'mol-geo/geometry/size-data';
-import { Geometry, updateRenderableState } from 'mol-geo/geometry/geometry';
+import { Geometry, updateRenderableState, Theme } 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';
@@ -36,7 +36,7 @@ type ComplexRenderObject = MeshRenderObject | LinesRenderObject | PointsRenderOb
 
 interface ComplexVisualBuilder<P extends ComplexProps, G extends Geometry> {
     defaultProps: P
-    createGeometry(ctx: VisualContext, structure: Structure, props: P, geometry?: G): Promise<G>
+    createGeometry(ctx: VisualContext, structure: Structure, theme: Theme, props: P, geometry?: G): Promise<G>
     createLocationIterator(structure: Structure): LocationIterator
     getLoci(pickingId: PickingId, structure: Structure, id: number): Loci
     mark(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean): boolean,
@@ -45,7 +45,7 @@ interface ComplexVisualBuilder<P extends ComplexProps, G extends Geometry> {
 
 interface ComplexVisualGeometryBuilder<P extends ComplexProps, G extends Geometry> extends ComplexVisualBuilder<P, G> {
     createEmptyGeometry(geometry?: G): G
-    createRenderObject(ctx: VisualContext, structure: Structure, geometry: Geometry, locationIt: LocationIterator, currentProps: P): Promise<ComplexRenderObject>
+    createRenderObject(ctx: VisualContext, structure: Structure, geometry: Geometry, locationIt: LocationIterator, theme: Theme, currentProps: P): Promise<ComplexRenderObject>
     updateValues(values: RenderableValues, newProps: P): void
 }
 
@@ -61,18 +61,18 @@ export function ComplexVisual<P extends ComplexMeshProps>(builder: ComplexVisual
     let locationIt: LocationIterator
     let conformationHash: number
 
-    async function create(ctx: VisualContext, structure: Structure, props: Partial<P> = {}) {
-        currentProps = Object.assign({}, defaultProps, props, { structure })
+    async function create(ctx: VisualContext, structure: Structure, theme: Theme, props: Partial<P> = {}) {
+        currentProps = Object.assign({}, defaultProps, props)
         currentStructure = structure
 
         conformationHash = Structure.conformationHash(currentStructure)
-        geometry = await createGeometry(ctx, currentStructure, currentProps, geometry)
+        geometry = await createGeometry(ctx, currentStructure, theme, currentProps, geometry)
 
         locationIt = createLocationIterator(structure)
-        renderObject = await createRenderObject(ctx, structure, geometry, locationIt, currentProps)
+        renderObject = await createRenderObject(ctx, structure, geometry, locationIt, theme, currentProps)
     }
 
-    async function update(ctx: VisualContext, props: Partial<P>) {
+    async function update(ctx: VisualContext, theme: Theme, props: Partial<P>) {
         const newProps = Object.assign({}, currentProps, props, { structure: currentStructure })
 
         if (!renderObject) return false
@@ -93,7 +93,7 @@ export function ComplexVisual<P extends ComplexMeshProps>(builder: ComplexVisual
         //
 
         if (updateState.createGeometry) {
-            geometry = await createGeometry(ctx, currentStructure, newProps, geometry)
+            geometry = await createGeometry(ctx, currentStructure, theme, newProps, geometry)
             ValueCell.update(renderObject.values.drawCount, Geometry.getDrawCount(geometry))
             updateState.updateColor = true
         }
@@ -101,12 +101,12 @@ export function ComplexVisual<P extends ComplexMeshProps>(builder: ComplexVisual
         if (updateState.updateSize) {
             // not all geometries have size data, so check here
             if ('uSize' in renderObject.values) {
-                await createSizes(ctx.runtime, locationIt, newProps, renderObject.values)
+                await createSizes(ctx.runtime, locationIt, theme.size, renderObject.values)
             }
         }
 
         if (updateState.updateColor) {
-            await createColors(ctx.runtime, locationIt, newProps, renderObject.values)
+            await createColors(ctx.runtime, locationIt, theme.color, renderObject.values)
         }
 
         updateValues(renderObject.values, newProps)
@@ -118,18 +118,18 @@ export function ComplexVisual<P extends ComplexMeshProps>(builder: ComplexVisual
 
     return {
         get renderObject () { return renderObject },
-        async createOrUpdate(ctx: VisualContext, props: Partial<P> = {}, structure?: Structure) {
+        async createOrUpdate(ctx: VisualContext, theme: Theme, props: Partial<P> = {}, structure?: Structure) {
             if (!structure && !currentStructure) {
                 throw new Error('missing structure')
             } else if (structure && (!currentStructure || !renderObject)) {
-                await create(ctx, structure, props)
+                await create(ctx, structure, theme, props)
             } else if (structure && structure.hashCode !== currentStructure.hashCode) {
-                await create(ctx, structure, props)
+                await create(ctx, structure, theme, props)
             } else {
                 if (structure && Structure.conformationHash(structure) !== Structure.conformationHash(currentStructure)) {
                     currentStructure = structure
                 }
-                await update(ctx, props)
+                await update(ctx, theme, props)
             }
         },
         getLoci(pickingId: PickingId) {

+ 6 - 6
src/mol-repr/structure/representation/cartoon.ts

@@ -12,17 +12,17 @@ import { paramDefaultValues, SelectParam, NumberParam } from 'mol-util/parameter
 import { UnitsRepresentation } from '../units-representation';
 import { StructureRepresentation } from '../index';
 import { Representation } from 'mol-repr';
-// import { PolymerDirectionVisual, DefaultPolymerDirectionProps } from '../visual/polymer-direction-wedge';
+import { PolymerDirectionVisual, PolymerDirectionParams } from '../visual/polymer-direction-wedge';
 
 export const CartoonParams = {
     ...PolymerTraceParams,
     ...PolymerGapParams,
     ...NucleotideBlockParams,
-    // ...PolymerDirectionParams,
+    ...PolymerDirectionParams,
     sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions),
-    sizeValue: NumberParam('Size Value', '', 0.6, 0, 10, 0.1),
+    sizeValue: NumberParam('Size Value', '', 0.2, 0, 10, 0.1),
 }
-export const DefaultCartoonProps = paramDefaultValues(CartoonParams)
+export const DefaultCartoonProps = { ...paramDefaultValues(CartoonParams), visuals: [ '0', '1', '2' ] }
 export type CartoonProps = typeof DefaultCartoonProps
 
 export type CartoonRepresentation = StructureRepresentation<CartoonProps>
@@ -32,6 +32,6 @@ export function CartoonRepresentation(): CartoonRepresentation {
         UnitsRepresentation('Polymer trace mesh', PolymerTraceVisual),
         UnitsRepresentation('Polymer gap cylinder', PolymerGapVisual),
         UnitsRepresentation('Nucleotide block mesh', NucleotideBlockVisual),
-        // UnitsRepresentation('Polymer direction wedge', PolymerDirectionVisual)
-    ] as StructureRepresentation<CartoonProps>[])
+        UnitsRepresentation('Polymer direction wedge', PolymerDirectionVisual)
+    ] as unknown as StructureRepresentation<CartoonProps>[]) // TODO avoid cast to unknown
 }

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

@@ -14,6 +14,7 @@ import { StructureGroup } from './units-visual';
 import { StructureProps, StructureParams, StructureRepresentation } from './index';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { MarkerAction } from 'mol-geo/geometry/marker-data';
+import { Theme, createTheme } from 'mol-geo/geometry/geometry';
 
 export interface UnitsVisual<P extends RepresentationProps = {}> extends Visual<StructureGroup, P> { }
 
@@ -21,11 +22,13 @@ export function UnitsRepresentation<P extends StructureProps>(label: string, vis
     let visuals = new Map<number, { group: Unit.SymmetryGroup, visual: UnitsVisual<P> }>()
 
     let _props: P
+    let _theme: Theme
     let _structure: Structure
     let _groups: ReadonlyArray<Unit.SymmetryGroup>
 
     function createOrUpdate(ctx: RepresentationContext, props: Partial<P> = {}, structure?: Structure) {
-        _props = Object.assign({}, _props, props)
+        _props = Object.assign({}, _props, props, { structure: structure || _structure })
+        _theme = createTheme(_props)
 
         return Task.create('Creating or updating UnitsRepresentation', async runtime => {
             if (!_structure && !structure) {
@@ -37,7 +40,7 @@ export function UnitsRepresentation<P extends StructureProps>(label: string, vis
                 for (let i = 0; i < _groups.length; i++) {
                     const group = _groups[i];
                     const visual = visualCtor()
-                    await visual.createOrUpdate({ ...ctx, runtime }, _props, { group, structure })
+                    await visual.createOrUpdate({ ...ctx, runtime }, _theme, _props, { group, structure })
                     visuals.set(group.hashCode, { visual, group })
                 }
             } else if (structure && _structure.hashCode !== structure.hashCode) {
@@ -53,13 +56,13 @@ export function UnitsRepresentation<P extends StructureProps>(label: string, vis
                     const visualGroup = oldVisuals.get(group.hashCode)
                     if (visualGroup) {
                         const { visual } = visualGroup
-                        await visual.createOrUpdate({ ...ctx, runtime }, _props, { group, structure })
+                        await visual.createOrUpdate({ ...ctx, runtime }, _theme, _props, { group, structure })
                         visuals.set(group.hashCode, { visual, group })
                         oldVisuals.delete(group.hashCode)
                     } else {
                         // newGroups.push(group)
                         const visual = visualCtor()
-                        await visual.createOrUpdate({ ...ctx, runtime }, _props, { group, structure })
+                        await visual.createOrUpdate({ ...ctx, runtime }, _theme, _props, { group, structure })
                         visuals.set(group.hashCode, { visual, group })
                     }
                 }
@@ -85,7 +88,7 @@ export function UnitsRepresentation<P extends StructureProps>(label: string, vis
                     const group = _groups[i];
                     const visualGroup = visuals.get(group.hashCode)
                     if (visualGroup) {
-                        await visualGroup.visual.createOrUpdate({ ...ctx, runtime }, _props, { group, structure })
+                        await visualGroup.visual.createOrUpdate({ ...ctx, runtime }, _theme, _props, { group, structure })
                         visualGroup.group = group
                     } else {
                         throw new Error(`expected to find visual for hashCode ${group.hashCode}`)
@@ -98,7 +101,7 @@ export function UnitsRepresentation<P extends StructureProps>(label: string, vis
                 visuals.forEach(({ visual, group }) => visualsList.push([ visual, group ]))
                 for (let i = 0, il = visualsList.length; i < il; ++i) {
                     const [ visual, group ] = visualsList[i]
-                    await visual.createOrUpdate({ ...ctx, runtime }, _props, { group, structure: _structure })
+                    await visual.createOrUpdate({ ...ctx, runtime }, _theme, _props, { group, structure: _structure })
                 }
             }
             if (structure) _structure = structure

+ 14 - 14
src/mol-repr/structure/units-visual.ts

@@ -14,7 +14,7 @@ import { deepEqual, ValueCell, UUID } from 'mol-util';
 import { Interval } from 'mol-data/int';
 import { MultiSelectParam, paramDefaultValues } from 'mol-util/parameter';
 import { RenderableValues } from 'mol-gl/renderable/schema';
-import { Geometry, updateRenderableState } from 'mol-geo/geometry/geometry';
+import { Geometry, updateRenderableState, Theme } 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';
@@ -48,7 +48,7 @@ type UnitsRenderObject = MeshRenderObject | LinesRenderObject | PointsRenderObje
 
 interface UnitsVisualBuilder<P extends UnitsProps, G extends Geometry> {
     defaultProps: P
-    createGeometry(ctx: VisualContext, unit: Unit, structure: Structure, props: P, geometry?: G): Promise<G>
+    createGeometry(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: P, geometry?: G): Promise<G>
     createLocationIterator(group: Unit.SymmetryGroup): LocationIterator
     getLoci(pickingId: PickingId, group: Unit.SymmetryGroup, id: number): Loci
     mark(loci: Loci, group: Unit.SymmetryGroup, apply: (interval: Interval) => boolean): boolean
@@ -57,7 +57,7 @@ interface UnitsVisualBuilder<P extends UnitsProps, G extends Geometry> {
 
 interface UnitsVisualGeometryBuilder<P extends UnitsProps, G extends Geometry> extends UnitsVisualBuilder<P, G> {
     createEmptyGeometry(geometry?: G): G
-    createRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, geometry: Geometry, locationIt: LocationIterator, currentProps: P): Promise<UnitsRenderObject>
+    createRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, geometry: Geometry, locationIt: LocationIterator, theme: Theme, currentProps: P): Promise<UnitsRenderObject>
     updateValues(values: RenderableValues, newProps: P): void
 }
 
@@ -74,22 +74,22 @@ export function UnitsVisual<P extends UnitsProps>(builder: UnitsVisualGeometryBu
     let locationIt: LocationIterator
     let currentConformationId: UUID
 
-    async function create(ctx: VisualContext, group: Unit.SymmetryGroup, props: Partial<P> = {}) {
+    async function create(ctx: VisualContext, group: Unit.SymmetryGroup, theme: Theme, props: Partial<P> = {}) {
         currentProps = Object.assign({}, defaultProps, props, { structure: currentStructure })
         currentGroup = group
 
         const unit = group.units[0]
         currentConformationId = Unit.conformationId(unit)
         geometry = includesUnitKind(currentProps.unitKinds, unit)
-            ? await createGeometry(ctx, unit, currentStructure, currentProps, geometry)
+            ? await createGeometry(ctx, unit, currentStructure, theme, currentProps, geometry)
             : createEmptyGeometry(geometry)
 
         // TODO create empty location iterator when not in unitKinds
         locationIt = createLocationIterator(group)
-        renderObject = await createRenderObject(ctx, group, geometry, locationIt, currentProps)
+        renderObject = await createRenderObject(ctx, group, geometry, locationIt, theme, currentProps)
     }
 
-    async function update(ctx: VisualContext, props: Partial<P> = {}) {
+    async function update(ctx: VisualContext, theme: Theme, props: Partial<P> = {}) {
         if (!renderObject) return
 
         const newProps = Object.assign({}, currentProps, props, { structure: currentStructure })
@@ -122,7 +122,7 @@ export function UnitsVisual<P extends UnitsProps>(builder: UnitsVisualGeometryBu
 
         if (updateState.createGeometry) {
             geometry = includesUnitKind(newProps.unitKinds, unit)
-                ? await createGeometry(ctx, unit, currentStructure, newProps, geometry)
+                ? await createGeometry(ctx, unit, currentStructure, theme, newProps, geometry)
                 : createEmptyGeometry(geometry)
             ValueCell.update(renderObject.values.drawCount, Geometry.getDrawCount(geometry))
             updateState.updateColor = true
@@ -131,12 +131,12 @@ export function UnitsVisual<P extends UnitsProps>(builder: UnitsVisualGeometryBu
         if (updateState.updateSize) {
             // not all geometries have size data, so check here
             if ('uSize' in renderObject.values) {
-                await createSizes(ctx.runtime, locationIt, newProps, renderObject.values)
+                await createSizes(ctx.runtime, locationIt, theme.size, renderObject.values)
             }
         }
 
         if (updateState.updateColor) {
-            await createColors(ctx.runtime, locationIt, newProps, renderObject.values)
+            await createColors(ctx.runtime, locationIt, theme.color, renderObject.values)
         }
 
         updateValues(renderObject.values, newProps)
@@ -147,24 +147,24 @@ export function UnitsVisual<P extends UnitsProps>(builder: UnitsVisualGeometryBu
 
     return {
         get renderObject () { return renderObject },
-        async createOrUpdate(ctx: VisualContext, props: Partial<P> = {}, structureGroup?: StructureGroup) {
+        async createOrUpdate(ctx: VisualContext, theme: Theme, props: Partial<P> = {}, structureGroup?: StructureGroup) {
             if (structureGroup) currentStructure = structureGroup.structure
             const group = structureGroup ? structureGroup.group : undefined
             if (!group && !currentGroup) {
                 throw new Error('missing group')
             } else if (group && (!currentGroup || !renderObject)) {
                 // console.log('unit-visual first create')
-                await create(ctx, group, props)
+                await create(ctx, group, theme, props)
             } else if (group && group.hashCode !== currentGroup.hashCode) {
                 // console.log('unit-visual group.hashCode !== currentGroup.hashCode')
-                await create(ctx, group, props)
+                await create(ctx, group, theme, props)
             } else {
                 // console.log('unit-visual update')
                 if (group && !sameGroupConformation(group, currentGroup)) {
                     // console.log('unit-visual new conformation')
                     currentGroup = group
                 }
-                await update(ctx, props)
+                await update(ctx, theme, props)
             }
         },
         getLoci(pickingId: PickingId) {

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

@@ -10,7 +10,7 @@ import { Vec3 } from 'mol-math/linear-algebra';
 import { createLinkCylinderMesh, LinkCylinderProps, LinkCylinderParams } from './util/link';
 import { OrderedSet, Interval } from 'mol-data/int';
 import { ComplexMeshVisual, ComplexVisual } from '../complex-visual';
-import { SizeTheme, SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
+import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
 import { LinkType } from 'mol-model/structure/model/types';
 import { BitFlags } from 'mol-util';
 import { UnitsMeshParams } from '../units-visual';
@@ -20,6 +20,7 @@ import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { VisualUpdateState } from '../../util';
 import { VisualContext } from 'mol-repr';
+import { Theme } from 'mol-geo/geometry/geometry';
 
 // TODO create seperate visual
 // for (let i = 0, il = carbohydrates.terminalLinks.length; i < il; ++i) {
@@ -35,9 +36,8 @@ import { VisualContext } from 'mol-repr';
 
 const radiusFactor = 0.3
 
-async function createCarbohydrateLinkCylinderMesh(ctx: VisualContext, structure: Structure, props: LinkCylinderProps, mesh?: Mesh) {
+async function createCarbohydrateLinkCylinderMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: LinkCylinderProps, mesh?: Mesh) {
     const { links, elements } = structure.carbohydrates
-    const sizeTheme = SizeTheme({ name: props.sizeTheme, value: props.sizeValue })
     const location = StructureElement.create()
 
     const builderProps = {
@@ -54,7 +54,7 @@ async function createCarbohydrateLinkCylinderMesh(ctx: VisualContext, structure:
             const l = links[edgeIndex]
             location.unit = elements[l.carbohydrateIndexA].unit
             location.element = elements[l.carbohydrateIndexA].anomericCarbon
-            return sizeTheme.size(location) * radiusFactor
+            return theme.size.size(location) * radiusFactor
         }
     }
 

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

@@ -13,7 +13,7 @@ import { DiamondPrism, PentagonalPrism, HexagonalPrism } from 'mol-geo/primitive
 import { Structure, StructureElement } from 'mol-model/structure';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
-import { SizeTheme, SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
+import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
 import { getSaccharideShape, SaccharideShapes } from 'mol-model/structure/structure/carbohydrates/constants';
 import { addSphere } from 'mol-geo/geometry/mesh/builder/sphere';
 import { ComplexMeshParams, ComplexMeshVisual } from '../complex-visual';
@@ -25,6 +25,7 @@ import { PickingId } from 'mol-geo/geometry/picking';
 import { OrderedSet, Interval } from 'mol-data/int';
 import { EmptyLoci, Loci } from 'mol-model/loci';
 import { VisualContext } from 'mol-repr';
+import { Theme } from 'mol-geo/geometry/geometry';
 
 const t = Mat4.identity()
 const sVec = Vec3.zero()
@@ -44,10 +45,9 @@ const diamondPrism = DiamondPrism()
 const pentagonalPrism = PentagonalPrism()
 const hexagonalPrism = HexagonalPrism()
 
-async function createCarbohydrateSymbolMesh(ctx: VisualContext, structure: Structure, props: CarbohydrateSymbolProps, mesh?: Mesh) {
+async function createCarbohydrateSymbolMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: CarbohydrateSymbolProps, mesh?: Mesh) {
     const builder = MeshBuilder.create(256, 128, mesh)
 
-    const sizeTheme = SizeTheme({ name: props.sizeTheme, value: props.sizeValue })
     const { detail } = props
 
     const carbohydrates = structure.carbohydrates
@@ -60,7 +60,7 @@ async function createCarbohydrateSymbolMesh(ctx: VisualContext, structure: Struc
 
         l.unit = c.unit
         l.element = c.unit.elements[c.anomericCarbon]
-        const size = sizeTheme.size(l)
+        const size = theme.size.size(l)
         const radius = size * radiusFactor
         const side = size * sideFactor
 

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

@@ -12,7 +12,7 @@ import { Vec3 } from 'mol-math/linear-algebra';
 import { Loci, EmptyLoci } from 'mol-model/loci';
 import { ComplexMeshVisual, ComplexMeshParams } from '../complex-visual';
 import { Interval } from 'mol-data/int';
-import { SizeTheme, SizeThemeOptions, SizeThemeName } from 'mol-theme/size';
+import { SizeThemeOptions, SizeThemeName } from 'mol-theme/size';
 import { BitFlags } from 'mol-util';
 import { LinkType } from 'mol-model/structure/model/types';
 import { SelectParam, NumberParam, paramDefaultValues } from 'mol-util/parameter';
@@ -20,13 +20,13 @@ import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { VisualContext } from 'mol-repr';
+import { Theme } from 'mol-geo/geometry/geometry';
 
-async function createCrossLinkRestraintCylinderMesh(ctx: VisualContext, structure: Structure, props: LinkCylinderProps, mesh?: Mesh) {
+async function createCrossLinkRestraintCylinderMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: LinkCylinderProps, mesh?: Mesh) {
 
     const crossLinks = structure.crossLinkRestraints
     if (!crossLinks.count) return Mesh.createEmpty(mesh)
 
-    const sizeTheme = SizeTheme({ name: props.sizeTheme, value: props.sizeValue })
     const location = StructureElement.create()
 
     const builderProps = {
@@ -44,7 +44,7 @@ async function createCrossLinkRestraintCylinderMesh(ctx: VisualContext, structur
             const b = crossLinks.pairs[edgeIndex]
             location.unit = b.unitA
             location.element = b.unitA.elements[b.indexA]
-            return sizeTheme.size(location)
+            return theme.size.size(location)
         }
     }
 

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

@@ -15,6 +15,7 @@ import { SelectParam, NumberParam, BooleanParam, paramDefaultValues } from 'mol-
 import { Points } from 'mol-geo/geometry/points/points';
 import { PointsBuilder } from 'mol-geo/geometry/points/points-builder';
 import { VisualContext } from 'mol-repr';
+import { Theme } from 'mol-geo/geometry/geometry';
 
 export const ElementPointParams = {
     ...UnitsPointsParams,
@@ -27,7 +28,7 @@ export type ElementPointProps = typeof DefaultElementPointProps
 
 // TODO size
 
-export async function createElementPoint(ctx: VisualContext, unit: Unit, structure: Structure, props: ElementPointProps, points: Points) {
+export async function createElementPoint(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: ElementPointProps, points: Points) {
     const elements = unit.elements
     const n = elements.length
     const builder = PointsBuilder.create(n, n / 10, points)

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

@@ -17,6 +17,7 @@ import { paramDefaultValues, SelectParam, NumberParam, BooleanParam } from 'mol-
 import { Points } from 'mol-geo/geometry/points/points';
 import { PointsBuilder } from 'mol-geo/geometry/points/points-builder';
 import { VisualContext } from 'mol-repr';
+import { Theme } from 'mol-geo/geometry/geometry';
 
 export const GaussianDensityPointParams = {
     ...UnitsPointsParams,
@@ -28,7 +29,7 @@ export const GaussianDensityPointParams = {
 export const DefaultGaussianDensityPointProps = paramDefaultValues(GaussianDensityPointParams)
 export type GaussianDensityPointProps = typeof DefaultGaussianDensityPointProps
 
-export async function createGaussianDensityPoint(ctx: VisualContext, unit: Unit, structure: Structure, props: GaussianDensityProps, points?: Points) {
+export async function createGaussianDensityPoint(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: GaussianDensityProps, points?: Points) {
     const { transform, field: { space, data } } = await unit.computeGaussianDensity(props, ctx.runtime, ctx.webgl)
 
     const { dimensions, get } = space

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

@@ -13,8 +13,9 @@ import { GaussianDensityProps, GaussianDensityParams, computeUnitGaussianDensity
 import { paramDefaultValues } from 'mol-util/parameter';
 import { DirectVolume } from 'mol-geo/geometry/direct-volume/direct-volume';
 import { VisualContext } from 'mol-repr';
+import { Theme } from 'mol-geo/geometry/geometry';
 
-async function createGaussianDensityVolume(ctx: VisualContext, unit: Unit, structure: Structure, props: GaussianDensityProps, directVolume?: DirectVolume): Promise<DirectVolume> {
+async function createGaussianDensityVolume(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: GaussianDensityProps, directVolume?: DirectVolume): Promise<DirectVolume> {
     const { runtime, webgl } = ctx
     if (webgl === undefined) throw new Error('createGaussianDensityVolume requires `webgl` object in VisualContext')
 

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

@@ -14,8 +14,9 @@ import { paramDefaultValues } from 'mol-util/parameter';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { computeMarchingCubesMesh } from 'mol-geo/util/marching-cubes/algorithm';
 import { VisualContext } from 'mol-repr';
+import { Theme } from 'mol-geo/geometry/geometry';
 
-async function createGaussianSurfaceMesh(ctx: VisualContext, unit: Unit, structure: Structure, props: GaussianDensityProps, mesh?: Mesh): Promise<Mesh> {
+async function createGaussianSurfaceMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: GaussianDensityProps, mesh?: Mesh): Promise<Mesh> {
     const { smoothness } = props
     const { transform, field, idField } = await unit.computeGaussianDensity(props, ctx.runtime, ctx.webgl)
 

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

@@ -15,8 +15,9 @@ import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
 import { Lines } from 'mol-geo/geometry/lines/lines';
 import { computeMarchingCubesLines } from 'mol-geo/util/marching-cubes/algorithm';
 import { VisualContext } from 'mol-repr';
+import { Theme } from 'mol-geo/geometry/geometry';
 
-async function createGaussianWireframe(ctx: VisualContext, unit: Unit, structure: Structure, props: GaussianDensityProps, lines?: Lines): Promise<Lines> {
+async function createGaussianWireframe(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: GaussianDensityProps, lines?: Lines): Promise<Lines> {
     const { smoothness } = props
     const { transform, field, idField } = await unit.computeGaussianDensity(props, ctx.runtime)
 

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

@@ -12,20 +12,19 @@ import { Vec3 } from 'mol-math/linear-algebra';
 import { Loci, EmptyLoci } from 'mol-model/loci';
 import { ComplexMeshVisual, ComplexMeshParams } from '../complex-visual';
 import { Interval } from 'mol-data/int';
-import { SizeTheme, SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
 import { BitFlags } from 'mol-util';
-import { SelectParam, NumberParam, paramDefaultValues } from 'mol-util/parameter';
+import { paramDefaultValues } from 'mol-util/parameter';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { VisualContext } from 'mol-repr';
+import { Theme } from 'mol-geo/geometry/geometry';
 
-async function createInterUnitLinkCylinderMesh(ctx: VisualContext, structure: Structure, props: LinkCylinderProps, mesh?: Mesh) {
+async function createInterUnitLinkCylinderMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: LinkCylinderProps, mesh?: Mesh) {
     const links = structure.links
     const { bondCount, bonds } = links
 
     if (!bondCount) return Mesh.createEmpty(mesh)
 
-    const sizeTheme = SizeTheme({ name: props.sizeTheme, value: props.sizeValue, factor: props.sizeFactor })
     const location = StructureElement.create()
 
     const builderProps = {
@@ -43,7 +42,7 @@ async function createInterUnitLinkCylinderMesh(ctx: VisualContext, structure: St
             const b = bonds[edgeIndex]
             location.unit = b.unitA
             location.element = b.unitA.elements[b.indexA]
-            return sizeTheme.size(location)
+            return theme.size.size(location)
         }
     }
 
@@ -53,9 +52,6 @@ async function createInterUnitLinkCylinderMesh(ctx: VisualContext, structure: St
 export const InterUnitLinkParams = {
     ...ComplexMeshParams,
     ...LinkCylinderParams,
-    sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions),
-    sizeValue: NumberParam('Size Value', '', 0.2, 0, 10, 0.1),
-    sizeFactor: NumberParam('Size Factor', '', 1, 0, 10, 0.1),
 }
 export const DefaultInterUnitLinkProps = paramDefaultValues(InterUnitLinkParams)
 export type InterUnitLinkProps = typeof DefaultInterUnitLinkProps

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

@@ -13,17 +13,17 @@ import { Vec3 } from 'mol-math/linear-algebra';
 import { Loci, EmptyLoci } from 'mol-model/loci';
 import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual';
 import { Interval } from 'mol-data/int';
-import { SizeTheme, SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
+import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
 import { BitFlags } from 'mol-util';
 import { SelectParam, NumberParam, paramDefaultValues } from 'mol-util/parameter';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { VisualContext } from 'mol-repr';
+import { Theme } from 'mol-geo/geometry/geometry';
 
-async function createIntraUnitLinkCylinderMesh(ctx: VisualContext, unit: Unit, structure: Structure, props: LinkCylinderProps, mesh?: Mesh) {
+async function createIntraUnitLinkCylinderMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: LinkCylinderProps, mesh?: Mesh) {
     if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh)
 
-    const sizeTheme = SizeTheme({ name: props.sizeTheme, value: props.sizeValue, factor: props.sizeFactor })
     const location = StructureElement.create(unit)
 
     const elements = unit.elements;
@@ -57,7 +57,7 @@ async function createIntraUnitLinkCylinderMesh(ctx: VisualContext, unit: Unit, s
         flags: (edgeIndex: number) => BitFlags.create(_flags[edgeIndex]),
         radius: (edgeIndex: number) => {
             location.element = elements[a[edgeIndex]]
-            return sizeTheme.size(location)
+            return theme.size.size(location)
         }
     }
 

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

@@ -18,6 +18,7 @@ import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
 import { addCylinder } from 'mol-geo/geometry/mesh/builder/cylinder';
 import { VisualContext } from 'mol-repr';
+import { Theme } from 'mol-geo/geometry/geometry';
 
 const p1 = Vec3.zero()
 const p2 = Vec3.zero()
@@ -34,7 +35,7 @@ const sVec = Vec3.zero()
 const box = Box()
 
 // TODO define props, should be scalable
-async function createNucleotideBlockMesh(ctx: VisualContext, unit: Unit, structure: Structure, props: {}, mesh?: Mesh) {
+async function createNucleotideBlockMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: {}, mesh?: Mesh) {
     if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh)
 
     // TODO better vertex count estimate

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

@@ -11,28 +11,25 @@ import { PolymerBackboneIterator } from './util/polymer';
 import { getElementLoci, markElement, StructureElementIterator } from './util/element';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual';
-import { SizeTheme, SizeThemeOptions, SizeThemeName } from 'mol-theme/size';
 import { OrderedSet } from 'mol-data/int';
-import { paramDefaultValues, NumberParam, SelectParam } from 'mol-util/parameter';
+import { paramDefaultValues, NumberParam } from 'mol-util/parameter';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
 import { CylinderProps } from 'mol-geo/primitive/cylinder';
 import { addCylinder } from 'mol-geo/geometry/mesh/builder/cylinder';
 import { VisualContext } from 'mol-repr';
+import { Theme } from 'mol-geo/geometry/geometry';
 
 export const PolymerBackboneCylinderParams = {
-    sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions),
-    sizeValue: NumberParam('Size Value', '', 1, 0, 10, 0.1),
     radialSegments: NumberParam('Radial Segments', '', 16, 3, 56, 1),
 }
 export const DefaultPolymerBackboneCylinderProps = paramDefaultValues(PolymerBackboneCylinderParams)
 export type PolymerBackboneCylinderProps = typeof DefaultPolymerBackboneCylinderProps
 
-async function createPolymerBackboneCylinderMesh(ctx: VisualContext, unit: Unit, structure: Structure, props: PolymerBackboneCylinderProps, mesh?: Mesh) {
+async function createPolymerBackboneCylinderMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PolymerBackboneCylinderProps, mesh?: Mesh) {
     const polymerElementCount = unit.polymerElements.length
     if (!polymerElementCount) return Mesh.createEmpty(mesh)
 
-    const sizeTheme = SizeTheme({ name: props.sizeTheme, value: props.sizeValue })
     const { radialSegments } = props
 
     const vertexCountEstimate = radialSegments * 2 * polymerElementCount * 2
@@ -51,11 +48,11 @@ async function createPolymerBackboneCylinderMesh(ctx: VisualContext, unit: Unit,
         pos(centerA.element, pA)
         pos(centerB.element, pB)
 
-        cylinderProps.radiusTop = cylinderProps.radiusBottom = sizeTheme.size(centerA)
+        cylinderProps.radiusTop = cylinderProps.radiusBottom = theme.size.size(centerA)
         builder.setGroup(OrderedSet.indexOf(elements, centerA.element))
         addCylinder(builder, pA, pB, 0.5, cylinderProps)
 
-        cylinderProps.radiusTop = cylinderProps.radiusBottom = sizeTheme.size(centerB)
+        cylinderProps.radiusTop = cylinderProps.radiusBottom = theme.size.size(centerB)
         builder.setGroup(OrderedSet.indexOf(elements, centerB.element))
         addCylinder(builder, pB, pA, 0.5, cylinderProps)
 

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

@@ -10,12 +10,13 @@ import { PolymerTraceIterator, createCurveSegmentState, interpolateCurveSegment,
 import { Vec3, Mat4 } from 'mol-math/linear-algebra';
 import { SecondaryStructureType, isNucleic } from 'mol-model/structure/model/types';
 import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual';
-import { SizeTheme, SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
+import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
 import { SelectParam, NumberParam, paramDefaultValues } from 'mol-util/parameter';
 import { Wedge } from 'mol-geo/primitive/wedge';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
 import { VisualContext } from 'mol-repr';
+import { Theme } from 'mol-geo/geometry/geometry';
 
 const t = Mat4.identity()
 const sVec = Vec3.zero()
@@ -36,12 +37,10 @@ export const PolymerDirectionWedgeParams = {
 export const DefaultPolymerDirectionWedgeProps = paramDefaultValues(PolymerDirectionWedgeParams)
 export type PolymerDirectionWedgeProps = typeof DefaultPolymerDirectionWedgeProps
 
-async function createPolymerDirectionWedgeMesh(ctx: VisualContext, unit: Unit, structure: Structure, props: PolymerDirectionWedgeProps, mesh?: Mesh) {
+async function createPolymerDirectionWedgeMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PolymerDirectionWedgeProps, mesh?: Mesh) {
     const polymerElementCount = unit.polymerElements.length
     if (!polymerElementCount) return Mesh.createEmpty(mesh)
 
-    const sizeTheme = SizeTheme({ name: props.sizeTheme, value: props.sizeValue })
-
     const vertexCount = polymerElementCount * 24
     const builder = MeshBuilder.create(vertexCount, vertexCount / 10, mesh)
     const linearSegments = 1
@@ -63,7 +62,7 @@ async function createPolymerDirectionWedgeMesh(ctx: VisualContext, unit: Unit, s
         interpolateCurveSegment(state, v, tension, shift)
 
         if ((isSheet && !v.secStrucChange) || !isSheet) {
-            const size = sizeTheme.size(v.center)
+            const size = theme.size.size(v.center)
             const depth = depthFactor * size
             const width = widthFactor * size
             const height = heightFactor * size

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

@@ -10,7 +10,7 @@ import { VisualUpdateState } from '../../util';
 import { PolymerGapIterator, PolymerGapLocationIterator, markPolymerGapElement, getPolymerGapElementLoci } from './util/polymer';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual';
-import { SizeTheme, SizeThemeOptions, SizeThemeName } from 'mol-theme/size';
+import { SizeThemeOptions, SizeThemeName } from 'mol-theme/size';
 import { SelectParam, NumberParam, paramDefaultValues } from 'mol-util/parameter';
 import { LinkCylinderParams } from './util/link';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
@@ -19,23 +19,20 @@ import { CylinderProps } from 'mol-geo/primitive/cylinder';
 import { addSphere } from 'mol-geo/geometry/mesh/builder/sphere';
 import { addFixedCountDashedCylinder } from 'mol-geo/geometry/mesh/builder/cylinder';
 import { VisualContext } from 'mol-repr';
+import { Theme } from 'mol-geo/geometry/geometry';
 
 const segmentCount = 10
 
 export const PolymerGapCylinderParams = {
-    sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions),
-    sizeValue: NumberParam('Size Value', '', 1, 0, 10, 0.1),
-    sizeFactor: NumberParam('Size Factor', '', 0.3, 0, 10, 0.1),
     radialSegments: NumberParam('Radial Segments', '', 16, 3, 56, 1),
 }
 export const DefaultPolymerGapCylinderProps = paramDefaultValues(PolymerGapCylinderParams)
 export type PolymerGapCylinderProps = typeof DefaultPolymerGapCylinderProps
 
-async function createPolymerGapCylinderMesh(ctx: VisualContext, unit: Unit, structure: Structure, props: PolymerGapCylinderProps, mesh?: Mesh) {
+async function createPolymerGapCylinderMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PolymerGapCylinderProps, mesh?: Mesh) {
     const polymerGapCount = unit.gapElements.length
     if (!polymerGapCount) return Mesh.createEmpty(mesh)
 
-    const sizeTheme = SizeTheme({ name: props.sizeTheme, value: props.sizeValue, factor: props.sizeValue })
     const { radialSegments } = props
 
     const vertexCountEstimate = segmentCount * radialSegments * 2 * polymerGapCount * 2
@@ -60,11 +57,11 @@ async function createPolymerGapCylinderMesh(ctx: VisualContext, unit: Unit, stru
             pos(centerA.element, pA)
             pos(centerB.element, pB)
 
-            cylinderProps.radiusTop = cylinderProps.radiusBottom = sizeTheme.size(centerA)
+            cylinderProps.radiusTop = cylinderProps.radiusBottom = theme.size.size(centerA)
             builder.setGroup(i)
             addFixedCountDashedCylinder(builder, pA, pB, 0.5, segmentCount, cylinderProps)
 
-            cylinderProps.radiusTop = cylinderProps.radiusBottom = sizeTheme.size(centerB)
+            cylinderProps.radiusTop = cylinderProps.radiusBottom = theme.size.size(centerB)
             builder.setGroup(i + 1)
             addFixedCountDashedCylinder(builder, pB, pA, 0.5, segmentCount, cylinderProps)
         }

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

@@ -10,18 +10,15 @@ import { VisualUpdateState } from '../../util';
 import { PolymerTraceIterator, createCurveSegmentState, interpolateCurveSegment, PolymerLocationIterator, getPolymerElementLoci, markPolymerElement } from './util/polymer';
 import { SecondaryStructureType, isNucleic } from 'mol-model/structure/model/types';
 import { UnitsMeshVisual, UnitsMeshParams } from '../units-visual';
-import { SizeTheme, SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
-import { SelectParam, NumberParam, paramDefaultValues } from 'mol-util/parameter';
+import {  NumberParam, paramDefaultValues } from 'mol-util/parameter';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
 import { addSheet } from 'mol-geo/geometry/mesh/builder/sheet';
 import { addTube } from 'mol-geo/geometry/mesh/builder/tube';
 import { VisualContext } from 'mol-repr';
+import { Theme } from 'mol-geo/geometry/geometry';
 
 export const PolymerTraceMeshParams = {
-    sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'physical', SizeThemeOptions),
-    sizeValue: NumberParam('Size Value', '', 1, 0, 20, 0.1),
-    sizeFactor: NumberParam('Size Factor', '', 0.3, 0, 10, 0.1),
     linearSegments: NumberParam('Linear Segments', '', 8, 1, 48, 1),
     radialSegments: NumberParam('Radial Segments', '', 16, 3, 56, 1),
     aspectRatio: NumberParam('Aspect Ratio', '', 5, 0.1, 5, 0.1),
@@ -32,11 +29,10 @@ export type PolymerTraceMeshProps = typeof DefaultPolymerTraceMeshProps
 
 // TODO handle polymer ends properly
 
-async function createPolymerTraceMesh(ctx: VisualContext, unit: Unit, structure: Structure, props: PolymerTraceMeshProps, mesh?: Mesh) {
+async function createPolymerTraceMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PolymerTraceMeshProps, mesh?: Mesh) {
     const polymerElementCount = unit.polymerElements.length
 
     if (!polymerElementCount) return Mesh.createEmpty(mesh)
-    const sizeTheme = SizeTheme({ name: props.sizeTheme, value: props.sizeValue, factor: props.sizeFactor })
     const { linearSegments, radialSegments, aspectRatio, arrowFactor } = props
 
     const vertexCount = linearSegments * radialSegments * polymerElementCount + (radialSegments + 1) * polymerElementCount * 2
@@ -60,7 +56,7 @@ async function createPolymerTraceMesh(ctx: VisualContext, unit: Unit, structure:
 
         interpolateCurveSegment(state, v, tension, shift)
 
-        let width = sizeTheme.size(v.center)
+        let width = theme.size.size(v.center)
         if (isCoarse) width *= aspectRatio / 2
 
         if (isSheet) {

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

@@ -11,7 +11,7 @@ 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 { createRenderableState } from 'mol-geo/geometry/geometry';
+import { createRenderableState, Theme } 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';
@@ -49,16 +49,16 @@ export function includesUnitKind(unitKinds: UnitKind[], unit: Unit) {
 
 type StructureMeshProps = Mesh.Props & StructureProps
 
-export async function createComplexMeshRenderObject(ctx: VisualContext, structure: Structure, mesh: Mesh, locationIt: LocationIterator, props: StructureMeshProps) {
+export async function createComplexMeshRenderObject(ctx: VisualContext, structure: Structure, mesh: Mesh, locationIt: LocationIterator, theme: Theme, props: StructureMeshProps) {
     const transform = createIdentityTransform()
-    const values = await Mesh.createValues(ctx.runtime, mesh, transform, locationIt, props)
+    const values = await Mesh.createValues(ctx.runtime, mesh, transform, locationIt, theme, props)
     const state = createRenderableState(props)
     return createMeshRenderObject(values, state)
 }
 
-export async function createUnitsMeshRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, mesh: Mesh, locationIt: LocationIterator, props: StructureMeshProps) {
+export async function createUnitsMeshRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, mesh: Mesh, locationIt: LocationIterator, theme: Theme, props: StructureMeshProps) {
     const transform = createUnitsTransform(group)
-    const values = await Mesh.createValues(ctx.runtime, mesh, transform, locationIt, props)
+    const values = await Mesh.createValues(ctx.runtime, mesh, transform, locationIt, theme, props)
     const state = createRenderableState(props)
     return createMeshRenderObject(values, state)
 }
@@ -67,9 +67,9 @@ export async function createUnitsMeshRenderObject(ctx: VisualContext, group: Uni
 
 type StructurePointsProps = Points.Props & StructureProps
 
-export async function createUnitsPointsRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, points: Points, locationIt: LocationIterator, props: StructurePointsProps) {
+export async function createUnitsPointsRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, points: Points, locationIt: LocationIterator, theme: Theme, props: StructurePointsProps) {
     const transform = createUnitsTransform(group)
-    const values = await Points.createValues(ctx.runtime, points, transform, locationIt, props)
+    const values = await Points.createValues(ctx.runtime, points, transform, locationIt, theme, props)
     const state = createRenderableState(props)
     return createPointsRenderObject(values, state)
 }
@@ -78,9 +78,9 @@ export async function createUnitsPointsRenderObject(ctx: VisualContext, group: U
 
 type StructureLinesProps = Lines.Props & StructureProps
 
-export async function createUnitsLinesRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, lines: Lines, locationIt: LocationIterator, props: StructureLinesProps) {
+export async function createUnitsLinesRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, lines: Lines, locationIt: LocationIterator, theme: Theme, props: StructureLinesProps) {
     const transform = createUnitsTransform(group)
-    const values = await Lines.createValues(ctx.runtime, lines, transform, locationIt, props)
+    const values = await Lines.createValues(ctx.runtime, lines, transform, locationIt, theme, props)
     const state = createRenderableState(props)
     return createLinesRenderObject(values, state)
 }
@@ -89,9 +89,9 @@ export async function createUnitsLinesRenderObject(ctx: VisualContext, group: Un
 
 type StructureDirectVolumeProps = DirectVolume.Props & StructureProps
 
-export async function createUnitsDirectVolumeRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, directVolume: DirectVolume, locationIt: LocationIterator, props: StructureDirectVolumeProps) {
+export async function createUnitsDirectVolumeRenderObject(ctx: VisualContext, group: Unit.SymmetryGroup, directVolume: DirectVolume, locationIt: LocationIterator, theme: Theme, props: StructureDirectVolumeProps) {
     const transform = createUnitsTransform(group)
-    const values = await DirectVolume.createValues(ctx.runtime, directVolume, transform, locationIt, props)
+    const values = await DirectVolume.createValues(ctx.runtime, directVolume, transform, locationIt, theme, props)
     const state = createRenderableState(props)
     return createDirectVolumeRenderObject(values, state)
 }

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

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

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

@@ -6,9 +6,8 @@
 
 import { Vec3 } from 'mol-math/linear-algebra';
 import { LinkType } from 'mol-model/structure/model/types';
-import { SizeThemeName, SizeThemeOptions } from 'mol-theme/size';
 import { Unit, StructureElement, Structure, Link } from 'mol-model/structure';
-import { SelectParam, RangeParam, NumberParam, paramDefaultValues } from 'mol-util/parameter';
+import { RangeParam, NumberParam, paramDefaultValues } from 'mol-util/parameter';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
 import { CylinderProps } from 'mol-geo/primitive/cylinder';
@@ -17,9 +16,6 @@ import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { VisualContext } from 'mol-repr';
 
 export const LinkCylinderParams = {
-    sizeTheme: SelectParam<SizeThemeName>('Size Theme', '', 'uniform', SizeThemeOptions),
-    sizeValue: NumberParam('Size Value', '', 1, 0, 20, 0.1),
-    sizeFactor: NumberParam('Size Factor', '', 1, 0, 10, 0.1),
     linkScale: RangeParam('Link Scale', '', 0.4, 0, 1, 0.1),
     linkSpacing: RangeParam('Link Spacing', '', 1, 0, 2, 0.01),
     linkRadius: RangeParam('Link Radius', '', 0.25, 0, 10, 0.05),

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

@@ -61,16 +61,16 @@ export interface QualityProps {
     resolution: number
 }
 
-export function getQualityProps(props: Partial<QualityProps>, structure?: Structure) {
+export function getQualityProps(props: Partial<QualityProps>, data?: any) {
     let quality = defaults(props.quality, 'auto' as VisualQuality)
     let detail = defaults(props.detail, 1)
     let radialSegments = defaults(props.radialSegments, 12)
     let linearSegments = defaults(props.linearSegments, 8)
     let resolution = defaults(props.resolution, 2)
 
-    if (quality === 'auto' && structure) {
-        let score = structure.elementCount
-        if (structure.isCoarse) score *= 10
+    if (quality === 'auto' && data instanceof Structure) {
+        let score = data.elementCount
+        if (data.isCoarse) score *= 10
         if (score > 500_000) {
             quality = 'lowest'
         } else if (score > 300_000) {

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

@@ -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, createRenderableState } from 'mol-geo/geometry/geometry';
+import { Geometry, createRenderableState, Theme } from 'mol-geo/geometry/geometry';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { MarkerAction } from 'mol-geo/geometry/marker-data';
 import { VisualUpdateState } from 'mol-repr/util';
@@ -198,9 +198,9 @@ export function DirectVolumeVisual(): VolumeVisual<DirectVolumeProps> {
         mark: () => false,
         setUpdateState: (state: VisualUpdateState, newProps: DirectVolumeProps, currentProps: DirectVolumeProps) => {
         },
-        createRenderObject: async (ctx: VisualContext, geometry: DirectVolume, locationIt: LocationIterator, props: DirectVolumeProps) => {
+        createRenderObject: async (ctx: VisualContext, geometry: DirectVolume, locationIt: LocationIterator, theme: Theme, props: DirectVolumeProps) => {
             const transform = createIdentityTransform()
-            const values = await DirectVolume.createValues(ctx.runtime, geometry, transform, locationIt, props)
+            const values = await DirectVolume.createValues(ctx.runtime, geometry, transform, locationIt, theme, props)
             const state = createRenderableState(props)
             return createDirectVolumeRenderObject(values, state)
         },

+ 16 - 13
src/mol-repr/volume/index.ts

@@ -9,7 +9,7 @@ import { RepresentationProps, Representation, Visual, RepresentationContext, Vis
 import { VolumeData, VolumeIsoValue } from 'mol-model/volume';
 import { Loci, EmptyLoci, isEveryLoci } from 'mol-model/loci';
 import { paramDefaultValues, RangeParam } from 'mol-util/parameter';
-import { Geometry, updateRenderableState } from 'mol-geo/geometry/geometry';
+import { Geometry, updateRenderableState, Theme, createTheme } from 'mol-geo/geometry/geometry';
 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';
@@ -33,7 +33,7 @@ interface VolumeVisualBuilder<P extends VolumeProps, G extends Geometry> {
 }
 
 interface VolumeVisualGeometryBuilder<P extends VolumeProps, G extends Geometry> extends VolumeVisualBuilder<P, G> {
-    createRenderObject(ctx: VisualContext, geometry: G, locationIt: LocationIterator, currentProps: P): Promise<VolumeRenderObject>
+    createRenderObject(ctx: VisualContext, geometry: G, locationIt: LocationIterator, theme: Theme, currentProps: P): Promise<VolumeRenderObject>
     updateValues(values: RenderableValues, newProps: P): void
 }
 
@@ -48,25 +48,25 @@ export function VolumeVisual<P extends VolumeProps>(builder: VolumeVisualGeometr
     let geometry: Geometry
     let locationIt: LocationIterator
 
-    async function create(ctx: VisualContext, volume: VolumeData, props: Partial<VolumeProps> = {}) {
+    async function create(ctx: VisualContext, volume: VolumeData, theme: Theme, props: Partial<VolumeProps> = {}) {
         currentProps = Object.assign({}, defaultProps, props)
         if (props.isoValueRelative) {
             currentProps.isoValueAbsolute = VolumeIsoValue.calcAbsolute(currentVolume.dataStats, props.isoValueRelative)
-            console.log('create props.isoValueRelative', props.isoValueRelative, currentProps.isoValueAbsolute, currentVolume.dataStats)
+            // console.log('create props.isoValueRelative', props.isoValueRelative, currentProps.isoValueAbsolute, currentVolume.dataStats)
         }
 
         geometry = await createGeometry(ctx, volume, currentProps, geometry)
         locationIt = LocationIterator(1, 1, () => NullLocation)
-        renderObject = await createRenderObject(ctx, geometry, locationIt, currentProps)
+        renderObject = await createRenderObject(ctx, geometry, locationIt, theme, currentProps)
     }
 
-    async function update(ctx: VisualContext, props: Partial<VolumeProps> = {}) {
+    async function update(ctx: VisualContext, theme: Theme, props: Partial<VolumeProps> = {}) {
         if (!renderObject) return
         const newProps = Object.assign({}, currentProps, props)
 
         if (props.isoValueRelative) {
             newProps.isoValueAbsolute = VolumeIsoValue.calcAbsolute(currentVolume.dataStats, props.isoValueRelative)
-            console.log('update props.isoValueRelative', props.isoValueRelative, newProps.isoValueAbsolute, currentVolume.dataStats)
+            // console.log('update props.isoValueRelative', props.isoValueRelative, newProps.isoValueAbsolute, currentVolume.dataStats)
         }
 
         VisualUpdateState.reset(updateState)
@@ -85,17 +85,17 @@ export function VolumeVisual<P extends VolumeProps>(builder: VolumeVisualGeometr
 
     return {
         get renderObject () { return renderObject },
-        async createOrUpdate(ctx: VisualContext, props: Partial<VolumeProps> = {}, volume?: VolumeData) {
+        async createOrUpdate(ctx: VisualContext, theme: Theme, props: Partial<VolumeProps> = {}, volume?: VolumeData) {
             if (!volume && !currentVolume) {
                 throw new Error('missing volume')
             } else if (volume && (!currentVolume || !renderObject)) {
                 currentVolume = volume
-                await create(ctx, volume, props)
+                await create(ctx, volume, theme, props)
             } else if (volume && volume !== currentVolume) {
                 currentVolume = volume
-                await create(ctx, volume, props)
+                await create(ctx, volume, theme, props)
             } else {
-                await update(ctx, props)
+                await update(ctx, theme, props)
             }
 
             currentProps = Object.assign({}, defaultProps, props)
@@ -145,10 +145,13 @@ export type VolumeProps = typeof DefaultVolumeProps
 export function VolumeRepresentation<P extends VolumeProps>(visualCtor: (volumeData: VolumeData) => VolumeVisual<P>): VolumeRepresentation<P> {
     let visual: VolumeVisual<any>
     let _props: P
+    let _theme: Theme
     let busy = false
 
     function createOrUpdate(ctx: RepresentationContext, props: Partial<P> = {}, volumeData?: VolumeData) {
         _props = Object.assign({}, DefaultVolumeProps, _props, props)
+        _theme = createTheme(_props)
+
         return Task.create('VolumeRepresentation.create', async runtime => {
             // TODO queue it somehow
             if (busy) return
@@ -158,11 +161,11 @@ export function VolumeRepresentation<P extends VolumeProps>(visualCtor: (volumeD
             } else if (volumeData && !visual) {
                 busy = true
                 visual = visualCtor(volumeData)
-                await visual.createOrUpdate({ ...ctx, runtime } , props, volumeData)
+                await visual.createOrUpdate({ ...ctx, runtime }, _theme, _props, volumeData)
                 busy = false
             } else {
                 busy = true
-                await visual.createOrUpdate({ ...ctx, runtime }, props, volumeData)
+                await visual.createOrUpdate({ ...ctx, runtime }, _theme, _props, volumeData)
                 busy = false
             }
         });

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

@@ -14,7 +14,7 @@ import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { computeMarchingCubesMesh } from 'mol-geo/util/marching-cubes/algorithm';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { createIdentityTransform } from 'mol-geo/geometry/transform-data';
-import { createRenderableState } from 'mol-geo/geometry/geometry';
+import { createRenderableState, Theme } from 'mol-geo/geometry/geometry';
 import { PickingId } from 'mol-geo/geometry/picking';
 import { MarkerAction } from 'mol-geo/geometry/marker-data';
 import { VisualUpdateState } from 'mol-repr/util';
@@ -57,9 +57,9 @@ export function IsosurfaceVisual(): VolumeVisual<IsosurfaceProps> {
         setUpdateState: (state: VisualUpdateState, newProps: IsosurfaceProps, currentProps: IsosurfaceProps) => {
             if (newProps.isoValueAbsolute !== currentProps.isoValueAbsolute) state.createGeometry = true
         },
-        createRenderObject: async (ctx: VisualContext, geometry: Mesh, locationIt: LocationIterator, props: IsosurfaceProps) => {
+        createRenderObject: async (ctx: VisualContext, geometry: Mesh, locationIt: LocationIterator, theme: Theme, props: IsosurfaceProps) => {
             const transform = createIdentityTransform()
-            const values = await Mesh.createValues(ctx.runtime, geometry, transform, locationIt, props)
+            const values = await Mesh.createValues(ctx.runtime, geometry, transform, locationIt, theme, props)
             const state = createRenderableState(props)
             return createMeshRenderObject(values, state)
         },

+ 3 - 3
src/mol-theme/color/chain-id.ts

@@ -25,7 +25,7 @@ function getAsymId(unit: Unit): StructureElement.Property<string> {
 
 export function ChainIdColorTheme(props: ColorThemeProps): ColorTheme {
     let color: LocationColor
-    let scale: ColorScale | undefined = undefined
+    const scale = ColorScale.create({ list: props.list, minLabel: 'Start', maxLabel: 'End' })
     // const table: [string, Color][] = []
 
     if (props.structure) {
@@ -41,7 +41,7 @@ export function ChainIdColorTheme(props: ColorThemeProps): ColorTheme {
                 }
             })
         }
-        scale = ColorScale.create({ domain: [ 0, asymIdSerialMap.size - 1 ] })
+        scale.setDomain(0, asymIdSerialMap.size - 1)
         const scaleColor = scale.color
 
         // asymIdSerialMap.forEach((v, k) => table.push([k, scaleColor(v)]))
@@ -63,7 +63,7 @@ export function ChainIdColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
-        features: {},
+        features: { structure: true, list: true },
         granularity: 'group',
         color,
         description: Description,

+ 1 - 1
src/mol-theme/color/cross-link.ts

@@ -49,7 +49,7 @@ export function CrossLinkColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
-        features: { list: true, domain: true },
+        features: { list: true, domain: true, structure: true },
         granularity: 'group',
         color,
         description: Description,

+ 3 - 3
src/mol-theme/color/element-index.ts

@@ -15,7 +15,7 @@ const Description = 'Gives every element (atom or coarse sphere/gaussian) a uniq
 
 export function ElementIndexColorTheme(props: ColorThemeProps): ColorTheme {
     let color: LocationColor
-    let scale: ColorScale | undefined = undefined
+    let scale = ColorScale.create({ list: props.list, minLabel: 'Start', maxLabel: 'End' })
 
     if (props.structure) {
         const { units } = props.structure
@@ -29,7 +29,7 @@ export function ElementIndexColorTheme(props: ColorThemeProps): ColorTheme {
             elementCount += units[i].elements.length
             unitIdIndex.set(units[i].id, i)
         }
-        scale = ColorScale.create({ domain: [ 0, elementCount - 1 ], list: props.list })
+        scale.setDomain(0, elementCount - 1)
         const scaleColor = scale.color
 
         color = (location: Location): Color => {
@@ -48,7 +48,7 @@ export function ElementIndexColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
-        features: { list: true },
+        features: { structure: true, list: true },
         granularity: 'groupInstance',
         color,
         description: Description,

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

@@ -14,7 +14,7 @@ const Description = 'Gives every polymer a unique color based on the position (i
 
 export function PolymerIndexColorTheme(props: ColorThemeProps): ColorTheme {
     let color: LocationColor
-    let scale: ColorScale | undefined = undefined
+    const scale = ColorScale.create({ list: props.list, minLabel: 'Start', maxLabel: 'End' })
 
     if (props.structure) {
         const { units } = props.structure
@@ -22,7 +22,7 @@ export function PolymerIndexColorTheme(props: ColorThemeProps): ColorTheme {
         for (let i = 0, il = units.length; i <il; ++i) {
             if (units[i].polymerElements.length > 0) ++polymerCount
         }
-        scale = ColorScale.create({ list: props.list, domain: [ 0, polymerCount - 1 ] })
+        scale.setDomain(0, polymerCount - 1)
         const unitIdColor = new Map<number, Color>()
         for (let i = 0, j = 0, il = units.length; i <il; ++i) {
             if (units[i].polymerElements.length > 0) {

+ 3 - 3
src/mol-theme/color/unit-index.ts

@@ -14,11 +14,11 @@ const Description = 'Gives every unit (single chain or collection of single elem
 
 export function UnitIndexColorTheme(props: ColorThemeProps): ColorTheme {
     let color: LocationColor
-    let scale: ColorScale | undefined = undefined
+    const scale = ColorScale.create({ list: props.list, minLabel: 'Start', maxLabel: 'End' })
 
     if (props.structure) {
         const { units } = props.structure
-        scale = ColorScale.create({ domain: [ 0, units.length - 1 ] })
+        scale.setDomain(0, units.length - 1)
         const unitIdColor = new Map<number, Color>()
         for (let i = 0, il = units.length; i <il; ++i) {
             unitIdColor.set(units[i].id, scale.color(i))
@@ -37,7 +37,7 @@ export function UnitIndexColorTheme(props: ColorThemeProps): ColorTheme {
     }
 
     return {
-        features: {},
+        features: { structure: true, list: true },
         granularity: 'instance',
         color,
         description: Description,