
gov.nih.ncats.molwitch.renderer.ChemicalRenderer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of molwitch-renderer Show documentation
Show all versions of molwitch-renderer Show documentation
Chemical Renderer that uses Molwitch
/*
* NCATS-MOLWITCH-RENDERER
*
* Copyright 2023 NIH/NCATS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package gov.nih.ncats.molwitch.renderer;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Objects;
import java.util.Optional;
import com.fasterxml.jackson.annotation.*;
import gov.nih.ncats.molwitch.Atom;
import gov.nih.ncats.molwitch.Bond;
import gov.nih.ncats.molwitch.Chemical;
public class ChemicalRenderer {
@JsonIgnore
private final NchemicalRenderer renderer;
public ChemicalRenderer() {
this(RendererOptions.createDefault());
}
@JsonCreator
public ChemicalRenderer(@JsonProperty("options") RendererOptions options){
this.renderer = new NchemicalRenderer(Objects.requireNonNull(options));
}
@JsonGetter("color-bg")
public ARGBColor getBackgroundColorARGB(){
return renderer.getBackgroundColor();
}
public ChemicalRenderer copy(){
ChemicalRenderer copy = new ChemicalRenderer(renderer.getOptions().copy());
copy.setBorderVisible(renderer.getBorderVisible());
copy.setShadowVisible(renderer.getShadowVisible());
copy.setBackgroundColor(renderer.getBackgroundColor().asColor());
copy.setBorderColor(renderer.getBorderColor().asColor());
return copy;
}
@JsonIgnore
public ARGBColor getBackgroundColor(){
return renderer.getBackgroundColor();
}
@JsonSetter("color-bg")
public ChemicalRenderer setBackgroundColorARGB(String argbHex) {
return setBackgroundColor(fromArgb(argbHex));
}
private static Color fromArgb(String argbHex) {
if(argbHex ==null){
return null;
}
return new Color(Integer.parseUnsignedInt(argbHex, 16),true);
}
@JsonIgnore
public ChemicalRenderer setBackgroundColor(Color bg) {
this.renderer.setBackgroundColor(new ARGBColor(bg));
return this;
}
@JsonGetter("color-border")
public ARGBColor getBorderColorARGB(){
return renderer.getBorderColor();
}
@JsonIgnore
public ARGBColor getBorderColor(){
return renderer.getBorderColor();
}
@JsonSetter("color-border")
public ChemicalRenderer setBorderColorARGB(String argbHex){
return setBorderColor(fromArgb(argbHex));
}
@JsonIgnore
public ChemicalRenderer setBorderColor(Color bg) {
this.renderer.setBorderColor(new ARGBColor(bg));
return this;
}
@JsonGetter("options")
public RendererOptions getOptions() {
return renderer.getOptions();
}
@JsonGetter("add-shadow")
public boolean isShadowVisible(){
return renderer.getShadowVisible();
}
@JsonSetter("add-shadow")
public ChemicalRenderer setShadowVisible(boolean visible) {
this.renderer.setShadowVisible(visible);
return this;
}
@JsonGetter("add-border")
public boolean isBorderVisible(){
return renderer.getShadowVisible();
}
@JsonSetter("add-border")
public ChemicalRenderer setBorderVisible(boolean visible) {
this.renderer.setBorderVisible(visible);
return this;
}
public void render(Graphics2D g2d, File inputMol, int x, int y, int width, int height, boolean round) throws IOException{
render(g2d, Chemical.parseMol(inputMol), x,y, width, height, round);
}
public void render(Graphics2D g2d, String inputMol, int x, int y, int width, int height, boolean round) throws IOException{
render(g2d, Chemical.parseMol(inputMol), x,y, width, height, round);
}
public void render(Graphics2D g2d, Chemical c, int x, int y, int width, int height, boolean round) {
//the offsets on the bounds rectangle needed to draw the captions
double ddy=0; //expect positive
double ddh=0; //expect negative
Optional capTop = getOptions().captionTop(c);
Optional capBottom = getOptions().captionBottom(c);
//calculate bounds for the captions to restrict the area for drawing
if(capTop.isPresent()){
Rectangle2D.Double boundstop = renderer.drawText(g2d,x,y,width,height,capTop.get(),0, true); // 1 is bottom, 0 is top
//the y-coordinate must be moved down by
//the top gap amount
ddy = ((y)-boundstop.getMaxY());
//the height must be decreased by the same amount
ddh += ddy;
}
if(capBottom.isPresent()){
Rectangle2D.Double boundsBot = renderer.drawText(g2d,x,y,width,height,capBottom.get(),1, true); // 1 is bottom, 0 is top
//the height must be decreased by the bottom gap amount
ddh = ((y+height)-boundsBot.getMinY());
}
//then draw
renderer.renderChem (g2d, c, x, (int) ( y+ddy), width, (int)(height-ddh), round);
//then draw captions
if(capTop.isPresent()){
renderer.drawText(g2d,x,y,width,height,capTop.get(),0, false); // 1 is bottom, 0 is top
}
if(capBottom.isPresent()){
renderer.drawText(g2d,x,y,width,height,capBottom.get(),1, false); // 1 is bottom, 0 is top
}
}
public BufferedImage createImage (String inputMol, int size) throws IOException{
return createImage (Chemical.parse(inputMol), size, size, true);
}
public BufferedImage createImage (File inputMol, int size) throws IOException{
return createImage (Chemical.parseMol(inputMol), size, size, true);
}
public BufferedImage createImage (Chemical c, int size) {
return createImage (c, size, size, true);
}
public BufferedImage createImageAutoAdjust (Chemical c, int size) {
double totalBondLength=0.0;
Optional averageBondLength=computeAverageBondLength(c);
for(Bond bond : c.getBonds()){
totalBondLength+= bond.getBondLength();
}
double avgBondLength= totalBondLength/c.getBondCount();
//System.out.printf("average bond length: %f\n", avgBondLength);
Rectangle2D.Double bounds = computeAtomicCoordinateBounds(c);
double xSpread = bounds.getWidth();
double ySpread = bounds.getHeight();
//System.out.printf("xSpread: %f . ySpread: %f \n", xSpread, ySpread);
double averageSpread = (xSpread+ySpread)/2;
int width= (int) Math.round( size * xSpread/averageSpread);
if(width<10) width=size;
int height= (int) Math.round(size * ySpread/averageSpread);
if(height<10) height= size;
//System.out.printf("width: %d; height: %d\n", width, height);
return createImage (c, width, height,false);
}
public BufferedImage createImageAutoAdjust (Chemical c, int maxWidth, int minWidth, int maxHeight, int minHeight,
double requestedAverageBondLength) {
/*System.out.printf("In createImageAutoAdjust: maxWidth: %d; minWidth: %d; maxHeight: %d; minHeight: %d\n",
maxWidth, minWidth, maxHeight, minHeight);*/
Rectangle2D.Double rectangle= getApproximateBoundsFor(c, maxWidth, minWidth, maxHeight, minHeight, requestedAverageBondLength);
int width= (int) Math.round(rectangle.getWidth());
int height= (int) Math.round(rectangle.getHeight());
//System.out.printf("Calculated width: %d; height: %d\n", width, height);
//System.out.printf("final width: %d; height: %d.\n", width, height);
return createImage (c, width, height, false);
}
public Rectangle2D.Double getApproximateBoundsFor (Chemical c, int maxWidth, int minWidth, int maxHeight, int minHeight,
double requestedAverageBondLength) {
Optional foundAverageBondLength = computeAverageBondLength(c);
//System.out.printf("average bond length: %f\n", foundAverageBondLength.isPresent() ? foundAverageBondLength.get() : 0.0);
double scaleFactor =1.0;
if( foundAverageBondLength.isPresent() && foundAverageBondLength.get()> 0) {
scaleFactor= requestedAverageBondLength / foundAverageBondLength.get();
}
Rectangle2D.Double atomicCoordinateBounds = computeAtomicCoordinateBounds(c);
double xSpread0 = atomicCoordinateBounds.getWidth();
double ySpread0 = atomicCoordinateBounds.getHeight();
//System.out.printf("xSpread0: %f. ySpread0: %f \n", xSpread0, ySpread0);
double xSpread= scaleFactor*xSpread0;
double ySpread= scaleFactor*ySpread0;
//System.out.printf("scaled xSpread: %f. ySpread: %f \n", xSpread, ySpread);
int width= (int) Math.round( xSpread);
int height=(int) Math.round(ySpread);
//System.out.printf("initial width: %d; height: %d\n", width, height);
if(widthmaxWidth) width=maxWidth;
if(heightmaxHeight) height=maxHeight;
//When there is one heavy atom, we see overflows. Prevent that by checking for *spread<=0
if(xSpread<= 0) xSpread=1;
if(ySpread<= 0) ySpread=1;
double xScale = width/xSpread; //xSpread==1 ? 1.0 :
double yScale = height/ySpread;//ySpread==1 ? 1.0 :
double scaleFinal=Math.min(xScale,yScale);
width= (int) Math.round(scaleFinal*xSpread);
height=(int) Math.round(scaleFinal*ySpread);
//one last check
if(height< minHeight) height= minHeight;
if(width< minWidth) width=minWidth;
//System.out.printf("final width: %d; height: %d. scaleFinal: %f\n", width, height, scaleFinal);
return new Rectangle2D.Double(0, 0, width, height );
}
public BufferedImage createImage (File inputMol, int width, int height, boolean round) throws IOException{
return createImage(Chemical.parseMol(inputMol), width, height, round);
}
public BufferedImage createImage (String inputMol, int width, int height, boolean round) throws IOException{
return createImage(Chemical.parse(inputMol), width, height, round);
}
public BufferedImage createImage (Chemical c, int width, int height, boolean round) {
BufferedImage img = new BufferedImage (width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
render (g2, c,0,0, width, height, round);
g2.dispose();
return img;
}
public static double computeAverageInterAtomDistance(Chemical c){
int totalDistances = 0;
double totalDistance =0.0d;
for(int i=0; i computeLowestInterAtomDistance(Chemical c){
Double lowest =null;
for(int i=0; i 0 && (lowest==null || distance< lowest)) {
lowest = distance;
}
}
}
return lowest>0 ? Optional.of(lowest) : Optional.empty();
}
public static Optional computeAverageBondLength(Chemical c) {
double totalBondLength=0.0;
if( c.getBondCount()>0) {
for (Bond bond : c.getBonds()) {
totalBondLength += bond.getBondLength();
}
return Optional.of( totalBondLength / c.getBondCount());
}
return Optional.empty();
}
public static Rectangle2D.Double computeAtomicCoordinateBounds(Chemical chemical) {
double minX = Integer.MAX_VALUE;
double minY = Integer.MAX_VALUE;
double maxX = Integer.MIN_VALUE;
double maxY = Integer.MIN_VALUE;
for(Atom atom : chemical.getAtoms()) {
if( atom.getAtomCoordinates().getX() > maxX) {
maxX= atom.getAtomCoordinates().getX();
}
if( atom.getAtomCoordinates().getY() > maxY) {
maxY= atom.getAtomCoordinates().getY();
}
if( atom.getAtomCoordinates().getX() < minX) {
minX= atom.getAtomCoordinates().getX();
}
if( atom.getAtomCoordinates().getY() < minY) {
minY= atom.getAtomCoordinates().getY();
}
}
double xSpread = maxX - minX;
double ySpread = maxY - minY;
/*System.out.printf("xSpread: %f (minX: %f, maxX: %f). ySpread: %f (minY: %f, maxY: %f)\n", xSpread, minX, maxX,
ySpread, minY, maxY);*/
return new Rectangle2D.Double(minX, minY, xSpread, ySpread);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy