using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Media; using System.Windows; using System.Diagnostics; using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary; using Microsoft.Research.DynamicDataDisplay.Charts.Shapes; using System.Windows.Threading; using System.Globalization; using Microsoft.Research.DynamicDataDisplay.Charts.NewLine; using System.Windows.Data; namespace Microsoft.Research.DynamicDataDisplay.Charts.Isolines { public class FastIsolineRenderer : IsolineRenderer { private List additionalLines = new List(); private const int subDivisionNum = 10; protected override void CreateUIRepresentation() { InvalidateVisual(); } protected override void OnPlotterAttached() { base.OnPlotterAttached(); FrameworkElement parent = (FrameworkElement)Parent; Binding collectionBinding = new Binding("IsolineCollection") { Source = this }; parent.SetBinding(IsolineCollectionProperty, collectionBinding); } protected override void OnRender(DrawingContext drawingContext) { if (Plotter2D == null) return; if (Collection == null) return; if (DataSource == null) return; if (Collection.Lines.Count == 0) { IsolineBuilder.DataSource = DataSource; IsolineBuilder.MissingValue = MissingValue; Collection = IsolineBuilder.BuildIsoline(); } IsolineCollection = Collection; var dc = drawingContext; var strokeThickness = StrokeThickness; var collection = Collection; var bounds = DataRect.Empty; // determining content bounds foreach (LevelLine line in collection) { foreach (Point point in line.AllPoints) { bounds.Union(point); } } Viewport2D.SetContentBounds(this, bounds); ViewportPanel.SetViewportBounds(this, bounds); if (bounds.IsEmpty) return; // custom transform with output set to renderSize of this control var transform = Plotter2D.Transform.WithRects(bounds, new Rect(RenderSize)); // actual drawing of isolines RenderIsolineCollection(dc, strokeThickness, collection, transform); //var additionalLevels = GetAdditionalIsolines(collection); //var additionalIsolineCollections = additionalLevels.Select(level => IsolineBuilder.BuildIsoline(level)); //foreach (var additionalCollection in additionalIsolineCollections) //{ // RenderIsolineCollection(dc, strokeThickness, additionalCollection, transform); //} RenderLabels(dc, collection); // foreach (var additionalCollection in additionalIsolineCollections) // { // RenderLabels(dc, additionalCollection); // } } private IEnumerable GetAdditionalIsolines(IsolineCollection collection) { var dataSource = DataSource; var visibleMinMax = dataSource.GetMinMax(Plotter2D.Visible); var visibleMinMaxRatio = (collection.Max - collection.Min) / visibleMinMax.GetLength(); var log = Math.Log10(visibleMinMaxRatio); if (log > 0.9) { var upperLog = Math.Ceiling(log); var divisionsNum = Math.Pow(10, upperLog); var delta = (collection.Max - collection.Min) / divisionsNum; var start = Math.Ceiling(visibleMinMax.Min / delta) * delta; var x = start; while (x < visibleMinMax.Max) { yield return x; x += delta; } } } private void RenderLabels(DrawingContext dc, IsolineCollection collection) { if (Plotter2D == null) return; if (collection == null) return; if (!DrawLabels) return; var viewportBounds = ViewportPanel.GetViewportBounds(this); if (viewportBounds.IsEmpty) return; var strokeThickness = StrokeThickness; var visible = Plotter2D.Visible; var output = Plotter2D.Viewport.Output; var transform = Plotter2D.Transform.WithRects(viewportBounds, new Rect(RenderSize)); var labelStringFormat = LabelStringFormat; // drawing constants var labelRectangleFill = Brushes.White; var biggerViewport = viewportBounds.ZoomOutFromCenter(1.1); // getting and filtering annotations to draw only visible ones Annotater.WayBeforeText = Math.Sqrt(visible.Width * visible.Width + visible.Height * visible.Height) / 8 * WayBeforeTextMultiplier; var annotations = Annotater.Annotate(collection, visible) .Where(annotation => { Point viewportPosition = annotation.Position.DataToViewport(transform); return biggerViewport.Contains(viewportPosition); }); var labelsScale = LabelsScaling; // drawing annotations foreach (var annotation in annotations) { FormattedText text = CreateFormattedText(annotation.Value.ToString(LabelStringFormat)); Point position = annotation.Position.DataToScreen(transform); var labelTransform = CreateTransform(annotation, text, position); // creating rectange stroke double colorRatio = (annotation.Value - collection.Min) / (collection.Max - collection.Min); colorRatio = MathHelper.Clamp(colorRatio); Color rectangleStrokeColor = Palette.GetColor(colorRatio); SolidColorBrush rectangleStroke = new SolidColorBrush(rectangleStrokeColor); Pen labelRectangleStrokePen = new Pen(rectangleStroke, 2); dc.PushTransform(new ScaleTransform(1, labelsScale)); dc.PushTransform(labelTransform); { var bounds = RectExtensions.FromCenterSize(position, new Size(text.Width, text.Height)); bounds = bounds.ZoomOutFromCenter(1.3); dc.DrawRoundedRectangle(labelRectangleFill, labelRectangleStrokePen, bounds, 8, 8); DrawTextInPosition(dc, text, position); } dc.Pop(); dc.Pop(); } } private static void DrawTextInPosition(DrawingContext dc, FormattedText text, Point position) { var textPosition = position; textPosition.Offset(-text.Width / 2, -text.Height / 2); dc.DrawText(text, textPosition); } private static Transform CreateTransform(IsolineTextLabel isolineLabel, FormattedText text, Point position) { double angle = isolineLabel.Rotation; if (angle < 0) angle += 360; if (90 < angle && angle < 270) angle -= 180; RotateTransform transform = new RotateTransform(angle, position.X, position.Y); return transform; } private static FormattedText CreateFormattedText(string text) { FormattedText result = new FormattedText(text, CultureInfo.CurrentCulture, System.Windows.FlowDirection.LeftToRight, new Typeface("Arial"), 12, Brushes.Black); return result; } } }