package iiuf.swing.graph;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Component;
import java.awt.Rectangle;
import java.awt.Color;
import iiuf.awt.Awt;
/**
Orthogonal edge router, connects two points using orthogonal bends only.<p>
(c) 2000, 2001, IIUF, DIUF<p>
@author $Author: ohitz $
@version $Name: $ $Revision: 1.1 $
*/
public final class OrthogonalRouter
implements
GraphRouter
{
private static final double PI2 = Math.PI / 2;
private static final int MAXLOOP = 10;
private static final int DINC = 2;
private void hline(int x, int y) {
int lastx = xpoints[npoints - 1];
int lasty = ypoints[npoints - 1];
if(lastx < x) lr(lastx, lasty, x, y);
else rl(lastx, lasty, x, y);
}
private void lr(int lastx, int lasty, int x, int y) {
hcnt += hdir;
if(y < 0 || y >= height) {
xpoints[npoints] = x;
ypoints[npoints++] = y;
return;
}
int[] idxs = getYCmpsCa[y];
if(idxs == null) {
xpoints[npoints] = x;
ypoints[npoints++] = y;
return;
}
int is = -1;
int minx = Integer.MAX_VALUE;
int miny = Integer.MAX_VALUE;
int maxy = Integer.MIN_VALUE;
int stop = idxs.length;
for(int j = 0; j < stop; j++) {
int i = idxs[j];
if(x <= x1ca[i]) continue;
if(lastx >= x2ca[i]) continue;
if(exclude[i]) continue;
if(x1ca[i] < minx) {
is = i;
minx = x1ca[i];
}
miny = y1ca[i] < miny ? y1ca[i] : miny;
maxy = y2ca[i] > maxy ? y2ca[i] : maxy;
}
if(minx != Integer.MAX_VALUE) {
wbrac[is]++;
int dx = minx - lastx;
int off = wbrac[is] * DINC;
int nx = lastx + (dx > off ? dx - off : dx);
xpoints[npoints] = nx;
ypoints[npoints++] = y;
if(hcnt < 0) {
sbrac[is]++;
vline(nx, maxy + (sbrac[is] * DINC));
}
else {
nbrac[is]++;
vline(nx, miny - (nbrac[is] * DINC));
}
hline(x, ypoints[npoints - 1]);
}
else {
xpoints[npoints] = x;
ypoints[npoints++] = y;
}
}
private void rl(int lastx, int lasty, int x, int y) {
hcnt -= hdir;
int is = -1;
int maxx = Integer.MIN_VALUE;
int miny = Integer.MAX_VALUE;
int maxy = Integer.MIN_VALUE;
if(y < 0 || y >= height) {
xpoints[npoints] = x;
ypoints[npoints++] = y;
return;
}
int[] idxs = getYCmpsCa[y];
if(idxs == null) {
xpoints[npoints] = x;
ypoints[npoints++] = y;
return;
}
int stop = idxs.length;
for(int j = 0; j < stop; j++) {
int i = idxs[j];
if(lastx <= x1ca[i]) continue;
if(x >= x2ca[i]) continue;
if(exclude[i]) continue;
if(x2ca[i] > maxx) {
is = i;
maxx = x2ca[i];
}
miny = y1ca[i] < miny ? y1ca[i] : miny;
maxy = y2ca[i] > maxy ? y2ca[i] : maxy;
}
if(maxx != Integer.MIN_VALUE) {
ebrac[is]++;
int dx = lastx - maxx;
int off = ebrac[is] * DINC;
int nx = lastx - (dx > off ? dx - off : dx);
xpoints[npoints] = nx;
ypoints[npoints++] = y;
if(hcnt < 0) {
sbrac[is]++;
vline(nx, maxy + (sbrac[is] * DINC));
}
else {
nbrac[is]++;
vline(nx, miny - (nbrac[is] * DINC));
}
hline(x, ypoints[npoints - 1]);
}
else {
xpoints[npoints] = x;
ypoints[npoints++] = y;
}
}
private void vline(int x, int y) {
int lastx = xpoints[npoints - 1];
int lasty = ypoints[npoints - 1];
if(lasty < y) tb(lastx, lasty, x, y);
else bt(lastx, lasty, x, y);
}
private void tb(int lastx, int lasty, int x, int y) {
vcnt += vdir;
if(x < 0 || x >= width) {
xpoints[npoints] = x;
ypoints[npoints++] = y;
return;
}
int[] idxs = getXCmpsCa[x];
if(idxs == null) {
xpoints[npoints] = x;
ypoints[npoints++] = y;
return;
}
int is = -1;
int minx = Integer.MAX_VALUE;
int maxx = Integer.MIN_VALUE;
int miny = Integer.MAX_VALUE;
int stop = idxs.length;
for(int j = 0; j < stop; j++) {
int i = idxs[j];
if(y <= y1ca[i]) continue;
if(lasty >= y2ca[i]) continue;
if(exclude[i]) continue;
if(y1ca[i] < miny) {
is = i;
miny = y1ca[i];
}
minx = x1ca[i] < minx ? x1ca[i] : minx;
maxx = x2ca[i] > maxx ? x2ca[i] : maxx;
}
if(miny != Integer.MAX_VALUE) {
nbrac[is]++;
int dy = miny - lasty;
int off = nbrac[is] * DINC;
int ny = lasty + (dy > off ? dy - off : dy);
xpoints[npoints] = x;
ypoints[npoints++] = ny;
if(vcnt > 0) {
wbrac[is]++;
hline(minx - (wbrac[is] * DINC), ny);
}
else {
ebrac[is]++;
hline(maxx + (ebrac[is] * DINC), ny);
}
vline(xpoints[npoints - 1], y);
}
else {
xpoints[npoints] = x;
ypoints[npoints++] = y;
}
}
private void bt(int lastx, int lasty, int x, int y) {
vcnt -= vdir;
if(x < 0 || x >= width) {
xpoints[npoints] = x;
ypoints[npoints++] = y;
return;
}
int[] idxs = getXCmpsCa[x];
if(idxs == null) {
xpoints[npoints] = x;
ypoints[npoints++] = y;
return;
}
int is = -1;
int minx = Integer.MAX_VALUE;
int maxx = Integer.MIN_VALUE;
int maxy = Integer.MIN_VALUE;
int stop = idxs.length;
for(int j = 0; j < stop; j++) {
int i = idxs[j];
if(lasty <= y1ca[i]) continue;
if(y >= y2ca[i]) continue;
if(exclude[i]) continue;
if(y2ca[i] > maxy) {
is = i;
maxy = y2ca[i];
}
minx = x1ca[i] < minx ? x1ca[i] : minx;
maxx = x2ca[i] > maxx ? x2ca[i] : maxx;
}
if(maxy != Integer.MIN_VALUE) {
sbrac[is]++;
int dy = lasty - maxy;
int off = sbrac[is] * DINC;
int ny = lasty - (dy > off ? dy - off : dy);
xpoints[npoints] = x;
ypoints[npoints++] = ny;
if(vcnt > 0) {
wbrac[is]++;
hline(minx - (wbrac[is] * DINC), ny);
}
else {
ebrac[is]++;
hline(maxx + (ebrac[is] * DINC), ny);
}
vline(xpoints[npoints - 1], y);
}
else {
xpoints[npoints] = x;
ypoints[npoints++] = y;
}
}
private void hconnect(int x, int y) {
int q = 0;
while(xpoints[npoints - 1] != x ||
ypoints[npoints - 1] != y) {
hline(x, ypoints[npoints - 1]);
vline(xpoints[npoints - 1], y);
if(q++ > MAXLOOP) {
xpoints[npoints] = x;
ypoints[npoints++] = y;
break;
}
}
}
private void vconnect(int x, int y) {
int q = 0;
while(xpoints[npoints - 1] != x ||
ypoints[npoints - 1] != y) {
vline(xpoints[npoints - 1], y);
hline(x, ypoints[npoints - 1]);
if(q++ > MAXLOOP) {
xpoints[npoints] = x;
ypoints[npoints++] = y;
break;
}
}
}
private int vdir;
private int hdir;
private int hcnt;
private int vcnt;
private int endx;
private int endy;
private int startx;
private int starty;
private int[] x1ca;
private int[] y1ca;
private int[] x2ca;
private int[] y2ca;
private int[] rtca;
private boolean[] exclude;
private int[] nbrac;
private int[] ebrac;
private int[] sbrac;
private int[] wbrac;
private int nnodes = -1;
private boolean changed;
private boolean reinit;
private int[][] getYCmpsCa;
private int[] getYCmpsCnt;
private int[][] getXCmpsCa;
private int[] getXCmpsCnt;
private int width;
private int oldwidth = -1;
private int height;
private int oldheight = -1;
private int[] xpoints = new int[1024];
private int[] ypoints = new int[1024];
private int[] cummlen = new int[1024];
private int npoints;
private Rectangle tmpRect = new Rectangle();
public void init() {
nnodes = -1;
}
private void stateUpdate(Component[] nodes, GraphEdge[] edges) {
changed = false;
reinit = false;
if(nnodes != nodes.length) {
nnodes = nodes.length;
x1ca = new int[nnodes];
y1ca = new int[nnodes];
x2ca = new int[nnodes];
y2ca = new int[nnodes];
rtca = new int[nnodes];
}
nbrac = new int[nnodes];
ebrac = new int[nnodes];
sbrac = new int[nnodes];
wbrac = new int[nnodes];
width = Integer.MIN_VALUE;
height = Integer.MIN_VALUE;
int chx1 = 0;
int chy1 = 0;
int chx2 = 0;
int chy2 = 0;
int chi = -1;
for(int i = 0; i < nnodes; i++) {
Component node = nodes[i];
tmpRect = node.getBounds(tmpRect);
int x1 = tmpRect.x;
int y1 = tmpRect.y;
int x2 = x1 + tmpRect.width;
int y2 = y1 + tmpRect.height;
int r = node instanceof GraphNodeComponent ? ((GraphNodeComponent)node).getRotation() : 0;
if(x1 != x1ca[i] || y1 != y1ca[i] || x2 != x2ca[i] || y2 != y2ca[i] || r != rtca[i]) {
if(!changed) {
chx1 = x1ca[i];
chy1 = y1ca[i];
chx2 = x2ca[i];
chy2 = y2ca[i];
chi = i;
changed = true;
} else reinit = true;
}
x1ca[i] = x1;
y1ca[i] = y1;
x2ca[i] = x2;
y2ca[i] = y2;
rtca[i] = r;
width = x2 > width ? x2 : width;
height = y2 > height ? y2 : height;
}
width++;
height++;
if(oldwidth != width) {
reinit = true;
oldwidth = width;
}
if(oldheight != height) {
reinit = true;
oldheight = height;
}
if(changed) {
exclude = new boolean[nnodes];
for(int j = 0; j < nnodes; j++)
for(int k = j + 1; k < nnodes; k++) {
boolean inter =
x2ca[j] >= x1ca[k] - 3 &&
x2ca[k] >= x1ca[j] - 3 &&
y2ca[j] >= y1ca[k] - 3 &&
y2ca[k] >= y1ca[j] - 3;
exclude[k] |= inter;
exclude[j] |= inter;
}
if(reinit) {
getYCmpsCnt = new int[height];
getXCmpsCnt = new int[width];
// calc array sizes
for(int i = 0; i < nnodes; i++) {
int stop = y2ca[i];
for(int k = y1ca[i] < 1 ? 1 : y1ca[i] + 1; k < stop; k++)
getYCmpsCnt[k]++;
stop = x2ca[i];
for(int k = x1ca[i] < 1 ? 1 : x1ca[i] + 1; k < stop; k++)
getXCmpsCnt[k]++;
}
// alloc arrays
getYCmpsCa = new int[height][];
for(int i = 0; i < height; i++)
if(getYCmpsCnt[i] > 0) {
getYCmpsCa[i] = new int[getYCmpsCnt[i]];
getYCmpsCnt[i] = 0;
}
getXCmpsCa = new int[width][];
for(int i = 0; i < width; i++)
if(getXCmpsCnt[i] > 0) {
getXCmpsCa[i] = new int[getXCmpsCnt[i]];
getXCmpsCnt[i] = 0;
}
// setup array
for(int i = 0; i < nnodes; i++) {
int stop = y2ca[i];
for(int k = y1ca[i] < 1 ? 1 : y1ca[i] + 1; k < stop; k++) {
getYCmpsCa[k][getYCmpsCnt[k]] = i;
getYCmpsCnt[k]++;
}
stop = x2ca[i];
for(int k = x1ca[i] < 1 ? 1 : x1ca[i] + 1; k < stop; k++) {
getXCmpsCa[k][getXCmpsCnt[k]] = i;
getXCmpsCnt[k]++;
}
}
} else {
// remove changed component
int stop = chy2;
for(int k = chy1 < 1 ? 1 : chy1 + 1; k < stop; k++) {
getYCmpsCnt[k]--;
if(getYCmpsCnt[k] == 0) {
getYCmpsCa[k] = null;
continue;
}
int[] a = getYCmpsCa[k];
int[] result = new int[getYCmpsCnt[k]];
int ri = 0;
for(int i = 0; i < a.length; i++)
if(a[i] != chi) result[ri++] = a[i];
getYCmpsCa[k] = result;
}
stop = chx2;
for(int k = chx1 < 1 ? 1 : chx1 + 1; k < stop; k++) {
getXCmpsCnt[k]--;
if(getXCmpsCnt[k] == 0) {
getXCmpsCa[k] = null;
continue;
}
int[] a = getXCmpsCa[k];
int[] result = new int[getXCmpsCnt[k]];
int ri = 0;
for(int i = 0; i < a.length; i++)
if(a[i] != chi) result[ri++] = a[i];
getXCmpsCa[k] = result;
}
// add changed component
stop = y2ca[chi];
for(int k = y1ca[chi] < 1 ? 1 : y1ca[chi] + 1; k < stop; k++) {
getYCmpsCnt[k]++;
int[] tmp = getYCmpsCa[k];
getYCmpsCa[k] = new int[getYCmpsCnt[k]];
if(getYCmpsCnt[k] > 1) System.arraycopy(tmp, 0, getYCmpsCa[k], 1, tmp.length);
getYCmpsCa[k][0] = chi;
}
stop = x2ca[chi];
for(int k = x1ca[chi] < 1 ? 1 : x1ca[chi] + 1; k < stop; k++) {
getXCmpsCnt[k]++;
int[] tmp = getXCmpsCa[k];
getXCmpsCa[k] = new int[getXCmpsCnt[k]];
if(getXCmpsCnt[k] > 1) System.arraycopy(tmp, 0, getXCmpsCa[k], 1, tmp.length);
getXCmpsCa[k][0] = chi;
}
}
}
}
public synchronized void setupEdges(GraphPanel panel, GraphEdge[] edges, Component[] nodes) {
if(edges.length == 0) return;
stateUpdate(nodes, edges);
if(changed) {
for(int i = 0; i < edges.length; i++) {
GraphEdge edge = edges[i];
hcnt = vcnt = 0;
tmpRect = edge.endcmp.getBounds(tmpRect);
int ecx1 = tmpRect.x;
int ecy1 = tmpRect.y;
int ecx2 = tmpRect.width;
int ecy2 = tmpRect.height;
tmpRect = edge.startcmp.getBounds(tmpRect);
int scx1 = tmpRect.x;
int scy1 = tmpRect.y;
int scx2 = tmpRect.width;
int scy2 = tmpRect.height;
endx = (int)(edge.endport.x * ecx2) + ecx1;
endy = (int)(edge.endport.y * ecy2) + ecy1;
startx = (int)(edge.startport.x * scx2) + scx1;
starty = (int)(edge.startport.y * scy2) + scy1;
hdir = endx > startx ? -1 : 1;
vdir = endy > starty ? -1 : 1;
Polygon hpoly = null;
Polygon vpoly = null;
npoints = 0;
xpoints[npoints] = startx;
ypoints[npoints++] = starty;
int eci = -1;
int sci = -1;
boolean endsv = false;
boolean startsv = false;
boolean endC = edge.endport.x == 0.5 && edge.endport.y == 0.5;
boolean startC = edge.startport.x == 0.5 && edge.startport.y == 0.5;
if(endC || startC) {
for(int j = 0; j < nodes.length; j++)
if(nodes[j] == edge.endcmp) {
eci = j;
break;
}
for(int j = 0; j < nodes.length; j++)
if(nodes[j] == edge.startcmp) {
sci = j;
break;
}
endsv = exclude[eci];
startsv = exclude[sci];
exclude[eci] = true;
exclude[sci] = true;
hconnect(endx, endy);
hpoly = new Polygon(xpoints, ypoints, npoints);
npoints = 1;
vconnect(endx, endy);
vpoly = new Polygon(xpoints, ypoints, npoints);
} else {
ecx2 += ecx1;
ecy2 += ecy1;
scx2 += scx1;
scy2 += scy1;
int minc = 0;
int min = startx - scx1;
int tmp = scx2 - startx;
if(tmp < min) {min = tmp; minc = 1;}
tmp = starty - scy1;
if(tmp < min) {min = tmp; minc = 2;}
tmp = scy2 - starty;
if(tmp < min) {min = tmp; minc = 3;}
switch(minc) {
case 0: xpoints[npoints] = scx1 - DINC * edge.startport.index; ypoints[npoints++] = starty; break;
case 1: xpoints[npoints] = scx2 + DINC * edge.startport.index; ypoints[npoints++] = starty; break;
case 2: xpoints[npoints] = startx; ypoints[npoints++] = scy1 - DINC * edge.startport.index; break;
case 3: xpoints[npoints] = startx; ypoints[npoints++] = scy2 + DINC * edge.startport.index; break;
}
minc = 0;
min = endx - ecx1;
tmp = ecx2 - endx;
if(tmp < min) {min = tmp; minc = 1;}
tmp = endy - ecy1;
if(tmp < min) {min = tmp; minc = 2;}
tmp = ecy2 - endy;
if(tmp < min) {min = tmp; minc = 3;}
switch(minc) {
case 0:
ecx1 -= DINC * edge.endport.index;
hconnect(ecx1, endy);
hpoly = new Polygon(xpoints, ypoints, npoints);
npoints = 2;
vconnect(ecx1, endy);
vpoly = new Polygon(xpoints, ypoints, npoints);
break;
case 1:
ecx2 += DINC * edge.endport.index;
hconnect(ecx2, endy);
hpoly = new Polygon(xpoints, ypoints, npoints);
npoints = 2;
vconnect(ecx2, endy);
vpoly = new Polygon(xpoints, ypoints, npoints);
break;
case 2:
ecy1 -= DINC * edge.endport.index;
hconnect(endx, ecy1);
hpoly = new Polygon(xpoints, ypoints, npoints);
npoints = 2;
vconnect(endx, ecy1);
vpoly = new Polygon(xpoints, ypoints, npoints);
break;
case 3:
ecy2 += DINC * edge.endport.index;
hconnect(endx, ecy2);
hpoly = new Polygon(xpoints, ypoints, npoints);
npoints = 2;
vconnect(endx, ecy2);
vpoly = new Polygon(xpoints, ypoints, npoints);
break;
}
}
edge.polyline = hpoly.npoints < vpoly.npoints ? hpoly : vpoly;
if(endC || startC) {
exclude[eci] = endsv;
exclude[sci] = startsv;
} else
edge.polyline.addPoint(endx, endy);
}
}
for(int i = 0; i < edges.length; i++) {
GraphEdge edge = edges[i];
// setup markers
if(edge.markers.length > 0) {
int ns = edge.polyline.npoints;
int[] xs = edge.polyline.xpoints;
int[] ys = edge.polyline.ypoints;
int len = 0;
for(int j = 1; j < ns; j++) {
int l =
xs[j] - xs[j - 1] +
ys[j] - ys[j - 1];
if(l < 0) l = -l;
len += l;
cummlen[j - 1] = len;
}
if(ns < 3) {
for(int j = 0; j < edge.markers.length; j++)
GraphEdgeUtils.setupMTrans(edge, j, 0, 0, 0, 0, 0);
} else {
ns--;
for(int j = 0; j < edge.markers.length; j++) {
int mp = (int)(edge.markerpos[j] * len);
int k = 0;
while(cummlen[k] < mp && k < ns) k++;
GraphEdgeUtils.setupMTrans(edge, j, Awt.getAngle(xs[k], ys[k], xs[k + 1], ys[k + 1]) + PI2,
xs[k], ys[k], xs[k + 1], ys[k + 1]);
}
}
}
}
}
}
/*
$Log: OrthogonalRouter.java,v $
Revision 1.1 2002/07/11 12:09:52 ohitz
Initial checkin
Revision 1.7 2001/03/16 18:08:20 schubige
improved orthogonal router
Revision 1.6 2001/03/11 17:59:39 schubige
Fixed various soundium and iiuf.swing.graph bugs
Revision 1.5 2001/03/09 21:24:58 schubige
Added preferences to edge editor
Revision 1.4 2001/03/09 15:30:51 schubige
Added markers to graph panel
Revision 1.3 2001/02/22 15:59:23 schubige
Worked on FileReader soundlet
Revision 1.2 2001/02/19 15:10:38 schubige
Fixed graph edge port location bug
Revision 1.1 2001/02/17 09:54:22 schubige
moved graph stuff to iiuf.swing.graph, started work on rotatable GraphNodeComponents
Revision 1.6 2001/01/04 16:28:39 schubige
Header update for 2001 and DIUF
Revision 1.5 2000/12/29 08:03:55 schubige
SourceWatch beta debug iter 1
Revision 1.4 2000/12/28 09:29:10 schubige
SourceWatch beta
Revision 1.3 2000/12/18 12:39:09 schubige
Added ports to iiuf.util.graph
Revision 1.2 2000/10/03 08:39:39 schubige
Added tree view and contect menu stuff
Revision 1.1 2000/08/17 16:22:14 schubige
Swing cleanup & TreeView added
Revision 1.1 2000/07/28 12:07:58 schubige
Graph stuff update
*/