private void SquarifyChildren(List <TreemapNode> children, List <TreemapNode> row, double containerWidth, double containerHeight) { double currentX = 0; double currentY = 0; double totalArea = 0; double width = 0; double height = 0; double aspectRatio = 0; bool drawVertically = DrawVertically(containerWidth, containerHeight); // add next child to the current row row.Add(children.ElementAt(0)); foreach (TreemapNode element in row) { totalArea += element.NormalisedArea; } if (drawVertically) { width = totalArea / containerHeight; height = row.Last().NormalisedArea / width; } else { height = totalArea / containerWidth; width = row.Last().NormalisedArea / height; } aspectRatio = AspectRatio(height, width); // Compare aspect ratios to decide whether to accept or reject the rectangle if (children.Count > 1 && _drawingArea.AspectRatio == 0 || aspectRatio < _drawingArea.AspectRatio) { // we have improved the aspect ratio, so update the latest accepted values and continue _drawingArea.AspectRatio = aspectRatio; if (drawVertically) { _drawingArea.RowSize = width; } else { _drawingArea.RowSize = height; } SquarifyChildren(children.Skip(1).ToList(), row, containerWidth, containerHeight); } else { // lockout row - remove last entry as it has been rejected, but // only if it is not the last element, which just takes up the remaining space if (children.Count > 1) { int lastElement = row.Count - 1; row.RemoveAt(lastElement); } else { // This is the last data entry - update row size if (drawVertically) { _drawingArea.RowSize = width; } else { _drawingArea.RowSize = height; } } // Create a new row to add the nodes into TreemapRow lockoutRow = new TreemapRow(); // create nodes for each element in the row and calculate the sizes foreach (TreemapNode element in row) { double nodeSize = element.NormalisedArea / _drawingArea.RowSize; if (drawVertically) { element.NodeWidth = _drawingArea.RowSize; element.NodeHeight = nodeSize; element.XPos = 0; element.YPos = 0; } else { element.NodeWidth = nodeSize; element.NodeHeight = _drawingArea.RowSize; element.XPos = 0; element.YPos = 0; } // assign event handlers element.RightTapped += treemapNode_RightTapped; element.XPos = currentX + _offsetChildX; element.YPos = currentY + _offsetChildY; lockoutRow.AddNodeToRow(element); if (drawVertically) { currentY += nodeSize; } else { currentX += nodeSize; } } // Add row to collection _childRows.Add(lockoutRow); // Update remaning screen width and offsets, reset aspect ratio ready for the new row if (drawVertically) { containerWidth -= _drawingArea.RowSize; _offsetChildX += _drawingArea.RowSize; } else { containerHeight -= _drawingArea.RowSize; _offsetChildY += _drawingArea.RowSize; } _drawingArea.AspectRatio = 0; row.Clear(); // Continue if we have more than 1 data entry remaining if (children.Count > 1) { SquarifyChildren(children, row, containerWidth, containerHeight); } } }
// Squarified treemap algorithm implementation private void Squarify(List<TreemapNode> children, List<TreemapNode> row) { double currentX = 0; double currentY = 0; double totalArea = 0; double width = 0; double height = 0; double aspectRatio = 0; bool drawVertically = DrawVertically(); // add next child to the current row row.Add(children.ElementAt(0)); foreach (TreemapNode element in row) { totalArea += element.NormalisedArea; } if (drawVertically) { width = totalArea / _drawingArea.Height; height = row.Last().NormalisedArea / width; } else { height = totalArea / _drawingArea.Width; width = row.Last().NormalisedArea / height; } aspectRatio = AspectRatio(height, width); // Compare aspect ratios to decide whether to accept or reject the rectangle if (_drawingArea.AspectRatio == 0 || aspectRatio < _drawingArea.AspectRatio) { // we have improved the aspect ratio, so update the latest accepted values and continue _drawingArea.AspectRatio = aspectRatio; if (drawVertically) { _drawingArea.RowSize = width; } else { _drawingArea.RowSize = height; } Squarify(children.Skip(1).ToList(), row); } else { // lockout row - remove last entry as it has been rejected, but // only if it is not the last element, which just takes up the remaining space if (children.Count > 1) { int lastElement = row.Count - 1; row.RemoveAt(lastElement); } else { // This is the last data entry - update row size if (drawVertically) { _drawingArea.RowSize = width; } else { _drawingArea.RowSize = height; } } // Create a new row to add the nodes into TreemapRow lockoutRow = new TreemapRow(); // create nodes for each element in the row and calculate the sizes foreach (TreemapNode element in row) { double nodeSize = element.NormalisedArea / _drawingArea.RowSize; if (drawVertically) { element.NodeWidth = _drawingArea.RowSize; element.NodeHeight = nodeSize; element.XPos = 0; element.YPos = 0; } else { element.NodeWidth = nodeSize; element.NodeHeight = _drawingArea.RowSize; element.XPos = 0; element.YPos = 0; } element.XPos = currentX + _offsetX; element.YPos = currentY + _offsetY; lockoutRow.AddNodeToRow(element); if (drawVertically) { currentY += nodeSize; } else { currentX += nodeSize; } } // Add row to collection _rows.Add(lockoutRow); // Update remaning screen width and offsets, reset aspect ratio ready for the new row if (drawVertically) { _drawingArea.Width -= _drawingArea.RowSize; _offsetX += _drawingArea.RowSize; } else { _drawingArea.Height -= _drawingArea.RowSize; _offsetY += _drawingArea.RowSize; } _drawingArea.AspectRatio = 0; row.Clear(); // Continue if we have more than 1 data entry remaining if (children.Count > 1) { Squarify(children, row); } } }