View Javadoc
1   /*
2    * Copyright (c) 2003, The JUNG Authors
3    * All rights reserved.
4    * 
5    * This software is open-source under the BSD license; see either "license.txt"
6    * or https://github.com/jrtom/jung/blob/master/LICENSE for a description.
7    * 
8    * 
9    * 
10   */
11  package edu.uci.ics.jung.algorithms.layout;
12  
13  import java.awt.Dimension;
14  import java.awt.geom.AffineTransform;
15  import java.awt.geom.Point2D;
16  import java.util.HashMap;
17  import java.util.Map;
18  
19  import com.google.common.base.Function;
20  
21  import edu.uci.ics.jung.algorithms.util.IterativeContext;
22  import edu.uci.ics.jung.graph.Graph;
23  
24  /**
25   * A {@code Layout} implementation that combines 
26   * multiple other layouts so that they may be manipulated
27   * as one layout. The relaxer thread will step each layout
28   * in sequence.
29   * 
30   * @author Tom Nelson - tomnelson@dev.java.net
31   *
32   * @param <V> the vertex type
33   * @param <E> the edge type
34   */
35  public class AggregateLayout<V, E> implements Layout<V,E>, IterativeContext {
36  
37  	protected Layout<V,E> delegate;
38  	protected Map<Layout<V,E>,Point2D> layouts = new HashMap<Layout<V,E>,Point2D>();
39  
40  	/**
41  	 * Creates an instance backed by the specified {@code delegate}.
42  	 * @param delegate the layout to which this instance is delegating
43  	 */
44  	public AggregateLayout(Layout<V, E> delegate) {
45  		this.delegate = delegate;
46  	}
47  
48  	/**
49  	 * @return the delegate
50  	 */
51  	public Layout<V, E> getDelegate() {
52  		return delegate;
53  	}
54  
55  	/**
56  	 * @param delegate the delegate to set
57  	 */
58  	public void setDelegate(Layout<V, E> delegate) {
59  		this.delegate = delegate;
60  	}
61  
62  	/**
63  	 * Adds the passed layout as a sublayout, and specifies
64  	 * the center of where this sublayout should appear.
65  	 * @param layout the layout algorithm to use as a sublayout
66  	 * @param center the center of the coordinates for the sublayout
67  	 */
68  	public void put(Layout<V,E> layout, Point2D center) {
69  		layouts.put(layout,center);
70  	}
71  	
72  	/**
73  	 * @param layout the layout whose center is to be returned
74  	 * @return the center of the passed layout
75  	 */
76  	public Point2D get(Layout<V,E> layout) {
77  		return layouts.get(layout);
78  	}
79  	
80  	/**
81  	 * Removes {@code layout} from this instance.
82  	 * @param layout the layout to remove
83  	 */
84  	public void remove(Layout<V,E> layout) {
85  		layouts.remove(layout);
86  	}
87  	
88  	/**
89  	 * Removes all layouts from this instance.
90  	 */
91  	public void removeAll() {
92  		layouts.clear();
93  	}
94  	
95  	public Graph<V, E> getGraph() {
96  		return delegate.getGraph();
97  	}
98  
99  	public Dimension getSize() {
100 		return delegate.getSize();
101 	}
102 
103 	public void initialize() {
104 		delegate.initialize();
105 		for(Layout<V,E> layout : layouts.keySet()) {
106 			layout.initialize();
107 		}
108 	}
109 
110 	/**
111 	 * @param v the vertex whose locked state is to be returned
112 	 * @return true if v is locked in any of the layouts, and false otherwise
113 	 */
114 	public boolean isLocked(V v) {
115 		for(Layout<V,E> layout : layouts.keySet()) {
116 			if (layout.isLocked(v)) {
117 				return true;
118 			}
119 		}
120 		return delegate.isLocked(v);
121 	}
122 
123 	/**
124 	 * Locks this vertex in the main layout and in any sublayouts whose graph contains
125 	 * this vertex.
126 	 * @param v the vertex whose locked state is to be set
127 	 * @param state {@code true} if the vertex is to be locked, and {@code false} if unlocked
128 	 */
129 	public void lock(V v, boolean state) {
130 		for(Layout<V,E> layout : layouts.keySet()) {
131 			if(layout.getGraph().getVertices().contains(v)) {
132 				layout.lock(v, state);
133 			}
134 		}
135 		delegate.lock(v, state);
136 	}
137 
138 	public void reset() {
139 		for(Layout<V,E> layout : layouts.keySet()) {
140 			layout.reset();
141 		}
142 		delegate.reset();
143 	}
144 
145 	public void setGraph(Graph<V, E> graph) {
146 		delegate.setGraph(graph);
147 	}
148 
149 	public void setInitializer(Function<V, Point2D> initializer) {
150 		delegate.setInitializer(initializer);
151 	}
152 
153 	public void setLocation(V v, Point2D location) {
154 		boolean wasInSublayout = false;
155 		for(Layout<V,E> layout : layouts.keySet()) {
156 			if(layout.getGraph().getVertices().contains(v)) {
157 				Point2D center = layouts.get(layout);
158 				// transform by the layout itself, but offset to the
159 				// center of the sublayout
160 				Dimension d = layout.getSize();
161 
162 				AffineTransform at = 
163 					AffineTransform.getTranslateInstance(-center.getX()+d.width/2,-center.getY()+d.height/2);
164 				Point2D localLocation = at.transform(location, null);
165 				layout.setLocation(v, localLocation);
166 				wasInSublayout = true;
167 			}
168 		}
169 		if(wasInSublayout == false && getGraph().getVertices().contains(v)) {
170 			delegate.setLocation(v, location);
171 		}
172 	}
173 
174 	public void setSize(Dimension d) {
175 		delegate.setSize(d);
176 	}
177 	
178 	/**
179 	 * @return a map from each {@code Layout} instance to its center point.
180 	 */
181 	public Map<Layout<V,E>,Point2D> getLayouts() {
182 		return layouts;
183 	}
184 
185 	/**
186 	 * Returns the location of the vertex.  The location is specified first
187 	 * by the sublayouts, and then by the base layout if no sublayouts operate
188 	 * on this vertex.
189 	 * @return the location of the vertex
190 	 */
191 	public Point2D apply(V v) {
192 		boolean wasInSublayout = false;
193 		for(Layout<V,E> layout : layouts.keySet()) {
194 			if(layout.getGraph().getVertices().contains(v)) {
195 				wasInSublayout = true;
196 				Point2D center = layouts.get(layout);
197 				// transform by the layout itself, but offset to the
198 				// center of the sublayout
199 				Dimension d = layout.getSize();
200 				AffineTransform at = 
201 					AffineTransform.getTranslateInstance(center.getX()-d.width/2,
202 							center.getY()-d.height/2);
203 				return at.transform(layout.apply(v),null);
204 			}
205 		}
206 		if(wasInSublayout == false) {
207 			return delegate.apply(v);
208 		}
209 		return null;
210 	
211 	}
212 
213 	/**
214 	 * @return {@code true} iff the delegate layout and all sublayouts are done
215 	 */
216 	public boolean done() {
217 		for (Layout<V,E> layout : layouts.keySet()) {
218 			if (layout instanceof IterativeContext) {
219 				if (! ((IterativeContext) layout).done() ) {
220 					return false;
221 				}
222 			}
223 		}
224 		if(delegate instanceof IterativeContext) {
225 			return ((IterativeContext)delegate).done();
226 		}
227 		return true;
228 	}
229 
230 	/**
231 	 * Call step on any sublayout that is also an IterativeContext and is not done
232 	 */
233 	public void step() {
234 		for(Layout<V,E> layout : layouts.keySet()) {
235 			if(layout instanceof IterativeContext) {
236 				IterativeContext context = (IterativeContext)layout;
237 				if(context.done() == false) {
238 					context.step();
239 				}
240 			}
241 		}
242 		if(delegate instanceof IterativeContext) {
243 			IterativeContext context = (IterativeContext)delegate;
244 			if(context.done() == false) {
245 				context.step();
246 			}
247 		}
248 	}
249 	
250 }