[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Graph.class



Hi Dan

> Sounds good -- can't see it in Lynx browser so won't be able to check it
> out till next week. Can I see your tweaked code?

Sure, I'll attach those two files (RDFGraph.java and Node.java) here.

Janne
--
Janne Saarela           | Visiting scientist
Email: jsaarela@w3.org  | World Wide Web Consortium http://www.w3.org/
/**
 * Node class holds corresponding graphical positions
 */

import java.awt.*;
import java.util.Vector;

public class Node
{
  private String	m_sName = null;
  private Point 	m_point = null;
  private Vector	m_nextNodes = new Vector();
  private Color		m_color = Color.black;

  static public boolean	GENIDS = false;
  static public boolean	SHORTNAMES = true;

  public static final Color DEFAULT_COLOR = Color.black;
  public static final Color SELECTED_COLOR = Color.blue;

  double dx;
  double dy;

  boolean fixed;

  Node (String sName) {
    m_sName = sName;
    m_point = new Point (0,0);
  }

  Node (String sName, Point p) {
    m_sName = sName;
    m_point = p;
  }

  public void setPoint (int x, int y) {
    m_point.move (x, y);
  }

  public void setPoint (Point p) {
    m_point = p;
  }

  public void addNextNode (Node n) {
    m_nextNodes.addElement (n);
  }

  public Vector nextNodes () {
    return m_nextNodes;
  }

  public String visualName() {
      if (!GENIDS &&
	  m_sName.startsWith ("genid"))
	  return new String ();
      if (SHORTNAMES) {
	  int iHashIndex = m_sName.lastIndexOf('#');
	  if (iHashIndex > -1)
	      return m_sName.substring (iHashIndex+1);
	  else
	      return m_sName;
      } else {
	  return m_sName;
      }
  }

  public String name() {
      return m_sName;
  }

  public void name(String sName) {
      m_sName = sName;
  }

  public int x () {
    return m_point.x;
  }

  public void x (int x) {
      m_point.x = x;
  }

  public int y () {
    return m_point.y;
  }

  public void y (int y) {
      m_point.y = y;
  }

  public Color color () {
    return m_color;
  }

  public void setColor (Color c) {
    m_color = c;
  }

  public double distance (Point p) {
      int dx = m_point.x-p.x;
      int dy = m_point.y-p.y;
      return dx*dx + dy*dy;
  }
}

/*
 * Copyright (c) 1994-1996 Sun Microsystems, Inc. All Rights Reserved.
 * @modified 96/04/24 Jim Hagen : changed stressColor
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and
 * without fee is hereby granted.
 * Please refer to the file http://java.sun.com/copy_trademarks.html
 * for further important copyright and trademark information and to
 * http://java.sun.com/licensing.html for further important licensing
 * information for the Java (tm) Technology.
 *
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 *
 * THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE
 * CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE
 * PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT
 * NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE
 * SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
 * SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE
 * PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES").  SUN
 * SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR
 * HIGH RISK ACTIVITIES.
 */

import java.io.*;
import java.util.*;
import java.awt.*;
import java.applet.Applet;

class Edge {
    int from;
    int to;

    String lbl;
    double len;
}

class GraphPanel extends Panel implements Runnable {
    RDFGraph graph;
    int nnodes;
    Node nodes[] = new Node[500];

    int nedges;
    Edge edges[] = new Edge[100];

    Thread relaxer;
    boolean genids = false;
    boolean shortnames = true;

    GraphPanel(RDFGraph graph) {
	this.graph = graph;
    }

    int findNode(String lbl) {
	for (int i = 0 ; i < nnodes ; i++) {
	    String sName = nodes[i].name();
	    if (sName != null && sName.equals(lbl)) {
		return i;
	    }
	}
	return addNode(lbl);
    }
    int addNode(String lbl) {
	Node n = new Node(lbl);
	n.x(10 + (int)(380*Math.random()));
	n.y(10 + (int)(380*Math.random()));
	nodes[nnodes] = n;
	return nnodes++;
    }
    void addEdge(String lbl, String from, String to, int len) {
	Edge e = new Edge();
        e.lbl = lbl;
	e.from = findNode(from);
	e.to = findNode(to);
	e.len = len;
	edges[nedges++] = e;
    }

    public void run() {
	while (true) {
	    relax();
	    try {
		Thread.sleep(100);
	    } catch (InterruptedException e) {
		break;
	    }
	}
    }

    synchronized void relax() {
	for (int i = 0 ; i < nedges ; i++) {
	    Edge e = edges[i];
	    double vx = nodes[e.to].x() - nodes[e.from].x();
	    double vy = nodes[e.to].y() - nodes[e.from].y();
	    double len = Math.sqrt(vx * vx + vy * vy);
	    double f = (edges[i].len - len) / (len * 3) ;
	    double dx = f * vx;
	    double dy = f * vy;

	    nodes[e.to].dx += dx;
	    nodes[e.to].dy += dy;
	    nodes[e.from].dx += -dx;
	    nodes[e.from].dy += -dy;
	}

	for (int i = 0 ; i < nnodes ; i++) {
	    Node n1 = nodes[i];
	    double dx = 0;
	    double dy = 0;

	    for (int j = 0 ; j < nnodes ; j++) {
		if (i == j) {
		    continue;
		}
		Node n2 = nodes[j];
		double vx = n1.x() - n2.x();
		double vy = n1.y() - n2.y();
		double len = vx * vx + vy * vy;
		if (len == 0) {
		    dx += Math.random();
		    dy += Math.random();
		} else if (len < 100*100) {
		    dx += vx / len;
		    dy += vy / len;
		}
	    }
	    double dlen = dx * dx + dy * dy;
	    if (dlen > 0) {
		dlen = Math.sqrt(dlen) / 2;
		n1.dx += dx / dlen;
		n1.dy += dy / dlen;
	    }
	}

	Dimension d = size();
	for (int i = 0 ; i < nnodes ; i++) {
	    Node n = nodes[i];
	    if (!n.fixed) {
		n.x (n.x() + (int)(Math.max(-5, Math.min(5, n.dx))));
		n.y (n.y() + (int)(Math.max(-5, Math.min(5, n.dy))));
		if (n.x() < 0) {
		    n.x(0);
		} else if (n.x() > d.width) {
		    n.x(d.width);
		}
		if (n.y() < 0) {
		    n.y(0);
		} else if (n.y() > d.height) {
		    n.y(d.height);
		}
	    }
	    n.dx /= 2;
	    n.dy /= 2;
	}
	repaint();
    }

    Node pick;
    boolean pickfixed;
    Image offscreen;
    Dimension offscreensize;
    Graphics offgraphics;
    
    Font labelFont = new Font ("Helvetica", Font.PLAIN, 10);
    Font nodeFont = new Font ("Helvetica", Font.PLAIN, 12);

    final Color fixedColor = Color.red;
    final Color selectColor = Color.pink;
    final Color edgeColor = Color.black;
    final Color nodeColor = new Color(250, 220, 100);
    final Color arcColor1 = Color.black;
    final Color arcColor2 = Color.pink;
    final Color arcColor3 = Color.red;

    public void paintNode(Graphics g, Node n, FontMetrics fm) {
	int x = (int)n.x();
	int y = (int)n.y();
	if (n.name() == null)
	    n.name(new String ());

	g.setColor(Color.green);
	g.fillOval (x - 3, y - 3, 6, 6);

	if (!genids && n.name().startsWith ("genid"))
	    return;
	String sNodeName = n.name();
	if (shortnames) {
	    int iIndex = sNodeName.indexOf ('#');
	    if (iIndex > 0)
		sNodeName = sNodeName.substring (iIndex+1);
	}

	int w = fm.stringWidth(sNodeName) + 10;
	int h = fm.getHeight() + 4;
	g.setColor(Color.black);

	g.setFont (nodeFont);
	g.drawString(sNodeName,
		     x - (w-10)/2 + 5,
		     (y - (h-4)/2) + fm.getAscent() + 15);
    }

    public synchronized void update(Graphics g) {
	Dimension d = size();
	if ((offscreen == null) || (d.width != offscreensize.width) || (d.height != offscreensize.height)) {
	    offscreen = createImage(d.width, d.height);
	    offscreensize = d;
	    offgraphics = offscreen.getGraphics();
	    offgraphics.setFont(getFont());
	}

	offgraphics.setColor(getBackground());
	offgraphics.fillRect(0, 0, d.width, d.height);
	for (int i = 0 ; i < nedges ; i++) {
	    Edge e = edges[i];
	    int x1 = (int)nodes[e.from].x();
	    int y1 = (int)nodes[e.from].y();
	    int x2 = (int)nodes[e.to].x();
	    int y2 = (int)nodes[e.to].y();

	    offgraphics.setColor(arcColor1);
	    offgraphics.drawLine(x1, y1, x2, y2);

	    String sLabelName = e.lbl;
	    if (shortnames) {
		int iIndex = sLabelName.indexOf ('#');
		if (iIndex > 0)
		    sLabelName = sLabelName.substring (iIndex+1);
	    }

	    int w = offgraphics.getFontMetrics().stringWidth(sLabelName) + 10;
	    g.setFont (labelFont);
	    offgraphics.drawString (sLabelName,
				    (x1+x2)/2 - w/2 + 3,
				    (y1+y2)/2 + 3);
	    /**
	     * Try to make the line look like an arrow
	     */
	    int dY = y2 - y1;
	    int dX = x2 - x1;
	    double dLength = Math.sqrt (dX*dX + dY*dY);
	    double dP = 10.0;
	    double dQ = 0.6;
	    
	    int iX1 = (int)(x2 - (dX + dQ * dY) * dP/dLength);
	    int iY1 = (int)(y2 - (dY - dQ * dX) * dP/dLength);
	    
	    int iX2 = (int)(x2 - (dX - dQ * dY) * dP/dLength);
	    int iY2 = (int)(y2 - (dY + dQ * dX) * dP/dLength);
	    
	    offgraphics.drawLine (x2, y2, iX1, iY1);
	    offgraphics.drawLine (x2, y2, iX2, iY2);
	}

	FontMetrics fm = offgraphics.getFontMetrics();
	for (int i = 0 ; i < nnodes ; i++) {
	    paintNode(offgraphics, nodes[i], fm);
	}

	g.drawImage(offscreen, 0, 0, null);
    }

    public synchronized boolean mouseDown(Event evt, int x, int y) {
	double bestdist = Double.MAX_VALUE;
	for (int i = 0 ; i < nnodes ; i++) {
	    Node n = nodes[i];
	    double dist = (n.x() - x) * (n.x() - x) + (n.y() - y) * (n.y() - y);
	    if (dist < bestdist) {
		pick = n;
		bestdist = dist;
	    }
	}
	pickfixed = pick.fixed;
	pick.fixed = true;
	pick.x(x);
	pick.y(y);
	repaint();
	return true;
    }

    public synchronized boolean mouseDrag(Event evt, int x, int y) {
	pick.x(x);
	pick.y(y);
	repaint();
	return true;
    }

    public synchronized boolean mouseUp(Event evt, int x, int y) {
	pick.x(x);
	pick.y(y);
	pick.fixed = pickfixed;
	pick = null;
	
	repaint();
	return true;
    }

    public void start() {
	relaxer = new Thread(this);
	relaxer.start();
    }
    public void stop() {
	relaxer.stop();
    }
}

public class RDFGraph extends Applet {
    GraphPanel panel;
    private Checkbox	m_genidsCB = new Checkbox ("Show generated IDs");
    private Checkbox	m_shortnamesCB = new Checkbox ("Element names only");

    public static final String VERSION = "$Id: RDFGraph.java,v 1.1 1998/08/14 08:46:14 jsaarela Exp $";

    public String getAppletInfo() {
	String info = "This is RDF visualization applet version "+VERSION+
	    "\nby Janne Saarela <jsaarela@w3.org>. You can use it to\n" +
	    "interactively browse and modify RDF data models\n" +
	    "with some simple menu commands. "+
	    "This applet is based on the Graph demo applet from JavaSoft " +
	    "shipped with the JDK 1.0.2 distribution";
	return info;
    }

    public void init() {
	setLayout(new BorderLayout());

	panel = new GraphPanel(this);
	add("Center", panel);
	Panel p = new Panel();
	add("South", p);
	p.add(new Button("Scramble"));
	p.add(m_genidsCB);
	m_shortnamesCB.setState (true);
	p.add(m_shortnamesCB);

	String edges = getParameter("triples");

	Dimension dim = size();

	try {
	    StreamTokenizer st = new StreamTokenizer (new StringBufferInputStream (edges));
	    st.whitespaceChars(',', ',');
	    
	    int token1, token2, token3, token4, token5, token6, token7;
	    while ((token1 = st.nextToken()) != StreamTokenizer.TT_EOF) {
		token2 = st.nextToken ();
		token3 = st.nextToken ();
		String sProperty = st.sval;
		token4 = st.nextToken ();
		String sPropertyObject = st.sval;
		token5 = st.nextToken ();
		String sPropertyValue = st.sval;
		token6 = st.nextToken ();
		token7 = st.nextToken ();

		panel.addEdge(sProperty, sPropertyObject, sPropertyValue,
			      70);
	    }
	} catch (Exception e) {
	    e.printStackTrace();
	}
	Dimension d = size();
	String center = getParameter("center");
	if (center != null){
	    Node n = panel.nodes[panel.findNode(center)];
	    n.x(d.width / 2);
	    n.y(d.height / 2);
	    n.fixed = true;
	}
    }

    public void start() {
	panel.start();
    }
    public void stop() {
	panel.stop();
    }
    public boolean action(Event evt, Object arg) {
	if (arg instanceof Boolean) {
	    if (evt.target == m_genidsCB) {
		panel.genids = ((Boolean)arg).booleanValue();
	    } else if (evt.target == m_shortnamesCB) {
		panel.shortnames = ((Boolean)arg).booleanValue();
	    }
	    return true;
	} 
	if ("Scramble".equals(arg)) {
	    Dimension d = size();
	    for (int i = 0 ; i < panel.nnodes ; i++) {
		Node n = panel.nodes[i];
		if (!n.fixed) {
		    n.x(10 + (int)((d.width-20)*Math.random()));
		    n.y(10 + (int)((d.height-20)*Math.random()));
		}
	    }
	    return true;
	}
	return false;
    }
}