dsehnal / LiteMol

A library/plugin for handling 3D structural molecular data (not only) in the browser.
Other
155 stars 36 forks source link

Simplest way to... color/style a particular selection? #10

Open sillitoe opened 7 years ago

sillitoe commented 7 years ago

Morning - another doc request.

Given the starting point from the FAQ:

plugin.loadMolecule({
    id: '1tqn',
    url: 'https://www.ebi.ac.uk/pdbe/static/entry/1tqn_updated.cif',
    format: 'cif' // default
});

What's the simplest, stand-alone way to style a selection, e.g.

I can do this by adjusting the code from the examples and creating my own plugin, but that's a lot of internal machinery to (sort of) understand before I can achieve what looks like it should be a fairly simple task.

Would be useful to be able to isolate simple, stand-alone JS code (i.e. when the full source is not available) for casual users to interact with their own components rather than require a full source build.

dsehnal commented 7 years ago

I will create an utility function that creates the corresponding theme and show how to apply it in the commands example.

As you point out currently the only way is to do it "low level".

sillitoe commented 7 years ago

Sounds great, thanks.

Those utility functions (i.e. the "external" facing functions) would be a great place to start if you're still looking at generating API docs from source. The "internal" API docs would obviously be useful for reference too, but TypeScript is generally pretty readable.

dsehnal commented 7 years ago

I didn't have a look at the generated docs yet, but yes I agree. In that case tho this should probably be a "standalone" project that wraps the plugin. Tho this adds another maintenance cost... will think about it.

dsehnal commented 7 years ago

I have spent some time thinking about how to do this (i.e. color the molecule loaded using loadMolecule and make selection on it) so that it does not cause any trouble down the road ... and I didn't find one.

The issue is that LiteMol does not represent the state the same way you typical web viewer does. There is no single object that would store the molecule and all of its selections/visual representations/etc. Instead, it is required to query the state tree and find what you are looking for.

When you call loadMolecule, it creates a state transform composed of these transforms:

This results in a tree of entities

String/BinaryData (depending on format)
  |_ Molecule
     |_ MoleculeModel
        |_ Macromolecule Visuals
           |_ Polymer
              |_ Visual (cartoons)
           |_ HET
              |_ Visual (balls and sticks)
           |_ Water
              |_ Visual (balls and sticks)

So to be able to make the "simple" selection work, I would have to store somewhere the references to the different parts of the molecule, because "color chain X" needs to be applied to the "Visual (cartoons)" entity and "create balls and sticks of residues A, B, C" needs to be created by creating a selection on MoleculeModel and then creating a visual on that selection.

Of course I can store the names to enable this particular use case. But then you come and ask "how do I also do X starting from the loadMolecule function?" and the code would need to be modified again. It becomes a tangled mess really fast. I have added the loadMolecule for the single use case where you only want to show a molecule and don't care about it any more.

Having said all that, I really need to write a design goals/principles documents for LiteMol :)


Now to the issue of the simplest way to accomplish what you need. (note: this requires you to update to the latest version of LiteMol because I've removed a behavior from the default spec that automatically creates molecule visuals when a MoleculeModel entity is created and instead added it to the loadMolecule function)

// import Transformer = LiteMol.Bootstrap.Entity.Transformer
let t = plugin.createTransform();
t.add(plugin.root, Transformer.Data.Download, { url: "https://...", type: "String" })
  .then(Transformer.Molecule.CreateFromData, { format: Core.Formats.Molecule.SupportedFormats.mmCIF }, { })
  .then(Transformer.Molecule.CreateModel, { modelIndex: 0 }, { ref: 'model' })
  .then(Transformer.Molecule.CreateMacromoleculeVisual, { polymer: true, polymerRef: 'polymer-visual', het: true, water: true });

plugin.applyTransform(t);

Notice the ref and polymerRef properties. This generates a state tree:

String/BinaryData (depending on format)
  |_ Molecule
     |_ MoleculeModel [ref = "model"]
        |_ Macromolecule Visuals
           |_ Polymer
              |_ Visual (cartoons) [ref = "polymer-visual"]
           |_ HET
              |_ Visual (balls and sticks)
           |_ Water
              |_ Visual (balls and sticks)
let query = LiteMol.Core.Structure.Query.chains({ authAsymId: 'A' });
let t = plugin.createTransform();
t.add('polymer-visual', Transformer.Molecule.CreateSelectionFromQuery, { query, name: 'Chain A' }, { }))
plugin.applyTransform(t);

Alternatively, have a look at the Commands example. It shows how to create a theme that color a chain with a different color. (Altho there is currently a very annoying drawback to doing this: using the "Reset Scene" command, i.e. the "refresh" button in top right corner, will remove any themes applied by the Command.Visual.UpdateBasicTheme command; it is something I am working on to fix)

let query = LiteMol.Core.Structure.Query.residues({ authAsymId: 'A', authSeqNumber: 132 }, { authAsymId: 'A', authSeqNumber: 144 }, ...);
let t = plugin.createTransform();
t.add('polymer-visual', Transformer.Molecule.CreateSelectionFromQuery, { query, name: 'Residues' }, { }))
  .then(Transformer.Molecule.CreateVisual, { style: Bootstrap.Visualization.Molecule.Default.ForType.get('BallsAndSticks') });
plugin.applyTransform(t);  

This is also shown in the Commands example.

sillitoe commented 7 years ago

Thanks very much for taking the time to answer this so thoroughly. I think this may well be useful documentation for others too.

I'm in a meeting all day tomorrow (discussing all this with the Genome3D consortium amongst other things) - I'll have a go implementing this and writing it up after the weekend.

sillitoe commented 7 years ago

I'm getting a bit stuck with type casting:

The above worked okay - though I found that I needed to change the line...

let data = t.add( plugin.root, Transformer.Data.Download, { url: url, type: 'String', id: id })

to...

let data = t.add( plugin.root,
    <Bootstrap.Tree.Transformer.To<Entity.Data.String | Entity.Data.Binary>>Transformer.Data.Download,
    { url: url, type: 'String', id: id })

Which seems a bit of a mouthful (and makes me think I'm probably doing something wrong).

Perhaps related - I thought I should be able to get the molecule displaying as C-alpha trace (rather than cartoon)...

let data = t.add( plugin.root,
    <Bootstrap.Tree.Transformer.To<Entity.Data.String | Entity.Data.Binary>>Transformer.Data.Download,
    { url: url, type: 'String', id: id })
    .then(Transformer.Molecule.CreateFromData, { format: Core.Formats.Molecule.SupportedFormats.PDB, customId: id }, { isBinding: true, ref: 'mol-'+id })
    .then(Transformer.Molecule.CreateModel, { modelIndex: 0 }, { isBinding: false, ref: 'model-'+id })
    .then(Transformer.Molecule.CreateVisual, { style: Bootstrap.Visualization.Molecule.Default.ForType.get('Calpha') }, {} );

However the final line complains with:

Argument of type 'Transformer<Model | Selection, Visual, CreateVisualParams>' is not assignable to parameter of type 'Transformer<Model, Visual, CreateVisualParams>'

Any pointers?

dsehnal commented 7 years ago

Ah yes, sorry. The types are indeed messed up a bit at the moment.

When I was designing the type system, unfortunately discriminated unions were not yet available, but the basic union types (type T = A | B) were. And I foolishly assumed that the compiler will be able to interfere the correct type. And when I found out that was not the case, it was "too late" and I had to resort to the ugly type annotation to resolve the issue.

<Bootstrap.Tree.Transformer.To<Entity.Data.String | Entity.Data.Binary>>

is "correct" workaround.

For the visual, using the type

<Bootstrap.Tree.Transformer.To<Entity.Molecule.Visual>>

should do the trick.

Sorry for the inconvenience, I will try to fix it using the discriminated unions as soon as possible (tho it will probably introduce a minor breaking change when creating custom entities).

sillitoe commented 7 years ago

Thanks - that sheds a bit more light on coercions as well.

On Tue, 22 Nov 2016, 00:14 David Sehnal, notifications@github.com wrote:

Ah yes, sorry. The types are indeed messed up a bit at the moment.

When I was designing the type system, unfortunately discriminated unions https://basarat.gitbooks.io/typescript/content/docs/types/discriminated-unions.html were not yet available, but the basic union types (type T = A | B) were. And I foolishly assumed that the compiler will be able to interfere the correct type. And when I found out that was not the case, it was "too late" and I had to resort to the ugly type annotation to resolve the issue.

<Bootstrap.Tree.Transformer.To<Entity.Data.String | Entity.Data.Binary>>

is "correct" workaround.

For the visual, using the type

<Bootstrap.Tree.Transformer.To>

should do the trick.

Sorry for the inconvenience, I will try to fix it using the discriminated unions as soon as possible (tho it will probably introduce a minor breaking change when creating custom entities).

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/dsehnal/LiteMol/issues/10#issuecomment-262108919, or mute the thread https://github.com/notifications/unsubscribe-auth/AAJVeuquQbPUNyZdUe9ZBXt0kX1k5Ew8ks5rAjPdgaJpZM4K1Od3 .

dsehnal commented 7 years ago

I have changed the Builder class so that the type annotations should not be needed (and removed the Transformer.To type).