home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / JBuilder8.iso / Solaris / resource / jre / demo / plugin / applets / GraphLayout / Graph.java < prev    next >
Encoding:
Java Source  |  2002-09-06  |  11.3 KB  |  438 lines

  1. /*
  2.  * Copyright (c) 2002 Sun Microsystems, Inc. All  Rights Reserved.
  3.  * 
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions
  6.  * are met:
  7.  * 
  8.  * -Redistributions of source code must retain the above copyright
  9.  *  notice, this list of conditions and the following disclaimer.
  10.  * 
  11.  * -Redistribution in binary form must reproduct the above copyright
  12.  *  notice, this list of conditions and the following disclaimer in
  13.  *  the documentation and/or other materials provided with the distribution.
  14.  * 
  15.  * Neither the name of Sun Microsystems, Inc. or the names of contributors
  16.  * may be used to endorse or promote products derived from this software
  17.  * without specific prior written permission.
  18.  * 
  19.  * This software is provided "AS IS," without a warranty of any kind. ALL
  20.  * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
  21.  * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
  22.  * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT
  23.  * BE LIABLE FOR ANY DAMAGES OR LIABILITIES SUFFERED BY LICENSEE AS A RESULT
  24.  * OF OR RELATING TO USE, MODIFICATION OR DISTRIBUTION OF THE SOFTWARE OR ITS
  25.  * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
  26.  * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
  27.  * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
  28.  * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE SOFTWARE, EVEN
  29.  * IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
  30.  * 
  31.  * You acknowledge that Software is not designed, licensed or intended for
  32.  * use in the design, construction, operation or maintenance of any nuclear
  33.  * facility.
  34.  */
  35.  
  36. /*
  37.  * @(#)Graph.java    1.12 02/06/13
  38.  */
  39.  
  40. import java.util.*;
  41. import java.awt.*;
  42. import java.applet.Applet;
  43. import java.awt.event.*;
  44.  
  45.  
  46. class Node {
  47.     double x;
  48.     double y;
  49.  
  50.     double dx;
  51.     double dy;
  52.  
  53.     boolean fixed;
  54.  
  55.     String lbl;
  56. }
  57.  
  58.  
  59. class Edge {
  60.     int from;
  61.     int to;
  62.  
  63.     double len;
  64. }
  65.  
  66.  
  67. class GraphPanel extends Panel
  68.     implements Runnable, MouseListener, MouseMotionListener {
  69.     Graph graph;
  70.     int nnodes;
  71.     Node nodes[] = new Node[100];
  72.  
  73.     int nedges;
  74.     Edge edges[] = new Edge[200];
  75.  
  76.     Thread relaxer;
  77.     boolean stress;
  78.     boolean random;
  79.  
  80.     GraphPanel(Graph graph) {
  81.     this.graph = graph;
  82.     addMouseListener(this);
  83.     }
  84.  
  85.     int findNode(String lbl) {
  86.     for (int i = 0 ; i < nnodes ; i++) {
  87.         if (nodes[i].lbl.equals(lbl)) {
  88.         return i;
  89.         }
  90.     }
  91.     return addNode(lbl);
  92.     }
  93.     int addNode(String lbl) {
  94.     Node n = new Node();
  95.     n.x = 10 + 380*Math.random();
  96.     n.y = 10 + 380*Math.random();
  97.     n.lbl = lbl;
  98.     nodes[nnodes] = n;
  99.     return nnodes++;
  100.     }
  101.     void addEdge(String from, String to, int len) {
  102.     Edge e = new Edge();
  103.     e.from = findNode(from);
  104.     e.to = findNode(to);
  105.     e.len = len;
  106.     edges[nedges++] = e;
  107.     }
  108.  
  109.     public void run() {
  110.         Thread me = Thread.currentThread();
  111.     while (relaxer == me) {
  112.         relax();
  113.         if (random && (Math.random() < 0.03)) {
  114.         Node n = nodes[(int)(Math.random() * nnodes)];
  115.         if (!n.fixed) {
  116.             n.x += 100*Math.random() - 50;
  117.             n.y += 100*Math.random() - 50;
  118.         }
  119.         graph.play(graph.getCodeBase(), "audio/drip.au");
  120.         }
  121.         try {
  122.         Thread.sleep(100);
  123.         } catch (InterruptedException e) {
  124.         break;
  125.         }
  126.     }
  127.     }
  128.  
  129.     synchronized void relax() {
  130.     for (int i = 0 ; i < nedges ; i++) {
  131.         Edge e = edges[i];
  132.         double vx = nodes[e.to].x - nodes[e.from].x;
  133.         double vy = nodes[e.to].y - nodes[e.from].y;
  134.         double len = Math.sqrt(vx * vx + vy * vy);
  135.             len = (len == 0) ? .0001 : len;
  136.         double f = (edges[i].len - len) / (len * 3);
  137.         double dx = f * vx;
  138.         double dy = f * vy;
  139.  
  140.         nodes[e.to].dx += dx;
  141.         nodes[e.to].dy += dy;
  142.         nodes[e.from].dx += -dx;
  143.         nodes[e.from].dy += -dy;
  144.     }
  145.  
  146.     for (int i = 0 ; i < nnodes ; i++) {
  147.         Node n1 = nodes[i];
  148.         double dx = 0;
  149.         double dy = 0;
  150.  
  151.         for (int j = 0 ; j < nnodes ; j++) {
  152.         if (i == j) {
  153.             continue;
  154.         }
  155.         Node n2 = nodes[j];
  156.         double vx = n1.x - n2.x;
  157.         double vy = n1.y - n2.y;
  158.         double len = vx * vx + vy * vy;
  159.         if (len == 0) {
  160.             dx += Math.random();
  161.             dy += Math.random();
  162.         } else if (len < 100*100) {
  163.             dx += vx / len;
  164.             dy += vy / len;
  165.         }
  166.         }
  167.         double dlen = dx * dx + dy * dy;
  168.         if (dlen > 0) {
  169.         dlen = Math.sqrt(dlen) / 2;
  170.         n1.dx += dx / dlen;
  171.         n1.dy += dy / dlen;
  172.         }
  173.     }
  174.  
  175.     Dimension d = getSize();
  176.     for (int i = 0 ; i < nnodes ; i++) {
  177.         Node n = nodes[i];
  178.         if (!n.fixed) {
  179.         n.x += Math.max(-5, Math.min(5, n.dx));
  180.         n.y += Math.max(-5, Math.min(5, n.dy));
  181.             }
  182.             if (n.x < 0) {
  183.                 n.x = 0;
  184.             } else if (n.x > d.width) {
  185.                 n.x = d.width;
  186.             }
  187.             if (n.y < 0) {
  188.                 n.y = 0;
  189.             } else if (n.y > d.height) {
  190.                 n.y = d.height;
  191.             }
  192.         n.dx /= 2;
  193.         n.dy /= 2;
  194.     }
  195.     repaint();
  196.     }
  197.  
  198.     Node pick;
  199.     boolean pickfixed;
  200.     Image offscreen;
  201.     Dimension offscreensize;
  202.     Graphics offgraphics;
  203.  
  204.     final Color fixedColor = Color.red;
  205.     final Color selectColor = Color.pink;
  206.     final Color edgeColor = Color.black;
  207.     final Color nodeColor = new Color(250, 220, 100);
  208.     final Color stressColor = Color.darkGray;
  209.     final Color arcColor1 = Color.black;
  210.     final Color arcColor2 = Color.pink;
  211.     final Color arcColor3 = Color.red;
  212.  
  213.     public void paintNode(Graphics g, Node n, FontMetrics fm) {
  214.     int x = (int)n.x;
  215.     int y = (int)n.y;
  216.     g.setColor((n == pick) ? selectColor : (n.fixed ? fixedColor : nodeColor));
  217.     int w = fm.stringWidth(n.lbl) + 10;
  218.     int h = fm.getHeight() + 4;
  219.     g.fillRect(x - w/2, y - h / 2, w, h);
  220.     g.setColor(Color.black);
  221.     g.drawRect(x - w/2, y - h / 2, w-1, h-1);
  222.     g.drawString(n.lbl, x - (w-10)/2, (y - (h-4)/2) + fm.getAscent());
  223.     }
  224.  
  225.     public synchronized void update(Graphics g) {
  226.     Dimension d = getSize();
  227.     if ((offscreen == null) || (d.width != offscreensize.width) || (d.height != offscreensize.height)) {
  228.         offscreen = createImage(d.width, d.height);
  229.         offscreensize = d;
  230.         if (offgraphics != null) {
  231.             offgraphics.dispose();
  232.         }
  233.         offgraphics = offscreen.getGraphics();
  234.         offgraphics.setFont(getFont());
  235.     }
  236.  
  237.     offgraphics.setColor(getBackground());
  238.     offgraphics.fillRect(0, 0, d.width, d.height);
  239.     for (int i = 0 ; i < nedges ; i++) {
  240.         Edge e = edges[i];
  241.         int x1 = (int)nodes[e.from].x;
  242.         int y1 = (int)nodes[e.from].y;
  243.         int x2 = (int)nodes[e.to].x;
  244.         int y2 = (int)nodes[e.to].y;
  245.         int len = (int)Math.abs(Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)) - e.len);
  246.         offgraphics.setColor((len < 10) ? arcColor1 : (len < 20 ? arcColor2 : arcColor3)) ;
  247.         offgraphics.drawLine(x1, y1, x2, y2);
  248.         if (stress) {
  249.         String lbl = String.valueOf(len);
  250.         offgraphics.setColor(stressColor);
  251.         offgraphics.drawString(lbl, x1 + (x2-x1)/2, y1 + (y2-y1)/2);
  252.         offgraphics.setColor(edgeColor);
  253.         }
  254.     }
  255.  
  256.     FontMetrics fm = offgraphics.getFontMetrics();
  257.     for (int i = 0 ; i < nnodes ; i++) {
  258.         paintNode(offgraphics, nodes[i], fm);
  259.     }
  260.     g.drawImage(offscreen, 0, 0, null);
  261.     }
  262.  
  263.     //1.1 event handling
  264.     public void mouseClicked(MouseEvent e) {
  265.     }
  266.  
  267.     public void mousePressed(MouseEvent e) {
  268.         addMouseMotionListener(this);
  269.     double bestdist = Double.MAX_VALUE;
  270.     int x = e.getX();
  271.     int y = e.getY();
  272.     for (int i = 0 ; i < nnodes ; i++) {
  273.         Node n = nodes[i];
  274.         double dist = (n.x - x) * (n.x - x) + (n.y - y) * (n.y - y);
  275.         if (dist < bestdist) {
  276.         pick = n;
  277.         bestdist = dist;
  278.         }
  279.     }
  280.     pickfixed = pick.fixed;
  281.     pick.fixed = true;
  282.     pick.x = x;
  283.     pick.y = y;
  284.     repaint();
  285.     e.consume();
  286.     }
  287.  
  288.     public void mouseReleased(MouseEvent e) {
  289.         removeMouseMotionListener(this);
  290.         if (pick != null) {
  291.             pick.x = e.getX();
  292.             pick.y = e.getY();
  293.             pick.fixed = pickfixed;
  294.             pick = null;
  295.         }
  296.     repaint();
  297.     e.consume();
  298.     }
  299.  
  300.     public void mouseEntered(MouseEvent e) {
  301.     }
  302.  
  303.     public void mouseExited(MouseEvent e) {
  304.     }
  305.  
  306.     public void mouseDragged(MouseEvent e) {
  307.     pick.x = e.getX();
  308.     pick.y = e.getY();
  309.     repaint();
  310.     e.consume();
  311.     }
  312.  
  313.     public void mouseMoved(MouseEvent e) {
  314.     }
  315.  
  316.     public void start() {
  317.     relaxer = new Thread(this);
  318.     relaxer.start();
  319.     }
  320.  
  321.     public void stop() {
  322.     relaxer = null;
  323.     }
  324.  
  325. }
  326.  
  327.  
  328. public class Graph extends Applet implements ActionListener, ItemListener {
  329.  
  330.     GraphPanel panel;
  331.     Panel controlPanel;
  332.  
  333.     Button scramble = new Button("Scramble");
  334.     Button shake = new Button("Shake");
  335.     Checkbox stress = new Checkbox("Stress");
  336.     Checkbox random = new Checkbox("Random");
  337.  
  338.     public void init() {
  339.     setLayout(new BorderLayout());
  340.  
  341.     panel = new GraphPanel(this);
  342.     add("Center", panel);
  343.     controlPanel = new Panel();
  344.     add("South", controlPanel);
  345.  
  346.     controlPanel.add(scramble); scramble.addActionListener(this);
  347.     controlPanel.add(shake); shake.addActionListener(this);
  348.     controlPanel.add(stress); stress.addItemListener(this);
  349.     controlPanel.add(random); random.addItemListener(this);
  350.  
  351.     String edges = getParameter("edges");
  352.     for (StringTokenizer t = new StringTokenizer(edges, ",") ; t.hasMoreTokens() ; ) {
  353.         String str = t.nextToken();
  354.         int i = str.indexOf('-');
  355.         if (i > 0) {
  356.         int len = 50;
  357.         int j = str.indexOf('/');
  358.         if (j > 0) {
  359.             len = Integer.valueOf(str.substring(j+1)).intValue();
  360.             str = str.substring(0, j);
  361.         }
  362.         panel.addEdge(str.substring(0,i), str.substring(i+1), len);
  363.         }
  364.     }
  365.     Dimension d = getSize();
  366.     String center = getParameter("center");
  367.     if (center != null){
  368.         Node n = panel.nodes[panel.findNode(center)];
  369.         n.x = d.width / 2;
  370.         n.y = d.height / 2;
  371.         n.fixed = true;
  372.     }
  373.     }
  374.  
  375.     public void destroy() {
  376.         remove(panel);
  377.         remove(controlPanel);
  378.     }
  379.  
  380.     public void start() {
  381.     panel.start();
  382.     }
  383.  
  384.     public void stop() {
  385.     panel.stop();
  386.     }
  387.  
  388.     public void actionPerformed(ActionEvent e) {
  389.     Object src = e.getSource();
  390.  
  391.     if (src == scramble) {
  392.         play(getCodeBase(), "audio/computer.au");
  393.         Dimension d = getSize();
  394.         for (int i = 0 ; i < panel.nnodes ; i++) {
  395.         Node n = panel.nodes[i];
  396.         if (!n.fixed) {
  397.             n.x = 10 + (d.width-20)*Math.random();
  398.             n.y = 10 + (d.height-20)*Math.random();
  399.         }
  400.         }
  401.         return;
  402.     }
  403.  
  404.     if (src == shake) {
  405.         play(getCodeBase(), "audio/gong.au");
  406.         Dimension d = getSize();
  407.         for (int i = 0 ; i < panel.nnodes ; i++) {
  408.         Node n = panel.nodes[i];
  409.         if (!n.fixed) {
  410.             n.x += 80*Math.random() - 40;
  411.             n.y += 80*Math.random() - 40;
  412.         }
  413.         }
  414.     }
  415.  
  416.     }
  417.  
  418.     public void itemStateChanged(ItemEvent e) {
  419.     Object src = e.getSource();
  420.     boolean on = e.getStateChange() == ItemEvent.SELECTED;
  421.     if (src == stress) panel.stress = on;
  422.     else if (src == random) panel.random = on;
  423.     }
  424.  
  425.     public String getAppletInfo() {
  426.     return "Title: GraphLayout \nAuthor: <unknown>";
  427.     }
  428.  
  429.     public String[][] getParameterInfo() {
  430.     String[][] info = {
  431.         {"edges", "delimited string", "A comma-delimited list of all the edges.  It takes the form of 'C-N1,C-N2,C-N3,C-NX,N1-N2/M12,N2-N3/M23,N3-NX/M3X,...' where C is the name of center node (see 'center' parameter) and NX is a node attached to the center node.  For the edges connecting nodes to eachother (and not to the center node) you may (optionally) specify a length MXY separated from the edge name by a forward slash."},
  432.         {"center", "string", "The name of the center node."}
  433.     };
  434.     return info;
  435.     }
  436.  
  437. }
  438.