Skip to content

Commit

Permalink
Merge pull request #200 from cdk/patch/cxsmiles-read
Browse files Browse the repository at this point in the history
Patch/cxsmiles read
  • Loading branch information
egonw committed Apr 21, 2016
2 parents a07f829 + 937f107 commit 18639d9
Show file tree
Hide file tree
Showing 13 changed files with 1,994 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -498,10 +498,16 @@ public Depiction depict(IReaction rxn) throws CDKException {
productTitles.add(copy.generateTitle(product));
}

final Bounds conditions = generateReactionConditions(rxn, fgcol);

return new ReactionDepiction(model,
reactantBounds, productBounds, agentBounds,
plus, rxn.getDirection(), dimensions,
reactantTitles, productTitles, title, fgcol);
reactantTitles,
productTitles,
title,
conditions,
fgcol);
}

/**
Expand Down Expand Up @@ -648,6 +654,16 @@ private Bounds generateTitle(IChemObject chemObj) {
"title"));
}

private Bounds generateReactionConditions(IReaction chemObj, Color fg) {
String title = chemObj.getProperty(CDKConstants.REACTION_CONDITIONS);
if (title == null || title.isEmpty())
return new Bounds();
final double scale = 1 / getParameterValue(BasicSceneGenerator.Scale.class);

return new Bounds(MarkedElement.markup(StandardGenerator.embedText(font, title, fg, scale),
"conditions"));
}


/**
* Automatically generate coordinates if a user has provided a molecule without them.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import org.openscience.cdk.renderer.RendererModel;
import org.openscience.cdk.renderer.elements.Bounds;
import org.openscience.cdk.renderer.elements.GeneralPath;
import org.openscience.cdk.renderer.elements.IRenderingElement;
import org.openscience.cdk.renderer.elements.LineElement;
import org.openscience.cdk.renderer.elements.RectangleElement;
import org.openscience.cdk.renderer.generators.BasicSceneGenerator;
Expand Down Expand Up @@ -60,6 +59,7 @@ final class ReactionDepiction extends Depiction {
// molecule sets and titles
private final List<Bounds> mainComp = new ArrayList<>();
private final List<Bounds> sideComps = new ArrayList<>();
private final Bounds conditions;
private final Bounds title;

// arrow info
Expand All @@ -71,6 +71,8 @@ final class ReactionDepiction extends Depiction {
// dimensions and spacing of side components
private final Dimensions sideDim;
private final Dimensions mainDim;
private final Dimensions condDim;

private final double[] xOffsets, yOffsets;
private final double[] xOffsetSide, yOffsetSide;

Expand All @@ -88,6 +90,7 @@ public ReactionDepiction(RendererModel model,
List<Bounds> reactantTitles,
List<Bounds> productTitles,
Bounds title,
Bounds conditions,
Color fgcol) {
super(model);
this.model = model;
Expand Down Expand Up @@ -159,6 +162,8 @@ public ReactionDepiction(RendererModel model,
this.nCol = mainComp.size();
}

this.conditions = conditions;

// arrow params
this.arrowIdx = Math.max(reactants.size() + reactants.size() - 1, 0);
this.direction = direction;
Expand All @@ -169,19 +174,37 @@ public ReactionDepiction(RendererModel model,
yOffsets = new double[nRow + 1],
xOffsets = new double[nCol + 1]);

// avoid v. small arrows
if (prelimSideDim.w < minArrowWidth) {
double middleRequired = Math.max(prelimSideDim.w, conditions.width());

// avoid v. small arrows, we take in to account the padding provided by the arrow head height/length
if (middleRequired < minArrowWidth - arrowHeight - arrowHeight) {
// adjust x-offset so side components are centered
double xAdjust = (minArrowWidth - prelimSideDim.w) / 2;
double xAdjust = (minArrowWidth - middleRequired) / 2;
for (int i = 0; i < xOffsetSide.length; i++)
xOffsetSide[i] += xAdjust;
// need to recenter agents
if (conditions.width() > prelimSideDim.w) {
for (int i = 0; i < xOffsetSide.length; i++)
xOffsetSide[i] += (conditions.width() - prelimSideDim.w) / 2;
}
// update side dims
this.sideDim = new Dimensions(minArrowWidth, prelimSideDim.h);
this.condDim = new Dimensions(minArrowWidth, conditions.height());
} else {
// arrow padding
for (int i = 0; i < xOffsetSide.length; i++)
xOffsetSide[i] += arrowHeight;
this.sideDim = new Dimensions(2*arrowHeight + prelimSideDim.w,

// need to recenter agents
if (conditions.width() > prelimSideDim.w) {
for (int i = 0; i < xOffsetSide.length; i++)
xOffsetSide[i] += (conditions.width() - prelimSideDim.w) / 2;
}

this.sideDim = new Dimensions(2 * arrowHeight + middleRequired,
prelimSideDim.h);
this.condDim = new Dimensions(2 * arrowHeight + middleRequired,
conditions.height());
}
}

Expand All @@ -201,6 +224,7 @@ public BufferedImage toImg() {

Dimensions sideRequired = sideDim.scale(scale * zoom);
Dimensions mainRequired = mainDim.scale(scale * zoom);
Dimensions condRequired = condDim.scale(scale * zoom);

Dimensions titleRequired = new Dimensions(title.width(), title.height()).scale(scale * zoom);

Expand Down Expand Up @@ -234,8 +258,8 @@ public BufferedImage toImg() {

// MAIN COMPONENTS DRAW
// x,y base coordinates include the margin and centering (only if fitting to a size)
final double totalRequiredWidth = 2 * margin + (nCol - 1) * padding + (nSideCol - 1) * padding + (rescale * xOffsets[nCol]);
final double totalRequiredHeight = 2 * margin + (nRow - 1) * padding + (!title.isEmpty() ? padding : 0) + Math.max(mainCompOffset, 0) + fitting * mainRequired.h + fitting * Math.max(0, titleRequired.h);
final double totalRequiredWidth = 2 * margin + Math.max(0, nCol - 1) * padding + Math.max(0, nSideCol - 1) * padding + (rescale * xOffsets[nCol]);
final double totalRequiredHeight = 2 * margin + Math.max(0, nRow - 1) * padding + (!title.isEmpty() ? padding : 0) + Math.max(mainCompOffset, 0) + fitting * mainRequired.h + fitting * Math.max(0, titleRequired.h);
double xBase = margin + (total.w - totalRequiredWidth) / 2;
double yBase = margin + Math.max(mainCompOffset, 0) + (total.h - totalRequiredHeight) / 2;
for (int i = 0; i < mainComp.size(); i++) {
Expand All @@ -253,7 +277,7 @@ public BufferedImage toImg() {

// intercept arrow draw and make it as big as need
if (i == arrowIdx) {
w = rescale * (xOffsets[i + 1] - xOffsets[i]) + (nSideCol - 1) * padding;
w = rescale * (xOffsets[i + 1] - xOffsets[i]) + Math.max(0, nSideCol - 1) * padding;
draw(visitor,
1, // no zoom since arrows is drawn as big as needed
createArrow(w, arrowHeight * rescale),
Expand All @@ -263,7 +287,7 @@ public BufferedImage toImg() {

// extra padding from the side components
if (i > arrowIdx)
x += (nSideCol - 1) * padding;
x += Math.max(0, nSideCol - 1) * padding;

// skip empty elements
final Bounds bounds = this.mainComp.get(i);
Expand Down Expand Up @@ -298,6 +322,15 @@ public BufferedImage toImg() {
draw(visitor, zoom, sideComps.get(i), rect(x, y, w, h));
}

// CONDITIONS DRAW
if (!conditions.isEmpty()) {
yBase += mainCompOffset; // back to top
yBase += mainRequired.h / 2; // now on center line (arrow)
yBase += padding; // now just bellow
draw(visitor, zoom, conditions, rect(xBase,
yBase,
condRequired.w, condRequired.h));
}

// reset shared xOffsets
if (!sideComps.isEmpty()) {
Expand Down Expand Up @@ -337,6 +370,7 @@ String toVecStr(String fmt) {

Dimensions sideRequired = sideDim.scale(scale * zoom);
Dimensions mainRequired = mainDim.scale(scale * zoom);
Dimensions condRequired = condDim.scale(scale * zoom);

Dimensions titleRequired = new Dimensions(title.width(), title.height()).scale(scale * zoom);

Expand All @@ -358,7 +392,7 @@ String toVecStr(String fmt) {
}

// background color
visitor.setTransform(AffineTransform.getScaleInstance(1,-1));
visitor.setTransform(AffineTransform.getScaleInstance(1, -1));
visitor.visit(new RectangleElement(0, -(int) Math.ceil(total.h), (int) Math.ceil(total.w), (int) Math.ceil(total.h),
true, model.get(BasicSceneGenerator.BackgroundColor.class)));

Expand All @@ -374,8 +408,8 @@ String toVecStr(String fmt) {

// MAIN COMPONENTS DRAW
// x,y base coordinates include the margin and centering (only if fitting to a size)
final double totalRequiredWidth = 2 * margin + (nCol - 1) * padding + (nSideCol - 1) * padding + (rescale * xOffsets[nCol]);
final double totalRequiredHeight = 2 * margin + (nRow - 1) * padding + (!title.isEmpty() ? padding : 0) + Math.max(mainCompOffset, 0) + fitting * mainRequired.h + fitting * Math.max(0, titleRequired.h);
final double totalRequiredWidth = 2 * margin + Math.max(0, nCol - 1) * padding + Math.max(0, nSideCol - 1) * padding + (rescale * xOffsets[nCol]);
final double totalRequiredHeight = 2 * margin + Math.max(0, nRow - 1) * padding + (!title.isEmpty() ? padding : 0) + Math.max(mainCompOffset, 0) + fitting * mainRequired.h + fitting * Math.max(0, titleRequired.h);
double xBase = margin + (total.w - totalRequiredWidth) / 2;
double yBase = margin + Math.max(mainCompOffset, 0) + (total.h - totalRequiredHeight) / 2;
for (int i = 0; i < mainComp.size(); i++) {
Expand All @@ -393,7 +427,7 @@ String toVecStr(String fmt) {

// intercept arrow draw and make it as big as need
if (i == arrowIdx) {
w = rescale * (xOffsets[i + 1] - xOffsets[i]) + (nSideCol - 1) * padding;
w = rescale * (xOffsets[i + 1] - xOffsets[i]) + Math.max(0, nSideCol - 1) * padding;
draw(visitor,
1, // no zoom since arrows is drawn as big as needed
createArrow(w, arrowHeight * rescale),
Expand All @@ -403,7 +437,7 @@ String toVecStr(String fmt) {

// extra padding from the side components
if (i > arrowIdx)
x += (nSideCol - 1) * padding;
x += Math.max(0, nSideCol - 1) * padding;

// skip empty elements
final Bounds bounds = this.mainComp.get(i);
Expand Down Expand Up @@ -438,6 +472,16 @@ String toVecStr(String fmt) {
draw(visitor, zoom, sideComps.get(i), rect(x, y, w, h));
}

// CONDITIONS DRAW
if (!conditions.isEmpty()) {
yBase += mainCompOffset; // back to top
yBase += mainRequired.h / 2; // now on center line (arrow)
yBase += padding; // now just bellow
draw(visitor, zoom, conditions, rect(xBase,
yBase,
condRequired.w, condRequired.h));
}

// reset shared xOffsets
if (!sideComps.isEmpty()) {
for (int i = arrowIdx + 1; i < xOffsets.length; i++)
Expand Down Expand Up @@ -511,11 +555,11 @@ private Dimensions calcTotalDimensions(double margin, double padding, Dimensions
titleExtra += padding;

return mainRequired.add(2 * margin, 2 * margin)
.add((nCol - 1) * padding, (nRow - 1) * padding)
.add(Math.max(0, sideRequired.w), 0) // side component extra width
.add((nSideCol - 1) * padding, 0) // side component padding
.add(0, mainCompOffset)
.add(0, titleExtra);
.add(Math.max(0, nCol - 1) * padding, (nRow - 1) * padding)
.add(Math.max(0, sideRequired.w), 0) // side component extra width
.add(Math.max(0, nSideCol - 1) * padding, 0) // side component padding
.add(0, mainCompOffset)
.add(0, titleExtra);

} else {
// we want all vector graphics dims in MM
Expand Down
6 changes: 6 additions & 0 deletions base/core/src/main/java/org/openscience/cdk/CDKConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,12 @@ public class CDKConstants {
public static final String CTAB_SGROUPS = "cdk:CtabSgroups";


/**
* Property for reaction objects where the conditions of reactions can be placed.
*/
public static final String REACTION_CONDITIONS = "cdk:ReactionConditions";


/* **************************************
* Some predefined property names for * AtomTypes *
* **************************************
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -645,9 +645,9 @@ private IRenderingElement generateDoubleBond(IBond bond) {
} else {
return generateCenteredDoubleBond(bond, atom1, atom2, atom1Bonds, atom2Bonds);
}
} else if (atom1Bonds.size() == 1 && !hasDisplayedSymbol(atom1)) {
} else if (atom1Bonds.size() == 1 && !hasDisplayedSymbol(atom1) && (!hasDisplayedSymbol(atom2) || atom2Bonds.isEmpty())) {
return generateOffsetDoubleBond(bond, atom1, atom2, atom1Bonds.get(0), atom2Bonds);
} else if (atom2Bonds.size() == 1 && !hasDisplayedSymbol(atom2)) {
} else if (atom2Bonds.size() == 1 && !hasDisplayedSymbol(atom2) && (!hasDisplayedSymbol(atom1) || atom1Bonds.isEmpty())) {
return generateOffsetDoubleBond(bond, atom2, atom1, atom2Bonds.get(0), atom1Bonds);
} else if (specialOffsetBondNextToWedge(atom1, atom1Bonds) && !hasDisplayedSymbol(atom1)) {
return generateOffsetDoubleBond(bond, atom1, atom2, selectPlainSingleBond(atom1Bonds), atom2Bonds);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -439,23 +439,37 @@ static TextOutline generateAnnotation(Point2d basePoint, String label, Vector2d


/**
* Make an embedded text label for display in a CDK renderer.
* Make an embedded text label for display in a CDK renderer. If a piece of text contains newlines
* they are centred aligned below each other with a line height of 1.4.
*
* @param font the font to embedded
* @param text the text label
* @param font the font to embedded
* @param text the text label
* @param color the color
* @param scale the resize, should include the model scale
* @return pre-rendered element
*/
public static IRenderingElement embedText(Font font, String text, Color color, double scale) {

final TextOutline outline = new TextOutline(text, font).resize(scale, -scale);
final String[] lines = text.split("\n");

ElementGroup group = new ElementGroup();
group.add(GeneralPath.shapeOf(outline.getOutline(), color));
Rectangle2D logicalBounds = outline.getLogicalBounds();
group.add(new Bounds(logicalBounds.getMinX(), logicalBounds.getMinY(),
logicalBounds.getMaxX(), logicalBounds.getMaxY()));

double yOffset = 0;
double lineHeight = 1.4d;

for (String line : lines) {
TextOutline outline = new TextOutline(line, font).resize(scale, -scale);
Point2D center = outline.getCenter();
outline = outline.translate(-center.getX(), -(center.getY() + yOffset));

yOffset += lineHeight * outline.getBounds().getHeight();

group.add(GeneralPath.shapeOf(outline.getOutline(), color));
Rectangle2D logicalBounds = outline.getLogicalBounds();
group.add(new Bounds(logicalBounds.getMinX(), logicalBounds.getMinY(),
logicalBounds.getMaxX(), logicalBounds.getMaxY()));
}

return group;
}

Expand Down Expand Up @@ -539,7 +553,7 @@ private static IRenderingElement recolor(IRenderingElement element, Color color)
* @param stroke the stroke width
* @return generated outer glow
*/
private static IRenderingElement outerGlow(IRenderingElement element, Color color, double glowWidth, double stroke) {
static IRenderingElement outerGlow(IRenderingElement element, Color color, double glowWidth, double stroke) {
if (element instanceof ElementGroup) {
ElementGroup orgGroup = (ElementGroup) element;
ElementGroup newGroup = new ElementGroup();
Expand Down

0 comments on commit 18639d9

Please sign in to comment.