/*
Violet - A program for editing UML diagrams.
Copyright (C) 2002 Cay S. Horstmann (http://horstmann.com)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.horstmann.violet;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import com.horstmann.violet.framework.SerializableEnumeration;
/**
A style for a segmented line that indicates the number
and sequence of bends.
*/
public class BentStyle extends SerializableEnumeration
{
private BentStyle() {}
/**
Gets the points at which a line joining two rectangles
is bent according to this bent style.
@param start the starting rectangle
@param end the ending rectangle
@return an array list of points at which to bend the
segmented line joining the two rectangles
*/
public ArrayList getPath(Rectangle2D start, Rectangle2D end)
{
ArrayList r = getPath(this, start, end);
if (r != null) return r;
if (start.equals(end)) r = getSelfPath(start);
else if (this == HVH) r = getPath(VHV, start, end);
else if (this == VHV) r = getPath(HVH, start, end);
else if (this == HV) r = getPath(VH, start, end);
else if (this == VH) r = getPath(HV, start, end);
if (r != null) return r;
return getPath(STRAIGHT, start, end);
}
/**
Gets the four connecting points at which a bent line
connects to a rectangle.
*/
private static Point2D[] connectionPoints(Rectangle2D r)
{
Point2D[] a = new Point2D[4];
a[0] = new Point2D.Double(r.getX(), r.getCenterY());
a[1] = new Point2D.Double(r.getMaxX(), r.getCenterY());
a[2] = new Point2D.Double(r.getCenterX(), r.getY());
a[3] = new Point2D.Double(r.getCenterX(), r.getMaxY());
return a;
}
/**
Gets the points at which a line joining two rectangles
is bent according to a bent style.
@param start the starting rectangle
@param end the ending rectangle
@return an array list of points at which to bend the
segmented line joining the two rectangles
*/
private static ArrayList getPath(BentStyle bent,
Rectangle2D s, Rectangle2D e)
{
ArrayList r = new ArrayList();
if (bent == STRAIGHT)
{
Point2D[] a = connectionPoints(s);
Point2D[] b = connectionPoints(e);
Point2D p = a[0];
Point2D q = b[0];
double distance = p.distance(q);
if (distance == 0) return null;
for (int i = 0; i < a.length; i++)
for (int j = 0; j < b.length; j++)
{
double d = a[i].distance(b[j]);
if (d < distance)
{
p = a[i]; q = b[j];
distance = d;
}
}
r.add(p);
r.add(q);
}
else if (bent == HV)
{
double x1;
double x2 = e.getCenterX();
double y1 = s.getCenterY();
double y2;
if (x2 + MIN_SEGMENT <= s.getX())
x1 = s.getX();
else if (x2 - MIN_SEGMENT >= s.getMaxX())
x1 = s.getMaxX();
else return null;
if (y1 + MIN_SEGMENT <= e.getY())
y2 = e.getY();
else if (y1 - MIN_SEGMENT >= e.getMaxY())
y2 = e.getMaxY();
else return null;
r.add(new Point2D.Double(x1, y1));
r.add(new Point2D.Double(x2, y1));
r.add(new Point2D.Double(x2, y2));
}
else if (bent == VH)
{
double x1 = s.getCenterX();
double x2;
double y1;
double y2 = e.getCenterY();
if (x1 + MIN_SEGMENT <= e.getX())
x2 = e.getX();
else if (x1 - MIN_SEGMENT >= e.getMaxX())
x2 = e.getMaxX();
else return null;
if (y2 + MIN_SEGMENT <= s.getY())
y1 = s.getY();
else if (y2 - MIN_SEGMENT >= s.getMaxY())
y1 = s.getMaxY();
else return null;
r.add(new Point2D.Double(x1, y1));
r.add(new Point2D.Double(x1, y2));
r.add(new Point2D.Double(x2, y2));
}
else if (bent == HVH)
{
double x1;
double x2;
double y1 = s.getCenterY();
double y2 = e.getCenterY();
if (s.getMaxX() + 2 * MIN_SEGMENT <= e.getX())
{
x1 = s.getMaxX();
x2 = e.getX();
}
else if (e.getMaxX() + 2 * MIN_SEGMENT <= s.getX())
{
x1 = s.getX();
x2 = e.getMaxX();
}
else return null;
if (Math.abs(y1 - y2) <= MIN_SEGMENT)
{
r.add(new Point2D.Double(x1, y2));
r.add(new Point2D.Double(x2, y2));
}
else
{
r.add(new Point2D.Double(x1, y1));
r.add(new Point2D.Double((x1 + x2) / 2, y1));
r.add(new Point2D.Double((x1 + x2) / 2, y2));
r.add(new Point2D.Double(x2, y2));
}
}
else if (bent == VHV)
{
double x1 = s.getCenterX();
double x2 = e.getCenterX();
double y1;
double y2;
if (s.getMaxY() + 2 * MIN_SEGMENT <= e.getY())
{
y1 = s.getMaxY();
y2 = e.getY();
}
else if (e.getMaxY() + 2 * MIN_SEGMENT <= s.getY())
{
y1 = s.getY();
y2 = e.getMaxY();
}
else return null;
if (Math.abs(x1 - x2) <= MIN_SEGMENT)
{
r.add(new Point2D.Double(x2, y1));
r.add(new Point2D.Double(x2, y2));
}
else
{
r.add(new Point2D.Double(x1, y1));
r.add(new Point2D.Double(x1, (y1 + y2) / 2));
r.add(new Point2D.Double(x2, (y1 + y2) / 2));
r.add(new Point2D.Double(x2, y2));
}
}
else return null;
return r;
}
/**
Gets the points at which a line joining two rectangles
is bent according to a bent style.
@param s the starting and ending rectangle
*/
private static ArrayList getSelfPath(Rectangle2D s)
{
ArrayList r = new ArrayList();
double x1 = s.getX() + s.getWidth() * 3 / 4;
double y1 = s.getY();
double y2 = s.getY() - SELF_HEIGHT;
double x2 = s.getX() + s.getWidth() + SELF_WIDTH;
double y3 = s.getY() + s.getHeight() / 4;
double x3 = s.getX() + s.getWidth();
r.add(new Point2D.Double(x1, y1));
r.add(new Point2D.Double(x1, y2));
r.add(new Point2D.Double(x2, y2));
r.add(new Point2D.Double(x2, y3));
r.add(new Point2D.Double(x3, y3));
return r;
}
private static final int MIN_SEGMENT = 10;
private static final int SELF_WIDTH = 30;
private static final int SELF_HEIGHT = 25;
public static final BentStyle STRAIGHT = new BentStyle();
public static final BentStyle HV = new BentStyle();
public static final BentStyle VH = new BentStyle();
public static final BentStyle HVH = new BentStyle();
public static final BentStyle VHV = new BentStyle();
}