Skip to content

Commit 01cd09a

Browse files
committed
Improve Sgroup layout and also show data values..
1 parent acead92 commit 01cd09a

File tree

1 file changed

+131
-13
lines changed

1 file changed

+131
-13
lines changed

display/renderbasic/src/main/java/org/openscience/cdk/renderer/generators/standard/StandardSgroupGenerator.java

Lines changed: 131 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,15 @@
2323

2424
package org.openscience.cdk.renderer.generators.standard;
2525

26+
import com.google.common.collect.HashMultimap;
27+
import com.google.common.collect.Multimap;
2628
import org.openscience.cdk.CDKConstants;
2729
import org.openscience.cdk.geometry.GeometryUtil;
2830
import org.openscience.cdk.interfaces.IAtom;
2931
import org.openscience.cdk.interfaces.IAtomContainer;
3032
import org.openscience.cdk.interfaces.IBond;
3133
import org.openscience.cdk.renderer.RendererModel;
34+
import org.openscience.cdk.renderer.elements.Bounds;
3235
import org.openscience.cdk.renderer.elements.ElementGroup;
3336
import org.openscience.cdk.renderer.elements.GeneralPath;
3437
import org.openscience.cdk.renderer.elements.IRenderingElement;
@@ -48,8 +51,12 @@
4851
import java.awt.geom.Path2D;
4952
import java.awt.geom.Point2D;
5053
import java.awt.geom.Rectangle2D;
54+
import java.util.ArrayDeque;
55+
import java.util.ArrayList;
5156
import java.util.Collection;
5257
import java.util.Collections;
58+
import java.util.Comparator;
59+
import java.util.Deque;
5360
import java.util.HashMap;
5461
import java.util.List;
5562
import java.util.Locale;
@@ -71,6 +78,8 @@ final class StandardSgroupGenerator {
7178
private final double labelScale;
7279
private final StandardAtomGenerator atomGenerator;
7380
private final RendererModel parameters;
81+
private final Multimap<Sgroup,Sgroup> children = HashMultimap.create();
82+
private final Map<Sgroup,Bounds> boundsMap = new HashMap<>();
7483

7584
private StandardSgroupGenerator(RendererModel parameters, StandardAtomGenerator atomGenerator, double stroke,
7685
Font font, Color foreground) {
@@ -274,6 +283,17 @@ private static void contractAbbreviation(IAtomContainer container, Map<IAtom, St
274283
}
275284
}
276285

286+
int getTotalChildCount(Multimap<Sgroup,Sgroup> map, Sgroup key) {
287+
int count = 0;
288+
Deque<Sgroup> deque = new ArrayDeque<>(map.get(key));
289+
while (!deque.isEmpty()) {
290+
Sgroup sgroup = deque.poll();
291+
deque.addAll(map.get(sgroup));
292+
++count;
293+
}
294+
return count;
295+
}
296+
277297
/**
278298
* Generate the Sgroup elements for the provided atom contains.
279299
*
@@ -293,6 +313,21 @@ IRenderingElement generateSgroups(IAtomContainer container, AtomSymbol[] symbols
293313
if (symbols[i] != null)
294314
symbolMap.put(container.getAtom(i), symbols[i]);
295315
}
316+
for (Sgroup sgroup : sgroups) {
317+
for (Sgroup parent : sgroup.getParents()) {
318+
children.put(parent, sgroup);
319+
}
320+
}
321+
322+
// generate child brackets first
323+
sgroups = new ArrayList<>(sgroups);
324+
Collections.sort(sgroups, new Comparator<Sgroup>() {
325+
@Override
326+
public int compare(Sgroup o1, Sgroup o2) {
327+
return Integer.compare(getTotalChildCount(children, o1),
328+
getTotalChildCount(children, o2));
329+
}
330+
});
296331

297332
for (Sgroup sgroup : sgroups) {
298333

@@ -316,7 +351,7 @@ IRenderingElement generateSgroups(IAtomContainer container, AtomSymbol[] symbols
316351
case CtabComponent:
317352
case CtabMixture:
318353
case CtabFormulation:
319-
result.add(generateMixtureSgroup(sgroup));
354+
result.add(generateMixtureSgroup(sgroup, sgroups, symbolMap));
320355
break;
321356
case CtabGeneric:
322357
// not strictly a polymer but okay to draw as one
@@ -484,7 +519,9 @@ else if ("ALT".equals(subtype))
484519
}
485520
}
486521

487-
private IRenderingElement generateMixtureSgroup(Sgroup sgroup) {
522+
private IRenderingElement generateMixtureSgroup(Sgroup sgroup,
523+
List<Sgroup> sgroups,
524+
Map<IAtom,AtomSymbol> symbolMap) {
488525
// draw the brackets
489526
// TODO - mixtures normally have attached Sgroup data
490527
// TODO - e.g. COMPONENT_FRACTION, ACTIVITY_TYPE, WEIGHT_PERCENT
@@ -509,11 +546,27 @@ private IRenderingElement generateMixtureSgroup(Sgroup sgroup) {
509546
break;
510547
}
511548

549+
String superscript = null;
550+
for (Sgroup child : sgroups) {
551+
if (child.getType() == SgroupType.CtabData &&
552+
child.getParents().contains(sgroup)) {
553+
String value = child.getValue(SgroupKey.Data);
554+
String units = child.getValue(SgroupKey.DataFieldUnits);
555+
if (value != null) {
556+
superscript = value;
557+
if (units != null)
558+
superscript += units;
559+
}
560+
break;
561+
}
562+
}
563+
512564
return generateSgroupBrackets(sgroup,
513565
brackets,
514-
null,
566+
symbolMap,
515567
subscript,
516-
null);
568+
null,
569+
superscript);
517570
} else {
518571
return new ElementGroup();
519572
}
@@ -539,6 +592,15 @@ private IRenderingElement generateSgroupBrackets(Sgroup sgroup,
539592
Map<IAtom, AtomSymbol> symbols,
540593
String subscriptSuffix,
541594
String superscriptSuffix) {
595+
return generateSgroupBrackets(sgroup, brackets, symbols, subscriptSuffix, superscriptSuffix, null);
596+
}
597+
598+
private IRenderingElement generateSgroupBrackets(Sgroup sgroup,
599+
List<SgroupBracket> brackets,
600+
Map<IAtom, AtomSymbol> symbols,
601+
String subscriptSuffix,
602+
String superscriptSuffix,
603+
String superscriptPrefix) {
542604

543605
// brackets are square by default (style:0)
544606
Integer style = sgroup.getValue(SgroupKey.CtabBracketStyle);
@@ -561,7 +623,9 @@ private IRenderingElement generateSgroupBrackets(Sgroup sgroup,
561623
: Collections.<SgroupBracket, IBond>emptyMap();
562624

563625
// override bracket layout around single atoms to bring them in closer
564-
if (atoms.size() == 1) {
626+
if (atoms.size() == 1 &&
627+
(sgroup.getType() == SgroupType.CtabStructureRepeatUnit ||
628+
sgroup.getType() == SgroupType.CtabMultipleGroup)) {
565629

566630
IAtom atom = atoms.iterator().next();
567631

@@ -624,6 +688,7 @@ else if (crossingBonds.size()>0) {
624688
result.add(GeneralPath.shapeOf(leftBracket.getOutline(), foreground));
625689
result.add(GeneralPath.shapeOf(rightBracket.getOutline(), foreground));
626690

691+
Rectangle2D leftBracketBounds = leftBracket.getBounds();
627692
Rectangle2D rightBracketBounds = rightBracket.getBounds();
628693

629694
// subscript/superscript suffix annotation
@@ -643,6 +708,14 @@ else if (crossingBonds.size()>0) {
643708
scriptscale));
644709
result.add(GeneralPath.shapeOf(superscriptOutline.getOutline(), foreground));
645710
}
711+
if (superscriptPrefix != null && !superscriptPrefix.isEmpty()) {
712+
TextOutline superscriptOutline = rightAlign(makeText(superscriptPrefix.toLowerCase(Locale.ROOT),
713+
new Point2d(leftBracketBounds.getMaxX(),
714+
leftBracketBounds.getMaxY() + 0.1),
715+
new Vector2d(-leftBracketBounds.getWidth(), 0),
716+
scriptscale));
717+
result.add(GeneralPath.shapeOf(superscriptOutline.getOutline(), foreground));
718+
}
646719
}
647720
} else if (!pairs.isEmpty()) {
648721

@@ -725,14 +798,40 @@ else if (crossingBonds.size()>0) {
725798
supSufPnt, suffixBracketPerp, labelScale));
726799
result.add(GeneralPath.shapeOf(superscriptOutline.getOutline(), foreground));
727800
}
728-
729801
}
730-
} else if (brackets.size() == 2) {
802+
}
803+
// no crossing-bonds we can shrink things down
804+
else if (brackets.size() == 2) {
805+
806+
Bounds bounds = new Bounds();
807+
for (IAtom atom : sgroup.getAtoms()) {
808+
AtomSymbol atomSymbol = symbols.get(atom);
809+
if (atomSymbol != null) {
810+
ConvexHull hull = atomSymbol.getConvexHull();
811+
Rectangle2D bounds2D = hull.outline().getBounds2D();
812+
bounds.add(bounds2D.getMinX(), bounds2D.getMinY());
813+
bounds.add(bounds2D.getMaxX(), bounds2D.getMaxY());
814+
} else {
815+
bounds.add(atom.getPoint2d().x, atom.getPoint2d().y);
816+
}
817+
}
818+
for (Sgroup child : children.get(sgroup)) {
819+
Bounds childBounds = boundsMap.get(child);
820+
if (childBounds != null)
821+
bounds.add(childBounds);
822+
}
823+
824+
Point2d b1p1 = brackets.get(0).getFirstPoint();
825+
Point2d b1p2 = brackets.get(0).getSecondPoint();
826+
Point2d b2p1 = brackets.get(1).getFirstPoint();
827+
Point2d b2p2 = brackets.get(1).getSecondPoint();
828+
829+
double margin = 5*(parameters.get(BasicSceneGenerator.Margin.class)/scale);
731830

732-
final Point2d b1p1 = brackets.get(0).getFirstPoint();
733-
final Point2d b1p2 = brackets.get(0).getSecondPoint();
734-
final Point2d b2p1 = brackets.get(1).getFirstPoint();
735-
final Point2d b2p2 = brackets.get(1).getSecondPoint();
831+
b1p1 = new Point2d(bounds.minX+margin, bounds.minY+margin);
832+
b1p2 = new Point2d(bounds.minX+margin, bounds.maxY-margin);
833+
b2p1 = new Point2d(bounds.maxX-margin, bounds.minY+margin);
834+
b2p2 = new Point2d(bounds.maxX-margin, bounds.maxY-margin);
736835

737836
final Vector2d b1vec = VecmathUtil.newUnitVector(b1p1, b1p2);
738837
final Vector2d b2vec = VecmathUtil.newUnitVector(b2p1, b2p2);
@@ -795,8 +894,9 @@ else if (crossingBonds.size()>0) {
795894
double b1MaxY = Math.max(b1p1.y, b1p2.y);
796895
double b2MaxY = Math.max(b2p1.y, b2p2.y);
797896

798-
Point2d subSufPnt = b2p2;
799-
Point2d supSufPnt = b2p1;
897+
Point2d subSufPnt = b2p2;
898+
Point2d supSufPnt = b2p1;
899+
Point2d supPrefPnt = b1p2;
800900
Vector2d subpvec = b2pvec;
801901

802902
double bXDiff = b1MaxX - b2MaxX;
@@ -828,8 +928,19 @@ else if (crossingBonds.size()>0) {
828928
supSufPnt, subpvec, labelScale));
829929
result.add(GeneralPath.shapeOf(superscriptOutline.getOutline(), foreground));
830930
}
931+
if (superscriptPrefix != null && !superscriptPrefix.isEmpty()) {
932+
subpvec.negate();
933+
TextOutline superscriptOutline = rightAlign(makeText(superscriptPrefix.toLowerCase(Locale.ROOT),
934+
supPrefPnt, subpvec, labelScale));
935+
result.add(GeneralPath.shapeOf(superscriptOutline.getOutline(), foreground));
936+
}
831937

832938
}
939+
940+
Bounds bounds = new Bounds();
941+
bounds.add(result);
942+
boundsMap.put(sgroup, bounds);
943+
833944
return result;
834945
}
835946

@@ -894,4 +1005,11 @@ private TextOutline leftAlign(TextOutline outline) {
8941005
return outline.translate(center.getX() - first.getX(),
8951006
center.getY() - first.getY());
8961007
}
1008+
1009+
private TextOutline rightAlign(TextOutline outline) {
1010+
Point2D center = outline.getCenter();
1011+
Point2D last = outline.getLastGlyphCenter();
1012+
return outline.translate(center.getX() - last.getX(),
1013+
center.getY() - last.getY());
1014+
}
8971015
}

0 commit comments

Comments
 (0)