📄 graphvisualizer.java
字号:
* Reads a graph description in DOT format from a string
*
*********************************************************
*/
public void readDOT(Reader input) {
DotParser dp = new DotParser(input, m_nodes, m_edges);
graphID = dp.parse();
setAppropriateNodeSize();
if(m_le!=null) {
m_le.setNodeSize(paddedNodeWidth, nodeHeight);
jBtLayout.setEnabled(false);
layoutGraph();
}
}
/**
* The panel which contains the actual graph.
*
*/
private class GraphPanel extends JPanel {
//Image buf;
public GraphPanel() {
this.addMouseListener( new GraphVisualizerMouseListener() );
this.addMouseMotionListener( new GraphVisualizerMouseMotionListener() );
this.setToolTipText("");
}
public String getToolTipText(MouseEvent me) {
int x, y, nx, ny;
Rectangle r;
GraphNode n;
Dimension d = m_gp.getPreferredSize();
//System.out.println("Preferred Size: "+this.getPreferredSize()+" Actual Size: "+this.getSize());
x=y=nx=ny=0;
if(d.width < m_gp.getWidth())
nx = (int)((nx + m_gp.getWidth()/2 - d.width/2)/scale);
if(d.height < m_gp.getHeight())
ny = (int)((ny + m_gp.getHeight()/2 - d.height/2)/scale);
r=new Rectangle(0, 0, (int)(paddedNodeWidth*scale), (int)(nodeHeight*scale));
x += me.getX(); y += me.getY();
int i;
for(i=0; i<m_nodes.size(); i++) {
n = (GraphNode) m_nodes.elementAt(i);
if(n.nodeType!=NORMAL)
return null;
r.x = (int)((nx+n.x)*scale); r.y = (int)((ny+n.y)*scale);
if(r.contains(x,y)) {
if(n.probs==null)
return n.lbl;
else
return n.lbl+" (click to view the probability dist. table)";
}
}
return null;
}
public void paintComponent(Graphics gr) {
Graphics2D g = (Graphics2D)gr;
RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
g.setRenderingHints(rh);
g.scale(scale, scale);
Rectangle r = g.getClipBounds();
g.clearRect(r.x,r.y,r.width,r.height);
//g.setColor(this.getBackground());
//g.fillRect(0, 0, width+5, height+5);
int x=0, y=0;
Dimension d = this.getPreferredSize();
//System.out.println("Preferred Size: "+this.getPreferredSize()+" Actual Size: "+this.getSize());
//initializing x & y to display the graph in the middle
//if the display area is larger than the graph
if(d.width < this.getWidth())
x = (int)((x + this.getWidth()/2 - d.width/2)/scale);
if(d.height < this.getHeight())
y = (int)((y + this.getHeight()/2 - d.height/2)/scale);
for(int index=0; index<m_nodes.size(); index++) {
GraphNode n = (GraphNode) m_nodes.elementAt(index);
if( n.nodeType==NORMAL) {
g.setColor( this.getBackground().darker().darker() );
g.fillOval(x+n.x+paddedNodeWidth-nodeWidth-(paddedNodeWidth-nodeWidth)/2, y+n.y,
nodeWidth, nodeHeight);
g.setColor(Color.white);
//g.setColor(Color.black);
//System.out.println("drawing "+((GraphNode)m_nodes.elementAt(index)).ID+" at "+
// " x: "+(x+n.x+paddedNodeWidth/2-fm.stringWidth( ((GraphNode)m_nodes.elementAt(index)).ID )/2)+
// " y: "+(y+n.y+nodeHeight/2+fm.getHeight()/2-2) );
//Draw the node's label if it can fit inside the node's current
// width otherwise display its ID or otherwise just display its
// idx in the FastVector (to distinguish it from others)
// if any can fit in node's current width
if(fm.stringWidth(n.lbl)<=nodeWidth)
g.drawString( n.lbl,
x+n.x+paddedNodeWidth/2
-fm.stringWidth( n.lbl )/2,
y+n.y+nodeHeight/2+fm.getHeight()/2-2 );
else if(fm.stringWidth(n.ID)<=nodeWidth)
g.drawString( n.ID,
x+n.x+paddedNodeWidth/2
-fm.stringWidth( n.ID )/2,
y+n.y+nodeHeight/2+fm.getHeight()/2-2 );
else if(fm.stringWidth( Integer.toString(index) )<=nodeWidth)
g.drawString( Integer.toString(index),
x+n.x+paddedNodeWidth/2
-fm.stringWidth( Integer.toString(index) )/2,
y+n.y+nodeHeight/2+fm.getHeight()/2-2 );
g.setColor(Color.black);
}
else {
//g.draw( new java.awt.geom.QuadCurve2D.Double(n.x+paddedNodeWidth/2, n.y,
// n.x+paddedNodeWidth-nodeSize
// -(paddedNodeWidth-nodeSize)/2, n.y+nodeHeight/2,
// n.x+paddedNodeWidth/2, n.y+nodeHeight) );
g.drawLine(x+n.x+paddedNodeWidth/2, y+n.y, x+n.x+paddedNodeWidth/2, y+n.y+nodeHeight);
}
GraphNode n2;
int x1, y1, x2, y2;
//System.out.println("Drawing edges of "+n.lbl);
//Drawing all the edges coming out from the node, including reversed and double ones
if(n.edges!=null)
for(int k=0; k<n.edges.length; k++) {
if(n.edges[k][1]>0) {
n2 = (GraphNode) m_nodes.elementAt(n.edges[k][0]); //m_nodes.elementAt(k);
//System.out.println(" -->to "+n2.lbl);
x1=n.x+paddedNodeWidth/2; y1=n.y+nodeHeight;
x2=n2.x+paddedNodeWidth/2; y2=n2.y;
g.drawLine(x+x1, y+y1, x+x2, y+y2);
if(n.edges[k][1]==DIRECTED) {
if(n2.nodeType==n2.NORMAL)
drawArrow(g, x+x1, y+y1, x+x2, y+y2);
}
else if(n.edges[k][1]==REVERSED) {
if(n.nodeType==NORMAL)
drawArrow(g, x+x2, y+y2, x+x1, y+y1);
}
else if(n.edges[k][1]==DOUBLE) {
if(n.nodeType==NORMAL)
drawArrow(g, x+x2, y+y2, x+x1, y+y1);
if(n2.nodeType==NORMAL)
drawArrow(g, x+x1, y+y1, x+x2, y+y2);
}
}
}
}
}
/**
This method draws an arrow on a line from (x1,y1)
to (x2,y2). The arrow head is seated on (x2,y2) and
is in the direction of the line.
If the arrow is needed to be drawn in the opposite
direction then simply swap the order of (x1, y1)
and (x2, y2) when calling this function.
*/
protected void drawArrow(Graphics g, int x1, int y1, int x2, int y2) {
if(x1==x2) {
if(y1<y2) {
g.drawLine(x2, y2, x2+4, y2-8);
g.drawLine(x2, y2, x2-4, y2-8);
}
else {
g.drawLine(x2, y2, x2+4, y2+8);
g.drawLine(x2, y2, x2-4, y2+8);
}
}
else {
//theta=line's angle from base, beta=angle of arrow's side from line
double hyp=0, base=0, perp=0, theta, beta;
int x3=0, y3=0;
if(x2<x1) {
base = x1-x2; hyp = Math.sqrt( (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) );
theta = Math.acos( base/hyp );
}
else { //x1>x2 as we already checked x1==x2 before
base = x1-x2; hyp = Math.sqrt( (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) );
theta = Math.acos( base/hyp );
}
beta = 30*Math.PI/180;
//System.out.println("Original base "+base+" perp "+perp+" hyp "+hyp+"\ntheta "+theta+" beta "+beta);
hyp = 8;
base = Math.cos(theta-beta)*hyp;
perp = Math.sin(theta-beta)*hyp;
x3 = (int)(x2+base);
if(y1<y2)
y3 = (int)(y2-perp);
else
y3 = (int)(y2+perp);
//System.out.println("Drawing 1 from "+x2+","+y2+" to "+x3+","+y3+" x1,y1 is "+x1+","+y1+" base "+base+
// " perp "+perp+" cos(theta-beta) "+Math.cos(theta-beta));
g.drawLine(x2, y2, x3, y3);
base = Math.cos(theta+beta)*hyp;
perp = Math.sin(theta+beta)*hyp;
x3 = (int)(x2+base);
if(y1<y2)
y3 = (int)(y2-perp);
else
y3 = (int)(y2+perp);
//System.out.println("Drawing 2 from "+x2+","+y2+" to "+x3+","+y3+" x1,y1 is "+x1+","+y1+" base "+base+
// " perp "+perp);
g.drawLine(x2, y2, x3, y3);
}
}
/**
This method highlights a given node and all its children
and the edges coming out of it.
*/
public void highLight(GraphNode n) {
Graphics2D g = (Graphics2D) this.getGraphics();
RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
g.setRenderingHints(rh);
g.setPaintMode();
g.scale(scale, scale);
int x=0, y=0;
Dimension d = this.getPreferredSize();
//System.out.println("Preferred Size: "+this.getPreferredSize()+" Actual Size: "+this.getSize());
//initializing x & y to display the graph in the middle
//if the display area is larger than the graph
if(d.width < this.getWidth())
x = (int)((x + this.getWidth()/2 - d.width/2)/scale);
if(d.height < this.getHeight())
y = (int)((y + this.getHeight()/2 - d.height/2)/scale);
//if the node is of type NORMAL only then highlight
if(n.nodeType==NORMAL) {
g.setXORMode(Color.green); //g.setColor(Color.green);
g.fillOval(x+n.x+paddedNodeWidth-nodeWidth-(paddedNodeWidth-nodeWidth)/2, y+n.y, nodeWidth, nodeHeight);
g.setXORMode(Color.red);
//Draw the node's label if it can fit inside the node's current
// width otherwise display its ID or otherwise just display its
// idx in the FastVector (to distinguish it from others)
// if any can fit in node's current width
if(fm.stringWidth(n.lbl)<=nodeWidth)
g.drawString( n.lbl,
x+n.x+paddedNodeWidth/2
-fm.stringWidth( n.lbl )/2,
y+n.y+nodeHeight/2+fm.getHeight()/2-2 );
else if(fm.stringWidth(n.ID)<=nodeWidth)
g.drawString( n.ID,
x+n.x+paddedNodeWidth/2
-fm.stringWidth( n.ID )/2,
y+n.y+nodeHeight/2+fm.getHeight()/2-2 );
else if(fm.stringWidth( Integer.toString(m_nodes.indexOf(n)) )<=nodeWidth)
g.drawString( Integer.toString(m_nodes.indexOf(n)),
x+n.x+paddedNodeWidth/2
-fm.stringWidth( Integer.toString(m_nodes.indexOf(n)) )/2,
y+n.y+nodeHeight/2+fm.getHeight()/2-2 );
g.setXORMode(Color.green);
GraphNode n2;
int x1, y1, x2, y2;
//System.out.println("Drawing edges of "+n.lbl);
if(n.edges!=null)
for(int k=0; k<n.edges.length; k++) { //Drawing all the edges from and upward ones coming to the node
if(n.edges[k][1]==DIRECTED || n.edges[k][1]==DOUBLE) {
n2 = (GraphNode) m_nodes.elementAt(n.edges[k][0]); //m_nodes.elementAt(k);
//System.out.println(" -->to "+n2.lbl);
x1=n.x+paddedNodeWidth/2; y1=n.y+nodeHeight;
x2=n2.x+paddedNodeWidth/2; y2=n2.y;
g.drawLine(x+x1, y+y1, x+x2, y+y2);
if(n.edges[k][1]==DIRECTED) {
if(n2.nodeType==n2.NORMAL) //!n2.dummy)
drawArrow(g, x+x1, y+y1, x+x2, y+y2);
}
else if(n.edges[k][1]==DOUBLE) {
if(n.nodeType==NORMAL) //!n.dummy)
drawArrow(g, x+x2, y+y2, x+x1, y+y1);
if(n2.nodeType==NORMAL) //!n2.dummy)
drawArrow(g, x+x1, y+y1, x+x2, y+y2);
}
if(n2.nodeType==NORMAL)
g.fillOval(x+n2.x+paddedNodeWidth-nodeWidth-(paddedNodeWidth-nodeWidth)/2, y+n2.y, nodeWidth, nodeHeight);
//If n2 is not of NORMAL type
// then carry on drawing all the edges and add all the
// dummy nodes encountered in a Vector until no
// more dummy nodes are found and all the child nodes(node n2)
// are of type normal
java.util.Vector t = new java.util.Vector();
while(n2.nodeType!=NORMAL || t.size()>0) { //n2.dummy==true) {
//System.out.println("in while processing "+n2.ID);
if(t.size()>0)
{ n2 = (GraphNode)t.elementAt(0);
t.removeElementAt(0); }
if(n2.nodeType!=NORMAL) {
g.drawLine(x+n2.x+paddedNodeWidth/2, y+n2.y, x+n2.x+paddedNodeWidth/2, y+n2.y+nodeHeight);
x1=n2.x+paddedNodeWidth/2; y1=n2.y+nodeHeight;
//System.out.println("Drawing from "+n2.lbl);
for(int m=0; m<n2.edges.length; m++) {
//System.out.println(" to "+n2.lbl+", "+graphMatrix[tmpIndex][m]);
if(n2.edges[m][1]>0) {
GraphNode n3 = (GraphNode) m_nodes.elementAt(n2.edges[m][0]); //m_nodes.elementAt(m);
g.drawLine(x+x1, y+y1, x+n3.x+paddedNodeWidth/2, y+n3.y);
if(n3.nodeType==NORMAL){ //!n2.dummy)
g.fillOval(x+n3.x+paddedNodeWidth-nodeWidth-(paddedNodeWidth-nodeWidth)/2, y+n3.y, nodeWidth, nodeHeight);
drawArrow(g, x+x1, y+y1, x+n3.x+paddedNodeWidth/2, y+n3.y);
}
//if(n3.nodeType!=n3.NORMAL)
t.addElement(n3);
//break;
}
}
}
}
}
else if(n.edges[k][1]==-REVERSED || n.edges[k][1]==-DOUBLE) {
//Drawing all the reversed and double edges which are going
//upwards in the drawing.
n2 = (GraphNode) m_nodes.elementAt(n.edges[k][0]); //m_nodes.elementAt(k);
//System.out.println(" -->to "+n2.lbl);
x1=n.x+paddedNodeWidth/2; y1=n.y;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -