using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Shapes; using Microsoft.Research.DynamicDataDisplay.Charts.Isolines; using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary; using Microsoft.Research.DynamicDataDisplay.Common; namespace Microsoft.Research.DynamicDataDisplay.Charts { /// /// Draws isolines on given two-dimensional scalar data. /// public sealed class IsolineGraph : IsolineRenderer { private static Brush labelBackground = new SolidColorBrush(Color.FromArgb(130, 255, 255, 255)); /// /// Initializes a new instance of the class. /// public IsolineGraph() { Content = content; Viewport2D.SetIsContentBoundsHost(this, true); } protected override void OnPlotterAttached() { CreateUIRepresentation(); UpdateUIRepresentation(); } private readonly Canvas content = new Canvas(); protected override void UpdateDataSource() { base.UpdateDataSource(); CreateUIRepresentation(); rebuildText = true; UpdateUIRepresentation(); } protected override void OnLineThicknessChanged() { foreach (var path in linePaths) { path.StrokeThickness = StrokeThickness; } } private List textBlocks = new List(); private List linePaths = new List(); protected override void CreateUIRepresentation() { if (Plotter2D == null) return; content.Children.Clear(); linePaths.Clear(); if (Collection != null) { DataRect bounds = DataRect.Empty; foreach (var line in Collection.Lines) { foreach (var point in line.AllPoints) { bounds.Union(point); } Path path = new Path { Stroke = new SolidColorBrush(Palette.GetColor(line.Value01)), StrokeThickness = StrokeThickness, Data = CreateGeometry(line), Tag = line }; content.Children.Add(path); linePaths.Add(path); } Viewport2D.SetContentBounds(this, bounds); if (DrawLabels) { var transform = Plotter2D.Viewport.Transform; double wayBeforeText = new Rect(new Size(2000, 2000)).ScreenToData(transform).Width; Annotater.WayBeforeText = wayBeforeText; var textLabels = Annotater.Annotate(Collection, Plotter2D.Viewport.Visible); foreach (var textLabel in textLabels) { var text = CreateTextLabel(textLabel); content.Children.Add(text); textBlocks.Add(text); } } } } private FrameworkElement CreateTextLabel(IsolineTextLabel textLabel) { var transform = Plotter2D.Viewport.Transform; Point screenPos = textLabel.Position.DataToScreen(transform); double angle = textLabel.Rotation; if (angle < 0) angle += 360; if (135 < angle && angle < 225) angle -= 180; string tooltip = textLabel.Value.ToString("F"); //String.Format("{0} at ({1}, {2})", textLabel.Text, textLabel.Position.X, textLabel.Position.Y); Grid grid = new Grid { RenderTransform = new RotateTransform(angle), Tag = textLabel, RenderTransformOrigin = new Point(0.5, 0.5), ToolTip = tooltip }; TextBlock res = new TextBlock { Text = textLabel.Value.ToString("F"), Margin = new Thickness(3,1,3,1) }; //res.Measure(SizeHelper.CreateInfiniteSize()); Rectangle rect = new Rectangle { Stroke = Brushes.Gray, Fill = labelBackground, RadiusX = 8, RadiusY = 8 }; grid.Children.Add(rect); grid.Children.Add(res); grid.Measure(SizeHelper.CreateInfiniteSize()); Size textSize = grid.DesiredSize; Point position = new Point(screenPos.X - textSize.Width / 2, screenPos.Y - textSize.Height / 2); Canvas.SetLeft(grid, position.X); Canvas.SetTop(grid, position.Y); return grid; } private Geometry CreateGeometry(LevelLine lineData) { var transform = Plotter2D.Viewport.Transform; StreamGeometry geometry = new StreamGeometry(); using (var context = geometry.Open()) { context.BeginFigure(lineData.StartPoint.DataToScreen(transform), false, false); context.PolyLineTo(lineData.OtherPoints.DataToScreenAsList(transform), true, true); } geometry.Freeze(); return geometry; } private bool rebuildText = true; protected override void OnViewportPropertyChanged(ExtendedPropertyChangedEventArgs e) { if (e.PropertyName == "Visible" || e.PropertyName == "Output") { bool isVisibleChanged = e.PropertyName == "Visible"; DataRect prevRect = isVisibleChanged ? (DataRect)e.OldValue : new DataRect((Rect)e.OldValue); DataRect currRect = isVisibleChanged ? (DataRect)e.NewValue : new DataRect((Rect)e.NewValue); // completely rebuild text only if width and height have changed many const double smallChangePercent = 0.05; bool widthChangedLittle = Math.Abs(currRect.Width - prevRect.Width) / currRect.Width < smallChangePercent; bool heightChangedLittle = Math.Abs(currRect.Height - prevRect.Height) / currRect.Height < smallChangePercent; rebuildText = !(widthChangedLittle && heightChangedLittle); } UpdateUIRepresentation(); } private void UpdateUIRepresentation() { if (Plotter2D == null) return; foreach (var path in linePaths) { LevelLine line = (LevelLine)path.Tag; path.Data = CreateGeometry(line); } var transform = Plotter2D.Viewport.Transform; Rect output = Plotter2D.Viewport.Output; DataRect visible = Plotter2D.Viewport.Visible; if (rebuildText && DrawLabels) { rebuildText = false; foreach (var text in textBlocks) { if (text.Visibility == Visibility.Visible) content.Children.Remove(text); } textBlocks.Clear(); double wayBeforeText = new Rect(new Size(100, 100)).ScreenToData(transform).Width; Annotater.WayBeforeText = wayBeforeText; var textLabels = Annotater.Annotate(Collection, Plotter2D.Viewport.Visible); foreach (var textLabel in textLabels) { var text = CreateTextLabel(textLabel); textBlocks.Add(text); if (visible.Contains(textLabel.Position)) { content.Children.Add(text); } else { text.Visibility = Visibility.Hidden; } } } else { foreach (var text in textBlocks) { IsolineTextLabel label = (IsolineTextLabel)text.Tag; Point screenPos = label.Position.DataToScreen(transform); Size textSize = text.DesiredSize; Point position = new Point(screenPos.X - textSize.Width / 2, screenPos.Y - textSize.Height / 2); if (output.Contains(position)) { Canvas.SetLeft(text, position.X); Canvas.SetTop(text, position.Y); if (text.Visibility == Visibility.Hidden) { text.Visibility = Visibility.Visible; content.Children.Add(text); } } else if (text.Visibility == Visibility.Visible) { text.Visibility = Visibility.Hidden; content.Children.Remove(text); } } } } } }