Browse Source

added operator name & hkl color themes

Alexander Rose 5 years ago
parent
commit
5d5fd0028f

+ 4 - 0
src/mol-theme/color.ts

@@ -31,6 +31,8 @@ import { IllustrativeColorThemeProvider } from './color/illustrative';
 import { HydrophobicityColorThemeProvider } from './color/hydrophobicity';
 import { ModelIndexColorThemeProvider } from './color/model-index';
 import { OccupancyColorThemeProvider } from './color/occupancy';
+import { OperatorNameColorThemeProvider } from './color/operator-name';
+import { OperatorHklColorThemeProvider } from './color/operator-hkl';
 
 export type LocationColor = (location: Location, isSecondary: boolean) => Color
 
@@ -83,6 +85,8 @@ export const BuiltInColorThemes = {
     'model-index': ModelIndexColorThemeProvider,
     'molecule-type': MoleculeTypeColorThemeProvider,
     'occupancy': OccupancyColorThemeProvider,
+    'operator-hkl': OperatorHklColorThemeProvider,
+    'operator-name': OperatorNameColorThemeProvider,
     'polymer-id': PolymerIdColorThemeProvider,
     'polymer-index': PolymerIndexColorThemeProvider,
     'residue-name': ResidueNameColorThemeProvider,

+ 124 - 0
src/mol-theme/color/operator-hkl.ts

@@ -0,0 +1,124 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { Color } from '../../mol-util/color';
+import { StructureElement, Link, Structure } from '../../mol-model/structure';
+import { Location } from '../../mol-model/location';
+import { ColorTheme, LocationColor } from '../color';
+import { ParamDefinition as PD } from '../../mol-util/param-definition'
+import { ThemeDataContext } from '../theme';
+import { getPaletteParams, getPalette } from '../../mol-util/color/palette';
+import { ScaleLegend, TableLegend } from '../../mol-util/legend';
+import { Vec3 } from '../../mol-math/linear-algebra';
+import { integerDigitCount } from '../../mol-util/number';
+
+const DefaultColor = Color(0xCCCCCC)
+const Description = `Assigns a color based on the operator HKL value of a transformed chain.`
+
+export const OperatorHklColorThemeParams = {
+    ...getPaletteParams({ type: 'set', setList: 'set-3' }),
+}
+export type OperatorHklColorThemeParams = typeof OperatorHklColorThemeParams
+export function getOperatorHklColorThemeParams(ctx: ThemeDataContext) {
+    const params = PD.clone(OperatorHklColorThemeParams)
+    if (ctx.structure) {
+        if (getOperatorHklSerialMap(ctx.structure.root).map.size > 12) {
+            params.palette.defaultValue.name = 'scale'
+            params.palette.defaultValue.params = {
+                ...params.palette.defaultValue.params,
+                list: 'red-yellow-blue'
+            }
+        }
+    }
+    return params
+}
+
+const hklOffset = 10000
+
+function hklKey(hkl: Vec3) {
+    return hkl.map(v => `${v + hklOffset}`.padStart(5, '0')).join('')
+}
+
+function hklKeySplit(key: string) {
+    const len = integerDigitCount(hklOffset, 0)
+    const h = parseInt(key.substr(0, len))
+    const k = parseInt(key.substr(len, len))
+    const l = parseInt(key.substr(len + len, len))
+    return [ h - hklOffset, k - hklOffset, l - hklOffset ] as Vec3
+}
+
+function formatHkl(hkl: Vec3) {
+    return hkl.map(v => v + 5).join('')
+}
+
+function getOperatorHklSerialMap(structure: Structure) {
+    const map = new Map<string, number>()
+    const set = new Set<string>()
+    for (let i = 0, il = structure.units.length; i < il; ++i) {
+        const k = hklKey(structure.units[i].conformation.operator.hkl)
+        set.add(k)
+    }
+    const arr = Array.from(set).sort()
+    arr.forEach(k => map.set(k, map.size))
+    const min = hklKeySplit(arr[0])
+    const max = hklKeySplit(arr[arr.length - 1])
+    return { min, max, map }
+}
+
+export function OperatorHklColorTheme(ctx: ThemeDataContext, props: PD.Values<OperatorHklColorThemeParams>): ColorTheme<OperatorHklColorThemeParams> {
+    let color: LocationColor
+    let legend: ScaleLegend | TableLegend | undefined
+
+    if (ctx.structure) {
+        const { min, max, map } = getOperatorHklSerialMap(ctx.structure.root)
+
+        const labelTable: string[] = []
+        map.forEach((v, k) => {
+            const i = v % map.size
+            const label = formatHkl(hklKeySplit(k))
+            if (labelTable[i] === undefined) labelTable[i] = label
+            else labelTable[i] += `, ${label}`
+        })
+
+        props.palette.params.minLabel = formatHkl(min)
+        props.palette.params.maxLabel = formatHkl(max)
+        props.palette.params.valueLabel = (i: number) => labelTable[i]
+
+        const palette = getPalette(map.size, props)
+        legend = palette.legend
+
+        color = (location: Location): Color => {
+            let serial: number | undefined = undefined
+            if (StructureElement.Location.is(location)) {
+                const k = hklKey(location.unit.conformation.operator.hkl)
+                serial = map.get(k)
+            } else if (Link.isLocation(location)) {
+                const k = hklKey(location.aUnit.conformation.operator.hkl)
+                serial = map.get(k)
+            }
+            return serial === undefined ? DefaultColor : palette.color(serial)
+        }
+    } else {
+        color = () => DefaultColor
+    }
+
+    return {
+        factory: OperatorHklColorTheme,
+        granularity: 'instance',
+        color,
+        props,
+        description: Description,
+        legend
+    }
+}
+
+export const OperatorHklColorThemeProvider: ColorTheme.Provider<OperatorHklColorThemeParams> = {
+    label: 'Operator HKL',
+    factory: OperatorHklColorTheme,
+    getParams: getOperatorHklColorThemeParams,
+    defaultValues: PD.getDefaultValues(OperatorHklColorThemeParams),
+    isApplicable: (ctx: ThemeDataContext) => !!ctx.structure
+}

+ 90 - 0
src/mol-theme/color/operator-name.ts

@@ -0,0 +1,90 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { Color } from '../../mol-util/color';
+import { StructureElement, Link, Structure } from '../../mol-model/structure';
+import { Location } from '../../mol-model/location';
+import { ColorTheme, LocationColor } from '../color';
+import { ParamDefinition as PD } from '../../mol-util/param-definition'
+import { ThemeDataContext } from '../theme';
+import { getPaletteParams, getPalette } from '../../mol-util/color/palette';
+import { ScaleLegend, TableLegend } from '../../mol-util/legend';
+
+const DefaultColor = Color(0xCCCCCC)
+const Description = `Assigns a color based on the operator name of a transformed chain.`
+
+export const OperatorNameColorThemeParams = {
+    ...getPaletteParams({ type: 'set', setList: 'set-3' }),
+}
+export type OperatorNameColorThemeParams = typeof OperatorNameColorThemeParams
+export function getOperatorNameColorThemeParams(ctx: ThemeDataContext) {
+    const params = PD.clone(OperatorNameColorThemeParams)
+    if (ctx.structure) {
+        if (getOperatorNameSerialMap(ctx.structure.root).size > 12) {
+            params.palette.defaultValue.name = 'scale'
+            params.palette.defaultValue.params = {
+                ...params.palette.defaultValue.params,
+                list: 'red-yellow-blue'
+            }
+        }
+    }
+    return params
+}
+
+function getOperatorNameSerialMap(structure: Structure) {
+    const map = new Map<string, number>()
+    for (let i = 0, il = structure.units.length; i < il; ++i) {
+        const name = structure.units[i].conformation.operator.name
+        if (!map.has(name)) map.set(name, map.size)
+    }
+    return map
+}
+
+export function OperatorNameColorTheme(ctx: ThemeDataContext, props: PD.Values<OperatorNameColorThemeParams>): ColorTheme<OperatorNameColorThemeParams> {
+    let color: LocationColor
+    let legend: ScaleLegend | TableLegend | undefined
+
+    if (ctx.structure) {
+        const operatorNameSerialMap = getOperatorNameSerialMap(ctx.structure.root)
+
+        const labelTable = Array.from(operatorNameSerialMap.keys())
+        props.palette.params.valueLabel = (i: number) => labelTable[i]
+
+        const palette = getPalette(operatorNameSerialMap.size, props)
+        legend = palette.legend
+
+        color = (location: Location): Color => {
+            let serial: number | undefined = undefined
+            if (StructureElement.Location.is(location)) {
+                const name = location.unit.conformation.operator.name
+                serial = operatorNameSerialMap.get(name)
+            } else if (Link.isLocation(location)) {
+                const name = location.aUnit.conformation.operator.name
+                serial = operatorNameSerialMap.get(name)
+            }
+            return serial === undefined ? DefaultColor : palette.color(serial)
+        }
+    } else {
+        color = () => DefaultColor
+    }
+
+    return {
+        factory: OperatorNameColorTheme,
+        granularity: 'instance',
+        color,
+        props,
+        description: Description,
+        legend
+    }
+}
+
+export const OperatorNameColorThemeProvider: ColorTheme.Provider<OperatorNameColorThemeParams> = {
+    label: 'Operator Name',
+    factory: OperatorNameColorTheme,
+    getParams: getOperatorNameColorThemeParams,
+    defaultValues: PD.getDefaultValues(OperatorNameColorThemeParams),
+    isApplicable: (ctx: ThemeDataContext) => !!ctx.structure
+}

+ 2 - 0
src/mol-util/number.ts

@@ -1,5 +1,7 @@
 /**
  * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
  */
 
 /**