patrickfuller / imolecule

An embeddable webGL molecule viewer and file format converter.
http://patrickfuller.github.io/imolecule/
MIT License
85 stars 18 forks source link

Changing camera view #27

Closed CorySimon closed 8 years ago

CorySimon commented 8 years ago

In my attempt to replicate the examples/mof.html, my MOF is rotated at a very awkward angle when I first open it. Is it easy to change the default view, through an up vector and view vector? First, for the example in examples/mof.html. Second, in the IPython Notebook, this would be nice too. Such as:

imolecule.draw("IRMOF-1.cif", show_save=True, up_vector=[1, 0, 0], view_vector=[0, 1, 0])
patrickfuller commented 8 years ago

In javascript, you can grab the camera with imolecule.camera. It's an instance of THREE.Camera, which has all sorts of properties. Something like imolecule.camera.rotation.set(0, 0, 0); will set the rotation. There's also position vectors and rotation matrices to play with.

Personally, I've found it cleaner to rotate the molecule I'm rendering with a throwaway script instead of the camera. It makes it easier to switch between molecules in a UI (like the MOF examples).

Regarding the python bindings, I'm hesitant to complicate the API for minor improvements. I could see the benefit of squeezing this into a **kwargs, but I (at least currently) prefer to avoid it.

On Monday, November 9, 2015, Cory Simon notifications@github.com wrote:

In my attempt to replicate the examples/mof.html, my MOF is rotated at a very awkward angle when I first open it. Is it easy to change the default view, through an up vector and view vector? First, for the example in examples/mof.html. Second, in the IPython Notebook, this would be nice too. Such as:

imolecule.draw("IRMOF-1.cif", show_save=True, up_vector=[1, 0, 0], view_vector=[0, 1, 0])

— Reply to this email directly or view it on GitHub https://github.com/patrickfuller/imolecule/issues/27.

CorySimon commented 8 years ago

Thanks. One cannot change the camera angle in the IPython notebook because camera is not an attribute of the module imolecule.

In three.js, it seems that a rotation is possible via:

camera.rotation.y = 90 * Math.PI / 180

However, in your MOF example .html, I modified as:

    <script type="text/javascript">
        imolecule.create('.molecule');
        imolecule.camera.rotation.y = 40 * Math.PI / 180;
        $.getJSON('IRMOF1.json', function (mof) {
            imolecule.draw(mof);
        });

    </script>

but the rotation did not work. I also tried adding the line imolecule.camera.rotation.y = 40 * Math.PI / 180; at different points.

Can you give an example of what you mean by using a throwaway script?

patrickfuller commented 8 years ago

You want to think about the molecule as the origin, and the camera as an object around it. By default, the camera is positioned at (0, 0, z), where z is calculated to fit the molecule into the viewing window. The camera is also tied to looking at the origin by default, so rotating it won't do much - especially if you're rotating around a symmetric molecule like a MOF.

Instead, try setting the position.

screen shot 2015-11-11 at 4 10 30 pm

Regarding the molecule rotation comment, I find it cleaner to bake the rotation into the JSON versus setting custom camera parameters. You can use whatever rotation algorithm you wish. Here's an example:

def rotate(v, axis, angle):
    """Rotates using axis-angle parameters."""

    # This is the Euler-Rodrigues rotation formula
    # http://en.wikipedia.org/wiki/Euler%E2%80%93Rodrigues_formula
    # http://stackoverflow.com/questions/6802577/python-rotation-of-3d-
    # vector
    a = np.cos(angle / 2)
    b, c, d = -axis * np.sin(angle / 2)
    rotation_vector = np.array([[a*a+b*b-c*c-d*d, 2*(b*c-a*d), 2*(b*d+a*c)],
                               [2*(b*c+a*d), a*a+c*c-b*b-d*d, 2*(c*d-a*b)],
                               [2*(b*d-a*c), 2*(c*d+a*b), a*a+d*d-b*b-c*c]])
    return np.dot(v, rotation_vector)

Then, iterate through your JSON and apply this to the locations. Here's an untested throwaway script example:

import numpy as np
import json  # or `import imolecule.json_formatter as json` to get a more molfile-like output

with open(your_file) as in_file:
    molecule = json.load(in_file)

for atom in molecule['atoms']:
    atom['location'] = rotate(np.array(atom['location']), axis, np.pi / 2).tolist()

print(json.dumps(molecule))

Something like that should work.

Finally, regarding the notebook, there's no global imolecule instance. There is an imolecule variable buried in there, but I'd instead recommend injecting code into the javascript string before rendering. I tested this one going off of the "Advanced Usage" example in the sample notebook:

from IPython.display import display, HTML

script = imolecule.draw('c1ccccc1', display_html=False).splitlines()
script.insert(11, '$d.imolecule.camera.position.set(5, 5, 5);')
display(HTML('\n'.join(script)))

Proof:

screen shot 2015-11-11 at 4 31 38 pm

Even with this, I'd still recommend baking the rotation into your JSON. I think clean code is worth the effort of a rotation script.