From 0eca7efb5bd71e78972dc61e5d5fb81671d1afb0 Mon Sep 17 00:00:00 2001 From: Simard302 Date: Tue, 11 Jun 2024 09:56:33 -0400 Subject: [PATCH 1/4] Added "list entities" --- inspect/list_entities/index.html | 14 +++ inspect/list_entities/package.json | 24 +++++ inspect/list_entities/src/common/init.ts | 19 ++++ inspect/list_entities/src/index.ts | 116 +++++++++++++++++++++++ inspect/list_entities/tsconfig.json | 16 ++++ 5 files changed, 189 insertions(+) create mode 100644 inspect/list_entities/index.html create mode 100644 inspect/list_entities/package.json create mode 100644 inspect/list_entities/src/common/init.ts create mode 100644 inspect/list_entities/src/index.ts create mode 100644 inspect/list_entities/tsconfig.json diff --git a/inspect/list_entities/index.html b/inspect/list_entities/index.html new file mode 100644 index 0000000..7c0b7b5 --- /dev/null +++ b/inspect/list_entities/index.html @@ -0,0 +1,14 @@ + + + Mol* Gallery + + + + +
+ +
+ + + + diff --git a/inspect/list_entities/package.json b/inspect/list_entities/package.json new file mode 100644 index 0000000..e58ba21 --- /dev/null +++ b/inspect/list_entities/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/inspect/list_entities/src/common/init.ts b/inspect/list_entities/src/common/init.ts new file mode 100644 index 0000000..3b5dbaa --- /dev/null +++ b/inspect/list_entities/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/inspect/list_entities/src/index.ts b/inspect/list_entities/src/index.ts new file mode 100644 index 0000000..be85b74 --- /dev/null +++ b/inspect/list_entities/src/index.ts @@ -0,0 +1,116 @@ +import { Queries, QueryContext, StructureElement, StructureProperties, StructureQuery, StructureSelection } from "molstar/lib/mol-model/structure"; +import { createRootViewer } from "./common/init"; +import { StructureSelectionQueries, StructureSelectionQuery } from "molstar/lib/mol-plugin-state/helpers/structure-selection-query"; +import { MolScriptBuilder as MS } from 'molstar/lib/mol-script/language/builder'; +import { Location } from "molstar/lib/mol-model/structure/structure/element/location"; +import { StructurePointsParams } from "molstar/lib/mol-repr/structure/params"; +import { Loci } from "molstar/lib/mol-model/loci"; +import { OrderedSet } from "molstar/lib/mol-data/int"; + + +async function init() { + // Create viewer + const plugin = await createRootViewer(); + + // Download PDB + const fileData = await plugin.builders.data.download( + { url: "https://models.rcsb.org/1PTH.bcif", isBinary: true } + ); + + // Load PDB 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"); + } + + // Get Structure object from the structure stateObject selector. + // The Structure object contains properties and accessors to the underlying molecular data such as chains, residues, atoms, etc. + const struct = presetStateObjects.structure.data!; + + // Create a QueryContext to be reused for all queries + // Limits the queries to only look at the structure + const ctx = new QueryContext(struct) + + + // ==== Number of Waters ==== + // Created a query to select all residues that are water + // but only select 1 atom per water (ensuring a Singleton selection) + const waterQuery = Queries.generators.atoms({ + 'entityTest': ctx => StructureProperties.entity.type(ctx.element) === 'water', + 'atomTest': ctx => StructureProperties.atom.type_symbol(ctx.element) === 'O' + }) + // Can cast as a Singleton selection since we are only selecting 1 atom per water + const waterSelection = waterQuery(ctx) as StructureSelection.Singletons; + const numWaters = waterSelection.structure.atomicResidueCount; + + + // ==== Covalent ligand names and residue code ==== + // Create a query expression for all ligands connected to the protein + const covalentLigandExp = MS.struct.filter.isConnectedTo({ + 0: StructureSelectionQueries.ligand.expression, // All ligands + target: StructureSelectionQueries.protein.expression, // All protein atoms + 'bond-test': true // Only atoms covalently bound to the protein + }) + // Query the atoms with the context to get a StructureSelection + const covLigQuery = StructureSelectionQuery('only-covalent-ligands', covalentLigandExp).query; + const covLigSelection = covLigQuery(ctx); + // Assume ligands in structure have >1 atoms. + // Therefore, the StructureSelection must be a Sequence + const covLigStructures = (covLigSelection as StructureSelection.Sequence).structures; + // Retrieve each ligand name and residue code + const covLigNames: string[] = []; + const covLigRes: string[] = []; + covLigStructures.forEach(s => s.units.map(u => { + // Create a location for the first element of the ligand + // to retrieve structure properties + const location = Location.create(s, u, u.elements[0]) + // Return the ligand name property for the ligand + const name = StructureProperties.entity.pdbx_description(location).join('|') + covLigNames.push(name); + // Return the residue code for the ligand + const res = StructureProperties.atom.label_comp_id(location) + covLigRes.push(res); + })) + + + // ==== Number of AltLoc positions ==== + const altLocQuery = Queries.generators.atoms({ + // Any atom with a non '' alt_id + 'atomTest': ctx => !!StructureProperties.atom.label_alt_id(ctx.element), + }); + // Can only select 1 atom at a time, must be a Singleton + const altLocSelection = altLocQuery(ctx) as StructureSelection.Singletons; + const numAltLocs = altLocSelection.structure.elementCount; + + + // ==== Polymer ASYM Unit name and chain ==== + const polymerSelection = StructureSelectionQueries.polymer.query(ctx) + // Assume more than 1 atom in the polymer entity + const polymerStructues = (polymerSelection as StructureSelection.Sequence).structures; + // Iterate over each polymer unit in each structure and get the name and chain + const namePolymers: string[] = []; + const chainPolymers: string[] = []; + polymerStructues.forEach(s => { + s.units.map(u => { + // Create a location for the polymer unit to retrieve structure properties + const location = Location.create(struct, u, u.elements[0]) + // Retrieve the polymer name and chain + const name = StructureProperties.entity.pdbx_description(location).join('|') + namePolymers.push(name); + const chain = StructureProperties.chain.auth_asym_id(location); + chainPolymers.push(chain); + }) + }) + + console.table([ + {title: 'Water count', value: numWaters}, + {title: 'Covalent Ligand name', value: covLigNames.join(', ')}, + {title: 'Covalent Ligand residue', value: covLigRes.join(', ')}, + {title: 'Alt Loc Count', value: numAltLocs}, + {title: 'Poly ASYM Unit name', value: namePolymers.join(', ')}, + {title: 'Poly ASYM Unit chain', value: chainPolymers.join(', ')} + ]) +} +init(); \ No newline at end of file diff --git a/inspect/list_entities/tsconfig.json b/inspect/list_entities/tsconfig.json new file mode 100644 index 0000000..cbf7936 --- /dev/null +++ b/inspect/list_entities/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 From 4ee8869e7b25467a3db7846b77e57abfcb4d590c Mon Sep 17 00:00:00 2001 From: Simard302 Date: Tue, 11 Jun 2024 10:11:58 -0400 Subject: [PATCH 2/4] Removed useless imports --- inspect/list_entities/src/index.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/inspect/list_entities/src/index.ts b/inspect/list_entities/src/index.ts index be85b74..71bd861 100644 --- a/inspect/list_entities/src/index.ts +++ b/inspect/list_entities/src/index.ts @@ -1,11 +1,8 @@ -import { Queries, QueryContext, StructureElement, StructureProperties, StructureQuery, StructureSelection } from "molstar/lib/mol-model/structure"; +import { Queries, QueryContext, StructureProperties, StructureSelection } from "molstar/lib/mol-model/structure"; import { createRootViewer } from "./common/init"; import { StructureSelectionQueries, StructureSelectionQuery } from "molstar/lib/mol-plugin-state/helpers/structure-selection-query"; import { MolScriptBuilder as MS } from 'molstar/lib/mol-script/language/builder'; import { Location } from "molstar/lib/mol-model/structure/structure/element/location"; -import { StructurePointsParams } from "molstar/lib/mol-repr/structure/params"; -import { Loci } from "molstar/lib/mol-model/loci"; -import { OrderedSet } from "molstar/lib/mol-data/int"; async function init() { @@ -33,7 +30,7 @@ async function init() { // Limits the queries to only look at the structure const ctx = new QueryContext(struct) - + // ==== Number of Waters ==== // Created a query to select all residues that are water // but only select 1 atom per water (ensuring a Singleton selection) From eeabd61cd6ab5753f85a565b0ef7d421cb056466 Mon Sep 17 00:00:00 2001 From: Simard302 Date: Tue, 11 Jun 2024 11:52:59 -0400 Subject: [PATCH 3/4] Updated readme --- readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/readme.md b/readme.md index b9f6738..df2a543 100644 --- a/readme.md +++ b/readme.md @@ -50,6 +50,8 @@ npm run watch - [Set transparency on selection](https://codesandbox.io/p/sandbox/github/molstar/example-gallery/master/representation/transparency_using_selection) - Coloring - [Color a selection](https://codesandbox.io/p/sandbox/github/molstar/example-gallery/master/coloring/color_a_selection) +- Inspect + - [List entities](https://codesandbox.io/p/sandbox/github/molstar/example-gallery/master/inspect/list_entities) - [Default](https://codesandbox.io/p/sandbox/github/molstar/example-gallery/master/default) ## Prebuilt Examples and CodePens From 59a9d3cbb40f26e661e997e9e422535f18dd4f45 Mon Sep 17 00:00:00 2001 From: Simard302 Date: Wed, 12 Jun 2024 16:34:20 -0400 Subject: [PATCH 4/4] Made changes for PR --- inspect/list_entities/src/index.ts | 51 +++++++++++++++++------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/inspect/list_entities/src/index.ts b/inspect/list_entities/src/index.ts index 71bd861..393d5c6 100644 --- a/inspect/list_entities/src/index.ts +++ b/inspect/list_entities/src/index.ts @@ -35,10 +35,9 @@ async function init() { // Created a query to select all residues that are water // but only select 1 atom per water (ensuring a Singleton selection) const waterQuery = Queries.generators.atoms({ - 'entityTest': ctx => StructureProperties.entity.type(ctx.element) === 'water', - 'atomTest': ctx => StructureProperties.atom.type_symbol(ctx.element) === 'O' + 'entityTest': ctx => StructureProperties.entity.type(ctx.element) === 'water' }) - // Can cast as a Singleton selection since we are only selecting 1 atom per water + // Since we used Queries.generators.atoms, our selection will by grouped by Atom and is therefore Singletons const waterSelection = waterQuery(ctx) as StructureSelection.Singletons; const numWaters = waterSelection.structure.atomicResidueCount; @@ -55,21 +54,26 @@ async function init() { const covLigSelection = covLigQuery(ctx); // Assume ligands in structure have >1 atoms. // Therefore, the StructureSelection must be a Sequence - const covLigStructures = (covLigSelection as StructureSelection.Sequence).structures; + // If the selection is empty, set the ligand structures to an empty array + const covLigStructures = StructureSelection.isEmpty(covLigSelection) ? [] : (covLigSelection as StructureSelection.Sequence).structures; // Retrieve each ligand name and residue code const covLigNames: string[] = []; const covLigRes: string[] = []; - covLigStructures.forEach(s => s.units.map(u => { - // Create a location for the first element of the ligand - // to retrieve structure properties - const location = Location.create(s, u, u.elements[0]) - // Return the ligand name property for the ligand - const name = StructureProperties.entity.pdbx_description(location).join('|') - covLigNames.push(name); - // Return the residue code for the ligand - const res = StructureProperties.atom.label_comp_id(location) - covLigRes.push(res); - })) + const auxCovLigLocation = Location.create(); // Create a Location object to retrieve properties + covLigStructures.forEach(s => { + auxCovLigLocation.structure = s; // Set the structure for the location + s.units.map(u => { + // Set the Location to the first element of the ligand + auxCovLigLocation.unit = u; + auxCovLigLocation.element = u.elements[0]; + // Use the Location to query the ligand name property of the ligand + const name = StructureProperties.entity.pdbx_description(auxCovLigLocation).join('|') + covLigNames.push(name); + s// Use the Location to query the reidue code for the ligand + const res = StructureProperties.atom.label_comp_id(auxCovLigLocation) + covLigRes.push(res); + }) + }) // ==== Number of AltLoc positions ==== @@ -77,26 +81,29 @@ async function init() { // Any atom with a non '' alt_id 'atomTest': ctx => !!StructureProperties.atom.label_alt_id(ctx.element), }); - // Can only select 1 atom at a time, must be a Singleton + // Since we used Queries.generators.atoms, our selection will by grouped by Atom and is therefore Singletons const altLocSelection = altLocQuery(ctx) as StructureSelection.Singletons; const numAltLocs = altLocSelection.structure.elementCount; // ==== Polymer ASYM Unit name and chain ==== const polymerSelection = StructureSelectionQueries.polymer.query(ctx) - // Assume more than 1 atom in the polymer entity + // Polymer query groups selected atoms by entity. Assume it creates a Sequence StructureSelection const polymerStructues = (polymerSelection as StructureSelection.Sequence).structures; // Iterate over each polymer unit in each structure and get the name and chain const namePolymers: string[] = []; const chainPolymers: string[] = []; + const auxPolymerLocation = Location.create(); // Create a Location object to retrieve properties polymerStructues.forEach(s => { + auxPolymerLocation.structure = s; // Set the structure for the location s.units.map(u => { - // Create a location for the polymer unit to retrieve structure properties - const location = Location.create(struct, u, u.elements[0]) - // Retrieve the polymer name and chain - const name = StructureProperties.entity.pdbx_description(location).join('|') + // Set the Location to the first element of the polymer + auxPolymerLocation.unit = u; + auxPolymerLocation.element = u.elements[0]; + // Use the Location to query the polymer name and chain + const name = StructureProperties.entity.pdbx_description(auxPolymerLocation).join('|') namePolymers.push(name); - const chain = StructureProperties.chain.auth_asym_id(location); + const chain = StructureProperties.chain.auth_asym_id(auxPolymerLocation); chainPolymers.push(chain); }) })