Skip to content

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

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ npm run watch
## Examples and SandBoxes
- Selections
- [Ligand with surrounding](https://codesandbox.io/p/sandbox/github/molstar/example-gallery/master/selection/select_ligand_and_surroundings)
- [Add label to whole selection](https://codesandbox.io/p/sandbox/github/molstar/example-gallery/master/selection/add_label_to_selection_whole)
- [Add labels to selection atoms](https://codesandbox.io/p/sandbox/github/molstar/example-gallery/master/selection/add_labels_to_selection_atoms)
- Representation
- [Create representations](https://codesandbox.io/p/sandbox/github/molstar/example-gallery/master/representation/create_representations)
- [Set transparency on selection](https://codesandbox.io/p/sandbox/github/molstar/example-gallery/master/representation/transparency_using_selection)
Expand Down
8 changes: 6 additions & 2 deletions representation/transparency_using_selection/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,12 @@ async function init() {
// Create a selection for chains A and B

// A query defines the logic to select elements based on a predicate.
// The predicate is executed from a generator which updates the current element (here, an atom)
// in the query context, at each iteration. The predicate returns true if the element should be selected.
// 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.
// If the predicate returns true, the entire chain segment is added to the selection.
const query = Queries.generators.chains({
chainTest: ctx => {
const chainName = StructureProperties.chain.label_asym_id(ctx.element);
Expand Down
14 changes: 14 additions & 0 deletions selection/add_label_to_selection_whole/index.html
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>
24 changes: 24 additions & 0 deletions selection/add_label_to_selection_whole/package.json
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"
]
}
19 changes: 19 additions & 0 deletions selection/add_label_to_selection_whole/src/common/init.ts
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;
}
70 changes: 70 additions & 0 deletions selection/add_label_to_selection_whole/src/index.ts
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
Copy link
Collaborator

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)

// Iterate over each Unit in the selection and create a Loci for each Unit
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Iterate over each Unit in the structure. By default the units in the struct initial object were segmented by chain identifiers. Each Unit corresponds to a distinct chain.

Copy link
Member

Choose a reason for hiding this comment

The 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)
Copy link
Collaborator

Choose a reason for hiding this comment

The 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
Copy link
Collaborator

Choose a reason for hiding this comment

The 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
...
loci.push(Structure.toStructureElementLoci(structure));

Copy link
Member

Choose a reason for hiding this comment

The 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();
16 changes: 16 additions & 0 deletions selection/add_label_to_selection_whole/tsconfig.json
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"
}
}
14 changes: 14 additions & 0 deletions selection/add_labels_to_selection_atoms/index.html
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>
24 changes: 24 additions & 0 deletions selection/add_labels_to_selection_atoms/package.json
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"
]
}
19 changes: 19 additions & 0 deletions selection/add_labels_to_selection_atoms/src/common/init.ts
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;
}
79 changes: 79 additions & 0 deletions selection/add_labels_to_selection_atoms/src/index.ts
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();
16 changes: 16 additions & 0 deletions selection/add_labels_to_selection_atoms/tsconfig.json
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"
}
}