nglviewer / ngl

WebGL protein viewer
http://nglviewer.org/ngl/
MIT License
657 stars 168 forks source link

getAtomSetWithinSelection and getAtomSetWithinGroup in several files #1023

Closed hellhorse123 closed 2 months ago

hellhorse123 commented 4 months ago

getAtomSetWithinSelection and getAtomSetWithinGroup doesn't work If we add two structures into one visual scene and label protein atoms from ligand by distance. Both protein and ligand came to scene from different files. How to display protein atoms around a ligand with a radius if the ligand is loaded as a separate file into the scene? I used example from Distance-based selection

ppillot commented 4 months ago

That's correct, these methods are attached to a given structure, so it's not currently possible to make selections across different structures/components using these methods. There is a workaround discussed in the following response, by using the spatial hash attached to a given structure: https://github.com/nglviewer/ngl/issues/971#issuecomment-1452074968

If one structure contains a ligand and another contains a receptor, you can use some code like the following to get the neighbouring atoms.

const ligandStructure = ligComponent.structure
const spatialHash = receptorComponent.structure.spatialHash
const pocketAtomSet = receptorComponent.structure.getAtomSet(false) // an empty selection
ligandStructure.eachAtom(
    ap => spatialHash.eachWithin(
        ap.x, ap.y, ap.z, 4.5, 
        (pocketAp) => pocketAtomSet.set(pocketAp.index)
    )
) 
hellhorse123 commented 4 months ago

and then how to display the resulting atoms from pocketAtomSet?

britesma commented 3 months ago

I think that when you have your AtomSet ready you can give it to a representation (such as stick+ball) by setting the representation selection with your AtomSet.

hellhorse123 commented 3 months ago

That's correct, these methods are attached to a given structure, so it's not currently possible to make selections across different structures/components using these methods. There is a workaround discussed in the following response, by using the spatial hash attached to a given structure: #971 (comment)

If one structure contains a ligand and another contains a receptor, you can use some code like the following to get the neighbouring atoms.

const ligandStructure = ligComponent.structure
const spatialHash = receptorComponent.structure.spatialHash
const pocketAtomSet = receptorComponent.structure.getAtomSet(false) // an empty selection
ligandStructure.eachAtom(
    ap => spatialHash.eachWithin(
        ap.x, ap.y, ap.z, 4.5, 
        (pocketAp) => pocketAtomSet.set(pocketAp.index)
    )
) 

The answer was not entirely complete, we also need to add protein.structure.getAtomSetWithinGroup for representation all atoms.

Ultimately, this is the code that works correctly:

const loadProteinAndLigand = async () => {
      const protein = await stage.loadFile(ProteinFile);
      protein.addRepresentation("cartoon", { assembly: "AU" });
      const ligand = await stage.loadFile(LigandFile, {});
      ligand.addRepresentation("ball+stick", {});

      const spatialHash = protein.structure.spatialHash;
      const pocketAtomSet = protein.structure.getAtomSet(false); // an empty selection

      ligand.structure.eachAtom((ap) => {
        spatialHash.eachWithin(ap.x, ap.y, ap.z, 4, (atomIndex, dSq) => {
          pocketAtomSet.set(atomIndex);
        });
      });

      const atomSet2 = protein.structure.getAtomSetWithinGroup(pocketAtomSet);
      protein.addRepresentation("ball+stick", {
        sele: atomSet2.toSeleString(),
      });

      ligand.autoView();
    };