import java.awt.*; import java.awt.event.*; import java.util.*; import java.net.*; import javax.swing.*; import javax.swing.text.*; import javax.swing.plaf.basic.*; import java.lang.reflect.*; import java.io.*; /** スプリングモデルによる描画を行うクラス */ public class SpringModel extends JPanel { /** Node一覧 contents are class Node */ private Vector m_nodes = new Vector(); /** Edge一覧 contents are class Edge */ private Vector m_edges = new Vector(); /** relax thread */ private Thread m_relax = null; /** ノードの追加時にLabelを作成する場合,true * falseにすると始めて描画されるときにLabelを作成する. * 画面表示せずグラフ構造のデータとして利用するときは, * falseにすると良い. */ private boolean m_createLabel = false; /** * 画面の有効領域 */ private double m_availableArea = 0.85; /** * ネットワーク密度の計算 */ public double getDensity(){ int max = countNodes()*(countNodes()-1); int count = 0; for(int ecnt=0;ecnt * ノード間を接続するパス一覧を取得する. * 引数には,パス一覧を取得したいノード一覧を取得する.そのノード一覧 * の全ての組み合わせについて調査する. * * 戻り値のHashMap(1)の構造は, * HashMap(1) * key, value * -----------------+---------------------------- * node name, 検索対象Node一覧(HashMap(2)) * SpringModel.Node * * HashMap(2) * key, value * ----------------+--------------------------------- * 対象Node名, 接続ルート(Vector whose contents * SpringModel.Node are SpringModel.Edge) * * 検索は,Edgeの方向を無視する.また,同じエッジ数のパスが複数あっても,最初に見つけた最もエッジ数の少ないパスのみを出力する. * */ public HashMap findPaths(String[] snodes){ HashMap map = new HashMap(); Vector nodestemp = new Vector(); for(int cnt=0;cnt * ノード間を接続するパス一覧を取得する.エッジの向きは無視する. * 引数には,パス一覧を取得したいノード一覧を取得する.そのノード一覧 * の全ての組み合わせについて調査する. * * 戻り値のHashMap(1)の構造は, * HashMap(1) * key, value * -----------------+---------------------------- * node name, 検索対象Node一覧(HashMap(2)) * SpringModel.Node * * HashMap(2) * key, value * ----------------+--------------------------------- * 対象Node名, 接続ルート(Vector whose contents * SpringModel.Node are SpringModel.Node) * * 検索は,Edgeの方向を無視する.また,同じエッジ数のパスが複数あっても,最初に見つけた最もエッジ数の少ないパスのみを出力する. * */ public HashMap findPathsIgnoreDirection(String[] snodes){ HashMap map = new HashMap(); Vector nodestemp = new Vector(); for(int cnt=0;cnt * JAVAでの演算子のオーバーロード方法が分かれば、そちらに変更する。 */ CVector plus(CVector cmp){ m_x += cmp.m_x; m_y += cmp.m_y; return this; } /** ベクトルの始点を中心に回転したベクトルを求める */ CVector rotate(double rad){ /* 回転行列は | c -s ||x| | s c ||y| */ double c = Math.cos(rad); double s = Math.sin(rad); return new CVector(m_x*c-m_y*s,m_x*s+m_y*c); } /** 長さを返す。 */ double getLength(){ return Math.sqrt(Math.pow(m_x,2)+Math.pow(m_y,2)); } /** 現在のx,yの値を使用して、角度(radians)を返す * @return 角度(radians) */ double getRad(){ // 返す角度の範囲は 0 <= rad < 2*Math.PI if(m_y == 0){ if(m_x >= 0) return 0; else return Math.PI; }else if(m_x == 0){ if(m_y >= 0) return Math.PI/2; else return 3*Math.PI/2; } double rad = Math.acos(m_x/Math.sqrt(Math.pow(m_x,2)+Math.pow(m_y,2))); if(m_y < 0) rad = (2*Math.PI)-rad; return rad; } /** 引数として与えられたベクトルに対して自分のベクトルをプラス方向 * に回転すれば角度が近づくか、マイナス方向に回転すれば近づくかを返 * す。
* 例えば、引数ベクトルが(1,1) 自分のベクトルが(1,-1)の場合、自分の * ベクトルをマイナス方向に回転させると、引数ベクトルに近づくのでこ * の関数はマイナス方向を示す -1 を返す。 * @param vec 対象となるベクトル * @return 1の場合はプラス方向、2の場合はマイナス方向。 */ int towordPlusMinus(CVector vec){ if((m_x == 0 && m_y == 0) || (vec.m_x == 0 && vec.m_y == 0)){ return 0; } int nDiff; switch(nDiff = diffWorld(vec)){ case 0: case 2: double x = ((double)vec.m_y/vec.m_x)-((double)m_y/m_x); if(x == 0) return 0; else{ if(nDiff==0) return x>0?1:-1; else return x>0?-1:1; } case -1: return -1; case 1: return 1; } return 0; // bug } /** {@link #getWorld()}により求められる象現の差分を求める。同じ象現 * の場合、差分は0。隣の象現に互いがある場合、1。対角線上に互いの * 象現が存在する場合、2となる。 * @param vec 比較対象のベクトル * @return 差分値(0-2) */ int diffWorld(CVector vec){ int w = getWorld(); int w2 = vec.getWorld(); if(w == w2) return 0; else if(Math.abs(w-w2)==2) return 2; else if(Math.max(w,w2)-Math.min(w,w2)==1) return 1; else return -1; } /** * m_x,m_yを以下の4つの象現に分ける
*
		 * 1.  ○      2.   ●    3.  |      4.  |
		 *     | |        | |         |          |
		 *     +-●      ○-+-     ●-+-        -+-○
		 *     |            |       | |          | |
		 *                            ○         ●
		 * 
* @return 象現の番号(1-4) */ int getWorld(){ if(m_x > 0 && m_y >= 0){ return 1; }else if(m_x <= 0 && m_y > 0){ return 2; }else if(m_x < 0 && m_y <= 0){ return 3; }else if(m_x >= 0 && m_y < 0){ return 4; } return 0;// bug } public double radToDeg(double rad){ return rad*180/Math.PI; } public double degToRad(double deg){ return deg*Math.PI/180; } } /** relax */ public void relax(){ final SpringModel own = this; m_relax = new Thread(){ public void run(){ while(m_relax!=null){ // initialize for(int ncnt=0;ncnt d.width-noarea_w) newx = d.width-noarea_w; int newy = (int)(p.y+n.m_dy+0.5); if(newy < noarea_h) newy = noarea_h; if(newy > d.height-noarea_h) newy = d.height-noarea_h; all += Math.abs(newx-p.x)+Math.abs(newy-p.y); n.m_label.setLocation(newx,newy); } } // repaint own.repaint(); if(m_nodes.size()==0 || all/m_nodes.size()<1.4) try{sleep(500);}catch(InterruptedException e){} else try{sleep(50);}catch(InterruptedException e){} } } }; m_relax.setPriority(Thread.MIN_PRIORITY); m_relax.start(); } /** stop relax */ public void stopRelax(){ m_relax = null; } public boolean getRelax(){ return m_relax!=null; } /** Nodeを表示するためのラベル */ class NodeLabel extends JLabel{ private Node m_parent; private Image m_image = null; public NodeLabel(String n,Node parent,Image image){ super(n); m_parent = parent; m_image = image; } public void paint(Graphics graphics){ Dimension d = getSize(); Color b = graphics.getColor(); if(m_parent.getFixed()) graphics.setColor(Color.pink); else graphics.setColor(Color.white); Point p = getLocation(); graphics.fillRect(p.x,p.y,d.width,d.height); graphics.setColor(Color.black); FontMetrics fm = graphics.getFontMetrics(); graphics.drawString(getText(),p.x+1,p.y+fm.getHeight()-4); if(m_image != null){ int imageWidth = 41; int imageHeigth = 60; graphics.drawImage(m_image,(int)(p.x+d.width/2-imageWidth/2+0.5),p.y+fm.getHeight()+2, imageWidth,imageHeigth,null); } graphics.setColor(b); } } /** Nodeをあらわすクラス */ class Node implements Comparable{ private String m_name; private HashMap m_userData = new HashMap(); private Image m_image = null; private JLabel m_label = null; private Point m_backupPosition = null; private boolean m_fixed = false; private boolean m_temporaryFixed = false; private double m_dx=0; private double m_dy=0; private boolean m_visible = true; public boolean getVisible(){ return m_visible; } public void setVisible(boolean v){ m_visible = v; } public void setLocation(int x,int y){ m_label.setLocation(x,y); } public void addDxy(double dx,double dy){ m_dx += dx; m_dy += dy; } public void clearDxy(){ m_dx = m_dy = 0; } public boolean getFixed(){ return m_fixed || m_temporaryFixed; } public boolean getFixedIgnoreTemporary(){ return m_fixed; } public void setFixed(boolean data){ m_fixed = data; } public void setTemporaryFixed(boolean data){ m_temporaryFixed = data; } public void backupPosition(){ if(m_label != null){ m_backupPosition = m_label.getLocation(null); }else{ m_backupPosition = null; } } public void restorePosition(){ if(m_backupPosition!=null&&m_label!=null){ m_label.setLocation(m_backupPosition.x,m_backupPosition.y); } m_backupPosition = null; } public Node(String name){ this(name,null); } public Node(String name,Object userData){ setName(name); setUserData(userData); } public Node(String name,Object userData,Image image){ setName(name); setUserData(userData); setImage(image); } public Node(Node node){ setName(node.getName()); setUserData(node.getUserData()); } public String toString(){ return m_name; } public String getName(){ return m_name; } public void setName(String data){ m_name = data; } public Object getUserData(){ return getUserData(0); } public Object getUserData(int index){ return m_userData.get(new Integer(index)); } public void setUserData(Object data){ setUserData(0,data); } public void setUserData(int index,Object data){ m_userData.put(new Integer(index),data); } public Image getImage(){ return m_image; } public void setImage(Image data){ m_image = data; } public boolean equals(Object obj){ if(obj instanceof Node){ return ((Node)obj).m_name.equals(m_name) && ((Node)obj).m_userData.equals(m_userData); } return false; } public int compareTo(Object o){ return toString().compareTo(o.toString()); } } /** エッジをあらわすクラス */ class Edge { private int m_length0 = 200; private Node m_from; private Node m_to; private boolean m_direction; public Node getTo(){ return m_to; } public Node getFrom(){ return m_from; } public boolean getDirection(){ return m_direction; } public boolean equals(Object obj){ if(obj instanceof Edge){ Edge e = (Edge)obj; if(e.m_direction!=m_direction) return false; if(m_direction){ return e.m_from.equals(m_from) && e.m_to.equals(m_to); }else{ return (e.m_from.equals(m_from) && e.m_to.equals(m_to)) || (e.m_from.equals(m_to) && e.m_to.equals(m_from)); } } return false; } /* public Edge(Node from,Node to){ this(from,to,true); } */ public Edge(Node from,Node to,boolean direction){ m_from = from; m_to = to; m_direction = direction; } public int getLength0(){ return m_length0; } public String toString(){ if(m_direction) return ""+m_from+"->"+m_to; else return ""+m_from+"--"+m_to; } } /** constructor */ public SpringModel(boolean createLabel){ this(); m_createLabel = createLabel; } /** constructor */ public SpringModel(){ final SpringModel own = this; setLayout(new FlowLayout(){ public void layoutContainer(Container target){ for(int cnt=0;cntMath.abs(yHigh-yLow)) graphics.fillPolygon(new int[]{xLow,xLow,xHigh},new int[]{yLow-2,yLow+2,yHigh},3); else graphics.fillPolygon(new int[]{xLow-2,xLow+2,xHigh},new int[]{yLow,yLow,yHigh},3); }else{ graphics.drawLine(xLow,yLow,xHigh,yHigh); } } } // repaint node (first paint is done in super.paint(..)) for(int cnt=0;cnt