Selaa lähdekoodia

refactored size/color theme, include factory

Alexander Rose 6 vuotta sitten
vanhempi
commit
03a2966beb

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

@@ -85,7 +85,7 @@ export function ComplexVisual<P extends ComplexParams>(builder: ComplexVisualGeo
         VisualUpdateState.reset(updateState)
         setUpdateState(updateState, newProps, currentProps, theme, currentTheme)
 
-        if (ColorTheme.areEqual(theme.color, currentTheme.color)) updateState.updateColor = true
+        if (!ColorTheme.areEqual(theme.color, currentTheme.color)) updateState.updateColor = true
         if (!deepEqual(newProps.unitKinds, currentProps.unitKinds)) updateState.createGeometry = true
 
         const newConformationHash = Structure.conformationHash(currentStructure)

+ 15 - 2
src/mol-repr/structure/units-visual.ts

@@ -91,10 +91,17 @@ export function UnitsVisual<P extends UnitsParams>(builder: UnitsVisualGeometryB
         VisualUpdateState.reset(updateState)
         setUpdateState(updateState, newProps, currentProps, theme, currentTheme)
 
-        if (ColorTheme.areEqual(theme.color, currentTheme.color)) updateState.updateColor = true
-        if (!deepEqual(newProps.unitKinds, currentProps.unitKinds)) updateState.createGeometry = true
+        if (!ColorTheme.areEqual(theme.color, currentTheme.color)) {
+            // console.log('new colorTheme')
+            updateState.updateColor = true
+        }
+        if (!deepEqual(newProps.unitKinds, currentProps.unitKinds)) {
+            // console.log('new unitKinds')
+            updateState.createGeometry = true
+        }
 
         if (group.transformHash !== currentGroup.transformHash) {
+            // console.log('new transformHash')
             if (group.units.length !== currentGroup.units.length || updateState.updateColor) {
                 updateState.updateTransform = true
             } else {
@@ -105,6 +112,7 @@ export function UnitsVisual<P extends UnitsParams>(builder: UnitsVisualGeometryB
         // check if the conformation of unit.model has changed
         const newConformationId = Unit.conformationId(unit)
         if (newConformationId !== currentConformationId) {
+            // console.log('new conformation')
             currentConformationId = newConformationId
             updateState.createGeometry = true
         }
@@ -112,6 +120,7 @@ export function UnitsVisual<P extends UnitsParams>(builder: UnitsVisualGeometryB
         //
 
         if (updateState.updateTransform) {
+            // console.log('update transform')
             locationIt = createLocationIterator(group)
             const { instanceCount, groupCount } = locationIt
             createMarkers(instanceCount * groupCount, renderObject.values)
@@ -120,10 +129,12 @@ export function UnitsVisual<P extends UnitsParams>(builder: UnitsVisualGeometryB
         }
 
         if (updateState.updateMatrix) {
+            // console.log('update matrix')
             createUnitsTransform(group, renderObject.values)
         }
 
         if (updateState.createGeometry) {
+            // console.log('update geometry')
             geometry = includesUnitKind(newProps.unitKinds, unit)
                 ? await createGeometry(ctx, unit, currentStructure, theme, newProps, geometry)
                 : createEmptyGeometry(geometry)
@@ -134,11 +145,13 @@ export function UnitsVisual<P extends UnitsParams>(builder: UnitsVisualGeometryB
         if (updateState.updateSize) {
             // not all geometries have size data, so check here
             if ('uSize' in renderObject.values) {
+                // console.log('update size')
                 await createSizes(ctx.runtime, locationIt, theme.size, renderObject.values)
             }
         }
 
         if (updateState.updateColor) {
+            // console.log('update color')
             await createColors(ctx.runtime, locationIt, theme.color, renderObject.values)
         }
 

+ 11 - 7
src/mol-theme/color.ts

@@ -8,7 +8,7 @@ import { Color } from 'mol-util/color';
 import { Location } from 'mol-model/location';
 import { ColorType } from 'mol-geo/geometry/color-data';
 import { CarbohydrateSymbolColorThemeProvider } from './color/carbohydrate-symbol';
-import { UniformColorTheme, UniformColorThemeProvider } from './color/uniform';
+import { UniformColorThemeProvider } from './color/uniform';
 import { deepEqual } from 'mol-util';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { ThemeDataContext } from './theme';
@@ -32,27 +32,31 @@ export type LocationColor = (location: Location, isSecondary: boolean) => Color
 export type ColorThemeProps = { [k: string]: any }
 
 export { ColorTheme }
-interface ColorTheme<P extends ColorThemeProps = {}> {
+interface ColorTheme<P extends PD.Params = {}> {
+    readonly factory: ColorTheme.Factory<P>
     readonly granularity: ColorType
     readonly color: LocationColor
-    readonly props: Readonly<P>
+    readonly props: Readonly<PD.Values<P>>
     readonly description?: string
     readonly legend?: Readonly<ScaleLegend | TableLegend>
 }
 namespace ColorTheme {
     export type Props = { [k: string]: any }
-    export const Empty = UniformColorTheme({}, { value: Color(0xCCCCCC) })
+    export type Factory<P extends PD.Params> = (ctx: ThemeDataContext, props: PD.Values<P>) => ColorTheme<P>
+    export const EmptyFactory = () => Empty
+    const EmptyColor = Color(0xCCCCCC)
+    export const Empty: ColorTheme<{}> = { factory: EmptyFactory, granularity: 'uniform', color: () => EmptyColor, props: {} }
 
     export function areEqual(themeA: ColorTheme, themeB: ColorTheme) {
-        return themeA === themeB && deepEqual(themeA.props, themeB.props)
+        return themeA.factory === themeB.factory && deepEqual(themeA.props, themeB.props)
     }
 
     export interface Provider<P extends PD.Params> {
         readonly label: string
-        readonly factory: (ctx: ThemeDataContext, props: PD.Values<P>) => ColorTheme<PD.Values<P>>
+        readonly factory: (ctx: ThemeDataContext, props: PD.Values<P>) => ColorTheme<P>
         readonly getParams: (ctx: ThemeDataContext) => P
     }
-    export const EmptyProvider: Provider<{}> = { label: '', factory: () => Empty, getParams: () => ({}) }
+    export const EmptyProvider: Provider<{}> = { label: '', factory:EmptyFactory, getParams: () => ({}) }
 
     export class Registry {
         private _list: { name: string, provider: Provider<any> }[] = []

+ 4 - 3
src/mol-theme/color/carbohydrate-symbol.ts

@@ -27,12 +27,12 @@ export const CarbohydrateSymbolColorThemeParams = {
     // domain: PD.Interval('Color Domain', '', [0, 1]),
     // value: PD.Color('Color Value', '', DefaultColor),
 }
+export type CarbohydrateSymbolColorThemeParams = typeof CarbohydrateSymbolColorThemeParams
 export function getCarbohydrateSymbolColorThemeParams(ctx: ThemeDataContext) {
     return CarbohydrateSymbolColorThemeParams // TODO return copy
 }
-export type CarbohydrateSymbolColorThemeProps = PD.Values<typeof CarbohydrateSymbolColorThemeParams>
 
-export function CarbohydrateSymbolColorTheme(ctx: ThemeDataContext, props: CarbohydrateSymbolColorThemeProps): ColorTheme<CarbohydrateSymbolColorThemeProps> {
+export function CarbohydrateSymbolColorTheme(ctx: ThemeDataContext, props: PD.Values<CarbohydrateSymbolColorThemeParams>): ColorTheme<CarbohydrateSymbolColorThemeParams> {
     let color: LocationColor
 
     if (ctx.structure) {
@@ -65,6 +65,7 @@ export function CarbohydrateSymbolColorTheme(ctx: ThemeDataContext, props: Carbo
     }
 
     return {
+        factory: CarbohydrateSymbolColorTheme,
         granularity: 'group',
         color: color,
         props: props,
@@ -73,7 +74,7 @@ export function CarbohydrateSymbolColorTheme(ctx: ThemeDataContext, props: Carbo
     }
 }
 
-export const CarbohydrateSymbolColorThemeProvider: ColorTheme.Provider<typeof CarbohydrateSymbolColorThemeParams> = {
+export const CarbohydrateSymbolColorThemeProvider: ColorTheme.Provider<CarbohydrateSymbolColorThemeParams> = {
     label: 'Carbohydrate Symbol',
     factory: CarbohydrateSymbolColorTheme,
     getParams: getCarbohydrateSymbolColorThemeParams

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

@@ -20,10 +20,10 @@ const Description = 'Gives every chain a color based on its `asym_id` value.'
 export const ChainIdColorThemeParams = {
     list: PD.Select<ColorListName>('RdYlBu', ColorListOptions),
 }
+export type ChainIdColorThemeParams = typeof ChainIdColorThemeParams
 export function getChainIdColorThemeParams(ctx: ThemeDataContext) {
     return ChainIdColorThemeParams // TODO return copy
 }
-export type ChainIdColorThemeProps = PD.Values<typeof ChainIdColorThemeParams>
 
 function getAsymId(unit: Unit): StructureElement.Property<string> {
     switch (unit.kind) {
@@ -46,7 +46,7 @@ function addAsymIds(map: Map<string, number>, data: Column<string>) {
     }
 }
 
-export function ChainIdColorTheme(ctx: ThemeDataContext, props: ChainIdColorThemeProps): ColorTheme<ChainIdColorThemeProps> {
+export function ChainIdColorTheme(ctx: ThemeDataContext, props: PD.Values<ChainIdColorThemeParams>): ColorTheme<ChainIdColorThemeParams> {
     let color: LocationColor
     const scale = ColorScale.create({ listOrName: props.list, minLabel: 'Start', maxLabel: 'End' })
 
@@ -82,6 +82,7 @@ export function ChainIdColorTheme(ctx: ThemeDataContext, props: ChainIdColorThem
     }
 
     return {
+        factory: ChainIdColorTheme,
         granularity: 'group',
         color,
         props,
@@ -90,7 +91,7 @@ export function ChainIdColorTheme(ctx: ThemeDataContext, props: ChainIdColorThem
     }
 }
 
-export const ChainIdColorThemeProvider: ColorTheme.Provider<typeof ChainIdColorThemeParams> = {
+export const ChainIdColorThemeProvider: ColorTheme.Provider<ChainIdColorThemeParams> = {
     label: 'Chain Id',
     factory: ChainIdColorTheme,
     getParams: getChainIdColorThemeParams

+ 4 - 3
src/mol-theme/color/cross-link.ts

@@ -21,10 +21,10 @@ export const CrossLinkColorThemeParams = {
     domain: PD.Interval([-10, 10]),
     list: PD.Select<ColorListName>('RdYlBu', ColorListOptions),
 }
+export type CrossLinkColorThemeParams = typeof CrossLinkColorThemeParams
 export function getCrossLinkColorThemeParams(ctx: ThemeDataContext) {
     return CrossLinkColorThemeParams // TODO return copy
 }
-export type CrossLinkColorThemeProps = PD.Values<typeof CrossLinkColorThemeParams>
 
 const distVecA = Vec3.zero(), distVecB = Vec3.zero()
 function linkDistance(link: Link.Location) {
@@ -33,7 +33,7 @@ function linkDistance(link: Link.Location) {
     return Vec3.distance(distVecA, distVecB)
 }
 
-export function CrossLinkColorTheme(ctx: ThemeDataContext, props: CrossLinkColorThemeProps): ColorTheme<CrossLinkColorThemeProps> {
+export function CrossLinkColorTheme(ctx: ThemeDataContext, props: PD.Values<CrossLinkColorThemeParams>): ColorTheme<CrossLinkColorThemeParams> {
     let color: LocationColor
     let scale: ColorScale | undefined = undefined
 
@@ -59,6 +59,7 @@ export function CrossLinkColorTheme(ctx: ThemeDataContext, props: CrossLinkColor
     }
 
     return {
+        factory: CrossLinkColorTheme,
         granularity: 'group',
         color,
         props,
@@ -67,7 +68,7 @@ export function CrossLinkColorTheme(ctx: ThemeDataContext, props: CrossLinkColor
     }
 }
 
-export const CrossLinkColorThemeProvider: ColorTheme.Provider<typeof CrossLinkColorThemeParams> = {
+export const CrossLinkColorThemeProvider: ColorTheme.Provider<CrossLinkColorThemeParams> = {
     label: 'Cross Link',
     factory: CrossLinkColorTheme,
     getParams: getCrossLinkColorThemeParams

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

@@ -19,12 +19,12 @@ const Description = 'Gives every element (atom or coarse sphere/gaussian) a uniq
 export const ElementIndexColorThemeParams = {
     list: PD.Select<ColorListName>('RdYlBu', ColorListOptions),
 }
+export type ElementIndexColorThemeParams = typeof ElementIndexColorThemeParams
 export function getElementIndexColorThemeParams(ctx: ThemeDataContext) {
     return ElementIndexColorThemeParams // TODO return copy
 }
-export type ElementIndexColorThemeProps = PD.Values<typeof ElementIndexColorThemeParams>
 
-export function ElementIndexColorTheme(ctx: ThemeDataContext, props: ElementIndexColorThemeProps): ColorTheme<ElementIndexColorThemeProps> {
+export function ElementIndexColorTheme(ctx: ThemeDataContext, props: PD.Values<ElementIndexColorThemeParams>): ColorTheme<ElementIndexColorThemeParams> {
     let color: LocationColor
     let scale = ColorScale.create({ listOrName: props.list, minLabel: 'Start', maxLabel: 'End' })
 
@@ -59,6 +59,7 @@ export function ElementIndexColorTheme(ctx: ThemeDataContext, props: ElementInde
     }
 
     return {
+        factory: ElementIndexColorTheme,
         granularity: 'groupInstance',
         color,
         props,
@@ -67,7 +68,7 @@ export function ElementIndexColorTheme(ctx: ThemeDataContext, props: ElementInde
     }
 }
 
-export const ElementIndexColorThemeProvider: ColorTheme.Provider<typeof ElementIndexColorThemeParams> = {
+export const ElementIndexColorThemeProvider: ColorTheme.Provider<ElementIndexColorThemeParams> = {
     label: 'Element Index',
     factory: ElementIndexColorTheme,
     getParams: getElementIndexColorThemeParams

+ 4 - 3
src/mol-theme/color/element-symbol.ts

@@ -22,17 +22,17 @@ const DefaultElementSymbolColor = Color(0xFFFFFF)
 const Description = 'Assigns a color to every atom according to its chemical element.'
 
 export const ElementSymbolColorThemeParams = {}
+export type ElementSymbolColorThemeParams = typeof ElementSymbolColorThemeParams
 export function getElementSymbolColorThemeParams(ctx: ThemeDataContext) {
     return ElementSymbolColorThemeParams // TODO return copy
 }
-export type ElementSymbolColorThemeProps = PD.Values<typeof ElementSymbolColorThemeParams>
 
 export function elementSymbolColor(element: ElementSymbol): Color {
     const c = (ElementSymbolColors as { [k: string]: Color })[element];
     return c === undefined ? DefaultElementSymbolColor : c
 }
 
-export function ElementSymbolColorTheme(ctx: ThemeDataContext, props: ElementSymbolColorThemeProps): ColorTheme<ElementSymbolColorThemeProps> {
+export function ElementSymbolColorTheme(ctx: ThemeDataContext, props: PD.Values<ElementSymbolColorThemeParams>): ColorTheme<ElementSymbolColorThemeParams> {
     function color(location: Location): Color {
         if (StructureElement.isLocation(location)) {
             if (Unit.isAtomic(location.unit)) {
@@ -49,6 +49,7 @@ export function ElementSymbolColorTheme(ctx: ThemeDataContext, props: ElementSym
     }
 
     return {
+        factory: ElementSymbolColorTheme,
         granularity: 'group',
         color,
         props,
@@ -59,7 +60,7 @@ export function ElementSymbolColorTheme(ctx: ThemeDataContext, props: ElementSym
     }
 }
 
-export const ElementSymbolColorThemeProvider: ColorTheme.Provider<typeof ElementSymbolColorThemeParams> = {
+export const ElementSymbolColorThemeProvider: ColorTheme.Provider<ElementSymbolColorThemeParams> = {
     label: 'Element Symbol',
     factory: ElementSymbolColorTheme,
     getParams: getElementSymbolColorThemeParams

+ 4 - 3
src/mol-theme/color/molecule-type.ts

@@ -28,10 +28,10 @@ const DefaultMoleculeTypeColor = Color(0xffff99)
 const Description = 'Assigns a color based on the molecule type of a residue.'
 
 export const MoleculeTypeColorThemeParams = {}
+export type MoleculeTypeColorThemeParams = typeof MoleculeTypeColorThemeParams
 export function getMoleculeTypeColorThemeParams(ctx: ThemeDataContext) {
     return MoleculeTypeColorThemeParams // TODO return copy
 }
-export type MoleculeTypeColorThemeProps = PD.Values<typeof MoleculeTypeColorThemeParams>
 
 export function moleculeTypeColor(unit: Unit, element: ElementIndex): Color {
     const moleculeType = getElementMoleculeType(unit, element)
@@ -47,7 +47,7 @@ export function moleculeTypeColor(unit: Unit, element: ElementIndex): Color {
     return DefaultMoleculeTypeColor
 }
 
-export function MoleculeTypeColorTheme(ctx: ThemeDataContext, props: MoleculeTypeColorThemeProps): ColorTheme<MoleculeTypeColorThemeProps> {
+export function MoleculeTypeColorTheme(ctx: ThemeDataContext, props: PD.Values<MoleculeTypeColorThemeParams>): ColorTheme<MoleculeTypeColorThemeParams> {
     function color(location: Location): Color {
         if (StructureElement.isLocation(location)) {
             return moleculeTypeColor(location.unit, location.element)
@@ -58,6 +58,7 @@ export function MoleculeTypeColorTheme(ctx: ThemeDataContext, props: MoleculeTyp
     }
 
     return {
+        factory: MoleculeTypeColorTheme,
         granularity: 'group',
         color,
         props,
@@ -68,7 +69,7 @@ export function MoleculeTypeColorTheme(ctx: ThemeDataContext, props: MoleculeTyp
     }
 }
 
-export const MoleculeTypeColorThemeProvider: ColorTheme.Provider<typeof MoleculeTypeColorThemeParams> = {
+export const MoleculeTypeColorThemeProvider: ColorTheme.Provider<MoleculeTypeColorThemeParams> = {
     label: 'Molecule Type',
     factory: MoleculeTypeColorTheme,
     getParams: getMoleculeTypeColorThemeParams

+ 4 - 3
src/mol-theme/color/polymer-id.ts

@@ -21,10 +21,10 @@ const Description = 'Gives every polymer chain a color based on its `asym_id` va
 export const PolymerIdColorThemeParams = {
     list: PD.Select<ColorListName>('RdYlBu', ColorListOptions),
 }
+export type PolymerIdColorThemeParams = typeof PolymerIdColorThemeParams
 export function getPolymerIdColorThemeParams(ctx: ThemeDataContext) {
     return PolymerIdColorThemeParams // TODO return copy
 }
-export type PolymerIdColorThemeProps = PD.Values<typeof PolymerIdColorThemeParams>
 
 function getAsymId(unit: Unit): StructureElement.Property<string> {
     switch (unit.kind) {
@@ -51,7 +51,7 @@ function addPolymerAsymIds(map: Map<string, number>, asymId: Column<string>, ent
     }
 }
 
-export function PolymerIdColorTheme(ctx: ThemeDataContext, props: PolymerIdColorThemeProps): ColorTheme<PolymerIdColorThemeProps> {
+export function PolymerIdColorTheme(ctx: ThemeDataContext, props: PD.Values<PolymerIdColorThemeParams>): ColorTheme<PolymerIdColorThemeParams> {
     let color: LocationColor
     const scale = ColorScale.create({ listOrName: props.list, minLabel: 'Start', maxLabel: 'End' })
 
@@ -89,6 +89,7 @@ export function PolymerIdColorTheme(ctx: ThemeDataContext, props: PolymerIdColor
     }
 
     return {
+        factory: PolymerIdColorTheme,
         granularity: 'group',
         color,
         props,
@@ -97,7 +98,7 @@ export function PolymerIdColorTheme(ctx: ThemeDataContext, props: PolymerIdColor
     }
 }
 
-export const PolymerIdColorThemeProvider: ColorTheme.Provider<typeof PolymerIdColorThemeParams> = {
+export const PolymerIdColorThemeProvider: ColorTheme.Provider<PolymerIdColorThemeParams> = {
     label: 'Polymer Id',
     factory: PolymerIdColorTheme,
     getParams: getPolymerIdColorThemeParams

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

@@ -18,12 +18,13 @@ const Description = 'Gives every polymer a unique color based on the position (i
 export const PolymerIndexColorThemeParams = {
     list: PD.Select<ColorListName>('RdYlBu', ColorListOptions),
 }
+export type PolymerIndexColorThemeParams = typeof PolymerIndexColorThemeParams
 export function getPolymerIndexColorThemeParams(ctx: ThemeDataContext) {
     return PolymerIndexColorThemeParams // TODO return copy
 }
 export type PolymerIndexColorThemeProps = PD.Values<typeof PolymerIndexColorThemeParams>
 
-export function PolymerIndexColorTheme(ctx: ThemeDataContext, props: PolymerIndexColorThemeProps): ColorTheme<PolymerIndexColorThemeProps> {
+export function PolymerIndexColorTheme(ctx: ThemeDataContext, props: PD.Values<PolymerIndexColorThemeParams>): ColorTheme<PolymerIndexColorThemeParams> {
     let color: LocationColor
     const scale = ColorScale.create({ listOrName: props.list, minLabel: 'Start', maxLabel: 'End' })
 
@@ -56,6 +57,7 @@ export function PolymerIndexColorTheme(ctx: ThemeDataContext, props: PolymerInde
     }
 
     return {
+        factory: PolymerIndexColorTheme,
         granularity: 'instance',
         color,
         props,
@@ -64,7 +66,7 @@ export function PolymerIndexColorTheme(ctx: ThemeDataContext, props: PolymerInde
     }
 }
 
-export const PolymerIndexColorThemeProvider: ColorTheme.Provider<typeof PolymerIndexColorThemeParams> = {
+export const PolymerIndexColorThemeProvider: ColorTheme.Provider<PolymerIndexColorThemeParams> = {
     label: 'Polymer Index',
     factory: PolymerIndexColorTheme,
     getParams: getPolymerIndexColorThemeParams

+ 4 - 3
src/mol-theme/color/residue-name.ts

@@ -63,10 +63,10 @@ const DefaultResidueNameColor = Color(0xFF00FF)
 const Description = 'Assigns a color to every residue according to its name.'
 
 export const ResidueNameColorThemeParams = {}
+export type ResidueNameColorThemeParams = typeof ResidueNameColorThemeParams
 export function getResidueNameColorThemeParams(ctx: ThemeDataContext) {
     return ResidueNameColorThemeParams // TODO return copy
 }
-export type ResidueNameColorThemeProps = PD.Values<typeof ResidueNameColorThemeParams>
 
 export function residueNameColor(residueName: string): Color {
     const c = (ResidueNameColors as { [k: string]: Color })[residueName];
@@ -93,7 +93,7 @@ function getCoarseCompId(unit: Unit.Spheres | Unit.Gaussians, element: ElementIn
     }
 }
 
-export function ResidueNameColorTheme(ctx: ThemeDataContext, props: ResidueNameColorThemeProps): ColorTheme<ResidueNameColorThemeProps> {
+export function ResidueNameColorTheme(ctx: ThemeDataContext, props: PD.Values<ResidueNameColorThemeParams>): ColorTheme<ResidueNameColorThemeParams> {
     function color(location: Location): Color {
         if (StructureElement.isLocation(location)) {
             if (Unit.isAtomic(location.unit)) {
@@ -114,6 +114,7 @@ export function ResidueNameColorTheme(ctx: ThemeDataContext, props: ResidueNameC
     }
 
     return {
+        factory: ResidueNameColorTheme,
         granularity: 'group',
         color,
         props,
@@ -124,7 +125,7 @@ export function ResidueNameColorTheme(ctx: ThemeDataContext, props: ResidueNameC
     }
 }
 
-export const ResidueNameColorThemeProvider: ColorTheme.Provider<typeof ResidueNameColorThemeParams> = {
+export const ResidueNameColorThemeProvider: ColorTheme.Provider<ResidueNameColorThemeParams> = {
     label: 'Residue Name',
     factory: ResidueNameColorTheme,
     getParams: getResidueNameColorThemeParams

+ 4 - 3
src/mol-theme/color/secondary-structure.ts

@@ -33,10 +33,10 @@ const DefaultSecondaryStructureColor = Color(0x808080)
 const Description = 'Assigns a color based on the type of secondary structure and basic molecule type.'
 
 export const SecondaryStructureColorThemeParams = {}
+export type SecondaryStructureColorThemeParams = typeof SecondaryStructureColorThemeParams
 export function getSecondaryStructureColorThemeParams(ctx: ThemeDataContext) {
     return SecondaryStructureColorThemeParams // TODO return copy
 }
-export type SecondaryStructureColorThemeProps = PD.Values<typeof SecondaryStructureColorThemeParams>
 
 export function secondaryStructureColor(unit: Unit, element: ElementIndex): Color {
     let secStrucType = SecondaryStructureType.create(SecondaryStructureType.Flag.None)
@@ -70,7 +70,7 @@ export function secondaryStructureColor(unit: Unit, element: ElementIndex): Colo
     return DefaultSecondaryStructureColor
 }
 
-export function SecondaryStructureColorTheme(ctx: ThemeDataContext, props: SecondaryStructureColorThemeProps): ColorTheme<SecondaryStructureColorThemeProps> {
+export function SecondaryStructureColorTheme(ctx: ThemeDataContext, props: PD.Values<SecondaryStructureColorThemeParams>): ColorTheme<SecondaryStructureColorThemeParams> {
     function color(location: Location): Color {
         if (StructureElement.isLocation(location)) {
             return secondaryStructureColor(location.unit, location.element)
@@ -81,6 +81,7 @@ export function SecondaryStructureColorTheme(ctx: ThemeDataContext, props: Secon
     }
 
     return {
+        factory: SecondaryStructureColorTheme,
         granularity: 'group',
         color,
         props,
@@ -91,7 +92,7 @@ export function SecondaryStructureColorTheme(ctx: ThemeDataContext, props: Secon
     }
 }
 
-export const SecondaryStructureColorThemeProvider: ColorTheme.Provider<typeof SecondaryStructureColorThemeParams> = {
+export const SecondaryStructureColorThemeProvider: ColorTheme.Provider<SecondaryStructureColorThemeParams> = {
     label: 'Secondary Structure',
     factory: SecondaryStructureColorTheme,
     getParams: getSecondaryStructureColorThemeParams

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

@@ -19,10 +19,10 @@ const Description = 'Gives every polymer residue a color based on its `seq_id` v
 export const SequenceIdColorThemeParams = {
     list: PD.Select<ColorListName>('rainbow', ColorListOptions),
 }
+export type SequenceIdColorThemeParams = typeof SequenceIdColorThemeParams
 export function getSequenceIdColorThemeParams(ctx: ThemeDataContext) {
     return SequenceIdColorThemeParams // TODO return copy
 }
-export type SequenceIdColorThemeProps = PD.Values<typeof SequenceIdColorThemeParams>
 
 function getSeqId(unit: Unit, element: ElementIndex): number {
     const { model } = unit
@@ -64,7 +64,7 @@ function getSequenceLength(unit: Unit, element: ElementIndex) {
     return model.sequence.byEntityKey[entityIndex].sequence.sequence.length
 }
 
-export function SequenceIdColorTheme(ctx: ThemeDataContext, props: SequenceIdColorThemeProps): ColorTheme<SequenceIdColorThemeProps> {
+export function SequenceIdColorTheme(ctx: ThemeDataContext, props: PD.Values<SequenceIdColorThemeParams>): ColorTheme<SequenceIdColorThemeParams> {
     const scale = ColorScale.create({
         listOrName: props.list,
         minLabel: 'Start',
@@ -90,6 +90,7 @@ export function SequenceIdColorTheme(ctx: ThemeDataContext, props: SequenceIdCol
     }
 
     return {
+        factory: SequenceIdColorTheme,
         granularity: 'group',
         color,
         props,
@@ -98,7 +99,7 @@ export function SequenceIdColorTheme(ctx: ThemeDataContext, props: SequenceIdCol
     }
 }
 
-export const SequenceIdColorThemeProvider: ColorTheme.Provider<typeof SequenceIdColorThemeParams> = {
+export const SequenceIdColorThemeProvider: ColorTheme.Provider<SequenceIdColorThemeParams> = {
     label: 'Sequence Id',
     factory: SequenceIdColorTheme,
     getParams: getSequenceIdColorThemeParams

+ 4 - 3
src/mol-theme/color/shape-group.ts

@@ -15,13 +15,14 @@ const DefaultColor = Color(0xCCCCCC)
 const Description = 'Assigns colors as defined by the shape object.'
 
 export const ShapeGroupColorThemeParams = {}
+export type ShapeGroupColorThemeParams = typeof ShapeGroupColorThemeParams
 export function getShapeGroupColorThemeParams(ctx: ThemeDataContext) {
     return ShapeGroupColorThemeParams // TODO return copy
 }
-export type ShapeGroupColorThemeProps = PD.Values<typeof ShapeGroupColorThemeParams>
 
-export function ShapeGroupColorTheme(ctx: ThemeDataContext, props: ShapeGroupColorThemeProps): ColorTheme<ShapeGroupColorThemeProps> {
+export function ShapeGroupColorTheme(ctx: ThemeDataContext, props: PD.Values<ShapeGroupColorThemeParams>): ColorTheme<ShapeGroupColorThemeParams> {
     return {
+        factory: ShapeGroupColorTheme,
         granularity: 'group',
         color: (location: Location): Color => {
             if (Shape.isLocation(location)) {
@@ -34,7 +35,7 @@ export function ShapeGroupColorTheme(ctx: ThemeDataContext, props: ShapeGroupCol
     }
 }
 
-export const ShapeGroupColorThemeProvider: ColorTheme.Provider<typeof ShapeGroupColorThemeParams> = {
+export const ShapeGroupColorThemeProvider: ColorTheme.Provider<ShapeGroupColorThemeParams> = {
     label: 'Shape Group',
     factory: ShapeGroupColorTheme,
     getParams: getShapeGroupColorThemeParams

+ 4 - 3
src/mol-theme/color/uniform.ts

@@ -16,15 +16,16 @@ const Description = 'Gives everything the same, uniform color.'
 export const UniformColorThemeParams = {
     value: PD.Color(DefaultColor),
 }
+export type UniformColorThemeParams = typeof UniformColorThemeParams
 export function getUniformColorThemeParams(ctx: ThemeDataContext) {
     return UniformColorThemeParams // TODO return copy
 }
-export type UniformColorThemeProps = PD.Values<typeof UniformColorThemeParams>
 
-export function UniformColorTheme(ctx: ThemeDataContext, props: UniformColorThemeProps): ColorTheme<UniformColorThemeProps> {
+export function UniformColorTheme(ctx: ThemeDataContext, props: PD.Values<UniformColorThemeParams>): ColorTheme<UniformColorThemeParams> {
     const color = props.value || DefaultColor
 
     return {
+        factory: UniformColorTheme,
         granularity: 'uniform',
         color: () => color,
         props: props,
@@ -33,7 +34,7 @@ export function UniformColorTheme(ctx: ThemeDataContext, props: UniformColorThem
     }
 }
 
-export const UniformColorThemeProvider: ColorTheme.Provider<typeof UniformColorThemeParams> = {
+export const UniformColorThemeProvider: ColorTheme.Provider<UniformColorThemeParams> = {
     label: 'Uniform',
     factory: UniformColorTheme,
     getParams: getUniformColorThemeParams

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

@@ -18,12 +18,12 @@ const Description = 'Gives every unit (single chain or collection of single elem
 export const UnitIndexColorThemeParams = {
     list: PD.Select<ColorListName>('RdYlBu', ColorListOptions),
 }
+export type UnitIndexColorThemeParams = typeof UnitIndexColorThemeParams
 export function getUnitIndexColorThemeParams(ctx: ThemeDataContext) {
     return UnitIndexColorThemeParams // TODO return copy
 }
-export type UnitIndexColorThemeProps = PD.Values<typeof UnitIndexColorThemeParams>
 
-export function UnitIndexColorTheme(ctx: ThemeDataContext, props: UnitIndexColorThemeProps): ColorTheme<UnitIndexColorThemeProps> {
+export function UnitIndexColorTheme(ctx: ThemeDataContext, props: PD.Values<UnitIndexColorThemeParams>): ColorTheme<UnitIndexColorThemeParams> {
     let color: LocationColor
     const scale = ColorScale.create({ listOrName: props.list, minLabel: 'Start', maxLabel: 'End' })
 
@@ -48,6 +48,7 @@ export function UnitIndexColorTheme(ctx: ThemeDataContext, props: UnitIndexColor
     }
 
     return {
+        factory: UnitIndexColorTheme,
         granularity: 'instance',
         color,
         props,
@@ -56,7 +57,7 @@ export function UnitIndexColorTheme(ctx: ThemeDataContext, props: UnitIndexColor
     }
 }
 
-export const UnitIndexColorThemeProvider: ColorTheme.Provider<typeof UnitIndexColorThemeParams> = {
+export const UnitIndexColorThemeProvider: ColorTheme.Provider<UnitIndexColorThemeParams> = {
     label: 'Unit Index',
     factory: UnitIndexColorTheme,
     getParams: getUnitIndexColorThemeParams

+ 10 - 7
src/mol-theme/size.ts

@@ -5,33 +5,36 @@
  */
 
 import { SizeType, LocationSize } from 'mol-geo/geometry/size-data';
-import { UniformSizeTheme, UniformSizeThemeProvider } from './size/uniform';
+import { UniformSizeThemeProvider } from './size/uniform';
 import { ParamDefinition as PD } from 'mol-util/param-definition';
 import { ThemeDataContext } from 'mol-theme/theme';
 import { PhysicalSizeThemeProvider } from './size/physical';
 import { deepEqual } from 'mol-util';
 
 export { SizeTheme }
-interface SizeTheme<P extends SizeTheme.Props = {}> {
+interface SizeTheme<P extends PD.Params = {}> {
+    readonly factory: SizeTheme.Factory<P>
     readonly granularity: SizeType
     readonly size: LocationSize
-    readonly props: Readonly<P>
+    readonly props: Readonly<PD.Values<P>>
     readonly description?: string
 }
 namespace SizeTheme {
     export type Props = { [k: string]: any }
-    export const Empty = UniformSizeTheme({}, { value: 1 })
+    export type Factory<P extends PD.Params> = (ctx: ThemeDataContext, props: PD.Values<P>) => SizeTheme<P>
+    export const EmptyFactory = () => Empty
+    export const Empty: SizeTheme<{}> = { factory: EmptyFactory, granularity: 'uniform', size: () => 1, props: {} }
 
     export function areEqual(themeA: SizeTheme, themeB: SizeTheme) {
-        return themeA === themeB && deepEqual(themeA.props, themeB.props)
+        return themeA.factory === themeB.factory && deepEqual(themeA.props, themeB.props)
     }
 
     export interface Provider<P extends PD.Params> {
         readonly label: string
-        readonly factory: (ctx: ThemeDataContext, props: PD.Values<P>) => SizeTheme<PD.Values<P>>
+        readonly factory: Factory<P>
         readonly getParams: (ctx: ThemeDataContext) => P
     }
-    export const EmptyProvider: Provider<{}> = { label: '', factory: () => Empty, getParams: () => ({}) }
+    export const EmptyProvider: Provider<{}> = { label: '', factory: EmptyFactory, getParams: () => ({}) }
 
     export class Registry {
         private _list: { name: string, provider: Provider<any> }[] = []

+ 4 - 3
src/mol-theme/size/physical.ts

@@ -15,10 +15,10 @@ const DefaultSize = 1
 const Description = 'Assigns a physical size.'
 
 export const PhysicalSizeThemeParams = {}
+export type PhysicalSizeThemeParams = typeof PhysicalSizeThemeParams
 export function getPhysicalSizeThemeParams(ctx: ThemeDataContext) {
     return PhysicalSizeThemeParams // TODO return copy
 }
-export type PhysicalSizeThemeProps = PD.Values<typeof PhysicalSizeThemeParams>
 
 export function getPhysicalRadius(unit: Unit, element: ElementIndex): number {
     if (Unit.isAtomic(unit)) {
@@ -34,7 +34,7 @@ export function getPhysicalRadius(unit: Unit, element: ElementIndex): number {
  * Create attribute data with the physical size of an element,
  * i.e. vdw for atoms and radius for coarse spheres
  */
-export function PhysicalSizeTheme(ctx: ThemeDataContext, props: PhysicalSizeThemeProps): SizeTheme<PhysicalSizeThemeProps> {
+export function PhysicalSizeTheme(ctx: ThemeDataContext, props: PD.Values<PhysicalSizeThemeParams>): SizeTheme<PhysicalSizeThemeParams> {
     function size(location: Location): number {
         let size: number
         if (StructureElement.isLocation(location)) {
@@ -48,6 +48,7 @@ export function PhysicalSizeTheme(ctx: ThemeDataContext, props: PhysicalSizeThem
     }
 
     return {
+        factory: PhysicalSizeTheme,
         granularity: 'group',
         size,
         props,
@@ -55,7 +56,7 @@ export function PhysicalSizeTheme(ctx: ThemeDataContext, props: PhysicalSizeThem
     }
 }
 
-export const PhysicalSizeThemeProvider: SizeTheme.Provider<typeof PhysicalSizeThemeParams> = {
+export const PhysicalSizeThemeProvider: SizeTheme.Provider<PhysicalSizeThemeParams> = {
     label: 'Physical',
     factory: PhysicalSizeTheme,
     getParams: getPhysicalSizeThemeParams

+ 4 - 3
src/mol-theme/size/uniform.ts

@@ -13,15 +13,16 @@ const Description = 'Gives everything the same, uniform size.'
 export const UniformSizeThemeParams = {
     value: PD.Numeric(1, { min: 0, max: 20, step: 0.1 }),
 }
+export type UniformSizeThemeParams = typeof UniformSizeThemeParams
 export function getUniformSizeThemeParams(ctx: ThemeDataContext) {
     return UniformSizeThemeParams // TODO return copy
 }
-export type UniformSizeThemeProps = PD.Values<typeof UniformSizeThemeParams>
 
-export function UniformSizeTheme(ctx: ThemeDataContext, props: UniformSizeThemeProps): SizeTheme<UniformSizeThemeProps> {
+export function UniformSizeTheme(ctx: ThemeDataContext, props: PD.Values<UniformSizeThemeParams>): SizeTheme<UniformSizeThemeParams> {
     const size = props.value
 
     return {
+        factory: UniformSizeTheme,
         granularity: 'uniform',
         size: () => size,
         props,
@@ -29,7 +30,7 @@ export function UniformSizeTheme(ctx: ThemeDataContext, props: UniformSizeThemeP
     }
 }
 
-export const UniformSizeThemeProvider: SizeTheme.Provider<typeof UniformSizeThemeParams> = {
+export const UniformSizeThemeProvider: SizeTheme.Provider<UniformSizeThemeParams> = {
     label: 'Uniform',
     factory: UniformSizeTheme,
     getParams: getUniformSizeThemeParams