/*
* EuroCarbDB, a framework for carbohydrate bioinformatics
*
* Copyright (c) 2006-2009, Eurocarb project, or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
* A copy of this license accompanies this distribution in the file LICENSE.txt.
*
* This program 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.
*
* Last commit: $Rev: 1210 $ by $Author: glycoslave $ on $Date:: 2009-06-12 #$
*/
package org.eurocarbdb.application.glycanbuilder;
import java.io.*;
import java.util.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
import javax.swing.*;
import static org.eurocarbdb.application.glycanbuilder.Geometry.*;
/**
Objects of this class are used to create a graphical representation
of a {@link Linkage} object given the current graphic options
({@link GraphicOptions}. The rules to draw the linkage in the
different notations are stored in the {@link
LinkageStyleDictionary}.
@author Alessio Ceroni (a.ceroni@imperial.ac.uk)
*/
public class LinkageRenderer {
protected LinkageStyleDictionary theLinkageStyleDictionary;
protected GraphicOptions theGraphicOptions;
/**
Empty constructor.
*/
public LinkageRenderer() {
theLinkageStyleDictionary = new LinkageStyleDictionary();
theGraphicOptions = new GraphicOptions();
}
/**
Create a new linkage renderer copying the style dictionary and
graphic options from the <code>src</code> object.
*/
public LinkageRenderer(GlycanRenderer src) {
theLinkageStyleDictionary = src.getLinkageStyleDictionary();
theGraphicOptions = src.getGraphicOptions();
}
/**
Return the graphic options used by this object.
*/
public GraphicOptions getGraphicOptions() {
return theGraphicOptions;
}
/**
Set the graphic options used by this object.
*/
public void setGraphicOptions(GraphicOptions opt) {
theGraphicOptions = opt;
}
/**
Return the linkage style dictionary used by this object.
*/
public LinkageStyleDictionary getLinkageStyleDictionary() {
return theLinkageStyleDictionary;
}
/**
Set the linkage style dictionary used by this object.
*/
public void setLinkageStyleDictionary(LinkageStyleDictionary linkageStyleDictionary) {
theLinkageStyleDictionary = linkageStyleDictionary;
}
// --------
/**
Draw the line part of a linkage on a graphic context using the
specified bounding boxes.
@param g2d the graphic context
@param link the linkage to be drawn
@param selected <code>true</code> if the residue should be
shown as selected
@param parent_bbox the bounding box of the parent residue
@param parent_border_bbox the bounding box of the parent
residue including the residues on border
@param child_bbox the bounding box of the child residue
@param child_border_bbox the bounding box of the child residue
including the residues on border
*/
public void paintEdge(Graphics2D g2d, Linkage link, boolean selected, Rectangle parent_bbox, Rectangle parent_border_bbox, Rectangle child_bbox, Rectangle child_border_bbox) {
if( link==null )
return;
Stroke edge_stroke = createStroke(link,selected);
Shape edge_shape = createShape(link,parent_bbox,child_bbox);
// draw edge
if( edge_shape!=null ) {
g2d.setStroke(edge_stroke);
g2d.setColor(Color.black);
g2d.draw(edge_shape);
g2d.setStroke(new BasicStroke(1));
}
// paint linkage info
//if( theGraphicOptions.SHOW_INFO )
//paintInfo(g2d,link,parent_bbox,parent_border_bbox,child_bbox,child_border_bbox);
}
/**
Draw the text part of a linkage on a graphic context using the
specified bounding boxes.
@param g2d the graphic context
@param link the linkage to be drawn
@param parent_bbox the bounding box of the parent residue
@param parent_border_bbox the bounding box of the parent
residue including the residues on border
@param child_bbox the bounding box of the child residue
@param child_border_bbox the bounding box of the child residue
including the residues on border
*/
public void paintInfo(Graphics2D g2d, Linkage link, Rectangle parent_bbox, Rectangle parent_border_bbox, Rectangle child_bbox, Rectangle child_border_bbox) {
if( link==null || !theGraphicOptions.SHOW_INFO )
return;
LinkageStyle style = theLinkageStyleDictionary.getStyle(link);
Font old_font = g2d.getFont();
Font new_font = new Font(theGraphicOptions.LINKAGE_INFO_FONT_FACE,Font.PLAIN,theGraphicOptions.LINKAGE_INFO_SIZE);
g2d.setFont(new_font);
Residue child = link.getChildResidue();
if( style.showParentLinkage(link) )
paintInfo(g2d,link.getParentPositionsString(),parent_bbox,parent_border_bbox,child_bbox,child_border_bbox,true,false,link.hasMultipleBonds());
if( style.showAnomericCarbon(link) )
paintInfo(g2d,link.getChildPositionsString(),parent_bbox,parent_border_bbox,child_bbox,child_border_bbox,false,true,link.hasMultipleBonds());
if( style.showAnomericState(link,child.getAnomericState()) )
paintInfo(g2d,TextUtils.toGreek(child.getAnomericState()),parent_bbox,parent_border_bbox,child_bbox,child_border_bbox,false,false,link.hasMultipleBonds());
g2d.setFont(old_font);
}
private void paintInfo(Graphics2D g2d, String text, Rectangle p, Rectangle pb, Rectangle c, Rectangle cb, boolean toparent, boolean above, boolean multiple) {
Dimension tb = textBounds(text,theGraphicOptions.LINKAGE_INFO_FONT_FACE,theGraphicOptions.LINKAGE_INFO_SIZE);
Point pos = computePosition(tb,p,pb,c,cb,toparent,above,multiple);
g2d.clearRect(pos.x,(int)(pos.y-tb.getHeight()),(int)tb.getWidth(),(int)tb.getHeight());
g2d.drawString(text,pos.x,pos.y);
}
private Point computePosition(Dimension tb, Rectangle p, Rectangle pb, Rectangle c, Rectangle cb, boolean toparent, boolean above, boolean multiple) {
Point cp = center(p);
Point cc = center(c);
double r = 0.5 * theGraphicOptions.LINKAGE_INFO_SIZE;
double cx=0.,cy=0.,R=0.,angle=0.;
if( toparent ) {
cx = cp.x;
cy = cp.y;
angle = angle(cc,cp);
R = getExclusionRadius(cp,angle,pb)+2;
}
else {
cx = c.x+c.width/2;
cy = c.y+c.height/2;
angle = angle(cp,cc);
R = getExclusionRadius(cc,angle,cb)+2;
}
double space = (multiple) ?4. :2.;
/*double pangle = angle(cc,cp);
boolean add = (pangle>-Math.PI/2. && pangle<Math.PI/2.);
if( !above )
add = !add;
*/
boolean add = above;
if( toparent )
add = !add;
double tx=0.,ty=0.;
if( add ) {
tx = cx+(R+r)*Math.cos(angle)+(r+space)*Math.cos(angle-Math.PI/2.);
ty = cy+(R+r)*Math.sin(angle)+(r+space)*Math.sin(angle-Math.PI/2.);
}
else {
tx = cx+(R+r)*Math.cos(angle)+(r+space)*Math.cos(angle+Math.PI/2.);
ty = cy+(R+r)*Math.sin(angle)+(r+space)*Math.sin(angle+Math.PI/2.);
}
//tx -= theGraphicOptions.LINKAGE_INFO_SIZE/2.;
//ty += theGraphicOptions.LINKAGE_INFO_SIZE/2.;
tx -= tb.getWidth()/2;
ty += tb.getHeight()/2;
return new Point((int)tx,(int)ty);
}
private Stroke createStroke(Linkage link, boolean selected) {
LinkageStyle style = theLinkageStyleDictionary.getStyle(link);
if( style.isDashed() ) {
float[] dashes = {5.f,5.f};
return new BasicStroke((selected) ?2.f :1.f,BasicStroke.CAP_BUTT,BasicStroke.JOIN_ROUND,1.f,dashes,0.f);
}
return new BasicStroke((selected) ?2.f :1.f);
}
static private Shape createLine(Point p1, Point p2, boolean multiple) {
if( multiple ) {
GeneralPath gp = new GeneralPath();
double a = angle(p1,p2);
Shape line1 = createLine(translate(p1,2.*Math.cos(a+Math.PI/2),2.*Math.sin(a+Math.PI/2)),
translate(p2,2.*Math.cos(a+Math.PI/2),2.*Math.sin(a+Math.PI/2)),
false);
gp.append(line1,false);
Shape line2 = createLine(translate(p1,2.*Math.cos(a-Math.PI/2),2.*Math.sin(a-Math.PI/2)),
translate(p2,2.*Math.cos(a-Math.PI/2),2.*Math.sin(a-Math.PI/2)),
false);
gp.append(line2,false);
return gp;
}
return createLine(p1,p2);
}
static private Shape createLine(Point p1, Point p2) {
Polygon l = new Polygon();
l.addPoint(p1.x,p1.y);
l.addPoint(p2.x,p2.y);
return l;
}
static private Shape createCurve(Point p1, Point p2) {
double cx = (p1.x+p2.x)/2.;
double cy = (p1.y+p2.y)/2.;
double r = distance(p1,p2)/2.;
double angle = angle(p1,p2);
// start point
double x1 = cx+r*Math.cos(angle);
double y1 = cy+r*Math.sin(angle);
// end point
double x2 = cx+r*Math.cos(angle+Math.PI);
double y2 = cy+r*Math.sin(angle+Math.PI);
// ctrl point 1
double cx1 = cx+0.1*r*Math.cos(angle);
double cy1 = cy+0.1*r*Math.sin(angle);
double tx1 = cx1+r*Math.cos(angle+Math.PI/2.);
double ty1 = cy1+r*Math.sin(angle+Math.PI/2.);
// ctrl point 2
double cx2 = cx+0.1*r*Math.cos(angle+Math.PI);
double cy2 = cy+0.1*r*Math.sin(angle+Math.PI);
double tx2 = cx2+r*Math.cos(angle-Math.PI/2.);
double ty2 = cy2+r*Math.sin(angle-Math.PI/2.);
return new CubicCurve2D.Double(x1,y1,tx1,ty1,tx2,ty2,x2,y2);
}
static private Shape createCurve(Point p1, Point p2, boolean multiple) {
if( multiple ) {
GeneralPath gp = new GeneralPath();
double a = angle(p1,p2);
Shape curve1 = createCurve(translate(p1,2.*Math.cos(a+Math.PI/2),2.*Math.sin(a+Math.PI/2)),
translate(p2,2.*Math.cos(a+Math.PI/2),2.*Math.sin(a+Math.PI/2)),
false);
gp.append(curve1,false);
Shape curve2 = createCurve(translate(p1,2.*Math.cos(a-Math.PI/2),2.*Math.sin(a-Math.PI/2)),
translate(p2,2.*Math.cos(a-Math.PI/2),2.*Math.sin(a-Math.PI/2)),
false);
gp.append(curve2,false);
return gp;
}
return createCurve(p1,p2);
}
private Shape createShape(Linkage link, Rectangle parent_bbox, Rectangle child_bbox) {
LinkageStyle style = theLinkageStyleDictionary.getStyle(link);
String edge_style = style.getShape();
Point parent_center = center(parent_bbox);
Point child_center = center(child_bbox);
if( edge_style.equals("none") )
return null;
if( edge_style.equals("empty") )
return null;
if( edge_style.equals("line") )
return createLine(parent_center,child_center,link.hasMultipleBonds());
if( edge_style.equals("curve") )
return createCurve(parent_center,child_center,link.hasMultipleBonds());
return createLine(parent_center,child_center);
}
}