108108 */
109109public class StructureDiagramGenerator {
110110
111- static final double DEFAULT_BOND_LENGTH = 1.5 ;
111+ static final double DEFAULT_BOND_LENGTH = 1.5 ;
112+ static final double SGROUP_BRACKET_PADDING_FACTOR = 0.5 ;
112113 private static final Vector2d DEFAULT_BOND_VECTOR = new Vector2d (0 , 1 );
113114 private static final IdentityTemplateLibrary DEFAULT_TEMPLATE_LIBRARY = IdentityTemplateLibrary .loadFromResource ("custom-templates.smi" )
114115 .add (IdentityTemplateLibrary .loadFromResource ("chebi-ring-templates.smi" ));
115116 private static final double RAD_30 = Math .toRadians (-30 );
116- private static final ILoggingTool logger = LoggingToolFactory .createLoggingTool (StructureDiagramGenerator .class );
117+ private static final ILoggingTool logger = LoggingToolFactory .createLoggingTool (StructureDiagramGenerator .class );
117118
118119 public static final Comparator <IAtomContainer > LARGEST_FIRST_COMPARATOR = new Comparator <IAtomContainer >() {
119120 @ Override
@@ -1170,12 +1171,42 @@ private void generateFragmentCoordinates(IAtomContainer mol, List<IAtomContainer
11701171 Set <IAtom > afixbackup = new HashSet <>(afix );
11711172 Set <IBond > bfixbackup = new HashSet <>(bfix );
11721173
1174+ List <Sgroup > sgroups = mol .getProperty (CDKConstants .CTAB_SGROUPS );
1175+
11731176 // generate the sub-layouts
11741177 for (IAtomContainer fragment : frags ) {
11751178 setMolecule (fragment , false , afix , bfix );
11761179 generateCoordinates (DEFAULT_BOND_VECTOR , true , true );
11771180 lengthenIonicBonds (ionicBonds , fragment );
1178- limits .add (getAprxBounds (fragment ));
1181+ double [] aprxBounds = getAprxBounds (fragment );
1182+
1183+ if (sgroups != null && sgroups .size () > 0 ) {
1184+ boolean hasBracket = false ;
1185+ for (Sgroup sgroup : sgroups ) {
1186+ if (!hasBrackets (sgroup ))
1187+ continue ;
1188+ boolean contained = true ;
1189+ Set <IAtom > aset = sgroup .getAtoms ();
1190+ for (IAtom atom : sgroup .getAtoms ()) {
1191+ if (!aset .contains (atom ))
1192+ contained = false ;
1193+ }
1194+ if (contained ) {
1195+ hasBracket = true ;
1196+ break ;
1197+ }
1198+ }
1199+
1200+ if (hasBracket ) {
1201+ // consider potential Sgroup brackets
1202+ aprxBounds [0 ] -= SGROUP_BRACKET_PADDING_FACTOR * bondLength ;
1203+ aprxBounds [1 ] -= SGROUP_BRACKET_PADDING_FACTOR * bondLength ;
1204+ aprxBounds [2 ] += SGROUP_BRACKET_PADDING_FACTOR * bondLength ;
1205+ aprxBounds [3 ] += SGROUP_BRACKET_PADDING_FACTOR * bondLength ;
1206+ }
1207+ }
1208+
1209+ limits .add (aprxBounds );
11791210 }
11801211
11811212 // restore
@@ -2613,6 +2644,12 @@ private static int numRingBonds(IAtomContainer mol, IAtom atom) {
26132644 return cnt ;
26142645 }
26152646
2647+ private void updateMinMax (double [] minmax , Point2d p ) {
2648+ minmax [0 ] = Math .min (p .x , minmax [0 ]);
2649+ minmax [1 ] = Math .min (p .y , minmax [1 ]);
2650+ minmax [2 ] = Math .max (p .x , minmax [2 ]);
2651+ minmax [3 ] = Math .max (p .y , minmax [3 ]);
2652+ }
26162653
26172654 /**
26182655 * Place and update brackets for polymer Sgroups.
@@ -2625,6 +2662,7 @@ private void placeSgroupBrackets(IAtomContainer mol) {
26252662
26262663 // index all crossing bonds
26272664 final Multimap <IBond ,Sgroup > bondMap = HashMultimap .create ();
2665+ final Multimap <Sgroup ,Sgroup > childMap = HashMultimap .create ();
26282666 final Map <IBond ,Integer > counter = new HashMap <>();
26292667 for (Sgroup sgroup : sgroups ) {
26302668 if (!hasBrackets (sgroup ))
@@ -2633,19 +2671,16 @@ private void placeSgroupBrackets(IAtomContainer mol) {
26332671 bondMap .put (bond , sgroup );
26342672 counter .put (bond , 0 );
26352673 }
2674+ for (Sgroup parent : sgroup .getParents ())
2675+ childMap .put (parent , sgroup );
26362676 }
26372677 sgroups = new ArrayList <>(sgroups );
2638- // place child sgroups first
2678+ // place child sgroups first, or those with less total children
26392679 Collections .sort (sgroups ,
26402680 new Comparator <Sgroup >() {
26412681 @ Override
26422682 public int compare (Sgroup o1 , Sgroup o2 ) {
2643- if (o1 .getParents ().isEmpty () != o2 .getParents ().isEmpty ()) {
2644- if (o1 .getParents ().isEmpty ())
2645- return +1 ;
2646- return -1 ;
2647- }
2648- return 0 ;
2683+ return Integer .compare (childMap .get (o1 ).size (), childMap .get (o2 ).size ());
26492684 }
26502685 });
26512686
@@ -2684,7 +2719,20 @@ public int compare(Sgroup o1, Sgroup o2) {
26842719 for (IAtom atom : atoms )
26852720 tmp .addAtom (atom );
26862721 double [] minmax = GeometryUtil .getMinMax (tmp );
2687- double padding = 0.7 * bondLength ;
2722+
2723+ // if a child Sgroup also has brackets, account for that in our
2724+ // bounds calculation
2725+ for (Sgroup child : childMap .get (sgroup )) {
2726+ List <SgroupBracket > brackets = child .getValue (SgroupKey .CtabBracket );
2727+ if (brackets != null ) {
2728+ for (SgroupBracket bracket : brackets ) {
2729+ updateMinMax (minmax , bracket .getFirstPoint ());
2730+ updateMinMax (minmax , bracket .getSecondPoint ());
2731+ }
2732+ }
2733+ }
2734+
2735+ double padding = SGROUP_BRACKET_PADDING_FACTOR * bondLength ;
26882736 sgroup .addBracket (new SgroupBracket (minmax [0 ] - padding , minmax [1 ] - padding ,
26892737 minmax [0 ] - padding , minmax [3 ] + padding ));
26902738 sgroup .addBracket (new SgroupBracket (minmax [2 ] + padding , minmax [1 ] - padding ,
0 commit comments