1 /* 2 * $Id: JGraphExampleGraph.java,v 1.1 2009/09/25 15:17:49 david Exp $ 3 * Copyright (c) 2001-2005, Gaudenz Alder 4 * 5 * All rights reserved. 6 * 7 * This file is licensed under the JGraph software license, a copy of which 8 * will have been provided to you in the file LICENSE at the root of your 9 * installation directory. If you are unable to locate this file please 10 * contact JGraph sales for another copy. 11 */ 12 package com.jgraph.layout; 13 14 import java.awt.event.ActionEvent; 15 import java.awt.geom.Rectangle2D; 16 import java.util.HashSet; 17 import java.util.Hashtable; 18 import java.util.Iterator; 19 import java.util.Map; 20 import java.util.Set; 21 22 import javax.swing.AbstractAction; 23 import javax.swing.Timer; 24 25 import org.jgraph.example.GraphEd.MyGraph; 26 import org.jgraph.graph.CellView; 27 import org.jgraph.graph.DefaultGraphModel; 28 import org.jgraph.graph.GraphConstants; 29 import org.jgraph.graph.GraphLayoutCache; 30 import org.jgraph.graph.GraphModel; 31 32 /** 33 * A graph that can animate changes (morph). 34 */ 35 public class JGraphExampleGraph extends MyGraph { 36 37 /** 38 * Specifies the delay between morphing steps 39 */ 40 protected int delay = 30; 41 42 /** 43 * Specified the number of steps in the morphing process 44 */ 45 protected int steps = 10; 46 47 /** 48 * Specified the current morhing step 49 */ 50 protected int step = 0; 51 52 /** 53 * Stores the previous bounds of morphed cells 54 */ 55 protected Map oldBounds = new Hashtable(); 56 57 /** 58 * Stores the future bounds of morphed cells 59 */ 60 protected Map newBounds = new Hashtable(); 61 62 /** 63 * Stores the previous collective bounds of the morphed cells 64 */ 65 protected Rectangle2D oldClipBounds; 66 67 /** 68 * Stores the new collective bounds of the morphed cells 69 */ 70 protected Rectangle2D newClipBounds; 71 72 /** 73 * Constructs an example graph for the specified graph model. 74 * 75 * @param model 76 */ JGraphExampleGraph(GraphModel model)77 public JGraphExampleGraph(GraphModel model) { 78 super(model); 79 setGraphLayoutCache(new JGraphExampleLayoutCache(this)); 80 } 81 JGraphExampleGraph(GraphModel model, GraphLayoutCache cache)82 public JGraphExampleGraph(GraphModel model, GraphLayoutCache cache) { 83 super(model, cache); 84 } 85 morph(final Map nestedMap, Set nomorph)86 public void morph(final Map nestedMap, Set nomorph) { 87 Set parents = initMorphing(nestedMap, nomorph); 88 if (!newBounds.isEmpty()) { 89 final Object[] cells = parents.toArray(); 90 Object[] edges = DefaultGraphModel.getEdges(getModel(), cells) 91 .toArray(); 92 final CellView[] edgeViews = getGraphLayoutCache() 93 .getMapping(edges); 94 95 // Execute the morphing. This spawns a timer 96 // to not block the dispatcher thread (repaint) 97 Timer timer = new Timer(delay, new AbstractAction() { 98 public void actionPerformed(ActionEvent e) { 99 if (step >= steps) { 100 Timer timer = (Timer) e.getSource(); 101 timer.stop(); 102 restore(); 103 getGraphLayoutCache().edit(nestedMap, null, null, null); 104 } else { 105 step++; 106 Iterator it = newBounds.keySet().iterator(); 107 while (it.hasNext()) { 108 morphCell(it.next(), step); 109 } 110 getGraphLayoutCache().refresh(edgeViews, false); 111 addOffscreenDirty(oldClipBounds); 112 addOffscreenDirty(newClipBounds); 113 oldClipBounds = newClipBounds = null; 114 repaint(); 115 } 116 } 117 }); 118 timer.start(); 119 } else { 120 getGraphLayoutCache().edit(nestedMap, null, null, null); 121 } 122 } 123 124 /** 125 * Initial step of the morphing process. Analyses the arguments and prepares 126 * internal datastructures for the morphing. 127 * 128 * @return Returns the set of all cells and ancestors to determine the dirty 129 * region and connected edges. 130 */ initMorphing(Map nestedMap, Set nomorph)131 protected Set initMorphing(Map nestedMap, Set nomorph) { 132 oldBounds.clear(); 133 newBounds.clear(); 134 step = 0; 135 Iterator it = nestedMap.entrySet().iterator(); 136 while (it.hasNext()) { 137 Map.Entry entry = (Map.Entry) it.next(); 138 Object cell = entry.getKey(); 139 Map attrs = (Map) entry.getValue(); 140 Rectangle2D rect = GraphConstants.getBounds(attrs); 141 if (rect != null) { 142 Rectangle2D old = getCellBounds(cell); 143 if (old != null && !old.equals(rect)) { 144 newBounds.put(cell, rect); 145 oldBounds.put(cell, old.clone()); 146 } 147 } 148 } 149 150 // Make sure the cells in nomorph are at their future 151 // locations and fetches the set of all parents. 152 HashSet parents = new HashSet(); 153 it = oldBounds.keySet().iterator(); 154 while (it.hasNext()) { 155 Object cell = it.next(); 156 Object parent = getModel().getParent(cell); 157 if (nomorph != null && nomorph.contains(cell)) { 158 Rectangle2D rect = (Rectangle2D) newBounds.remove(cell); 159 setCellBounds(cell, rect); 160 } 161 while (parent != null) { 162 parents.add(parent); 163 parent = getModel().getParent(parent); 164 } 165 } 166 parents.addAll(oldBounds.keySet()); 167 return parents; 168 } 169 170 /** 171 * Restore the old bounds values for all cells. (This is required at the end 172 * of the morphing animation and before calling the edit method for the 173 * command history to work correctly.) 174 * 175 */ restore()176 protected void restore() { 177 Iterator it = oldBounds.entrySet().iterator(); 178 while (it.hasNext()) { 179 Map.Entry entry = (Map.Entry) it.next(); 180 setCellBounds(entry.getKey(), (Rectangle2D) entry.getValue()); 181 } 182 } 183 184 /** 185 * Performs the morph positionon a cell for a particular step 186 * 187 * @param cell 188 * the cell being morphed 189 * @param step 190 * the number step into morph process 191 */ morphCell(Object cell, int step)192 protected void morphCell(Object cell, int step) { 193 Rectangle2D old = (Rectangle2D) oldBounds.get(cell); 194 Rectangle2D rect = (Rectangle2D) newBounds.get(cell); 195 // Add to total clip bounds 196 if (old != null) { 197 if (oldClipBounds == null) { 198 oldClipBounds = (Rectangle2D)old.clone(); 199 } else { 200 oldClipBounds.add(old); 201 } 202 } 203 if (rect != null) { 204 if (newClipBounds == null) { 205 newClipBounds = (Rectangle2D)rect.clone(); 206 } else { 207 newClipBounds.add(rect); 208 } 209 } double dx = (rect.getX() - old.getX()) * step / steps; 210 double dy = (rect.getY() - old.getY()) * step / steps; 211 Rectangle2D pos = new Rectangle2D.Double(old.getX() + dx, old.getY() 212 + dy, old.getWidth(), old.getHeight()); 213 setCellBounds(cell, pos); 214 } 215 216 /** 217 * Set the new cell bounds 218 * 219 * @param cell 220 * the cell whose bounds to set 221 * @param bounds 222 * the new bounds of the cell 223 */ setCellBounds(Object cell, Rectangle2D bounds)224 protected void setCellBounds(Object cell, Rectangle2D bounds) { 225 Rectangle2D rect = getCellBounds(cell); 226 if (rect != null && bounds != null) { 227 rect.setFrame(bounds.getX(), bounds.getY(), bounds.getWidth(), 228 bounds.getHeight()); 229 CellView view = getGraphLayoutCache().getMapping(cell, false); 230 if (view != null) 231 view.update(getGraphLayoutCache()); 232 } 233 } 234 235 } 236