public override void UpdateVisualization() { List<string>[] chartLabels = null; double[] dataArray = null; List<string> xLabels = new List<string>(); // Get the Factor Labels chartLabels = GetConfigDisplayLabels(); if (chartLabels == null) { return; } int xCount = 0; if (chartLabels[2] == null) { xCount = 1; } else { xCount = chartLabels[2].Count; } // Obtain grid if ((configDisplayPanel.Children == null) || (configDisplayPanel.Children.Count != 1) || (!(configDisplayPanel.Children[0] is Grid))) { return; } Grid grid = configDisplayPanel.Children[0] as Grid; //Get any old multi level bow tie in the grid var visualizationsToBeRemoved = grid.Children.OfType<Aptima.Visualization.MultilevelBowTie>(); //remove the old multi level bow tie from the grid visualizationsToBeRemoved.ToList().ForEach(x => grid.Children.Remove(x)); // Remove old labels var visualizationsToBeRemoved2 = grid.Children.OfType<Label>(); visualizationsToBeRemoved2.ToList().ForEach(x => grid.Children.Remove(x)); double bowTieWidth = configDisplay.Width - legendWidth; double bowTieHeight = configDisplay.Height; Aptima.Visualization.MultilevelBowTie bowTie = new Aptima.Visualization.MultilevelBowTie(); bowTie.center = new Point(bowTieWidth / 2, bowTieHeight / 2); bowTie.MaxRadius = Math.Min(bowTieWidth / 2, bowTieHeight / 2); bowTie.MaxRadius -= bowTieMargin; for (int z = 0; z < chartLabels[0].Count; z++) { HierarchicalValue rootHValue = null; HierarchicalValue outerRim1 = null; HierarchicalValue outerRim2 = null; // Set Factor 1, which is overall total split on two factors GetConfigDisplayData(z, ref dataArray); rootHValue = new HierarchicalValue(chartLabels[0][z] + " = " + dataArray[chartLabels[1].Count * xCount - 1], 0, chartLabels[0][z]); if (z == 0) { bowTie.LeftTotal = dataArray[chartLabels[1].Count * xCount - 1]; bowTie.left = rootHValue; } else if (z == 1) { bowTie.RightTotal = dataArray[chartLabels[1].Count * xCount - 1]; bowTie.right = rootHValue; } // Factor 2 for (int y = 0; y < chartLabels[1].Count - 1; y++) { outerRim1 = new HierarchicalValue(chartLabels[1][y] + " = " + dataArray[xCount * y + xCount - 1].ToString(), dataArray[xCount * y + xCount - 1], chartLabels[1][y]); rootHValue.children.Add(outerRim1); if (xCount > 1) { // Factor 3 for (int x = 0; x < xCount - 1; x++) { outerRim2 = new HierarchicalValue(chartLabels[2][x] + " = " + dataArray[xCount * y + x].ToString(), dataArray[xCount * y + x], chartLabels[2][x]); outerRim1.children.Add(outerRim2); } } } } //regenerate multi level bow tie canvas bowTie.generate(); // Add color legend int legendRow = 0; int legendRowCount = 0; // Count number of rows; for (int i = 0; i < chartLabels.Length; i++) { if (chartLabels[i] == null) { continue; } for (int j = 0; j < chartLabels[i].Count; j++) { if (((chartLabels[i][j].CompareTo("All")) == 0) || ((chartLabels[i][j].CompareTo("Team")) == 0) || ((chartLabels[i][j].CompareTo("Total")) == 0)) { continue; } legendRowCount += 2; } legendRowCount += 2; } for (int i = 0; i < chartLabels.Length; i++) { if (chartLabels[i] == null) { continue; } for (int j = 0; j < chartLabels[i].Count; j++) { if (((chartLabels[i][j].CompareTo("All")) == 0) || ((chartLabels[i][j].CompareTo("Team")) == 0) || ((chartLabels[i][j].CompareTo("Total")) == 0)) { continue; } // Show Label Label l = new Label(); Typeface myTypeface = new Typeface(l.FontFamily.ToString()); FormattedText ft = new FormattedText(chartLabels[i][j], CultureInfo.CurrentCulture, FlowDirection.LeftToRight, myTypeface, l.FontSize, Brushes.Black); l.Content = chartLabels[i][j]; l.HorizontalContentAlignment = HorizontalAlignment.Left; l.VerticalAlignment = VerticalAlignment.Top; double legendTopMargin = (bowTieHeight - topMargin - ft.Height * legendRowCount) / 2; l.RenderTransform = new TranslateTransform(bowTieWidth - legendWidth + 25, topMargin + legendTopMargin + legendRow * ft.Height); Grid.SetColumn(l, 0); Grid.SetRow(l, 0); grid.Children.Add(l); // Show color box Rectangle r = new Rectangle(); r.Width = 20; r.Height = 20; if (bowTie.Brushes.Keys.Contains(chartLabels[i][j])) { r.Fill = bowTie.Brushes[chartLabels[i][j]]; } else { r.Fill = System.Windows.Media.Brushes.White; } r.Stroke = System.Windows.Media.Brushes.Black; r.StrokeThickness = 0.5; r.HorizontalAlignment = HorizontalAlignment.Left; r.VerticalAlignment = VerticalAlignment.Top; r.RenderTransform = new TranslateTransform(bowTieWidth - legendWidth, topMargin + legendTopMargin + legendRow * ft.Height); Grid.SetColumn(r, 0); Grid.SetRow(r, 0); grid.Children.Add(r); legendRow += 2; } legendRow += 2; } Grid.SetColumn(bowTie, 0); Grid.SetRow(bowTie, 0); grid.Children.Add(bowTie); }
/// <summary> /// Recursively generates the wedges for each node in the hierarchical value tree. /// </summary> /// <param name="node">The node from which to create a wedge.</param> /// <param name="innerRadius">The inner radius of the wedge.</param> /// <param name="radius">The radius length (poor naming!).</param> /// <param name="arc">The arc of the wedge.</param> /// <param name="rotationalAngle">The rotational angle of the wedge (rotational transform, essentially).</param> private void generate(HierarchicalValue node, double innerRadius, double radius, double arc, double rotationalAngle) { Wedge wedge = new Wedge(); wedge.center = center; wedge.innerRadius = innerRadius; wedge.outerRadius = innerRadius + radius; wedge.sweep = arc; wedge.rotationAngle = rotationalAngle; //use a default black stroke around each wedge wedge.Stroke = System.Windows.Media.Brushes.Black; wedge.StrokeThickness = 0.5; wedge.Fill = brushes[node.tag]; //lookup the unique color based on the tag for this node wedge.ToolTip = node.name; wedges.Add(wedge); //arc percentage for children... var h = rotationalAngle; if (rotationalAngle < 180.0) { //create wedges for all of the children of this node foreach (HierarchicalValue child in node.children) { //sweep is related to the value the child contributes to the parent value var angle = arc * (child.totalValues() / (node.totalValues() - node.value)); generate(child, wedge.outerRadius, radius, angle, h); h += angle; //advance around the parent's arc } } else { // Process in reverse order //create wedges for all of the children of this node for(int i = node.children.Count - 1;i >= 0;i--) { HierarchicalValue child = node.children[i]; //sweep is related to the value the child contributes to the parent value var angle = arc * (child.totalValues() / (node.totalValues() - node.value)); generate(child, wedge.outerRadius, radius, angle, h); h += angle; //advance around the parent's arc } } }