From 1824b9eb4fcf6b930a2334429ac425d5dc976dea Mon Sep 17 00:00:00 2001 From: Simard302 Date: Wed, 12 Jun 2024 16:17:53 -0400 Subject: [PATCH 1/2] Added "custom_color_provider" --- coloring/custom_color_provider/index.html | 14 +++ coloring/custom_color_provider/package.json | 24 ++++ .../custom_color_provider/src/common/init.ts | 19 +++ coloring/custom_color_provider/src/index.ts | 118 ++++++++++++++++++ coloring/custom_color_provider/tsconfig.json | 16 +++ readme.md | 1 + 6 files changed, 192 insertions(+) create mode 100644 coloring/custom_color_provider/index.html create mode 100644 coloring/custom_color_provider/package.json create mode 100644 coloring/custom_color_provider/src/common/init.ts create mode 100644 coloring/custom_color_provider/src/index.ts create mode 100644 coloring/custom_color_provider/tsconfig.json diff --git a/coloring/custom_color_provider/index.html b/coloring/custom_color_provider/index.html new file mode 100644 index 0000000..7c0b7b5 --- /dev/null +++ b/coloring/custom_color_provider/index.html @@ -0,0 +1,14 @@ + + + Mol* Gallery + + + + +
+ +
+ + + + diff --git a/coloring/custom_color_provider/package.json b/coloring/custom_color_provider/package.json new file mode 100644 index 0000000..e58ba21 --- /dev/null +++ b/coloring/custom_color_provider/package.json @@ -0,0 +1,24 @@ +{ + "name": "molstar-typescript-example", + "version": "1.0.0", + "description": "Molstar and TypeScript example starter project", + "main": "index.html", + "scripts": { + "start": "parcel index.html", + "build": "parcel build index.html" + }, + "dependencies": { + "parcel-bundler": "1.12.5", + "molstar": "4.3.0" + }, + "devDependencies": { + "typescript": "4.4.4" + }, + "resolutions": { + "@babel/preset-env": "7.13.8" + }, + "keywords": [ + "typescript", + "molstar" + ] + } \ No newline at end of file diff --git a/coloring/custom_color_provider/src/common/init.ts b/coloring/custom_color_provider/src/common/init.ts new file mode 100644 index 0000000..3b5dbaa --- /dev/null +++ b/coloring/custom_color_provider/src/common/init.ts @@ -0,0 +1,19 @@ +import { PluginContext } from "molstar/lib/mol-plugin/context"; +import { DefaultPluginSpec } from "molstar/lib/mol-plugin/spec"; + +export async function createRootViewer() { + const viewport = document.getElementById("app") as HTMLDivElement; + const canvas = document.getElementById("canvas") as HTMLCanvasElement; + + const plugin = new PluginContext(DefaultPluginSpec()); + await plugin.init(); + + if (!plugin.initViewer(canvas, viewport)) { + viewport.innerHTML = "Failed to init Mol*"; + throw new Error("init failed"); + } + //@ts-ignore + window["molstar"] = plugin; + + return plugin; +} diff --git a/coloring/custom_color_provider/src/index.ts b/coloring/custom_color_provider/src/index.ts new file mode 100644 index 0000000..fe4cc4e --- /dev/null +++ b/coloring/custom_color_provider/src/index.ts @@ -0,0 +1,118 @@ +import { StructureSelectionQuery } from "molstar/lib/mol-plugin-state/helpers/structure-selection-query"; +import { createRootViewer } from "./common/init"; +import { Bond, StructureElement, StructureProperties } from "molstar/lib/mol-model/structure"; +import { ThemeDataContext } from "molstar/lib/mol-theme/theme"; +import { ParamDefinition as PD } from "molstar/lib/mol-util/param-definition"; +import { ColorTheme } from "molstar/lib/mol-theme/color"; +import { ColorNames } from 'molstar/lib/mol-util/color/names'; +import { MolScriptBuilder as MS } from 'molstar/lib/mol-script/language/builder'; + +async function init() { + // Create viewer + const plugin = await createRootViewer(); + + // Download mmCIF + const fileData = await plugin.builders.data.download( + { url: "https://models.rcsb.org/9INS.bcif", isBinary: true } + ); + + // Load mmCIF and create representation + const trajectory = await plugin.builders.structure.parseTrajectory(fileData, "mmcif"); + const presetStateObjects = await plugin.builders.structure.hierarchy.applyPreset(trajectory, "default"); + + if (!presetStateObjects) { + throw new Error("Structure not loaded"); + } + + // The goal here is to color all atoms that have alternate locations with a different color + // based on their alt location ID + + // Create an expression using molscript to select all atoms with an alt loc that is not empty + const altLocExp = MS.struct.generator.atomGroups({ + 'atom-test': MS.core.rel.neq([MS.struct.atomProperty.macromolecular.label_alt_id(), '']) + }); + // Create a StructureSelectionQuery from the expression to pass it to the component builder + const altLocSelectionQuery = StructureSelectionQuery('alt-loc', altLocExp) + + // From the alt-loc selection query, create a component for which we will later create a representation + const component = await plugin.builders.structure.tryCreateComponentFromSelection( + presetStateObjects.structure, // The structure state object to create components from + altLocSelectionQuery, // The selection query for the alt-loc atoms + "alt-loc" // A key to identify the component + ); + + // If the component failed to be created, throw an error + if (!component?.cell) { + throw new Error("Failed to create component from selection"); + } + + // Add the custom color theme to the plugin's color theme registry (only needs to be done once) + plugin.representation.structure.themes.colorThemeRegistry.add(BallAndStickAltLocColorThemeProvider); + + // Add a ball-and-stick representation to the component we created + // Use our custom color theme to color the atoms in our representation + await plugin.builders.structure.representation.addRepresentation( + component.cell, + { + type: "ball-and-stick", + color: BallAndStickAltLocColorThemeProvider.name as any // Need to cast as any because theme was added at runtime + }, + ); +} +init(); + +// Create our custom color theme provider +export const BallAndStickAltLocColorThemeProvider: ColorTheme.Provider<{}, 'ball-and-stick-alt-loc'> = { + name: 'ball-and-stick-alt-loc', // Name of the color theme to use in representation params + label: 'Ball and stick that colors based on alt loc', // Label for the color theme + category: ColorTheme.Category.Atom, // Category for use in the UI + factory: CustomColorTheme, // Factory function with logic to decide color + getParams: () => ({}), // No parameters needed for this theme + defaultValues: { }, // No default values for this theme + isApplicable: (ctx: ThemeDataContext) => true, // We can make this always applicable to any structure +}; + +export function CustomColorTheme( + ctx: ThemeDataContext, + props: PD.Values<{}> +): ColorTheme<{}> { + // List of colors to use for the alt locs, modulo 8 to wrap around + const colors = [ + ColorNames.red, + ColorNames.blue, + ColorNames.green, + ColorNames.yellow, + ColorNames.orange, + ColorNames.purple, + ColorNames.teal, + ColorNames.violet + ]; + // label_alt_id code to color function, maps A -> red, B -> blue, C -> green, etc. + const codeToColor = (code: string) => { + const charIndex = code.toUpperCase().charCodeAt(0) - 'A'.charCodeAt(0); + return colors[charIndex%8] // Modulo 8 to wrap around + } + + // StructureElement.Location object to use for querying atom properties + const auxLocation = StructureElement.Location.create(ctx.structure); + + return { + factory: CustomColorTheme, // Factory function to use for this theme + granularity: 'group', // What granularity to use for Location ("group" gives Bond and StructureElement Location objects) + color: location => { // Function to decide color based on Location + if (StructureElement.Location.is(location)) { + // If the Location is a StructureElement.Location, use the atom to decide color + return codeToColor(StructureProperties.atom.label_alt_id(location)) + } else if (Bond.isLocation(location)) { + // If Location is a Bond.Location, use the first atom to decide color + auxLocation.unit = location.aUnit; // Need a StructureElement.Location to query atom properties + auxLocation.element = location.aUnit.elements[location.aIndex]; + return codeToColor(StructureProperties.atom.label_alt_id(auxLocation)) + } else { + // Anything else, return white + return ColorNames.white; + } + }, + props: props, // keep the properties passed to the CustomColorTheme function + }; +} \ No newline at end of file diff --git a/coloring/custom_color_provider/tsconfig.json b/coloring/custom_color_provider/tsconfig.json new file mode 100644 index 0000000..cbf7936 --- /dev/null +++ b/coloring/custom_color_provider/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "strict": true, + "module": "commonjs", + "jsx": "preserve", + "esModuleInterop": true, + "sourceMap": true, + "allowJs": true, + "lib": [ + "es6", + "dom" + ], + "rootDir": "src", + "moduleResolution": "node" + } + } \ No newline at end of file diff --git a/readme.md b/readme.md index 349ca39..68b10c0 100644 --- a/readme.md +++ b/readme.md @@ -55,6 +55,7 @@ npm run watch - [Move camera](https://codesandbox.io/p/sandbox/github/molstar/example-gallery/master/camera/move_camera) - Coloring - [Color a selection](https://codesandbox.io/p/sandbox/github/molstar/example-gallery/master/coloring/color_a_selection) + - [Custom color provider](https://codesandbox.io/p/sandbox/github/molstar/example-gallery/master/coloring/custom_color_provider) - [Default](https://codesandbox.io/p/sandbox/github/molstar/example-gallery/master/default) ## Prebuilt Examples and CodePens From 97896bb5ce00df1b5c3fac8bd6b21e530b853ff7 Mon Sep 17 00:00:00 2001 From: Simard302 Date: Thu, 13 Jun 2024 14:27:20 -0400 Subject: [PATCH 2/2] Made changes according to PR review --- coloring/custom_color_provider/src/index.ts | 25 ++++++++++++--------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/coloring/custom_color_provider/src/index.ts b/coloring/custom_color_provider/src/index.ts index fe4cc4e..4bff187 100644 --- a/coloring/custom_color_provider/src/index.ts +++ b/coloring/custom_color_provider/src/index.ts @@ -6,6 +6,8 @@ import { ParamDefinition as PD } from "molstar/lib/mol-util/param-definition"; import { ColorTheme } from "molstar/lib/mol-theme/color"; import { ColorNames } from 'molstar/lib/mol-util/color/names'; import { MolScriptBuilder as MS } from 'molstar/lib/mol-script/language/builder'; +import { BallAndStickRepresentationProvider } from 'molstar/lib/mol-repr/structure/representation/ball-and-stick'; + async function init() { // Create viewer @@ -47,29 +49,30 @@ async function init() { } // Add the custom color theme to the plugin's color theme registry (only needs to be done once) - plugin.representation.structure.themes.colorThemeRegistry.add(BallAndStickAltLocColorThemeProvider); + plugin.representation.structure.themes.colorThemeRegistry.add(CustomAltLocColorThemeProvider); // Add a ball-and-stick representation to the component we created // Use our custom color theme to color the atoms in our representation + // NOTE, we can also pass in the provider names instead of the providers themselves await plugin.builders.structure.representation.addRepresentation( component.cell, { - type: "ball-and-stick", - color: BallAndStickAltLocColorThemeProvider.name as any // Need to cast as any because theme was added at runtime + type: BallAndStickRepresentationProvider, + color: CustomAltLocColorThemeProvider }, ); } init(); // Create our custom color theme provider -export const BallAndStickAltLocColorThemeProvider: ColorTheme.Provider<{}, 'ball-and-stick-alt-loc'> = { - name: 'ball-and-stick-alt-loc', // Name of the color theme to use in representation params - label: 'Ball and stick that colors based on alt loc', // Label for the color theme - category: ColorTheme.Category.Atom, // Category for use in the UI - factory: CustomColorTheme, // Factory function with logic to decide color - getParams: () => ({}), // No parameters needed for this theme - defaultValues: { }, // No default values for this theme - isApplicable: (ctx: ThemeDataContext) => true, // We can make this always applicable to any structure +export const CustomAltLocColorThemeProvider: ColorTheme.Provider<{}, 'mycustom-alt-loc'> = { + name: 'mycustom-alt-loc', // Name of the color theme to use in representation params + label: 'Custom color theme based on alt loc', // Label for the color theme + category: ColorTheme.Category.Atom, // Category for use in the UI + factory: CustomColorTheme, // Factory function with logic to decide color + getParams: () => ({}), // No parameters needed for this theme + defaultValues: { }, // No default values for this theme + isApplicable: (ctx: ThemeDataContext) => true, // We can make this always applicable to any structure }; export function CustomColorTheme(