@@ -948,6 +948,35 @@ private void finalizeLayout(IAtomContainer mol) {
948948 placeSgroupBrackets (mol );
949949 }
950950
951+ /**
952+ * Calculates a histogram of bond directions, this allows us to select an
953+ * orientation that has bonds at nice angles (e.g. 60/120 deg). The limit
954+ * parameter is used to quantize the vectors within a range. For example
955+ * a limit of 60 will fill the histogram 0..59 and Bond's orientated at 0,
956+ * 60, 120 degrees will all be counted in the 0 bucket.
957+ *
958+ * @param mol molecule
959+ * @param counts the histogram is stored here, will be cleared
960+ * @param lim wrap angles to the (180 max)
961+ * @return number of aligned bonds
962+ */
963+ private static void calcDirectionHistogram (IAtomContainer mol ,
964+ int [] counts ,
965+ int lim ) {
966+ if (lim > 180 )
967+ throw new IllegalArgumentException ("limit must be ≤ 180" );
968+ Arrays .fill (counts , 0 );
969+ for (IBond bond : mol .bonds ()) {
970+ Point2d beg = bond .getBegin ().getPoint2d ();
971+ Point2d end = bond .getEnd ().getPoint2d ();
972+ Vector2d vec = new Vector2d (end .x - beg .x , end .y - beg .y );
973+ if (vec .x < 0 )
974+ vec .negate ();
975+ double angle = Math .PI /2 + Math .atan2 (vec .y , vec .x );
976+ counts [(int )(Math .round (Math .toDegrees (angle ))%lim )]++;
977+ }
978+ }
979+
951980 /**
952981 * Select the global orientation of the layout. We click round at 30 degree increments
953982 * and select the orientation that a) is the widest or b) has the most bonds aligned to
@@ -958,23 +987,37 @@ private void finalizeLayout(IAtomContainer mol) {
958987 * @param alignDiff parameter at which we consider orientations equally good (bond align select)
959988 */
960989 private static void selectOrientation (IAtomContainer mol , double widthDiff , int alignDiff ) {
961- double [] minmax = GeometryUtil .getMinMax (mol );
990+
991+ int [] dirhist = new int [180 ];
992+ double [] minmax = GeometryUtil .getMinMax (mol );
962993 Point2d pivot = new Point2d (minmax [0 ] + ((minmax [2 ] - minmax [0 ]) / 2 ),
963994 minmax [1 ] + ((minmax [3 ] - minmax [1 ]) / 2 ));
964995
996+ // initial alignment to 60 degrees
997+ calcDirectionHistogram (mol , dirhist , 60 );
998+ int max = 0 ;
999+ for (int i = 1 ; i < dirhist .length ; i ++)
1000+ if (dirhist [i ] > dirhist [max ])
1001+ max = i ;
1002+ if (max != 0 )
1003+ GeometryUtil .rotate (mol , pivot , Math .toRadians (60 -max ));
9651004
9661005 double maxWidth = minmax [2 ] - minmax [0 ];
9671006 double begWidth = maxWidth ;
968- int maxAligned = countAlignedBonds (mol );
1007+ calcDirectionHistogram (mol , dirhist , 180 );
1008+ int maxAligned = dirhist [60 ]+dirhist [120 ];
9691009
9701010 Point2d [] coords = new Point2d [mol .getAtomCount ()];
9711011 for (int i = 0 ; i < mol .getAtomCount (); i ++)
9721012 coords [i ] = new Point2d (mol .getAtom (i ).getPoint2d ());
9731013
974- final double step = Math .toRadians (30 );
975- final int numSteps = (360 / 30 ) - 1 ;
976- for (int i = 0 ; i < numSteps ; i ++) {
1014+ double step = Math .PI /3 ;
1015+ double tau = 2 *Math .PI ;
1016+ double total = 0 ;
1017+
1018+ while (total < tau ) {
9771019
1020+ total += step ;
9781021 GeometryUtil .rotate (mol , pivot , step );
9791022 minmax = GeometryUtil .getMinMax (mol );
9801023
@@ -991,7 +1034,8 @@ private static void selectOrientation(IAtomContainer mol, double widthDiff, int
9911034 // width is not significantly better or worse so check
9921035 // the number of bonds aligned to 30 deg (aesthetics)
9931036 else if (delta <= widthDiff ) {
994- int aligned = countAlignedBonds (mol );
1037+ calcDirectionHistogram (mol , dirhist , 180 );
1038+ int aligned = dirhist [60 ]+dirhist [120 ];
9951039 int alignDelta = aligned - maxAligned ;
9961040 if (alignDelta > alignDiff || (alignDelta == 0 && width > maxWidth )) {
9971041 maxAligned = aligned ;
@@ -1007,34 +1051,6 @@ else if (delta <= widthDiff) {
10071051 mol .getAtom (i ).setPoint2d (coords [i ]);
10081052 }
10091053
1010- /**
1011- * Count the number of bonds aligned to 30 degrees.
1012- *
1013- * @param mol molecule
1014- * @return number of aligned bonds
1015- */
1016- private static int countAlignedBonds (IAtomContainer mol ) {
1017- final double ref = Math .toRadians (30 );
1018- final double diff = Math .toRadians (1 );
1019- int count = 0 ;
1020- for (IBond bond : mol .bonds ()) {
1021- Point2d beg = bond .getBegin ().getPoint2d ();
1022- Point2d end = bond .getEnd ().getPoint2d ();
1023- if (beg .x > end .x ) {
1024- Point2d tmp = beg ;
1025- beg = end ;
1026- end = tmp ;
1027- }
1028- Vector2d vec = new Vector2d (end .x - beg .x , end .y - beg .y );
1029- double angle = Math .atan2 (vec .y , vec .x );
1030-
1031- if (Math .abs (angle ) - ref < diff ) {
1032- count ++;
1033- }
1034- }
1035- return count ;
1036- }
1037-
10381054 private final double adjustForHydrogen (IAtom atom , IAtomContainer mol ) {
10391055 Integer hcnt = atom .getImplicitHydrogenCount ();
10401056 if (hcnt == null || hcnt == 0 )
0 commit comments