//===================================================================// // Constructors // //===================================================================// /// <summary> /// Create a new ContourTree from the specified JoinTree and SplitTree. /// </summary> /// <remarks> /// A small warning: this will not work properly if there are only two /// nodes in both the split and join trees. /// </br></br> /// A different small warning: at present, the contour tree constructor /// does not actually fill the "merging" list with its merge nodes. /// I didn't need it filled out for my purposes, so I didn't make it work. /// </remarks> /// <param name="joinTree">the join tree</param> /// <param name="splitTree">the split tree</param> public ContourTree(JoinTree joinTree, SplitTree splitTree) { this.maxValue = joinTree.maxValue; this.minValue = joinTree.minValue; JoinTree cloneJoinTree = new JoinTree( joinTree); SplitTree cloneSplitTree = new SplitTree(splitTree); List<TreeNode> upLeaves = new List<TreeNode>( cloneJoinTree.GetParentless()); List<TreeNode> downLeaves = new List<TreeNode>(cloneSplitTree.GetChildless()); List<BlobOfContourNodes> blobList = new List<BlobOfContourNodes>(); //===============================================================// // Add parentless nodes from join tree. //===============================================================// // For each of the parentless nodes in the join tree, foreach (TreeNode upLeaf in upLeaves) { // copy the node and add it to the contour tree. ContourNode newNode = new ContourNode(upLeaf); if (this.topMostLeaf == null) { this.topMostLeaf = newNode; } // Check if the child of the parentless node has already been // added to the contour tree. bool matchFound = false; int tempIndex = 0; while (!matchFound && tempIndex < blobList.Count) { // If a match is found, if (blobList[tempIndex].ContainsMatch(upLeaf.GetChildren()[0])) { // set the flag, matchFound = true; // make the parentless node a branch of the match, blobList[tempIndex].GetMostRecentlyAddedNode().AddBranch(newNode); // make the match the trunk of the parentless node, newNode.SetTrunk(blobList[tempIndex].GetMostRecentlyAddedNode()); // and add the parentless node to the correct blob. ((List<ContourNode>)blobList[tempIndex]).Add(newNode); } tempIndex++; } // If a match was not found, make a new blob. if (!matchFound) { // Create a BlobOfContourNodesnew blob and a new node for the trunk. BlobOfContourNodes newBlob = new BlobOfContourNodes(newNode); ContourNode newTrunk = new ContourNode(upLeaf.GetChildren()[0]); // Set up branch / trunk relationships. newNode.SetTrunk(newTrunk); newTrunk.AddBranch(newNode); // Add the new node to the new blob. newBlob.Add(newTrunk); // Add the new blob to the blob list. blobList.Add(newBlob); } // Remove the parentless node from both the split tree and the join tree. cloneSplitTree.RemoveMatch(upLeaf); cloneJoinTree.RemoveMatch(upLeaf); } // end of adding parentless nodes to the contour tree //===============================================================// // Add childless nodes from split tree. //===============================================================// foreach (TreeNode downLeaf in downLeaves) { // Copy the node and add it to the contour tree. ContourNode newNode = new ContourNode(downLeaf); if (this.botMostLeaf == null) { this.botMostLeaf = newNode; } // Check if the parent of the childless node has already been // added to the contour tree. bool matchFound = false; int tempIndex = 0; while (!matchFound && tempIndex < blobList.Count) { // If a match is found, if (blobList[tempIndex].ContainsMatch(downLeaf.GetParents()[0])) { // set the flag, matchFound = true; // make the childless node a branch of the match, blobList[tempIndex].GetMostRecentlyAddedNode().AddBranch(newNode); // make the match the trunk of the childless node, newNode.SetTrunk(blobList[tempIndex].GetMostRecentlyAddedNode()); // and add the childless node to the correct blob. ((List<ContourNode>)blobList[tempIndex]).Add(newNode); } tempIndex++; } // If a match was not found, make a new blob. if (!matchFound) { // Create a new blob and a new node for the branch. BlobOfContourNodes newBlob = new BlobOfContourNodes(newNode); ContourNode newTrunk = new ContourNode(downLeaf.GetParents()[0]); // Set up trunk / branch relationships. newNode.SetTrunk(newTrunk); newTrunk.AddBranch(newNode); // Add the new node to the new blob. newBlob.Add(newTrunk); // Add the new blob to the blob list. blobList.Add(newBlob); } // Remove the childless node from both the split tree and the join tree. cloneSplitTree.RemoveMatch(downLeaf); cloneJoinTree.RemoveMatch(downLeaf); } // end of adding childless nodes to the contour tree //===============================================================// // Add additional nodes from join tree. //===============================================================// // Add nodes from the Join tree until the join tree has no more // branches to add and only the trunk remains. // Please note: this means the node currently being examined will // always have a valid, non-null child. while (cloneJoinTree.GetParentless().Count > 1) { // Store the nodes we'll be working with. List<TreeNode> oldLevel = cloneJoinTree.GetParentless(); foreach (TreeNode oldNode in oldLevel) { // Store the child of the node being worked with. TreeNode oldChild = oldNode.GetChildren()[0]; // The blob that contains the current node. BlobOfContourNodes containingBlob = null; // The blob that contains the current node's child. BlobOfContourNodes trunkBlob = null; // Look through the blobs until both the containingBlob and // trunkBlob are found, or all of the blobs have been examined. int index = 0; while ((containingBlob == null || trunkBlob == null) && index < blobList.Count) { // Store current blob and its most recently added node in // temporary variables to avoid extra function calls. BlobOfContourNodes currentBlob = blobList[index]; ContourNode mostRecent = currentBlob.GetMostRecentlyAddedNode(); // Check if current blob is containingBlob. if (mostRecent.Matches(oldNode)) { containingBlob = currentBlob; } // Check if current blob is childBlob. else if (oldChild.Matches(mostRecent)) { trunkBlob = currentBlob; } index++; } // If trunkBlob was not found, make a new node for the trunk // and add it to the contour tree and the containingBlob. if (trunkBlob == null) { ContourNode newNode = containingBlob.GetMostRecentlyAddedNode(); ContourNode newTrunk = new ContourNode(oldChild); // Set trunk / branch relations. newNode.SetTrunk(newTrunk); newTrunk.AddBranch(newNode); // Add newTrunk to the blob. containingBlob.Add(newTrunk); } // If the trunkBlob was found, merge the trunkBlob and // containingBlob blobs. else { ContourNode newNode = containingBlob.GetMostRecentlyAddedNode(); ContourNode newTrunk = trunkBlob.GetMostRecentlyAddedNode(); // Set branch / trunk relations. newNode.SetTrunk(newTrunk); newTrunk.AddBranch(newNode); // Make a list of the two blobs so the blob merge // constructor can be used. List<BlobOfContourNodes> paramList = new List<BlobOfContourNodes>(); paramList.Add(containingBlob); paramList.Add(trunkBlob); // This prevents the newTrunk from being double-listed // in the new blob. trunkBlob.Remove(newTrunk); // Make the new blob, containing all the nodes from the // trunk and containing blobs, with newTrunk as root. BlobOfContourNodes newBlob = new BlobOfContourNodes(newTrunk, paramList); // Update the blob list. blobList.Remove(containingBlob); blobList.Remove(trunkBlob); blobList.Add(newBlob); } // Either way, remove the node from both trees. cloneSplitTree.RemoveMatch(oldNode); cloneJoinTree.RemoveMatch(oldNode); } } // end of adding additional nodes from the join tree //===============================================================// // Add remaining nodes from split tree. //===============================================================// // Now just add all the remaining nodes in the split tree, from bottom to top. while (cloneSplitTree.GetChildless().Count > 0) { // Store the node we'll be working with. TreeNode oldNode = cloneSplitTree.GetChildless()[0]; // Also store the parent of the node being worked with, unless // it has no parent, in which case we do different stuff later. TreeNode oldParent = oldNode.GetParents().Count == 0 ? null : oldNode.GetParents()[0]; // The blob that contains the current node. BlobOfContourNodes containingBlob = null; // The blob that contains the current node's parent, // unless there was no parent. BlobOfContourNodes trunkBlob = null; // Look through the blobs until both the containingBlob and // trunkBlob are found, or all the blobs have been examined, // or containingBlob is found and there was no parent. int index = 0; while ((containingBlob == null || (trunkBlob == null && oldParent != null)) && index < blobList.Count) { // Store current blob and its most recently added node in // temporary variables to avoid extra function calls. BlobOfContourNodes currentBlob = blobList[index]; ContourNode mostRecent = currentBlob.GetMostRecentlyAddedNode(); // Check if current blob is containingBlob. if (mostRecent.Matches(oldNode)) { containingBlob = currentBlob; } // Check if current blob is trunkBlob. else if (oldParent != null && oldParent.Matches(mostRecent)){ trunkBlob = currentBlob; } index++; } // If trunkBlob was not found and a parent node exists, // make a new node for the parent and add it to the contour // tree and the containingBlob. if (trunkBlob == null && oldParent != null) { ContourNode newNode = containingBlob.GetMostRecentlyAddedNode(); ContourNode newTrunk = new ContourNode(oldParent); // Set trunk / branch relations. newNode.SetTrunk(newTrunk); newTrunk.AddBranch(newNode); // Add newParent to the blob. containingBlob.Add(newTrunk); } // If the trunkBlob was found, merge the trunkBlob and // containingBlob blobs. else if (trunkBlob != null) { ContourNode newNode = containingBlob.GetMostRecentlyAddedNode(); ContourNode newTrunk = trunkBlob.GetMostRecentlyAddedNode(); // Set branch / trunk relations. newNode.SetTrunk(newTrunk); newTrunk.AddBranch(newNode); // Make a list of the two blobs so the blob merge // constructor can be used. List<BlobOfContourNodes> paramList = new List<BlobOfContourNodes>(); paramList.Add(containingBlob); paramList.Add( trunkBlob); // This prevents the newParent from being double-listed // in the new blob. trunkBlob.Remove(newTrunk); // Make the new blob, containing all the nodes from the // trunk and containing blobs, with newTrunk as root. BlobOfContourNodes newBlob = new BlobOfContourNodes(newTrunk, paramList); // Update the blob list. blobList.Remove(containingBlob); blobList.Remove(trunkBlob); blobList.Add(newBlob); } // Otherwise, if there was no parent node, store the current // node as the "root" of the contour tree. else { this.root = containingBlob.GetMostRecentlyAddedNode(); } // No matter what, remove the node from both trees. cloneSplitTree.RemoveMatch(oldNode); cloneJoinTree.RemoveMatch(oldNode); } }
/// <summary> /// Draws a line between two nodes. /// </summary> /// <param name="node1">one of the two nodes between wich to draw a line</param> /// <param name="node2">the other of the two nodes between wich to draw a line</param> /// <param name="graphics">the Graphics object that hadles drawing for this view</param> /// <param name="pen">the pen that determines the color and thickness of this line</param> private void drawLine(ContourNode node1, ContourNode node2, Graphics graphics, Pen pen) { graphics.DrawLine(pen, getX(node1.t), getY(node1.value), getX(node2.t), getY(node2.value)); }
/// <summary> /// Draws a branch in a tree. This function is recursive. /// </summary> /// <param name="branch">the branch to draw</param> /// <param name="graphics">allows graphics to be drawn</param> /// <param name="pen">for drawing lines</param> /// <param name="nodeFill">for drawing nodes</param> /// <param name="direction">the side of the tree the node is drawn on</param> /// <param name="slotsUsed">the number of slots on this side of the tree that have already been used</param> /// <param name="pixelsPerSlot">the number of pixels in a slot</param> /// <param name="childPositionX">the x-position of this branch's child</param> private void drawBranch(ContourNode branch, Graphics graphics, Pen pen, Brush nodeFill, int direction, int slotsUsed, float pixelsPerSlot, float childPositionX) { // calculate some useful numbers int slotsInThisBranch = branch.GetNumLeaves(); float nodeCenterX = (this.Width / 2) + (direction * (slotsUsed + ((slotsInThisBranch + 1) / 2)) * pixelsPerSlot); // draw this node graphics.FillEllipse(nodeFill, nodeCenterX - (AbstractTreeView.NODE_DIAMETER / 2), ValueToYCoordinate(branch.value) - (AbstractTreeView.NODE_DIAMETER / 2), AbstractTreeView.NODE_DIAMETER, AbstractTreeView.NODE_DIAMETER); // Draw lines from the branch node back to its trunk node. graphics.DrawLine(pen, childPositionX, ValueToYCoordinate(branch.GetTrunk().value), nodeCenterX, ValueToYCoordinate(branch.GetTrunk().value)); graphics.DrawLine(pen, nodeCenterX, ValueToYCoordinate(branch.GetTrunk().value), nodeCenterX, ValueToYCoordinate(branch.value)); int slotsUsedAfterLeaf = slotsUsed; foreach (ContourNode leaf in branch.GetBranches()) { drawBranch(leaf, graphics, pen, nodeFill, direction, slotsUsedAfterLeaf, pixelsPerSlot, nodeCenterX); slotsUsedAfterLeaf += leaf.GetNumLeaves(); } }
/// <summary> /// Draws an ellipse to represent a given node. /// </summary> /// <param name="node">the node in the treee to display</param> /// <param name="graphics">the Graphics object that hadles drawing for this view</param> /// <param name="nodeFill">the brush that determines the color of this node</param> private void drawNode(ContourNode node, Graphics graphics, Brush nodeFill) { graphics.FillEllipse(nodeFill, getX(node.t) - NODE_RADIUS, getY(node.value) - NODE_RADIUS, NODE_RADIUS * 2, NODE_RADIUS * 2); }