package org.mindswap.swoop.utils.graph.hierarchy; import java.awt.Color; import java.awt.Graphics2D; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; /* * Created on Jul 17, 2005 * * TODO To change the template for this generated file go to * Window - Preferences - Java - Code Style - Code Templates */ /** * @author Dave Wang * * TODO To change the template for this generated type comment go to * Window - Preferences - Java - Code Style - Code Templates */ public class HierarchicalVertexDataRenderer implements SizeConstants { //private static final Color classColor = new Color(128, 128, 128); //private static final Color selectedClassColor = new Color(255, 255, 225); //private static final Color highlightedColor = new Color(128, 255, 255); private static HierarchicalVertexDataRenderer myInstance = null; public static HierarchicalVertexDataRenderer getInstance() { if (myInstance == null) return new HierarchicalVertexDataRenderer(); else return myInstance; } private HierarchicalVertexDataRenderer() {} public void render( Graphics2D g2d, int x, int y, ClassTreeNode node) { int r = node.getSubTreeSize() * SizeConstants.unitSize; int numChildren = node.getNumChildren(); // center of top class node (OWL:Thing )is the same as the ontology node // this setting is important. Subsequent mouse clicks relies on this to // find its children. node.setLocalCenterPoint(x, y); Color aColor = node.getFillColor(); TreeNodeSizeInfo info = node.getChildNodeSizeInfo(); for (int i = 0; i < numChildren; i++ ) { ClassTreeNode child = node.getChild( i ); render(g2d, x, y, r, child, numChildren - 1, i, info, aColor ); } } // x,y are parent's center public void render( Graphics2D g2d, int x, int y, int radius, ClassTreeNode node, int numSiblings, int index, TreeNodeSizeInfo siblingInfo, Color currentColor) { int r = node.getRadius(); // if node is selected/highlighted, use those colors :) // if parent is 'selected' mark this child with selected color as well if ( currentColor != node.getColorScheme().getTreeNodeSelectFillColor( node ) ) currentColor = node.getFillColor(); g2d.setColor( currentColor ); // if a node is listBrowsed, we get that color and overrides the others if ( node.getIsListBrowsed() ) g2d.setColor( node.getFillColor() ); AffineTransform old_xform = g2d.getTransform(); double rotationAngle = 0; g2d.translate(x, y); AffineTransform localForm = node.getLocalXForm(); // if nodes do not have localForm yet, compute it; otherwise, reuse it if ( localForm == null ) { if (numSiblings == 0) { localForm = AffineTransform.getTranslateInstance(0,0); // no op transform to prevent NullPointerException } else { double increAngle = 360.00/(numSiblings + 1); double currentAngle = increAngle * index; if (siblingInfo.areSameSize) // all siblings are the same size. { rotationAngle = currentAngle * 2 * Math.PI / 360.00; localForm = AffineTransform.getRotateInstance( rotationAngle ); AffineTransform translation = AffineTransform.getTranslateInstance( radius/2 , 0 ); localForm.concatenate( translation ); //g2d.transform( localForm ); } else if ( index == 0) // largest node { rotationAngle = currentAngle * 2 * Math.PI / 360.00; localForm = AffineTransform.getRotateInstance( rotationAngle ); AffineTransform translation = AffineTransform.getTranslateInstance( radius - r - (SizeConstants.unitSize/2), 0 ); localForm.concatenate( translation ); //g2d.transform( localForm ); } else // has at least one sibling that's larger { if ( siblingInfo.isFirstChildBig ) // largest sibling dominates { ClassTreeNode bigSib = node.getParent().getChild(0); Point2D.Double bigSibCenter = bigSib.getLocalCenter(); if ( index == 1) { double angle = 0; double increment = 10; boolean isFitting = false; while ( !isFitting ) { rotationAngle = (90 + 20 + angle) * 2 * Math.PI / 360.0; localForm = AffineTransform.getTranslateInstance( bigSibCenter.x, bigSibCenter.y ); localForm.concatenate( AffineTransform.getRotateInstance(rotationAngle)); localForm.concatenate( AffineTransform.getTranslateInstance( node.getRadius() + bigSib.getRadius() + SizeConstants.unitSize, 0)); angle = angle + increment; Point2D.Double origin = new Point2D.Double(0, 0); origin = (Point2D.Double)localForm.transform(origin, origin); double dist = Math.sqrt( Math.pow( origin.x, 2) + Math.pow(origin.y, 2) ); if ( dist <= (radius - node.getRadius())) isFitting = true; } //g2d.transform( localForm ); } else { /* Computing the angle needed to rotate the current node by wrt the * largest sibling. * Computing the distance needed to translate the current node by * wrt to the center of the largest sibling. * * The computed distance is the same distance between current node's * previous sibling to the largest sibling. * The compute rotation gives a distance between the current node's * center and its previous sibling's center of (r1 + r2 + spacing*3), * where r1 is preivous sib's radius, r2 is current node's radius, and * spaceing = sizeConstants.unitSize/2 * * Computing theta relies on the fact that the triangle A-B-C * (where A is the center of the largest sibling, B is the * center of the previous sibling, and C is the center of the * projected current node), is isoscele. Splitting the triangle * by bisecting its lone odd angle into 2 right triangles, we use * arcsine to calculate the required angle. * */ ClassTreeNode prevSib = node.getParent().getChild( index - 1); int sibRadius = prevSib.getRadius(); int thisRadius = node.getRadius(); int radiiSum = sibRadius + thisRadius + SizeConstants.unitSize/2 + SizeConstants.unitSize; Point2D.Double sibCenter = prevSib.getLocalCenter(); double dCenterToSib = Math.sqrt( Math.pow(bigSibCenter.x - sibCenter.x, 2) + Math.pow(bigSibCenter.y - sibCenter.y, 2) ); double theta = Math.asin( (radiiSum/2) / dCenterToSib) * 2; rotationAngle = theta + prevSib.getRotationAngle(); localForm = AffineTransform.getTranslateInstance( bigSibCenter.x, bigSibCenter.y ); localForm.concatenate( AffineTransform.getRotateInstance(rotationAngle)); localForm.concatenate( AffineTransform.getTranslateInstance( dCenterToSib, 0)); //g2d.transform( localForm ); } } else // largest sibling isn't too dominating { ClassTreeNode prevSib = node.getParent().getChild( index - 1); int sibRadius = prevSib.getRadius(); int thisRadius = node.getRadius(); int radiiSum = sibRadius + thisRadius + SizeConstants.unitSize/2; Point2D.Double sibCenter = prevSib.getLocalCenter(); double dCenterToSib = Math.sqrt( Math.pow(sibCenter.x, 2) + Math.pow(sibCenter.y, 2) ); double theta = Math.asin( (radiiSum/2) / dCenterToSib) * 2; rotationAngle = theta + prevSib.getRotationAngle(); localForm = AffineTransform.getRotateInstance( rotationAngle ); AffineTransform translation = AffineTransform.getTranslateInstance( dCenterToSib, 0); localForm.concatenate( translation ); } } } //System.out.println("Computed localXForm"); } g2d.transform( localForm ); g2d.fillOval( -r, -r, 2*r, 2*r ); g2d.setColor( node.getOutlineColor() ); g2d.drawOval( -r, -r, 2*r, 2*r ); Point2D.Double origin = new Point2D.Double(0, 0); Point2D.Double result = (Point2D.Double)localForm.transform( origin, origin); node.setLocalCenterPoint( result.x, result.y ); node.setGlobalTransform( g2d.getTransform() ); node.setRotationAngle( rotationAngle ); node.setLocalXForm( localForm ); double [] mat = new double [6]; localForm.getMatrix( mat ); int numChildren = node.getNumChildren(); //int maxChildRadius = getMaxChildRadius( node ); TreeNodeSizeInfo info = node.getChildNodeSizeInfo(); for (int i = 0; i < numChildren; i++) { ClassTreeNode childNode = node.getChild( i ); render(g2d, 0, 0, r, childNode, numChildren - 1, i, info, currentColor ); } g2d.setTransform( old_xform ); } /* private TreeNodeSizeInfo getChildNodeSizeInfo( ClassTreeNode node) { int numChildren = node.getNumChildren(); if (numChildren == 0) return null; int maxChildRadius = Integer.MIN_VALUE; ClassTreeNode largestNode = null; boolean isFirstTime = true; boolean areSameSize = true; boolean isFirstChildLarge = false; for (int i = 0; i < numChildren; i++ ) { ClassTreeNode child = node.getChild( i ); if ( i == 0 ) //largest node { if ( (2 * child.getRadius()) > (node.getRadius() * 0.75) ) isFirstChildLarge = true; } int childsize = child.getRadius(); if ((!isFirstTime) && ( maxChildRadius != childsize)) areSameSize = false; if ( maxChildRadius < childsize) { maxChildRadius = childsize; largestNode = child; } if ( isFirstTime ) isFirstTime = !isFirstTime; } TreeNodeSizeInfo info = new TreeNodeSizeInfo(); info.areSameSize = areSameSize; info.maxRadius = maxChildRadius; info.myLargestNode = largestNode; info.isFirstChildBig = isFirstChildLarge; return info; } */ }