PageRenderTime 61ms CodeModel.GetById 17ms app.highlight 38ms RepoModel.GetById 1ms app.codeStats 0ms

/edu/uncc/parsets/parsets/VisualConnectionTree.java

https://code.google.com/p/parsets/
Java | 475 lines | 289 code | 102 blank | 84 comment | 85 complexity | e6624e1dd75214c611f86edbfa08e0b4 MD5 | raw file
  1package edu.uncc.parsets.parsets;
  2
  3import java.awt.Graphics2D;
  4import java.util.ArrayList;
  5import java.util.List;
  6
  7import edu.uncc.parsets.data.CategoryHandle;
  8import edu.uncc.parsets.data.CategoryNode;
  9import edu.uncc.parsets.data.CategoryTree;
 10import edu.uncc.parsets.data.DimensionHandle;
 11import edu.uncc.parsets.parsets.RibbonState;
 12
 13/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
 14 * Copyright (c) 2009, Robert Kosara, Caroline Ziemkiewicz,
 15 *                     and others (see Authors.txt for full list)
 16 * All rights reserved.
 17 * 
 18 * Redistribution and use in source and binary forms, with or without
 19 * modification, are permitted provided that the following conditions are met:
 20 * 
 21 *    * Redistributions of source code must retain the above copyright
 22 *      notice, this list of conditions and the following disclaimer.
 23 *    * Redistributions in binary form must reproduce the above copyright
 24 *      notice, this list of conditions and the following disclaimer in the
 25 *      documentation and/or other materials provided with the distribution.
 26 *    * Neither the name of UNC Charlotte nor the names of its contributors
 27 *      may be used to endorse or promote products derived from this software
 28 *      without specific prior written permission.
 29 *      
 30 * THIS SOFTWARE IS PROVIDED BY ITS AUTHORS ''AS IS'' AND ANY
 31 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 32 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 33 * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
 34 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 35 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 36 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 37 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 38 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 39 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 40\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 41
 42public class VisualConnectionTree {
 43
 44	private VisualConnection root; 
 45	private VisualConnection test;
 46	private RibbonLayoutStyle style = RibbonLayoutStyle.BRANCHING;
 47	private RibbonState currentRState;
 48
 49	public VisualConnectionTree() {
 50		super();
 51		root = new VisualConnection();
 52		setRibbonState(RibbonState.BASIC);
 53	}
 54	
 55	public void print(VisualConnection node) {
 56		System.out.println(node.toString());
 57		for (VisualConnection child : node.getChildren())
 58			print(child);
 59	}
 60
 61	
 62	/**
 63	 * Build a visual tree based on a data tree without any layout information.
 64	 * 
 65	 * @param tree The categorical data tree on which to base the visual connections.
 66	 */
 67	public void buildConnectionTree(ArrayList<VisualAxis> axes, CategoryTree tree) {
 68		
 69		root = new VisualConnection();
 70		addChildren(root, tree.getRootNode(), axes, 1);
 71	}	
 72	
 73	private void addChildren(VisualConnection parentNode, CategoryNode dataNode, ArrayList<VisualAxis> axes, int childLevel) {
 74
 75		if (childLevel == 1) {
 76			
 77			/* The first level is made up of invisible connections.
 78			 * These can be thought of as ribbons that represent only
 79			 * a single category in the first dimension, which then 
 80			 * branch out to form the lower ribbons.  They're not visible
 81			 * because they're redundant with the first level category
 82			 * bars, but they're convenient for treating the ribbons as
 83			 * a tree. 
 84			 */
 85			for (CategoryNode child : dataNode.getChildren()) {
 86				VisualConnection newChild = parentNode.addChild(new InvisibleConnection(parentNode, child));
 87				addChildren(newChild, child, axes, childLevel+1);
 88			}
 89			
 90			orderChildren(parentNode, ((CategoricalAxis)axes.get(0)).getCategoryOrder());
 91		} else if (childLevel <= axes.size()){
 92			
 93			// The lower levels are ribbons.
 94			if(currentRState == RibbonState.BASIC){
 95				for (CategoryNode child : dataNode.getChildren()) {
 96					VisualConnection newChild = parentNode.addChild(new BasicRibbon(parentNode, child, 
 97																(CategoricalAxis)axes.get(childLevel-2), 
 98																(CategoricalAxis)axes.get(childLevel-1)));
 99
100					addChildren(newChild, child, axes, childLevel+1);
101				}
102			
103				orderChildren(parentNode, ((CategoricalAxis)axes.get(childLevel-1)).getCategoryOrder());
104			}
105			else if(currentRState == RibbonState.CURVED){
106				for (CategoryNode child : dataNode.getChildren()) {
107					VisualConnection newChild = parentNode.addChild(new CurvedRibbon(parentNode, child, 
108																(CategoricalAxis)axes.get(childLevel-2), 
109																(CategoricalAxis)axes.get(childLevel-1)));
110
111					addChildren(newChild, child, axes, childLevel+1);
112				}
113			
114				orderChildren(parentNode, ((CategoricalAxis)axes.get(childLevel-1)).getCategoryOrder());
115				
116			}
117		}
118	}
119	
120	private void orderChildren(VisualConnection parentNode,	List<CategoryHandle> categoryOrder) {
121		
122		ArrayList<VisualConnection> newList = new ArrayList<VisualConnection>();
123		
124		newList.addAll(parentNode.getChildren());
125		
126		parentNode.getChildren().clear();
127		
128		for (CategoryHandle cat : categoryOrder) {
129			for (VisualConnection ribbon : newList) {
130				if (ribbon.getNode().getToCategory().equals(cat)) {
131					parentNode.getChildren().add(ribbon);
132				}
133			}
134		}
135	}
136	
137	public void orderChildren(DimensionHandle dimension, List<CategoryHandle> order) {
138		orderChildren(dimension, order, root);
139	}
140
141	private void orderChildren(DimensionHandle dimension, List<CategoryHandle> order, VisualConnection node) {
142		if (node.getChildren() == null)
143			return;
144		
145		if (!node.getChildren().isEmpty() && node.getChildren().get(0).getNode().getToCategory().getDimension().equals(dimension))
146			orderChildren(node, order);
147		else
148			for (VisualConnection vc : node.getChildren())
149				orderChildren(dimension, order, vc);
150	}
151
152	
153	public void doLayout(int minX, int maxX, BarState currentState) {
154		if (style == RibbonLayoutStyle.BRANCHING)
155			doLayoutBranching(minX, maxX, currentState);
156		else if (style == RibbonLayoutStyle.BUNDLED)
157			doLayoutBundled(minX, maxX, currentState);
158	}
159	
160
161	public void doLayoutBranching(int minX, int maxX, BarState currentState) {
162		root.setWidth(maxX - minX);
163		layoutChildren(root, currentState);
164		setColors(root);
165	}
166
167	public void layoutChildren(VisualConnection connectionNode, BarState currentState) {		
168		for (VisualConnection child : connectionNode.getChildren())
169			if (child.getNode().isVisible()) {
170				child.setState(currentState);
171				child.layout(connectionNode.getFutureWidth());
172				layoutChildren(child, currentState);
173
174			}
175	}
176	
177	public void updateState(int minX, int maxX, BarState currentState){
178		root.setWidth(maxX - minX);
179		updateChildren(root, currentState);
180	}
181	
182	public void updateChildren(VisualConnection connectionNode, BarState currentState){
183		for(VisualConnection child : connectionNode.getChildren())
184			if(child.getNode().isVisible()){
185				child.setState(currentState);
186				updateChildren(child,currentState);
187			}
188	}
189	
190
191	/**
192	 * Lays out the ribbons in a bundled style. Basically, this means doing a
193	 * series of phased depth-first traversals on each of the top-level category
194	 * nodes.
195	 * 
196	 * @param minX
197	 *            The left bound of the display space.
198	 * @param maxX
199	 *            The right bound of the display space.
200	 */
201	public void doLayoutBundled(int minX, int maxX, BarState currentState) {
202
203		root.setWidth(maxX - minX);
204
205		// Set all the layout and completeness flags to false.
206		resetForBundling(root);
207
208		// If there are no connections, return.
209		if (!root.getChildren().isEmpty()) {
210
211			// While the root is not set to complete...
212			while (!root.isComplete()) {
213
214				// Iterate through each of the invisible nodes, each of
215				// which represents a top-level category bar. At each
216				// step, lay out the leftmost incomplete branch of the
217				// top-level node.
218				for (VisualConnection invisible : root.getChildren()) {
219
220					if (!invisible.isComplete())
221						layoutBranch(invisible, currentState, (maxX-minX));
222
223					// If this node is complete and is the last of the root's
224					// children, then we're done.
225					else if (invisible == root.getChildren().get(root.getChildren().size() - 1))
226						root.setComplete(true);
227				}
228			}
229		}
230		setColors(root);
231	}
232
233	/**
234	 * Lay out the leftmost incomplete branch of this connection node.
235	 * 
236	 * @param connectionNode
237	 */
238	public boolean layoutBranch(VisualConnection connectionNode, BarState currentState, int totalWidth) {
239
240		// If this node isn't laid out already, lay it out.
241		if (!connectionNode.isLaidOut()) {
242
243			connectionNode.layout(connectionNode.getParent().getWidth());
244			connectionNode.setLaidOut(true);
245
246			// If this is a leaf, this node is completed.
247			if (connectionNode.getChildren().isEmpty()) {
248				connectionNode.setComplete(true);
249				return true;
250			}
251		}
252
253		// If this is not a leaf, call layoutBranch on this node's
254		// leftmost incomplete child.
255		for (VisualConnection child : connectionNode.getChildren())
256			if (!child.isComplete())
257				if (layoutBranch(child, currentState, totalWidth) == false)
258					return false;
259
260		// If all of the children are complete, this node is
261		// completed.
262		connectionNode.setComplete(true);
263		
264		return true;
265	}
266	
267	public void display(Graphics2D g, float alpha) {
268		display(g, root, alpha);
269		displaySelected(g, root);
270	}
271	
272	private void display(Graphics2D g, VisualConnection node, float alpha) {
273		
274		node.paint(g, alpha);
275		
276		for (VisualConnection child : node.getChildren())
277			if (child.getNode().isVisible()) 
278				display(g, child, alpha);
279	}
280	
281	private void displaySelected(Graphics2D g, VisualConnection node) {
282		if (node.isSelected())
283			node.paintSelected(g);
284		
285		for (VisualConnection child : node.getChildren())
286			if (child.getNode().isVisible())
287				displaySelected(g, child);
288	}
289
290	public void setColors(VisualConnection connectionNode) {
291		
292		// The top level categorical axis determines the color scheme.
293		if (connectionNode.getChildren().isEmpty())
294			return;
295
296		if (connectionNode == root) {
297			for (VisualConnection childNode : connectionNode.getChildren()) {
298				childNode.setColorBrewerIndex(childNode.getNode().getToCategory().getCategoryNum() - 1);
299				setColors(childNode);
300			}
301		} else {
302			for (VisualConnection childNode : connectionNode.getChildren()) {
303				childNode.setColorBrewerIndex(connectionNode.getColorBrewerIndex());
304				setColors(childNode);
305			}
306		}
307	}
308	
309	public String highlightRibbon(int x, int y, CategoryTree dataTree) {
310		clearSelection();
311		VisualConnection selectedRibbon = highlightRibbon(x, y, root);
312		if (selectedRibbon != null) {
313			setSelected(selectedRibbon);
314			return selectedRibbon.getTooltip(dataTree.getFilteredTotal());
315		}
316		return null;
317	}
318        
319        public VisualConnection getRibbon(int x, int y, CategoryTree dataTree) {
320		clearSelection();
321		VisualConnection selectedRibbon = highlightRibbon(x, y, root);
322		if (selectedRibbon != null) {
323                        setSelected(selectedRibbon);
324			return selectedRibbon;
325		}
326		return null;
327	}
328	
329	private VisualConnection highlightRibbon(int x, int y, VisualConnection node) {
330		
331		VisualConnection returnNode = null;
332		
333		if (node.contains(x, y))
334			returnNode = node;
335		
336		for (VisualConnection child : node.getChildren()) {
337			VisualConnection temp = highlightRibbon(x, y, child);
338			if (returnNode == null)
339				returnNode = temp;
340		}
341
342		return returnNode;
343	}
344
345	/**
346	 * Sets the selection flags on a given visual connection, as well as all its
347	 * direct ancestors and descendants.
348	 * 
349	 * @param connection
350	 *            The connection to highlight.
351	 */
352	public void setSelected(VisualConnection connection) {
353		selectUp(connection);
354		selectDown(connection);
355	}
356
357	private void selectDown(VisualConnection connection) {
358
359		connection.setSelected(true);
360
361		for (VisualConnection child : connection.getChildren())
362			selectDown(child);
363	}
364
365	private void selectUp(VisualConnection connection) {
366
367		connection.setSelected(true);
368
369		if (!connection.equals(root))
370			selectUp(connection.getParent());
371	}
372	
373	public void clearSelection() {
374		clearSelection(root);
375	}
376
377	private void clearSelection(VisualConnection node) {
378
379		node.setSelected(false);
380		
381		for (VisualConnection child : node.getChildren()) 
382			clearSelection(child);
383	}
384
385	public void clearConnections() {
386		root = new VisualConnection();
387	}
388	
389	public void resetForBundling(VisualConnection connectionNode) {
390		connectionNode.setComplete(false);
391		connectionNode.setLaidOut(false);
392		
393		for (VisualConnection child : connectionNode.getChildren())
394			resetForBundling(child);
395	}
396	
397	private void selectCategory(CategoryHandle category, VisualConnection node) {
398	
399		if (!node.equals(root) && node.getNode().getToCategory().equals(category))
400			for (VisualConnection child : node.getChildren())
401				selectDown(child);
402		else
403			for (VisualConnection child : node.getChildren())
404				selectCategory(category, child);
405	}
406
407	public void selectCategory(CategoryHandle category) {
408		clearSelection();
409		selectCategory(category, root);
410	}
411	
412	
413	// added in for activecategorybar selection instead of one ribbon
414	public VisualConnection getCategoryBarNode(CategoryHandle category){
415		findCategoryBarNode(category, root);
416		return test;
417		
418	}
419	
420	public void findCategoryBarNode(CategoryHandle category, VisualConnection node){
421		if (!node.equals(root) && node.getNode().getToCategory().equals(category)){
422			test = node;
423		}
424		else
425			for (VisualConnection child : node.getChildren())
426				findCategoryBarNode(category, child);
427	
428
429	}
430	
431	/**
432	 * @param style the style to set
433	 */
434	public void setStyle(RibbonLayoutStyle style) {
435		this.style = style;
436	}
437	
438	public void moveCategory(DimensionHandle dimension, CategoryHandle category, int index) {
439		moveCategory(dimension, category, index, root);
440	}
441
442	private void moveCategory(DimensionHandle dimension, CategoryHandle category, int index, VisualConnection node) {
443		
444		if (node.getChildren() == null)
445			return;
446		
447		ArrayList<VisualConnection> children = node.getChildren();
448		
449		if (!children.isEmpty() && children.get(0).getNode().getToCategory().getDimension().equals(dimension)) {
450			
451			VisualConnection moveCat = null; 
452			
453			for (VisualConnection child : children)
454				if (child.getNode().getToCategory().equals(category))
455					moveCat = child;
456			
457			if (moveCat != null) {
458				children.remove(moveCat);
459				if (index < children.size()) 
460					children.add(index, moveCat);
461				else 
462					children.add(moveCat);
463			}
464		} else {
465			for (VisualConnection child : children) {
466				moveCategory(dimension, category, index, child);
467			}
468		}		
469	}
470	
471	public void setRibbonState(RibbonState state){
472		currentRState = state;
473	}
474
475}