/*
* Copyright (C) 2004 The Concord Consortium, Inc.,
* 10 Concord Crossing, Concord, MA 01742
*
* Web Site: http://www.concord.org
* Email: info@concord.org
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* END LICENSE */
package org.concord.swing.map;
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
public class ArcItem
extends MapItem
{
protected final static int RADIUS = 5;
protected Polygon arrow = new Polygon();
protected Rectangle bounds = new Rectangle(0, 0, 0, 0);
protected int id;
protected Color color = Color.black;
protected Color selectedColor = Color.red;
protected NodeItem sourceItem;
protected NodeItem sinkItem;
protected Point sourcePoint = new Point(0, 0);
protected Point sinkPoint = new Point(0, 0);
protected Point p1 = new Point(0, 0);
protected Point p2 = new Point(0, 0);
protected int a = 3;
protected int b = 4;
protected int c = 1;
protected int d = 2;
protected String name;
protected static int arcNumber = 0;
public ArcItem(MapContainer container)
{
super(container);
}
public void delete()
{
if (sourceItem != null)
sourceItem.removeArc(this);
if (sinkItem != null)
sinkItem.removeArc(this);
if (parent != null)
parent.removeArcItem(this);
}
public void setSourceNode(NodeItem sourceItem)
{
if ((this.sourceItem instanceof NodeItem) && (sourceItem == null))
setSourceLocation(this.sourceItem.getNodeCenterX(), this.sourceItem.getNodeCenterY());
this.sourceItem = sourceItem;
if (sourceItem instanceof NodeItem)
sourceItem.addArc(this);
String sourceName = (sourceItem == null) ? "self" : sourceItem.getName();
String sinkName = (sinkItem == null) ? "self" : sinkItem.getName();
setName("Arc from " + sourceName + " to " + sinkName);
}
public NodeItem getSourceNode()
{
return sourceItem;
}
public void setSinkNode(NodeItem sinkItem)
{
if ((this.sinkItem instanceof NodeItem) && (sinkItem == null))
setSinkLocation(this.sinkItem.getNodeCenterX(), this.sinkItem.getNodeCenterY());
this.sinkItem = sinkItem;
if (sinkItem instanceof NodeItem)
sinkItem.addArc(this);
String sourceName = (sourceItem == null) ? "self" : sourceItem.getName();
String sinkName = (sinkItem == null) ? "self" : sinkItem.getName();
setName("Arc from " + sourceName + " to " + sinkName);
}
public NodeItem getSinkNode()
{
return sinkItem;
}
public void setArrowRatio(int num, int denom)
{
a = num;
b = denom;
}
public void setImageRatio(int num, int denom)
{
c = num;
d = denom;
}
public boolean isNear(int x, int y, Point point, int radius)
{
return (Math.abs(x - point.x) <= radius) && (Math.abs(y - point.y) <= radius);
}
public boolean isNearSourceLocation(int x, int y)
{
return (sourceItem == null) && isNear(x, y, sourcePoint, RADIUS);
}
public Point getSourcePoint()
{
return sourcePoint;
}
public void setSourceLocation(int x, int y)
{
sourcePoint.setLocation(x, y);
}
public boolean isNearSinkLocation(int x, int y)
{
return (sinkItem == null) && isNear(x, y, sinkPoint, RADIUS);
}
public Point getSinkPoint()
{
return sinkPoint;
}
public void setSinkLocation(int x, int y)
{
sinkPoint.setLocation(x, y);
}
public Rectangle getBounds()
{
if (image instanceof Image)
return imageBounds;
return bounds;
}
public void translate(int x, int y)
{
if (sourceItem == null)
{
sourcePoint.translate(x, y);
}
if (sinkItem == null)
{
sinkPoint.translate(x, y);
}
}
public void removeNode(NodeItem nodeItem)
{
if (nodeItem == sourceItem)
setSourceNode(null);
if (nodeItem == sinkItem)
setSinkNode(null);
}
public void removeNodes()
{
if (sourceItem != null)
{
sourceItem.removeArcItem(this);
sourceItem = null;
}
if (sinkItem != null)
{
sinkItem.removeArcItem(this);
sinkItem = null;
}
}
public boolean isDisconnected()
{
return (sourceItem == null) || (sinkItem == null);
}
protected void transformPolygon(Point p, Point d, float angle)
{
bounds.x = Integer.MAX_VALUE;
bounds.y = Integer.MAX_VALUE;
bounds.width = Integer.MIN_VALUE;
bounds.height= Integer.MIN_VALUE;
int f = d.x > 0 ? 1 : -1;
for (int i = 0; i < arrow.npoints; i++)
{
float x = (float) arrow.xpoints[i];
float y = (float) arrow.ypoints[i];
float x1 = (float) (f * x * Math.cos(angle) + y * Math.sin(angle));
float y1 = (float) (x * Math.sin(angle) + -f * y * Math.cos(angle));
arrow.xpoints[i] = (int) x1;
arrow.ypoints[i] = (int) y1;
bounds.x = Math.min(arrow.xpoints[i], bounds.x);
bounds.y = Math.min(arrow.ypoints[i], bounds.y);
bounds.width = Math.max(arrow.xpoints[i], bounds.width);
bounds.height = Math.max(arrow.ypoints[i], bounds.height);
}
bounds.width = bounds.width - bounds.x;
bounds.height = bounds.height - bounds.y;
bounds.x += p.x;
bounds.y += p.y;
for (int i = 0; i < arrow.npoints; i++)
{
arrow.xpoints[i] += p.x;
arrow.ypoints[i] += p.y;
}
}
protected void setPoints()
{
int [] xpts = { 20, 0, 0 };
int [] ypts = { 0, 8, -8 };
arrow.xpoints = xpts;
arrow.ypoints = ypts;
arrow.npoints = arrow.ypoints.length;
if (sourceItem == null)
{
p1.setLocation(sourcePoint);
}
else
{
Rectangle b = sourceItem.getBounds();
p1.x = b.x + b.width / 2;
p1.y = b.y + b.height / 2;
}
if (sinkItem == null)
{
p2.setLocation(sinkPoint);
}
else
{
Rectangle b = sinkItem.getBounds();
p2.x = b.x + b.width / 2;
p2.y = b.y + b.height / 2;
}
}
public void drawConnect(Graphics g, boolean selected)
{
Color saveColor = g.getColor();
g.setColor(selected ? selectedColor : color);
setPoints();
if (sourceItem == null)
{
g.fillOval(p1.x - RADIUS, p1.y - RADIUS, 2 * RADIUS, 2 * RADIUS);
}
g.drawLine(p1.x, p1.y, p2.x, p2.y);
if (sinkItem == null)
{
g.fillOval(p2.x - RADIUS, p2.y - RADIUS, 2 * RADIUS, 2 * RADIUS);
}
g.setColor(saveColor);
}
public void draw(Graphics g, boolean selected)
{
Color saveColor = g.getColor();
g.setColor(selected ? selectedColor: color);
setPoints();
p2.setLocation(p2.x - p1.x, p2.y - p1.y);
if (image instanceof Image)
{
Rectangle r = imageBounds;
// Place along arc line
r.setLocation(p1.x + c * p2.x / d, p1.y + c * p2.y / d);
r.width = image.getWidth(observer);
r.height = image.getHeight(observer);
// Align image center with arc line
r.setLocation(r.x - (r.width / 2), r.y - (r.height / 2));
g.drawImage(image, r.x, r.y, observer);
FontMetrics metrics = g.getFontMetrics(g.getFont());
textX = r.x + (r.width - metrics.stringWidth(getName())) / 2 ;
textY = r.y + r.height + metrics.getAscent();
g.setColor(textColor);
g.drawString(getName(), textX, textY);
g.setColor(selected ? selectedColor: color);
}
p1.setLocation(p1.x + a * p2.x / b, p1.y + a * p2.y / b);
float theta = (float) Math.asin(p2.y / Math.sqrt(p2.y * p2.y + p2.x * p2.x));
transformPolygon(p1, p2, theta);
g.fillPolygon(arrow);
g.setColor(saveColor);
}
public int getID()
{
return id;
}
public String toString()
{
return name;
}
}