/*
* (C) Copyright 2005 Arnaud Bailly (arnaud.oqube@gmail.com),
* Yves Roos (yroos@lifl.fr) and others.
*
* 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 rationals.distance;
import java.awt.BasicStroke;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
/**
* A subclass of canvas that displays a radial graph. This class may be used as
* a component in a UI.
*
* @author nono
* @version $Id: RadialGraph.java 2 2006-08-24 14:41:48Z oqube $
*/
public class RadialGraph extends Canvas {
private int dim;
private double eta = 0.0;
private BufferedImage image;
private double size = 400;
private double margin = 60;
/* coordinate of axis */
private Point2D coords[];
/* label of axis */
private String[] axis;
/* list of vectors to draw */
private List vectors = new ArrayList();
/* flag for filling shapes */
private boolean fill;
/* flag for direction connection of shapes */
private boolean connect;
/**
* Create a radial graph with given number of dimensions.
*
* @param dim
* the number of dimensions (axis) in this graph.
* @param axis
* the labels of each axis. Size must be equal to dim.
*/
public RadialGraph(int dim, String[] axis) {
if (axis.length != dim)
throw new IllegalArgumentException(
"Length of axis must be equals to " + dim);
this.dim = dim;
this.axis = axis;
this.coords = new Point2D[dim];
}
/**
* Draw the radii of the map.
*
* @param g
*/
public void draw(Graphics2D g) {
double cx = size / 2 + margin;
Graphics2D gd = (Graphics2D) g.create();
/* translate coordinates */
gd.translate(cx, cx);
coords[0] = new Point2D.Double(0, -size / 2);
Line2D line = new Line2D.Double(0, 0, 0, -size / 2);
/* draw each axis */
gd.setStroke(new BasicStroke(0.5f));
gd.setFont(new Font("Helvetica", Font.ITALIC, 10));
gd.draw(line);
for (int j = 0; j < dim; j++) {
double theta = Math.PI / 2 + Math.PI * 2 * j / dim;
double dx = Math.cos(theta);
double dy = Math.sin(theta);
coords[j] = new Point2D.Double(dx * (size / 2), -dy * (size / 2));
line = new Line2D.Double(0, 0, dx * (size / 2), -dy * (size / 2));
gd.draw(line);
String s = axis[j];
int w = gd.getFontMetrics().stringWidth(s);
gd.drawString(s,
(float) (dx * (size / 2) + (dx > 0 ? 10 : -w - 10)),
(float) (-dy * ((size) / 2)));
}
// draw a circle
gd.drawOval((int)-size/2,(int)-size/2,(int)size,(int)size);
}
public void drawEta(double eta, Graphics2D g) {
double cx = size / 2 + margin;
Graphics2D gd = (Graphics2D) g.create();
/* translate coordinates */
gd.translate(cx, cx);
/* calcul de l'angle */
double theta = Math.asin(1 - eta * eta / 2);
Ellipse2D dot = new Ellipse2D.Double(-2, -2, 4, 4);
for (int i = 0; i < dim; i++) {
double rho = Math.PI / 2 + Math.PI * 2 * i / dim;
double dx = Math.cos(rho);
double dy = Math.sin(rho);
double cura = theta;
do {
double ay = Math.sin(cura);
double x = ay * dx * (size / 2);
double y = -ay * dy * (size / 2);
/* mark */
dot.setFrame(x - 2, y - 2, 4, 4);
gd.draw(dot);
cura = cura - (Math.PI / 2 - theta);
} while (cura > 0);
}
}
/**
* Draw the word on given context with given color
*
* @param word
* @param g
* @param col
*/
public void draw(double[] vec, Graphics2D g, Color col, Stroke str) {
if(vec == null)
return;
double cx = size / 2 + margin;
GeneralPath path = new GeneralPath();
Rectangle2D pt = new Rectangle2D.Double(-2, -2, 4, 4);
Graphics2D gd = (Graphics2D) g.create();
/* translate coordinates */
gd.translate(cx, cx);
/* draw axis */
// draw(gd);
gd.setStroke(str);
gd.setColor(col);
int n = dim;
for (int i = 0; i < n; i++) {
double theta = Math.PI / 2 + Math.PI * 2 * i / n;
double dx = Math.cos(theta);
double dy = Math.sin(theta);
double x = vec[i] * dx * (size / 2);
double y = -vec[i] * dy * (size / 2);
if (i == 0) {
path.moveTo((float) x, (float) y);
pt.setFrame(x - 2, y - 2, 4, 4);
gd.draw(pt);
} else if (!connect || (x != 0 || y != 0)) {
path.lineTo((float) x, (float) y);
pt.setFrame(x - 2, y - 2, 4, 4);
gd.draw(pt);
}
}
path.closePath();
gd.draw(path);
}
/**
* Draw the word on given context with given color
*
* @param word
* @param g
* @param col
*/
public void fill(double[] vec, Graphics2D g, Color col, Stroke str,boolean coord) {
double cx = size / 2 + margin;
GeneralPath path = new GeneralPath();
Rectangle2D pt = new Rectangle2D.Double(-2, -2, 4, 4);
Graphics2D gd = (Graphics2D) g.create();
/* translate coordinates */
gd.translate(cx, cx);
/* draw axis */
// draw(gd);
gd.setStroke(str);
gd.setColor(col);
int n = dim;
double[][] pts = new double[n][2];
for (int i = 0; i < n; i++) {
double theta = Math.PI / 2 + Math.PI * 2 * i / n;
double dx = Math.cos(theta);
double dy = Math.sin(theta);
double x = vec[i] * dx * (size / 2);
double y = -vec[i] * dy * (size / 2);
pts[i][0]= x;
pts[i][1] = y;
if (i == 0) {
path.moveTo((float) x, (float) y);
pt.setFrame(x - 2, y - 2, 4, 4);
gd.draw(pt);
} else if(!connect || (x != 0 || y != 0)) {
path.lineTo((float) x, (float) y);
pt.setFrame(x - 2, y - 2, 4, 4);
gd.draw(pt);
}
}
path.closePath();
gd.fill(path);
/* show coordinates */
if(coord) {
gd.setColor(Color.black);
for(int i=0;i<n;i++)
gd.drawString(""+vec[i],(float)(pts[i][0]+5),(float)(pts[i][1]+5));
}
}
public void paintDirect(Graphics g) {
RenderingHints hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
hints.put(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
Graphics2D gd = (Graphics2D) g;
gd.setRenderingHints(hints);
gd.setColor(Color.white);
Shape sh = gd.getClip();
if (sh == null)
sh = new Rectangle2D.Double(0, 0, getWidth(), getHeight());
gd.fill(sh);
gd.setColor(Color.black);
EnumeratingPainters p = new EnumeratingPainters();
BasicStroke s = new BasicStroke(0.5f);
draw(gd);
if (eta != 0)
drawEta(eta, gd);
for (Iterator it = vectors.iterator(); it.hasNext();) {
double[] vec = (double[]) it.next();
if (fill)
fill(vec, gd, p.nextColor(), s,false);
else
draw(vec, gd, p.nextColor(), s);
}
}
public void paint(Graphics g) {
if (image == null)
image = new BufferedImage(getWidth(), getHeight(),
BufferedImage.TYPE_4BYTE_ABGR);
RenderingHints hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
hints.put(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
Graphics2D gd = (Graphics2D) image.getGraphics();
gd.setRenderingHints(hints);
gd.setColor(Color.white);
Shape sh = gd.getClip();
if (sh == null)
sh = new Rectangle2D.Double(0, 0, getWidth(), getHeight());
gd.fill(sh);
gd.setColor(Color.black);
EnumeratingPainters p = new EnumeratingPainters();
BasicStroke s = new BasicStroke(0.5f);
draw(gd);
if (eta != 0)
drawEta(eta, gd);
for (Iterator it = vectors.iterator(); it.hasNext();) {
double[] vec = (double[]) it.next();
if (fill)
fill(vec, gd, p.nextColor(), s,false);
else
draw(vec, gd, p.nextColor(), s);
}
/* copy to g */
((Graphics2D) g).drawImage(image, null, 0, 0);
}
class EnumeratingPainters {
private int curColor = 0x00fF0000;
public Color nextColor() {
curColor = (curColor + 0x00003203) & 0x00ffffff;
int r = (curColor >> 16) & 0xff;
int g = (curColor >> 8) & 0xff;
int b = curColor & 0xff;
return new Color(r, g, b, 0xf0);
}
}
/*
* (non-Javadoc)
*
* @see java.awt.Component#getPreferredSize()
*/
public Dimension getPreferredSize() {
return new Dimension((int) (size + margin), (int) (size + margin));
}
public void addVector(double[] vec) {
this.vectors.add(vec);
}
public void removeVector(double[] vec) {
this.vectors.remove(vec);
}
/**
* @return Returns the eta.
*/
public double getEta() {
return eta;
}
/**
* @param eta
* The eta to set.
*/
public void setEta(double eta) {
this.eta = eta;
}
/**
* @return Returns the fill.
*/
public boolean isFill() {
return fill;
}
/**
* @param fill
* The fill to set.
*/
public void setFill(boolean fill) {
this.fill = fill;
}
/**
* @return Returns the margin.
*/
public double getMargin() {
return margin;
}
/**
* @param margin
* The margin to set.
*/
public void setMargin(double margin) {
this.margin = margin;
}
/**
* @param size
* The size to set.
*/
public void setSize(double size) {
this.size = size;
}
/**
* @return Returns the vectors.
*/
public List getVectors() {
return vectors;
}
/**
* @param l
*/
public void addVectors(Collection l) {
vectors.addAll(l);
}
/**
* @return Returns the connect.
*/
public boolean isConnect() {
return connect;
}
/**
* @param connect
* The connect to set.
*/
public void setConnect(boolean connect) {
this.connect = connect;
}
}