ngl icon indicating copy to clipboard operation
ngl copied to clipboard

adding a molecule to the stage disables the ability to drag/rotate proteins individual

Open IvoLeist opened this issue 4 years ago • 8 comments

Hello ngl maintainers,

first thank you for your work and even though your follow up molstar is already out there it is good to see that this is still maintained :)

Now to my question: At my old lab I developed and ngl component for dash which caught the interest of the dash-bio team asking me for a PR. This PR is still on going https://github.com/plotly/dash-bio/pull/496

And one of the issue we cannot seem to solve right now is why does adding a molecule to the stage disables the ability to drag/rotate proteins individually? See that codepen

//do not add to stage --> mols can be moved/rotated indepently by
// drag-ctrl+shift + right click
this.loadStructure("1blu","A","red",false)
this.loadStructure("1crn","A","blue",false)

//add to stage --> mols can only be moved/rotated together
// this.loadStructure("1blu","A","red",true)
// this.loadStructure("1crn","A","blue",true)

We are kinda forced to add them to the stage for enabling the user to see the proteins side by side. See that code pen

So what would help us: Either someone can explain how to preserve the individual protein movements although they are added to stage or an idea how we could autorotate the molecules using principal axis and show them side by side w/o adding the molecule to the stage?

Best, Ivo

IvoLeist avatar Aug 08 '20 15:08 IvoLeist

Hi; components automatically get added to the stage when you use loadFile/addRepresentation. I'm not sure what the intent is of this code in your codepen:

  if (addToStage === true) {
    const selection = new NGL.Selection(":"+chain)
    const struc = o.structure.getView(selection)
    const comp = stage.addComponentFromObject(struc)
    o = comp
  }

In your codepen, I can see and manipulate both molecules (without running the code shown above). Can you explain more about what you're trying to do?

garyo avatar Aug 10 '20 12:08 garyo

@garyo

thank you for the quick answer. What do you mean by "in your codepen" If you mean the first codepen this is just an minimal working example to show if you set addToStage === true the molecules cannot be dragged/rotated individually any longer.

The secon pen shows our intent. The idea is to create a new stage object based on a selection

const selection = new NGL.Selection(':' + chain + ' and ' + residues)
const struc = stageObj.structure.getView(selection)
const comp = stage.addComponentFromObject(struc)

We need that new stage object and cannot simply just hide the parts of the molecules we are not interested in because we want to rotate it using principal axis. We found out if we just hide them .getPrincipalAxes() gets executed on the whole protein which leads to the "wrong" rotation.

Thus

const pa = struc.getPrincipalAxes()
comp.setRotation(pa.getRotationQuaternion())

In the end we need to show the molecule parts side by side which is also only working if we have created an new stage object. If we just hide the unwanted molecules parts we do not get the molecules evenly spaced from each other.

 const centre = comp.getCenter()
 const new_posn = [(0-centre.x)-xOffset, 0-centre.y, 0-centre.z]
 comp.setPosition(new_posn)

Everything works as expected but now we cannot drag/rotate the molecules individually any longer. https://github.com/plotly/dash-bio/pull/496#issuecomment-671076151 If you want to try it out in the python dash app: https://github.com/IvoLeist/dash_ngl

I hope this makes it more clear?

IvoLeist avatar Aug 10 '20 19:08 IvoLeist

What's happening is that the drag action is being applied to the first component (the one you don't display). This is because the picking code looks at the structure (not the structureview) which is common between the two components.

A couple of options:

Option A: We could ensure that the AtomPicker instances (there are one or more of these per representation) reference the view rather than the structure - this gets a bit messy...

Option B: Change behaviour so that drag-rotating always takes the view of the picked representation into account? I haven't looked into this in any detail but this way you'd just have a single component per file

Option C: Provide something like component.setRotationCenter?

What does anyone else think?

fredludlow avatar Aug 12 '20 08:08 fredludlow

Interesting. I've just spent a long time dealing with the rotation center of components myself. Currently a component rotates (and scales) about the center of the structure, even when there is an assembly that visually has a different center, which is a bit odd. Try 2RMU for example. Adding component.setRotationCenter sounds like a good idea to me.

garyo avatar Aug 12 '20 10:08 garyo

@fredludlow thank you for having a look into it as well :)

So did I understood it correctly. This is nothing which can be fixed on our side but we have to live at the moment with my workaround and wait till you have provided e.g. component.setRotationCenter ?

IvoLeist avatar Aug 12 '20 15:08 IvoLeist

Thinking aloud:

The bit that's tricky (as far as I can see) is that we no longer just rotate the component when we drag it, we'll need to update its translation too, based on some offset vector between the center of the whole component and the center of the selection. (When I say tricky I mean I'd need to sit down with a notebook and work the maths out, and I'm rusty at this stuff, not that it's fundamentally a hard problem!)

If you can figure out what the relevant transformations are you could even add it to an event listener (listen to component.signals.matrixChanged on the parent (non-displayed molecule), calculate the translation required and apply it to the second component (the one with the representations).

I'm not sure when I'll have time to look at this, but if you can figure out the maths (to do it properly it would I think need an adjustment in trackball-controls (the rotateComponent function) that would make it a much smaller job to integrate :)

fredludlow avatar Aug 12 '20 16:08 fredludlow

That seems like a good approach -- don't forget about scale factor too. One place to start is component.updateMatrix() -- that's where it accounts for the center.

garyo avatar Aug 12 '20 17:08 garyo

Sorry for the delayed follow up - I was on vacations.

@fredludlow thank you for thinking aloud that sounds indeed doable. However, since I am no longer part of the group in which I developed that dash component and I just started my PhD in Barcelona I am afraid but I guess there is no time for figuring out the math.

You sound quite busy as well so you cannot give me any estimate when do you find the time for it, right?

Meanwhile, I ask one of my former colleagues and the guys from plotly (https://github.com/plotly/dash-bio/pull/496) maybe someone else has the capacity and the necessary math skills for it :crossed_fingers:

@garyo thank you for your input, I'll forward that tip as well.

IvoLeist avatar Aug 24 '20 19:08 IvoLeist