package vishnu.testvis.dendro; import java.awt.image.*; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.Vector; import java.util.Enumeration; import vishnu.testvis.visual.*; import vishnu.testvis.object.*; import vishnu.testvis.sammon.*; import vishnu.datablock.*; public class TreePanel extends JPanel { DendroPanel parent; Vishnu currentMain; DataManager dataManager; DataBlock data; Image offScreen=null; Dimension offScreenSize; Graphics offGraphics; Graphics2D offGraphics2D; Vector clusterVect = null; Cluster root = null; Cluster selectedRoot = null; ClusterObj selectedCluster = null; ClusterObj oldCluster = null; int selectedClusterID = -1; int oldClusterID = -1; JPopupMenu popup; boolean constructed = false; int selectedDepth = -1; int initBranchLength = 85; boolean firstTime = true; Graphics2D big; BufferedImage bi; Rectangle area; Dimension d = null; public TreePanel(DendroPanel parent, Vishnu frame) { currentMain = frame; this.parent = parent; dataManager = frame.dataManager; popup = new JPopupMenu(); popup.setVisible(false); add(popup); this.addComponentListener( new ComponentAdapter() { public void componentResized(ComponentEvent evt) { repaint(); } } ); this.addMouseMotionListener( new MouseMotionAdapter() { public void mouseMoved(MouseEvent mouseEvent) { int x = mouseEvent.getX(); int y = mouseEvent.getY(); if( clusterVect != null ) { oldClusterID = selectedClusterID; oldCluster = selectedCluster; selectedClusterID = -1; selectedCluster = null; boolean notFound = true; int i = 0; while( notFound && (i < clusterVect.size()) ) { ClusterObj co = (ClusterObj)clusterVect.elementAt(i); if( co.inCircle(x,y) ) { selectedCluster = co; selectedClusterID = i; if( selectedClusterID != oldClusterID ) { repaint(); } notFound = false; } i++; } if( (oldClusterID != selectedClusterID) && ( oldClusterID != -1 ) ) { popup.setVisible(false); repaint(); //update(); } if( selectedClusterID != -1 ) { if( selectedClusterID != oldClusterID ) { popup.removeAll(); int size = selectedCluster.cluster._items; String str = size + ((size > 1) ? " documents" : " document"); JMenuItem menuItem = new JMenuItem(str); menuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent actionEvent) { getDocObjs(); String[] drillWords = selectedCluster.getFreqWords(); String keyWordStr = new String(drillWords[0]+" "+drillWords[1]+" "+drillWords[2]); updateHLinkPanel(); updateKeyWordTable(); } }); popup.add(menuItem); popup.add(new JPopupMenu.Separator()); int mode0 = -1; // if the currentRoot is selected, dont show "drilldown" if( selectedClusterID == 0 ) { // if we are at the top, dont show "undrill" if( ((Object)selectedCluster.cluster).equals((Object)root) ) { str = "Top of hierarchy"; mode0 = 2; } else { str = "Sequential undrill"; mode0 = 1; } } // if somewhere else in the tree else { if( selectedCluster.cluster._items > 1 ) { str = "Drill down"; mode0 = 0; } else { str = "Outermost twigs"; mode0 = 3; } } final int mode = mode0; menuItem = new JMenuItem(str); if( (mode != 0 ) && (mode != 1) ) menuItem.setEnabled(false); menuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { if( mode == 0 ) { selectedRoot = selectedCluster.cluster; String[] drillWords = selectedCluster.getFreqWords(); String keyWordStr = new String(drillWords[0]+" "+drillWords[1]+" "+drillWords[2]); // to establish new clusterobj vector constructed = false; repaint(); //update(); } if( mode == 1 ) { selectedRoot = selectedRoot._parent; constructed = false; repaint(); //update(); } } }); popup.add(menuItem); if( mode == 1 ) { menuItem = new JMenuItem("Total undrill"); menuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { selectedRoot = root; constructed = false; //update(); repaint(); } }); popup.add(menuItem); } popup.add(new JPopupMenu.Separator()); int[] wordFreq = selectedCluster.cluster.getWordFreq(); String[] words = getMostFrequentWords(wordFreq); selectedCluster.setFreqWords(words); menuItem = new JMenuItem(words[0]); menuItem.setEnabled(false); popup.add(menuItem); menuItem = new JMenuItem(words[1]); menuItem.setEnabled(false); popup.add(menuItem); menuItem = new JMenuItem(words[2]); menuItem.setEnabled(false); popup.add(menuItem); popup.validate(); popup.setVisible(true); } popup.show(mouseEvent.getComponent(), mouseEvent.getX(), mouseEvent.getY()); mouseEvent.consume(); } } // of clustervect != null } // of mouse moved }); setVisible(true); } public void clear() { constructed = false; } public String[] getMostFrequentWords(int[] wordFreq) { int max1Freq = 0; int max1Index = 0; int max2Freq = 0; int max2Index = 0; int max3Freq = 0; int max3Index = 0; for( int i = 0; i < wordFreq.length; i++ ) { if( wordFreq[i] > max3Freq ) { if( wordFreq[i] > max2Freq ) { if( wordFreq[i] > max1Freq ) { max3Freq = max2Freq; max3Index = max2Index; max2Freq = max1Freq; max2Index = max1Index; max1Freq = wordFreq[i]; max1Index = i; } else { max3Freq = max2Freq; max3Index = max2Index; max2Freq = wordFreq[i]; max2Index = i; } } else { max3Freq = wordFreq[i]; max3Index = i; } } } data = dataManager.getDataBlock(); String[] words = new String[3]; words[0] = data.words[max1Index]; words[1] = data.words[max2Index]; words[2] = data.words[max3Index]; return words; } public void getDocObjs() { Vector docVect = selectedCluster.cluster.getDocVect(); DocObj[] docs = new DocObj[docVect.size()]; for( int i = 0; i < docVect.size(); i++ ) { int index = ((Integer)docVect.elementAt(i)).intValue(); docs[i] = dataManager.getDocObjAt(index); } if( !selectedCluster.getOrderStatus() ) { QSortForDocObj qSort = new QSortForDocObj(); try { qSort.sort(docs); } catch (Exception e) {} selectedCluster.setDocObjs(docs); selectedCluster.setOrderStatus(true); } } public void updateHLinkPanel() { String htmlDescription = selectedCluster.formatDocs(); parent.hLinkPanel.setText(htmlDescription); } public void updateKeyWordTable() { parent.keywordPanel.update(selectedCluster); } public void setConstructed(boolean b) { constructed = b; } public void getData() { dataManager = currentMain.dataManager; data = dataManager.getDataBlock(); SparseMatrix m = data.matrix; Vector idVect = new Vector(); Vector sammCluster = dataManager.getSammonClusters(); for( int i = 0; i < sammCluster.size(); i++ ) { SammCluster cluster = (SammCluster)sammCluster.elementAt(i); idVect.addAll(cluster.getIDs()); } for( int c = 0; c < idVect.size(); c++ ) { Integer i = (Integer)idVect.elementAt(c); } for (int c = 0; c< idVect.size();c++) { // the document id of the cth document Integer i = (Integer)idVect.elementAt(c); } //int [][] full = m.getExpanded(); float [][] full = m.getExpanded(); // the number of new clusters generated is either 20 // or if there are not many docs half the #docs int limit = m.getRowCount(); int clusterNum = 10; Clustering clustering = new Clustering(dataManager,Clustering.COMPLETE_LINKAGE, full,m.getRowCount(), m.getColumnCount(),1, clusterNum,limit); root = clustering.getRoot(); selectedRoot = root; } public void update(Graphics g) { Graphics2D g2 = (Graphics2D)g; d = getSize(); int w = d.width; int h = d.height; area = new Rectangle(d); bi = (BufferedImage)createImage(w, h); offGraphics2D = bi.createGraphics(); area.setLocation(w/2-50, h/2-25); BasicStroke myStroke = new BasicStroke(2,BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER); offGraphics2D.setColor(Color.white); offGraphics2D.fillRect(0,0,d.width,d.height); if( !constructed ) { clusterVect = new Vector(); selectedCluster = null; selectedClusterID = -1; } int initRad = 0; initBranchLength = (int)(Math.min(d.width,d.height)*0.12); Cluster cc = selectedRoot; Point center = new Point(d.width/2,d.height/2); int radius = (int)(Math.log(cc._items))* 3; if( radius < 2 ) radius = 2; // all the leaf cluster ids underneath selected node // to be painted bright red Vector allSubNodeIDs = new Vector(); if( !constructed ){ ClusterObj rootObj = new ClusterObj(cc,center,radius,dataManager); clusterVect.addElement(rootObj); } else{ ClusterObj c = (ClusterObj)clusterVect.elementAt(0); c.setPosition(center); clusterVect.setElementAt(c,0); c = (ClusterObj)clusterVect.elementAt(0); } int depth = 1; TheInt ii = new TheInt(1); boolean selected = false; // used to paint the tree different colour under selected node selectedDepth = -1; if( cc._items > 1 ){ double alpha = (double)Math.PI/Math.pow(2,depth); double theta = 0; if( (selectedClusterID == 0) ) { selected = true; selectedDepth = 0; offGraphics2D.setColor(Color.blue); } paintPoke(offGraphics2D,cc._child_1,center.x,center.y,center,depth,theta,-alpha,initRad, selected, allSubNodeIDs, ii); paintPoke(offGraphics2D,cc._child_2,center.x,center.y,center,depth,theta,alpha,initRad, selected, allSubNodeIDs, ii); } for( int i = 0; i < clusterVect.size(); i++ ) { ClusterObj co = (ClusterObj)clusterVect.elementAt(i); // if leaf draw reddish if( co.cluster._items == 1 ) { int r = co.getRad(); offGraphics2D.setColor(new Color((float)1, (float)0.7, (float)0.7)); offGraphics2D.fillArc(co.getX()-5,co.getY()-5,2*r,2*r,0,360); offGraphics2D.setColor(Color.black); offGraphics2D.drawArc(co.getX()-5,co.getY()-5,2*r,2*r,0,360); } else // draw blueish { offGraphics2D.setColor(Color.white); int r = co.getRad(); offGraphics2D.fillArc(co.getX()-r,co.getY()-r,2*r,2*r,0,360); offGraphics2D.setColor(new Color((float)0.7, (float)0.7, (float) 1)); offGraphics2D.drawArc(co.getX()-r,co.getY()-r,2*r,2*r,0,360); } } for( int i = 0; i < allSubNodeIDs.size(); i++ ) { int value = ((Integer)allSubNodeIDs.elementAt(i)).intValue(); ClusterObj co = (ClusterObj)clusterVect.elementAt(value); // if leaf if( co.cluster._items == 1 ) { int r = co.getRad(); offGraphics2D.setColor(Color.red); offGraphics2D.fillArc(co.getX()-5,co.getY()-5,2*r,2*r,0,360); offGraphics2D.setColor(Color.black); offGraphics2D.drawArc(co.getX()-5,co.getY()-5,2*r,2*r,0,360); } else { offGraphics2D.setColor(Color.white); int r = co.getRad(); offGraphics2D.fillArc(co.getX()-r,co.getY()-r,2*r,2*r,0,360); offGraphics2D.setColor(Color.blue); offGraphics2D.drawArc(co.getX()-r,co.getY()-r,2*r,2*r,0,360); } } if( (selectedCluster != null) && (selectedCluster.cluster._items > 1) ) { offGraphics2D.setColor(Color.blue); offGraphics2D.fillArc(selectedCluster.getX()-selectedCluster.getRad(),selectedCluster.getY()-selectedCluster.getRad(),2*selectedCluster.getRad(),2*selectedCluster.getRad(),0,360); } g2.drawImage(bi, 0, 0, this); constructed = true; } public void update() { if( !constructed ) { clusterVect = new Vector(); selectedCluster = null; selectedClusterID = -1; } Dimension d = getSize(); offScreenSize = d; if( d.width <= 0 ) return; offScreen = createImage(d.width,d.height); offGraphics = offScreen.getGraphics(); Graphics2D offGraphics2D = (Graphics2D)offGraphics; offGraphics2D.setColor(Color.white); offGraphics2D.fillRect(0,0,d.width,d.height); offGraphics2D.setColor(new Color((float)0.7, (float)0.7, (float)1)); BasicStroke myStroke = new BasicStroke(2,BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER); offGraphics2D.setStroke(myStroke); int initRad = 0; Cluster cc = selectedRoot; Point center = new Point(d.width/2,d.height/2); int radius = (int)(Math.log(cc._items))* 3; if( radius < 2 ) radius = 2; // all the leaf cluster ids underneath selected node // to be painted bright red Vector allSubNodeIDs = new Vector(); if( !constructed ) { ClusterObj rootObj = new ClusterObj(cc,center,radius,dataManager); clusterVect.addElement(rootObj); } else { ClusterObj c = (ClusterObj)clusterVect.elementAt(0); c.setPosition(center); clusterVect.setElementAt(c,0); c = (ClusterObj)clusterVect.elementAt(0); } int depth = 1; TheInt ii = new TheInt(1); boolean selected = false; // used to paint the tree different colour under selected node selectedDepth = -1; if( cc._items > 1 ){ double alpha = (double)Math.PI/Math.pow(2,depth); double theta = 0; if( (selectedClusterID == 0) ){ selected = true; selectedDepth = 0; offGraphics2D.setColor(Color.blue); } paintPoke(offGraphics2D,cc._child_1,center.x,center.y,center,depth,theta,-alpha,initRad, selected, allSubNodeIDs, ii); paintPoke(offGraphics2D,cc._child_2,center.x,center.y,center,depth,theta,alpha,initRad, selected, allSubNodeIDs, ii); } for( int i = 0; i < clusterVect.size(); i++ ) { ClusterObj co = (ClusterObj)clusterVect.elementAt(i); // if leaf draw reddish if( co.cluster._items == 1 ) { int r = co.getRad(); offGraphics2D.setColor(new Color((float)1, (float)0.7, (float)0.7)); offGraphics2D.fillArc(co.getX()-5,co.getY()-5,2*r,2*r,0,360); offGraphics2D.setColor(Color.black); offGraphics2D.drawArc(co.getX()-5,co.getY()-5,2*r,2*r,0,360); } else // draw blueish { offGraphics2D.setColor(Color.white); int r = co.getRad(); offGraphics2D.fillArc(co.getX()-r,co.getY()-r,2*r,2*r,0,360); offGraphics2D.setColor(new Color((float)0.7, (float)0.7, (float) 1)); offGraphics2D.drawArc(co.getX()-r,co.getY()-r,2*r,2*r,0,360); } } for( int i = 0; i < allSubNodeIDs.size(); i++ ) { int value = ((Integer)allSubNodeIDs.elementAt(i)).intValue(); ClusterObj co = (ClusterObj)clusterVect.elementAt(value); // if leaf if( co.cluster._items == 1 ) { int r = co.getRad(); offGraphics2D.setColor(Color.red); offGraphics2D.fillArc(co.getX()-5,co.getY()-5,2*r,2*r,0,360); offGraphics2D.setColor(Color.black); offGraphics2D.drawArc(co.getX()-5,co.getY()-5,2*r,2*r,0,360); } else { offGraphics2D.setColor(Color.white); int r = co.getRad(); offGraphics2D.fillArc(co.getX()-r,co.getY()-r,2*r,2*r,0,360); offGraphics2D.setColor(Color.blue); offGraphics2D.drawArc(co.getX()-r,co.getY()-r,2*r,2*r,0,360); } } if( (selectedCluster != null) && (selectedCluster.cluster._items > 1) ) { offGraphics2D.setColor(Color.blue); offGraphics2D.fillArc(selectedCluster.getX()-selectedCluster.getRad(),selectedCluster.getY()-selectedCluster.getRad(),2*selectedCluster.getRad(),2*selectedCluster.getRad(),0,360); } repaint(); constructed = true; } // theta is the angle in the polar representation public void paintPoke(Graphics2D g, Cluster child, int x, int y, Point center, int d, double theta, double alpha,int r, boolean selected, Vector allSubNodeIDs, TheInt ii) { // translate parent cluster position to center int x1 = x - center.x; int y1 = y - center.y; // rotate cluster about origin by alpha to obtain intermediate tree point int x_inter = (int)(x1 * Math.cos(alpha) + y1 * Math.sin(alpha)) + center.x; int y_inter = (int)(x1 * - Math.sin(alpha) + y1 * Math.cos(alpha)) + center.y; // rotate cluster about origin by theta int x2 = (int)(x1 * Math.cos(-theta) + y1 * Math.sin(-theta)); int y2 = (int)(x1 * - Math.sin(-theta) + y1 * Math.cos(-theta)); // extend radius int dRad = (initBranchLength-d*10); if( dRad <= 0 ) dRad = 10; int x3 = x2 + dRad; int y3 = y2; // rotate back by (theta +/- alpha) int x4 = (int)(x3 * Math.cos(theta+alpha) + y3 * Math.sin(theta+alpha)); int y4 = (int)(x3 * -Math.sin(theta+alpha) + y3 * Math.sin(theta+alpha)); // finally, add the center back again int x_new = x4 + center.x; int y_new = y4 + center.y; int startAngle = (int)(180*theta/Math.PI); int rangeAngle = (int)(180*alpha/Math.PI); boolean hitSelectedNode = false; if( selectedClusterID != -1 ) { Object obj1 = (Object)child; ClusterObj selObj = (ClusterObj)clusterVect.elementAt(selectedClusterID); Object obj2 = (Object)selObj.cluster; hitSelectedNode = obj1.equals(obj2); } int currentClusterID = -1; if( selectedClusterID != -1 ) { int w = 0; boolean found = false; while( w < clusterVect.size() && !found ) { Object obj1 = (Object)child; ClusterObj obj = (ClusterObj)clusterVect.elementAt(w); Object obj2 = (Object)obj.cluster; if( obj1.equals(obj2) ) { currentClusterID = w; found = true; } w++; } } if( (d <= selectedDepth) && (!hitSelectedNode) ) { g.setColor(new Color( (float) 0.7, (float) 0.7, (float) 1)); selected = false; } g.drawArc(center.x-r,center.y-r,2*r,2*r,startAngle,rangeAngle); g.drawLine(x_inter,y_inter, x_new, y_new); if( hitSelectedNode ) { selectedDepth = d; selected = true; g.setColor(Color.blue); Vector docVector = child.getDocVect(); } g.setColor(selected ? Color.blue : new Color((float) 0.7, (float) 0.7, (float) 1) ); int radius = (int)(Math.log(child._items))* 3; if( radius < 2 ) radius = 2; if( child._items == 1 ) { Vector docVector = child.getDocVect(); radius = Math.max(5,(int)Math.sqrt(docVector.size())*2); } d++; r = (int)(Math.sqrt((x_new-center.x)*(x_new-center.x) + (y_new-center.y)*(y_new-center.y))); Point P = new Point(x_new,y_new); if( !constructed ) { ClusterObj cObj = new ClusterObj(child,P,radius,dataManager); clusterVect.addElement(cObj); } else { ClusterObj c = (ClusterObj)clusterVect.elementAt(ii.val); c.setPosition(P); clusterVect.setElementAt(c,ii.val); } ii.val++; double new_alpha = (double)alpha/2; if( selected ) allSubNodeIDs.addElement(new Integer(currentClusterID) ); if( child._items > 1 && d < 6 ){ paintPoke(g,child._child_1,x_new,y_new,center,d,theta+alpha,new_alpha,r,selected,allSubNodeIDs,ii); paintPoke(g,child._child_2,x_new,y_new,center,d,theta+alpha,-new_alpha,r,selected, allSubNodeIDs,ii); } // we have reached the outermost twigs else{ int[] wordFreq = child.getWordFreq(); String[] words = getMostFrequentWords(wordFreq); Font f = new Font("Courier",Font.PLAIN,16); FontMetrics fm = g.getFontMetrics(f); int ascent = fm.getAscent(); int height = fm.getHeight(); int wordLength = fm.stringWidth(words[0]); g.setFont(f); if( x_new > center.x ){ g.drawString(words[0],x_new + Math.max(radius,5) + 10,y_new + Math.max(radius,5)); } else{ g.drawString(words[0],x_new - Math.max(radius,5) - 10 - wordLength,y_new + Math.max(radius,5)); } } } public void paintTree(Cluster cc, Graphics g, int x, int y, int dist) { int dy = 10; int rad = 2; int dmt = 4; g.setColor(Color.white); g.fillArc(x-rad,y-rad,dmt,dmt,0,360); g.setColor(Color.blue); g.drawOval(x-rad,y-rad,dmt,dmt); String desc = String.valueOf(cc._items); g.drawString(desc,x-(int)rad/2 - (desc.length()-2)*3,y + (int)rad/2 - 2); if( (cc._items > 1) ) { g.drawLine(x,y+rad,x-dist,y-rad+dy); g.drawLine(x,y+rad,x+dist,y-rad+dy); if( cc._child_1 != null ) paintTree(cc._child_1, g, x - dist, y + dy, (int)dist/2); if( cc._child_2 != null ) paintTree(cc._child_2, g, x + dist, y + dy, (int)dist/2); } } public void paint(Graphics g) { update(g); } }