-
Notifications
You must be signed in to change notification settings - Fork 0
Selection label examples #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
d3edb9f
3ec27e0
6a8c633
d62f9e9
ea7ae76
364cd21
fc3e2aa
5685541
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<html> | ||
<head> | ||
<title>Mol* Gallery</title> | ||
<meta charset="UTF-8" /> | ||
</head> | ||
|
||
<body style="font-family: sans-serif; height: 100%; width: 100%; margin: 0;"> | ||
<div id="app" style="height: 100%;width: 100%;"> | ||
<canvas id="canvas" style="height: 100%;width: 100%;"></canvas> | ||
</div> | ||
|
||
<script src="src/index.ts"></script> | ||
</body> | ||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import { Queries, QueryContext, Structure, StructureElement, StructureProperties, StructureSelection } from "molstar/lib/mol-model/structure"; | ||
import { createRootViewer } from "./common/init"; | ||
import { OrderedSet } from "molstar/lib/mol-data/int"; | ||
import { Loci } from "molstar/lib/mol-model/structure/structure/element/loci"; | ||
import { groupBy } from "molstar/lib/mol-data/util"; | ||
|
||
async function init() { | ||
// Create viewer | ||
const plugin = await createRootViewer(); | ||
|
||
// Download PDB | ||
const fileData = await plugin.builders.data.download( | ||
{ url: "https://models.rcsb.org/4hhb.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 selection for each alpha carbon | ||
|
||
// A query defines the logic to select elements based on a predicate. | ||
// The predicate is executed from a generator that iterates over the | ||
// structure hierarchy (chains, then residues, then atoms). | ||
// At each iteration, the query context that is passed, is updated. | ||
// If the predicate returns, the entire chain segment is added to the selection. | ||
const query = Queries.generators.atoms({ | ||
chainTest: ctx=> { | ||
const chainName = StructureProperties.chain.label_asym_id(ctx.element); | ||
return chainName === 'A' || chainName === 'B'; | ||
}, | ||
groupBy: ctx=> StructureProperties.chain.label_asym_id(ctx.element) // Finally, we group our selection by label_asym_id | ||
}); | ||
|
||
const ctx = new QueryContext(struct); | ||
const selection = query(ctx); | ||
|
||
// A StructureSelection is made of Singletons if each iteration only adds a single element (atom) | ||
// A StructureSelection is a Sequence if any iteration adds multiple elements (atoms) | ||
const loci: Loci[] = []; | ||
if (StructureSelection.isSingleton(selection)) { // Singleton if each chain only has 1 atom | ||
// Iterate over each Unit in the selection and create a Loci for each Unit | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Iterate over each There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't take it for granted that each unit corresponds to a whole or single chain. Best to use selections! |
||
selection.structure.units.forEach(unit => { | ||
// Create an OrderedSet of indicies for the length of unit.elements | ||
const indices = OrderedSet.ofBounds(0 as StructureElement.UnitIndex, unit.elements.length as StructureElement.UnitIndex) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. const indices = OrderedSet.ofBounds(0, unit.elements.length) |
||
// Create a Loci from the unit and indices | ||
loci.push(StructureElement.Loci(struct, [{unit, indices}])) | ||
}) | ||
} else { // otherwise, it is a Sequence | ||
// Iterate over each Structure in the selection and create a Loci for each SubStructure | ||
selection.structures.forEach(structure => { | ||
loci.push(Structure.toSubStructureElementLoci(struct, structure)); | ||
}) | ||
Comment on lines
+58
to
+61
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. // Iterate over each Structure in the selection (one per chain) and create a Loci There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have a label structure representation, which can do many use-cases much more efficiently (and happy to see more params added as needed) |
||
} | ||
loci.forEach(l => { | ||
plugin.managers.structure.measurement.addLabel(l, {labelParams: { | ||
customText: 'Chain', | ||
textSize: 0.5 | ||
}}) | ||
}) | ||
} | ||
init(); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
"compilerOptions": { | ||
"strict": true, | ||
"module": "commonjs", | ||
"jsx": "preserve", | ||
"esModuleInterop": true, | ||
"sourceMap": true, | ||
"allowJs": true, | ||
"lib": [ | ||
"es6", | ||
"dom" | ||
], | ||
"rootDir": "src", | ||
"moduleResolution": "node" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<html> | ||
<head> | ||
<title>Mol* Gallery</title> | ||
<meta charset="UTF-8" /> | ||
</head> | ||
|
||
<body style="font-family: sans-serif; height: 100%; width: 100%; margin: 0;"> | ||
<div id="app" style="height: 100%;width: 100%;"> | ||
<canvas id="canvas" style="height: 100%;width: 100%;"></canvas> | ||
</div> | ||
|
||
<script src="src/index.ts"></script> | ||
</body> | ||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { Queries, QueryContext, StructureElement, StructureProperties, StructureSelection } from "molstar/lib/mol-model/structure"; | ||
import { createRootViewer } from "./common/init"; | ||
import { OrderedSet } from "molstar/lib/mol-data/int"; | ||
import { StateTransforms } from "molstar/lib/mol-plugin-state/transforms"; | ||
|
||
async function init() { | ||
// Create viewer | ||
const plugin = await createRootViewer(); | ||
|
||
// Download PDB | ||
const fileData = await plugin.builders.data.download( | ||
{ url: "https://models.rcsb.org/4hhb.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 selection for each alpha carbon | ||
|
||
// A query defines the logic to select elements based on a predicate. | ||
// The predicate is executed from a generator that iterates over the | ||
// structure hierarchy (chains, then residues, then atoms). | ||
// At each iteration, the query context that is passed, is updated. | ||
// The chainTest predicate is executed once per chain. The element property | ||
// represents the atom at the start of the current chain. | ||
// The atomTest predicate is executed for each atom in the structure that passes the chainTest. | ||
// If the both the predicate returns true, the atom is added to the selection. | ||
const query = Queries.generators.residues({ | ||
chainTest: ctx => StructureProperties.chain.label_asym_id(ctx.element) === 'A', | ||
atomTest: ctx => StructureProperties.atom.auth_atom_id(ctx.element) === 'CA' | ||
}); | ||
|
||
const ctx = new QueryContext(struct); | ||
const selection = query(ctx); | ||
|
||
// A StructureSelection is made of Singletons if each iteration only adds a single element (atom) | ||
// A StructureSelection is a Sequence if any iteration adds multiple elements (atoms) | ||
// Since we iterated over residues and only selected 1 atom per residue, the selection is made of Singletons. | ||
const structure = (selection as StructureSelection.Singletons).structure; | ||
|
||
// Create a StateBuilder to make changes to the state | ||
const builder = plugin.build(); | ||
const dependsOn = [presetStateObjects.structure.ref] // Define what state objects your changes depend on (in this case the structure SO) | ||
// Now, we will iterate over each Unit and its elements in the selection | ||
structure.units.forEach(unit => { | ||
for (let i = 0; i < unit.elements.length; i++) { // Iterate over each atom in the unit (each alpha carbon) | ||
// Create a Loci using the unit and the index of the atom | ||
const indices = OrderedSet.ofSingleton(i as StructureElement.UnitIndex); | ||
const loci = StructureElement.Loci(struct, [{unit, indices: indices}]); | ||
// Create a location to retrieve the atom's compID (residue name) | ||
const location = StructureElement.Location.create(struct, unit, unit.elements[i]); | ||
const resName = StructureProperties.atom.auth_comp_id(location); | ||
|
||
// Create a MultiStructureSelection from our Loci | ||
builder.toRoot().apply(StateTransforms.Model.MultiStructureSelectionFromExpression, { | ||
selections: [ | ||
{ key: 'a', ref: presetStateObjects.structure.ref, expression: StructureElement.Loci.toExpression(loci) }, | ||
], | ||
label: 'Label' // A label to give to our selection | ||
}, {dependsOn}).apply(StateTransforms.Representation.StructureSelectionsLabel3D, { // Create a 3DLabel on the selection | ||
customText: 'Alpha Carbon '+resName, // Custom text for our label | ||
textSize: 1 // Text size for our label | ||
}); | ||
} | ||
}) | ||
// Now that all the changes have been added to the builder, | ||
// Commit them to be added to the current plugin state | ||
builder.commit() | ||
} | ||
init(); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
"compilerOptions": { | ||
"strict": true, | ||
"module": "commonjs", | ||
"jsx": "preserve", | ||
"esModuleInterop": true, | ||
"sourceMap": true, | ||
"allowJs": true, | ||
"lib": [ | ||
"es6", | ||
"dom" | ||
], | ||
"rootDir": "src", | ||
"moduleResolution": "node" | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Singletons if grouping by atoms (no grouping)