Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SMILES extension and depiction of multipart reactions #986

Merged
merged 18 commits into from
Aug 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
37d79ab
SMILES extension to store a multi-step reaction.
johnmay Jul 19, 2023
29cb569
Use ReactionManipulator to simplify the CXSMILES layer application
johnmay Jul 19, 2023
7ae35c3
Some refactoring to make it easy to lay out multiple reactions.
johnmay Jul 20, 2023
3510977
Simplify the layout of reactions with a common method for both vector…
johnmay Jul 20, 2023
2fa2335
Allow the ReactionBounds to determine the final arrow size. Padding i…
johnmay Jul 25, 2023
7383a82
Preliminary depiction for reaction sets.
johnmay Jul 25, 2023
2a94e00
Minor formatting change, wrap the parameters.
johnmay Jul 25, 2023
5b7acb6
Detect a sequence of reactions and lay them out without replicating t…
johnmay Jul 25, 2023
0570b16
Fix a corner case now we have a null arrow.
johnmay Jul 25, 2023
89cd4bc
Handle CXSMILES on multistep reaction SMILES.
johnmay Jul 25, 2023
daab4f8
Fix typo, we need to grab all the product labels not just the first one
johnmay Jul 26, 2023
483cf70
Fix an issue with PDF rescaling, MM_TO_POINT when dimensions are auto…
johnmay Jul 26, 2023
df1812b
AtomNumber in reactions has been wrong for a while, track the number …
johnmay Jul 26, 2023
4d73834
Fix a corner case with empty reactants/products.
johnmay Jul 27, 2023
0f2be11
Improved positioning of the parts below the arrow (currently just the…
johnmay Jul 27, 2023
9c004fc
Fix SVG,PX unit spacing. Now padding is scaled we need to unscale it …
johnmay Jul 27, 2023
aad7deb
Cleanup some code smells.
johnmay Jul 27, 2023
a726600
Ensure scaling works correctly for raster images.
johnmay Jul 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 20 additions & 3 deletions app/depict/src/main/java/org/openscience/cdk/depict/Depiction.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public abstract class Depiction {
* When no fixed padding value is specified we use margin
* multiplied by this value.
*/
protected static final double DEFAULT_PADDING_FACTOR = 2;
protected static final double DEFAULT_PADDING_FACTOR = 2.5;

/**
* Structured Vector Graphics (SVG) format key.
Expand Down Expand Up @@ -111,7 +111,7 @@ public abstract class Depiction {

private static final char DOT = '.';

private final RendererModel model;
protected final RendererModel model;

/**
* Internal method passes in the rendering model parameters.
Expand Down Expand Up @@ -373,9 +373,26 @@ final double rescaleForBondLength(double bondLength) {
return bondLength / model.get(BasicSceneGenerator.BondLength.class);
}

protected void svgPrevisit(String fmt, double rescale, SvgDrawVisitor visitor, List<? extends IRenderingElement> elements) {
protected void svgStyleCache(String fmt,
double rescale,
SvgDrawVisitor visitor,
List<? extends IRenderingElement> elements) {
visitor.setTransform(AffineTransform.getScaleInstance(rescale, rescale));
visitor.previsit(elements);
visitor.setTransform(null);
}

protected double calcFitting(Dimensions srcDim,
Dimensions dstDim,
double margin) {
if (dstDim == Dimensions.AUTOMATIC)
return 1; // no fitting
dstDim = dstDim.add(2*-margin, 2*-margin);
double resize = Math.min(dstDim.w / srcDim.w,
dstDim.h / srcDim.h);
if (resize > 1 && !model.get(BasicSceneGenerator.FitToScreen.class))
resize = 1;
return resize;
}

}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ Dimensions add(double w, double h) {
}

Dimensions scale(double coef) {
if (this == AUTOMATIC)
return this;
return new Dimensions(coef * w, coef * h);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ String toVecStr(String fmt, String units) {
: AWTDrawVisitor.forVectorGraphics(wrapper.g2);

if (fmt.equals(SVG_FMT)) {
svgPrevisit(fmt, scale * zoom * fitting, (SvgDrawVisitor) visitor, elements);
svgStyleCache(fmt, scale * zoom * fitting, (SvgDrawVisitor) visitor, elements);
} else {
// pdf can handle fraction coords just fine
((AWTDrawVisitor) visitor).setRounding(false);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/*
* Copyright (C) 2023 John Mayfield
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/

package org.openscience.cdk.depict;

import org.openscience.cdk.interfaces.IReaction;
import org.openscience.cdk.renderer.RendererModel;
import org.openscience.cdk.renderer.elements.Bounds;

import java.awt.Dimension;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
* Helper class to capture all the pieces that can/need be
* positioned to display a reaction.
*/
final class ReactionBounds {
RendererModel model;
List<Bounds> reactants = new ArrayList<>();
List<Bounds> products = new ArrayList<>();
List<Bounds> reactantLabels = new ArrayList<>();
List<Bounds> productLabels = new ArrayList<>();
List<Bounds> aboveArrow = new ArrayList<>();
List<Bounds> belowArrow = new ArrayList<>();
IReaction.Direction direction = IReaction.Direction.FORWARD;
Bounds plus = null;
Bounds title = null;

List<Bounds> getMainRow() {
List<Bounds> mainRow = new ArrayList<>();
for (int i = 0; i < reactants.size(); i++) {
if (i != 0) mainRow.add(plus);
mainRow.add(reactants.get(i));
}
if (direction != null)
mainRow.add(new Bounds()); // arrow
for (int i = 0; i < products.size(); i++) {
if (i != 0) mainRow.add(plus);
mainRow.add(products.get(i));
}
return mainRow;
}

boolean hasMainRowLabels() {
return !reactantLabels.isEmpty() || !productLabels.isEmpty();
}

List<Bounds> getMainRowLabels() {
if (!hasMainRowLabels())
return Collections.emptyList();
List<Bounds> labels = new ArrayList<>();
for (int i = 0; i < reactants.size(); i++) {
if (i != 0) labels.add(new Bounds());
if (i < reactantLabels.size())
labels.add(reactantLabels.get(i));
else
labels.add(new Bounds());
}
if (direction != null)
labels.add(new Bounds()); // arrow
for (int i = 0; i < productLabels.size(); i++) {
if (i != 0) labels.add(new Bounds());
labels.add(productLabels.get(i));
}
return labels;
}

List<Bounds> getMainComponents() {
List<Bounds> bounds = new ArrayList<>(getMainRow());
if (hasMainRowLabels())
bounds.addAll(getMainRowLabels());
return bounds;
}

int getArrowIndex() {
int numGaps = Math.max(0, reactants.size()-1);
return reactants.size() + numGaps;
}

ReactionDimensions getDimensions(double padding) {

List<Bounds> mainComp = getMainComponents();

int nRow;
int nCol;
double arrowHeight = plus.height();
double arrowHeadLength = plus.height();
double minArrowWidth = direction != null ? 5 * arrowHeight : 0;

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

if (hasMainRowLabels()) {
nRow = 2;
nCol = mainComp.size()/2;
} else {
nRow = 1;
nCol = mainComp.size();
}

Dimensions mainDim = Dimensions.ofGrid(mainComp,
yOffsets = new double[nRow + 1],
xOffsets = new double[nCol + 1]);

// important we flip x/y so things above the arrow get stacked
Dimension sideGrid = Dimensions.determineGrid(aboveArrow.size());
Dimensions prelimSideDim = Dimensions.ofGrid(aboveArrow,
yOffsetSide = new double[sideGrid.width + 1],
xOffsetSide = new double[sideGrid.height + 1]);

Bounds conditions = belowArrow.isEmpty() ? new Bounds() : belowArrow.get(0);
double middleRequired = 2*padding + arrowHeadLength +
Math.max(prelimSideDim.w + Math.min(0, xOffsetSide.length-1) * padding,
conditions.width());

ReactionDimensions result;
if (middleRequired < minArrowWidth) {

// the arrow is bigger than the things above/below the arrow,
// ensure these are centered relative to the arrow

// adjust x-offset so side components are centered
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
Dimensions sideDim = new Dimensions(minArrowWidth, prelimSideDim.h);
Dimensions condDim = new Dimensions(minArrowWidth, conditions.height());
Dimensions titleDim = new Dimensions(title.width(),
title.height());

for (int j=getArrowIndex()+1; j<xOffsets.length; j++)
xOffsets[j] += minArrowWidth;
result = new ReactionDimensions(sideDim, mainDim, condDim, titleDim, padding);
}
else {

// above/below is larger than arrow, make the arrow as big as needed

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

for (int j=getArrowIndex()+1; j<xOffsets.length; j++)
xOffsets[j] += middleRequired;

Dimensions sideDim = new Dimensions(prelimSideDim.w,
prelimSideDim.h);
Dimensions condDim = new Dimensions(middleRequired,
conditions.height());
Dimensions titleDim = new Dimensions(title.width(),
title.height());
result = new ReactionDimensions(sideDim, mainDim, condDim, titleDim, padding);
}

result.xOffsets = xOffsets;
result.yOffsets = yOffsets;
result.xOffsetSide = xOffsetSide;
result.yOffsetSide = yOffsetSide;

return result;
}


}