Browse Source

add cartoon color theme

- separates colorings for for mainchain and sidechain visuals
- uses isSecondary mechanism of LocationIterator
Alexander Rose 1 year ago
parent
commit
35c9f39a69

+ 1 - 0
CHANGELOG.md

@@ -16,6 +16,7 @@ Note that since we don't clearly distinguish between a public and private interf
 - Improve SSAO for thin geometry (e.g. lines)
 - Add snapshot support for structure selections
 - Add `nucleicProfile` parameter to cartoon representation
+- Add `cartoon` theme with separate colorings for for mainchain and sidechain visuals
 
 ## [v3.35.0] - 2023-05-14
 

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

@@ -187,7 +187,7 @@ export function PolymerTraceVisual(materialId: number): UnitsVisual<PolymerTrace
     return UnitsMeshVisual<PolymerTraceParams>({
         defaultProps: PD.getDefaultValues(PolymerTraceParams),
         createGeometry: createPolymerTraceMesh,
-        createLocationIterator: PolymerLocationIterator.fromGroup,
+        createLocationIterator: sg => PolymerLocationIterator.fromGroup(sg, true),
         getLoci: getPolymerElementLoci,
         eachLocation: eachPolymerElement,
         setUpdateState: (state: VisualUpdateState, newProps: PD.Values<PolymerTraceParams>, currentProps: PD.Values<PolymerTraceParams>, newTheme: Theme, currentTheme: Theme, newStructureGroup: StructureGroup, currentStructureGroup: StructureGroup) => {

+ 2 - 2
src/mol-repr/structure/visual/polymer-tube-mesh.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -117,7 +117,7 @@ export function PolymerTubeVisual(materialId: number): UnitsVisual<PolymerTubePa
     return UnitsMeshVisual<PolymerTubeParams>({
         defaultProps: PD.getDefaultValues(PolymerTubeParams),
         createGeometry: createPolymerTubeMesh,
-        createLocationIterator: PolymerLocationIterator.fromGroup,
+        createLocationIterator: sg => PolymerLocationIterator.fromGroup(sg, true),
         getLoci: getPolymerElementLoci,
         eachLocation: eachPolymerElement,
         setUpdateState: (state: VisualUpdateState, newProps: PD.Values<PolymerTubeParams>, currentProps: PD.Values<PolymerTubeParams>) => {

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

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  * @author David Sehnal <david.sehnal@gmail.com>
@@ -41,7 +41,7 @@ export function getGapRanges(unit: Unit): SortedRanges<ElementIndex> {
 }
 
 export namespace PolymerLocationIterator {
-    export function fromGroup(structureGroup: StructureGroup): LocationIterator {
+    export function fromGroup(structureGroup: StructureGroup, asSecondary = false): LocationIterator {
         const { group, structure } = structureGroup;
         const polymerElements = group.units[0].polymerElements;
         const groupCount = polymerElements.length;
@@ -53,7 +53,10 @@ export namespace PolymerLocationIterator {
             location.element = polymerElements[groupIndex];
             return location;
         };
-        return LocationIterator(groupCount, instanceCount, 1, getLocation);
+        function isSecondary(elementIndex: number, instanceIndex: number) {
+            return asSecondary;
+        }
+        return LocationIterator(groupCount, instanceCount, 1, getLocation, false, isSecondary);
     }
 }
 

+ 3 - 1
src/mol-theme/color.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -43,6 +43,7 @@ import { StructureIndexColorThemeProvider } from './color/structure-index';
 import { VolumeSegmentColorThemeProvider } from './color/volume-segment';
 import { ExternalVolumeColorThemeProvider } from './color/external-volume';
 import { ColorThemeCategory } from './color/categories';
+import { CartoonColorThemeProvider } from './color/cartoon';
 
 export type LocationColor = (location: Location, isSecondary: boolean) => Color
 
@@ -123,6 +124,7 @@ namespace ColorTheme {
     export const BuiltIn = {
         'atom-id': AtomIdColorThemeProvider,
         'carbohydrate-symbol': CarbohydrateSymbolColorThemeProvider,
+        'cartoon': CartoonColorThemeProvider,
         'chain-id': ChainIdColorThemeProvider,
         'element-index': ElementIndexColorThemeProvider,
         'element-symbol': ElementSymbolColorThemeProvider,

+ 100 - 0
src/mol-theme/color/cartoon.ts

@@ -0,0 +1,100 @@
+/**
+ * Copyright (c) 2023 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 { Location } from '../../mol-model/location';
+import type { ColorTheme } from '../color';
+import { ParamDefinition as PD } from '../../mol-util/param-definition';
+import { ThemeDataContext } from '../theme';
+import { ChainIdColorTheme, ChainIdColorThemeParams } from './chain-id';
+import { UniformColorTheme, UniformColorThemeParams } from './uniform';
+import { assertUnreachable } from '../../mol-util/type-helpers';
+import { EntityIdColorTheme, EntityIdColorThemeParams } from './entity-id';
+import { MoleculeTypeColorTheme, MoleculeTypeColorThemeParams } from './molecule-type';
+import { EntitySourceColorTheme, EntitySourceColorThemeParams } from './entity-source';
+import { ModelIndexColorTheme, ModelIndexColorThemeParams } from './model-index';
+import { StructureIndexColorTheme, StructureIndexColorThemeParams } from './structure-index';
+import { ColorThemeCategory } from './categories';
+import { ResidueNameColorTheme, ResidueNameColorThemeParams } from './residue-name';
+import { ScaleLegend, TableLegend } from '../../mol-util/legend';
+import { SecondaryStructureColorTheme, SecondaryStructureColorThemeParams } from './secondary-structure';
+import { ElementSymbolColorTheme, ElementSymbolColorThemeParams } from './element-symbol';
+
+const Description = 'Uses separate themes for coloring mainchain and sidechain visuals.';
+
+export const CartoonColorThemeParams = {
+    mainchain: PD.MappedStatic('molecule-type', {
+        uniform: PD.Group(UniformColorThemeParams),
+        'chain-id': PD.Group(ChainIdColorThemeParams),
+        'entity-id': PD.Group(EntityIdColorThemeParams),
+        'entity-source': PD.Group(EntitySourceColorThemeParams),
+        'molecule-type': PD.Group(MoleculeTypeColorThemeParams),
+        'model-index': PD.Group(ModelIndexColorThemeParams),
+        'structure-index': PD.Group(StructureIndexColorThemeParams),
+        'secondary-structure': PD.Group(SecondaryStructureColorThemeParams),
+    }),
+    sidechain: PD.MappedStatic('residue-name', {
+        uniform: PD.Group(UniformColorThemeParams),
+        'residue-name': PD.Group(ResidueNameColorThemeParams),
+        'element-symbol': PD.Group(ElementSymbolColorThemeParams),
+    }),
+};
+export type CartoonColorThemeParams = typeof CartoonColorThemeParams
+export function getCartoonColorThemeParams(ctx: ThemeDataContext) {
+    const params = PD.clone(CartoonColorThemeParams);
+    return params;
+}
+
+export function CartoonColorTheme(ctx: ThemeDataContext, props: PD.Values<CartoonColorThemeParams>): ColorTheme<CartoonColorThemeParams> {
+    const mainchain =
+        props.mainchain.name === 'uniform' ? UniformColorTheme(ctx, props.mainchain.params) :
+            props.mainchain.name === 'chain-id' ? ChainIdColorTheme(ctx, props.mainchain.params) :
+                props.mainchain.name === 'entity-id' ? EntityIdColorTheme(ctx, props.mainchain.params) :
+                    props.mainchain.name === 'entity-source' ? EntitySourceColorTheme(ctx, props.mainchain.params) :
+                        props.mainchain.name === 'molecule-type' ? MoleculeTypeColorTheme(ctx, props.mainchain.params) :
+                            props.mainchain.name === 'model-index' ? ModelIndexColorTheme(ctx, props.mainchain.params) :
+                                props.mainchain.name === 'structure-index' ? StructureIndexColorTheme(ctx, props.mainchain.params) :
+                                    props.mainchain.name === 'secondary-structure' ? SecondaryStructureColorTheme(ctx, props.mainchain.params) :
+                                        assertUnreachable(props.mainchain);
+
+    const sidechain =
+        props.sidechain.name === 'uniform' ? UniformColorTheme(ctx, props.sidechain.params) :
+            props.sidechain.name === 'residue-name' ? ResidueNameColorTheme(ctx, props.sidechain.params) :
+                props.sidechain.name === 'element-symbol' ? ElementSymbolColorTheme(ctx, props.sidechain.params) :
+                    assertUnreachable(props.sidechain);
+
+    function color(location: Location, isSecondary: boolean): Color {
+        return isSecondary ? mainchain.color(location, false) : sidechain.color(location, false);
+    }
+
+    let legend: ScaleLegend | TableLegend | undefined = mainchain.legend;
+    if (mainchain.legend?.kind === 'table-legend' && sidechain.legend?.kind === 'table-legend') {
+        legend = {
+            kind: 'table-legend',
+            table: [...mainchain.legend.table, ...sidechain.legend.table]
+        };
+    }
+
+    return {
+        factory: CartoonColorTheme,
+        granularity: 'group',
+        preferSmoothing: false,
+        color,
+        props,
+        description: Description,
+        legend,
+    };
+}
+
+export const CartoonColorThemeProvider: ColorTheme.Provider<CartoonColorThemeParams, 'cartoon'> = {
+    name: 'cartoon',
+    label: 'Cartoon',
+    category: ColorThemeCategory.Misc,
+    factory: CartoonColorTheme,
+    getParams: getCartoonColorThemeParams,
+    defaultValues: PD.getDefaultValues(CartoonColorThemeParams),
+    isApplicable: (ctx: ThemeDataContext) => !!ctx.structure
+};