Skip to content

Commit f26add1

Browse files
committed
Improved angle snapping replacing countAlignedBonds with a more general histogram method. First a histogram is calculated for angles wrapper 0..60, we then use this to click into place and then rotate at 60 degree increments.
1 parent 94f44d4 commit f26add1

File tree

1 file changed

+50
-34
lines changed

1 file changed

+50
-34
lines changed

tool/sdg/src/main/java/org/openscience/cdk/layout/StructureDiagramGenerator.java

Lines changed: 50 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)