/* * 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.awt.*; import java.awt.geom.*; import java.awt.image.*; import java.awt.font.*; /** Utility class with different methods for computing coordinates and dimensions of geometrical shapes. @author Alessio Ceroni (a.ceroni@imperial.ac.uk) */ public class Geometry { /** Return the dimensions of a text in a given font. */ static public Dimension textBounds(String text, String font_face, int font_size) { if( text.length()==0 ) return new Dimension(0,font_size); // retrieve a graphics2d object BufferedImage img = GraphicUtils.createCompatibleImage(10,10,true); Graphics2D g2d = img.createGraphics(); // compute text bounds FontRenderContext frc = g2d.getFontRenderContext(); Font font = new Font(font_face,Font.PLAIN,font_size); Rectangle text_bound = new Rectangle(); text_bound.setRect(new TextLayout(text,font,frc).getBounds()); return new Dimension(text_bound.width,text_bound.height); } /** Return the shape used to draw a text in a given font. */ static public Shape getTextShape(String text, String font_face, int font_size) { // retrieve a graphics2d object BufferedImage img = GraphicUtils.createCompatibleImage(10,10,true); Graphics2D g2d = img.createGraphics(); // compute text layout FontRenderContext frc = g2d.getFontRenderContext(); Font font = new Font(font_face,Font.PLAIN,font_size); TextLayout tl = new TextLayout(text, font, frc); return tl.getOutline(null); } /** Return the shape used to draw a text in a given font. Initialize the shape at the given coordinates. */ static public Shape getTextShape(double x, double y, String text, String font_face, int font_size) { Shape s = getTextShape(text,font_face,font_size); AffineTransform t = new AffineTransform(); t.setToTranslation(x,y); return t.createTransformedShape(s); } /** Create a rectangle given two corners. */ static public Rectangle makeRectangle(Point a, Point b) { if( a!=null && b!=null ) { int x = (a.x<b.x) ?a.x :b.x; int y = (a.y<b.y) ?a.y :b.y; int w = Math.abs(a.x-b.x); int h = Math.abs(a.y-b.y); return new Rectangle(x,y,w,h); } return null; } /** Return the coordinates of the center of a rectangle. */ static public Point center(Rectangle r) { return new Point(midx(r),midy(r)); } /** Return the x coordinate of the center of a rectangle. */ static public int midx(Rectangle r) { return (r.x+(r.width/2)); } /** Return the y coordinate of the center of a rectangle. */ static public int midy(Rectangle r) { return (r.y+(r.height/2)); } /** Return the left coordinate of a rectangle. */ static public int left(Rectangle r) { return r.x; } /** Return the top coordinate of a rectangle. */ static public int top(Rectangle r) { return r.y; } /** Return the right coordinate of a rectangle. */ static public int right(Rectangle r) { return (r.x+r.width); } /** Return the bottom coordinate of a rectangle. */ static public int bottom(Rectangle r) { return (r.y+r.height); } /** Return the width of a rectangle. */ static public int width(Rectangle r) { return r.width; } /** Return the height of a rectangle. */ static public int height(Rectangle r) { return r.height; } /** Return the top-left coordinates of a rectangle. */ static public Point topleft(Rectangle r) { return new Point(left(r),top(r)); } /** Return the top-right coordinates of a rectangle. */ static public Point topright(Rectangle r) { return new Point(right(r),top(r)); } /** Return the bottom-left coordinates of a rectangle. */ static public Point bottomleft(Rectangle r) { return new Point(left(r),bottom(r)); } /** Return the bottom-right coordinates of a rectangle. */ static public Point bottomright(Rectangle r) { return new Point(right(r),bottom(r)); } /** Return the smallest rectangle containing the two rectangles. */ static public Rectangle union(Rectangle a, Rectangle b) { if( a==null && b==null ) return null; if( a==null ) return b; if( b==null ) return a; return a.union(b); } /** Expand both dimensions of a rectangle by the given size. @return the rectangle with the new dimension */ static public Rectangle expand(Rectangle r, int d) { return new Rectangle(r.x-d,r.y-d,r.width+2*d,r.height+2*d); } /** Return the distance between the centers of two rectangles. */ static public double distance(Rectangle a, Rectangle b) { return distance(center(a),center(b)); } /** Return the distance between a point and the center of a rectangle. */ static public double distance(Point a, Rectangle b) { return distance(a,center(b)); } /** Return the distance between two points. */ static public double distance(Point a, Point b) { return Math.sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } /** Return the distance between the point <code>p</code> and the line passing by the points <code>s1</code> and <code>s2</code>. */ static public double distance(Point p, Point s1, Point s2) { double u = (double)((p.x-s1.x)*(s2.x-s1.x) + (p.y-s1.y)*(s2.y-s1.y))/(double)((s2.x-s1.x)*(s2.x-s1.x)+(s2.y-s1.y)*(s2.y-s1.y)); if( u<0 ) return distance(p,s1); if( u>1 ) return distance(p,s2); return distance(p,new Point((int)(s1.x+u*(s2.x-s1.x)),(int)(s1.y+u*(s2.y-s1.y)))); } /** Return the angle of the vector joining two points. */ static public double angle(Point p1, Point p2) { // from p2 to p1 if( p1.equals(p2) ) return -Math.PI/2.; // point up by default double x1 = p1.x; double x2 = p2.x; double y1 = p1.y; double y2 = p2.y; double d = Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)); double a = Math.acos((x1-x2)/d); return (y1>=y2) ?a :-a; } /** Normalize an angle to stay between -PI and +PI. */ static public double normalize(double angle) { while(angle<=-Math.PI) angle+= 2.*Math.PI; while(angle>Math.PI) angle-= 2.*Math.PI; return angle; } static protected boolean isDown(double angle) { angle = normalize(angle); return (angle>=-0.75*Math.PI && angle<-0.25*Math.PI); } static protected boolean isRight(double angle) { angle = normalize(angle); return (angle>=-0.25*Math.PI && angle<0.25*Math.PI); } static protected boolean isUp(double angle) { angle = normalize(angle); return (angle>=0.25*Math.PI && angle<0.75*Math.PI); } static protected boolean isLeft(double angle) { angle = normalize(angle); return (angle>=0.75*Math.PI || angle<-0.75*Math.PI); } /** Return <code>true</code> if the first rectangle is above the second. */ static public boolean isUp(Rectangle f, Rectangle t) { int d_x = midx(f)-midx(t); int d_y = midy(f)-midy(t); return(d_y<0 && Math.abs(d_y)>Math.abs(d_x)); } /** Return <code>true</code> if the first rectangle is below the second. */ static public boolean isDown(Rectangle f, Rectangle t) { int d_x = midx(f)-midx(t); int d_y = midy(f)-midy(t); return(d_y>0 && Math.abs(d_y)>Math.abs(d_x)); } /** Return <code>true</code> if the first rectangle is on the left of the second. */ static public boolean isLeft(Rectangle f, Rectangle t) { int d_x = midx(f)-midx(t); int d_y = midy(f)-midy(t); return(d_x<0 && Math.abs(d_x)>Math.abs(d_y)); } /** Return <code>true</code> if the first rectangle is on the right of the second. */ static public boolean isRight(Rectangle f, Rectangle t) { int d_x = midx(f)-midx(t); int d_y = midy(f)-midy(t); return(d_x>0 && Math.abs(d_x)>Math.abs(d_y)); } /** Return the dimensions of the space between the two rectangles. Rectangle <code>in</code> must be contained in <code>out</code>. */ static public Insets getInsets(Rectangle in, Rectangle out) { return new Insets(top(in)-top(out),left(in)-left(out), bottom(out)-bottom(in),right(out)-right(in)); } static protected double getExclusionRadius(Point center, double angle, Rectangle bbox) { if( !bbox.contains(center) ) return 0.; double tla = angle(topleft(bbox),center); double tra = angle(topright(bbox),center); double bla = angle(bottomleft(bbox),center); double bra = angle(bottomright(bbox),center); double R = 0.; if( angle>=tra && angle<=bra ) R = (right(bbox)-center.x)/Math.cos(angle); else if( angle>=bra && angle<=bla ) R = (bottom(bbox)-center.y)/Math.cos(angle-0.5*Math.PI); else if( angle>=tla && angle<=tra ) R = (center.y-top(bbox))/Math.cos(angle+0.5*Math.PI); else R = (center.x-left(bbox))/Math.cos(angle+Math.PI); return R; } static protected boolean overlapx(Rectangle a, Rectangle b, int toll) { if( a==null || b==null ) return false; int la = left(a) - toll; int ra = right(a) + toll; int lb = left(b) - toll; int rb = right(b) + toll; return (la<=lb && lb<=ra) || (la<=rb && rb<=ra) || (lb<=la && la<=rb) || (lb<=ra && ra<=rb); } static protected boolean overlapy(Rectangle a, Rectangle b, int toll) { if( a==null || b==null ) return false; int ta = top(a) - toll; int ba = bottom(a) + toll; int tb = top(b) - toll; int bb = bottom(b) + toll; return (ta<=tb && tb<=ba) || (ta<=bb && bb<=ba) || (tb<=ta && ta<=bb) || (tb<=ba && ba<=bb); } /** Translate a point by a specified displacement. */ static public Point translate(Point p, double dx, double dy) { return new Point((int)(p.x+dx),(int)(p.y+dy)); } }