MolPrinter, Graphics2D, transposing Angstrom to pixel coords

User 1fbb6f43e9

09-09-2008 21:20:38

Hi,





I need to color arbitrary locations on a molecule rendered in a Graphics2D using MolPrinter. These locations may not exactly correspond to atom locations. I need to understand how the coordinates obtained using MolAtom.getLocation() are


converted to a pixel-based (x,y) coordinates when using MolPrinter.





The following code snippet might be useful..


MolPrinter p = new MolPrinter(mol);


Rectangle r = new Rectangle(0, 0, 360, 360);


p.setScale(p.maxScale(r)) ;


p.paint(g, r);






The above code snippet takes a 'mol' Molecule object and renders into 'g' which is a Graphics2D object. The molecule is centered within the Rectangle 'r'.


Could you please answer the following


1) How is the scale caculated, is it the same on the X and Y axes?


2) Does the center of the rectangle (i.e. the origin) coincide with the center of the molecule or with the center of the outer rectangle enclosing the molecule?


3) Is the location returned by MolAtom.getLocation() in Angstrom units?


4) If I want to retrieve the location of one of the atoms in pixel coordinates, how would I do it?





Any help would be much appreciated.





Thanks!





Chris

ChemAxon 7c2d26e5cf

11-09-2008 13:48:03

Hi Chris,
Quote:
1) How is the scale caculated, is it the same on the X and Y axes?
Yes, the horizontal and the vertical scaling factor are always the same.
Quote:
2) Does the center of the rectangle (i.e. the origin) coincide with the center of the molecule or with the center of the outer rectangle enclosing the molecule?
This question is not so simple.


The molecule rectangle is rendered to the center of the drawable area. In this case, the drawable area is the "r" rectangle ( new Rectangle(0,0,360,360) ). The visualized molecule is positioned in the center of the molecule rectangle. But it does not mean that the center of the molecule graph equals with the center of the visualized molecule.





To get the screen coordinates from a MolAtom, there are several transformations. The transformation matrix is not available from the public API.


If there are atom labels, the labels and texts modify the position of own and other node's position.


Atom labels vindicate extra spaces. It means that the molecule rectangle will be larger with atom labels than without them.


Unless the atom labels on the left and the right edge are symmetrical, the center of the molecule will be shifted left or right direction. Please see the attached images (molecule sources are also attached).


The two picture displays the same molecule graph (atom and bond coordinates are the same). The only difference is that the picture titled "benzene ring with long alias string" includes an alias string. This label requires more space on the right side. Since the molecule rectangle is extended on the right side, the visualized molecule ring is shifted to the left to keep the picture in the center.


By the way, other graphical objects (like text boxes, arrows, etc.) can also modify the screen position of a molecule fragment.
Quote:
3) Is the location returned by MolAtom.getLocation() in Angstrom units?
Yes, the coordinates are in Angstrom units. They are the same coordinates that you can find in the molecule source files (mol, sdf, mrv, etc..).
Quote:
4) If I want to retrieve the location of one of the atoms in pixel coordinates, how would I do it?
Since there are not an universal transformation matrix for calculating screen coordinates, I recommend you another way to do it.


Export the molecule into SVG. Pass the display settings of your MolPrinter to the exporter. After then, you got the same image in SVG than you draw to the Java Graphics.


Since SVG is an xml based format, you can retrieve the screen position of (atom) labels from the xml file. Please see the linking topic about it:


http://www.chemaxon.com/forum/ftopic2401.html

User 1fbb6f43e9

11-09-2008 17:34:57

Thanks for the response!





I don't suppose that there is any chance of making the transform matrix (after it has been computed) available in the public API? :)





Alternatively, is there a way to set the rendering style of individual atoms? We are not tied to using the MolPrinter to display the structure, any 2D representation would work.





Perhaps it would help if I give you a higher level explanation of our requirements: We need to display a molecule in 2D, and mark a pharmacophore feature at an arbitrary location relative to the atoms of said feature. We want to mark it using a colored circle. This could be a circle drawn by a Graphics2D, or a hack could be adding a 'pseudo atom' to the desired location and redering it differently than the rest.





We are hoping that it won't come to parsing SVG, but thank you for the suggestion! If you have other ideas, I am all ears!





Thanks again,


Chris

ChemAxon 7c2d26e5cf

12-09-2008 15:45:48

I draw this in MarvinSketch. There are two different ways for highlighting. Is one of these suitable for you?


In the first case, a line is curved in almost circle and its color is set to red.


In the second case, there is a normal selection.


Implementation of the second one with the Marvin API is more easy than the first ones:


Code:
mol.getAtom(0).setSelected(true)



With the help of the MDocument API, you can add graphical objects to the molecule graph but it requires some calculation to reproduce the same result that you can see on the picture.

User 1fbb6f43e9

12-09-2008 15:54:07

Thanks for the reply. This was actually our first idea, however the features sometimes share atoms, so we need to be able to mark an arbitrary location relative to the atoms, rather than marking the atoms themselves. Thanks for trying to help though!





We are still looking if anyone else has advice.





Chris

User 1fbb6f43e9

12-09-2008 20:45:40

I just wanted to clarify that the molecule that I am representing has no labels visable, so that should not be the problem. In the other post that you referred me to, the poster said that they were able to perform the transform correctly using the scale obtained from Molecule.getImageSize. I have tried using this but the atom coords I am trying to draw on seem to get scaled more than the molecule (see the image attached).





My Test code is attached, and pasted below. This seems like it should be a simple thing to do, but I am stumped.





PS the reason that we hope to accomplish this without parsing SVG is that we are afraid it will slow our application too much.





Thanks!


Chris








package com.vpharm.pharmViewer.sandbox;





import chemaxon.struc.Molecule;


import chemaxon.struc.MolAtom;


import chemaxon.struc.DPoint3;


import chemaxon.marvin.MolPrinter;


import chemaxon.marvin.util.MolImageSize;


import chemaxon.marvin.paint.DispOptConsts;


import chemaxon.formats.MolImporter;


import chemaxon.formats.MolFormatException;


import com.vpharm.pharmViewer.beans.FeatureBean;


import com.vpharm.library.gui.marvin.MoleculeViewer;





import javax.imageio.ImageIO;


import javax.swing.*;


import java.util.List;


import java.awt.image.BufferedImage;


import java.awt.*;


import java.awt.geom.Ellipse2D;


import java.io.File;


import java.io.IOException;





/**


* User: hughes


* Date: Sep 10, 2008


* Time: 11:35:34 AM


*/


public class Test


{





public static void main(String[] args)


{





try


{


createMolPrinterTest("c1ccccc1");


}


catch (MolFormatException e)


{


e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.


}





}





private static void createMolPrinterTest(String smiles) throws MolFormatException


{





Molecule mol = MolImporter.importMol(smiles);


mol.clean(2, null);


mol.aromatize(false);





BufferedImage im = new BufferedImage(400, 400,


BufferedImage.TYPE_INT_ARGB);


Graphics2D g = im.createGraphics();


g.setColor(Color.white);


g.fillRect(0, 0, im.getWidth(), im.getHeight());


g.setColor(Color.red);





Rectangle r = new Rectangle(0, 0, 398, 398);





double scaleX = r.width / mol.calcWidth() ;


double scaleY = r.height / mol.calcHeight() ;





double scale1 = Math.min(scaleX, scaleY) ;





MolImageSize mis = mol.getImageSize("image:w398h398");











double scale3 = mis.scale;





g.draw(r);





MolPrinter p = new MolPrinter(mol);


double useScale = scale3;


p.setScale(useScale);





System.out.println("scael-"+scale1);


// System.out.println("scale2-"+p.maxScale(r));


System.out.println("scale3-"+scale3);


System.out.println("getScale-"+p.getScale());





g.drawImage( (Image)(mol.toObject("image:w398,h398")), 0, 0, null );





double x;


double y;











for (int i = 0; i < mol.getAtomCount(); i++)


{





MolAtom atom = mol.getAtom(i);





DPoint3 atomLocation = new DPoint3();


atom.getLocation(atomLocation);





DPoint3 geomCenter = mol.getLocation() ;


geomCenter.x *= useScale ;


geomCenter.y *= useScale ;


geomCenter.y = -1 * geomCenter.y ;





x = atomLocation.x ;


y = atomLocation.y ;





x *= (useScale);


y *= (useScale);


y = -1 * y ;





// x += (r.getCenterX() - geomCenter.x) ;


// y += (r.getCenterY() - geomCenter.y) ;





x += r.getCenterX();


y += r.getCenterY();








Ellipse2D.Double circle = new Ellipse2D.Double(x-5, y - 5, 10, 10);


g.setColor(Color.green);


g.fill(circle);











}





try


{


ImageIO.write(im, "png", new File("test.png"));


}


catch (IOException e)


{


e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.


}





JFrame frame = new JFrame();


frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);


frame.setSize(500, 500);


frame.getContentPane().add(new JLabel(new ImageIcon(im)));


frame.setVisible(true);





}





}

ChemAxon 7c2d26e5cf

15-09-2008 14:05:08

We need time to check it.

User 1fbb6f43e9

17-09-2008 13:56:09

Thanks! I'll look forward to hearing from you again.





Chris

ChemAxon 7c2d26e5cf

18-09-2008 16:38:12

Hi Chris,





Thanks for the example. I have investigated the issue. I have managed to find out how to estimate the screen position of nodes.


The trick is that you have to divide the scaling factor with the bond length ( Molecule.bondLength() ). Instead of Molecule.getLocation(), use Molecule.calcCenter() (the geometrical center of the molecule) by the transformation of the atom coordinates.


Code:
x = ((atom.getLocation().x - mol.calcCenter().x) * scale / mol.bondLength()) + rect.getCenterX()



Calculation of the y coordinate is similar. But some correction is required at the end: shift it with the half of bond thickness ( MolPrinter.getBondSpacing() ).


Code:
y = y - (molprinter.getBondSpacing() * (scale/mol.bondLength())/2);



See the attached Java source.





Tamas

User 1fbb6f43e9

22-09-2008 16:05:51

Hi Tamas,





Thanks so much for your response. I tried your code which worked great for this molecule, but when I plugged it into my ap I found that the transform was still off for other molecules.





Because of time constraints, I have decided to try another approach. I am rendering the structure using the ball and stick style, and adding colored "pseudo atoms" as my markers. This is working well and has the added advantage that I can still display and MViewPane and let the user move the structure around.





Thanks again for the time that you put into helping me with my problem, I really appreciate it.





Chris

ChemAxon 7c2d26e5cf

22-09-2008 16:36:53

I am glad that finally the issue is solved.

ChemAxon 7c2d26e5cf

29-07-2009 16:48:08

In Marvin 5.2.4, a new method will be introduced that help you to calculate the mid point of the atom label: converts molecular coordinates to 2D screen coordinates.


MolPrinter.molToScreenCoords(DPoint3 molcoords, Point2D scrcoords)


The attached examples demonstrates its usage.


Currently, this code does not work since Marvin 5.2.4 is still not available. But it will be released in a couple of weeks.

User e7455c8684

10-03-2010 20:31:25

how to make it transparent?


I used molprinter.paint(graph2D, new Retangle(0,0,width,height), but the result is like picture No.2, I would like to get the result like picture No.1.


 


thanks,

ChemAxon 7c2d26e5cf

12-03-2010 16:49:16

Use the MolPrinter.setTransparent(boolean) method.


MolPrinter molPrinter = new MolPrinter(mol);
...
molPrinter.setTransparent(true);
molPrinter.paint(g, rect);