Initial Commit
This commit is contained in:
200
Charts/Isolines/FastIsolineRenderer.cs
Normal file
200
Charts/Isolines/FastIsolineRenderer.cs
Normal file
@@ -0,0 +1,200 @@
|
||||
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<IsolineCollection> additionalLines = new List<IsolineCollection>();
|
||||
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<double> 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) / 100 * WayBeforeTextMultiplier;
|
||||
var annotations = Annotater.Annotate(collection, visible)
|
||||
.Where(annotation =>
|
||||
{
|
||||
Point viewportPosition = annotation.Position.DataToViewport(transform);
|
||||
return biggerViewport.Contains(viewportPosition);
|
||||
});
|
||||
|
||||
// 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(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();
|
||||
}
|
||||
}
|
||||
|
||||
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,1.0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user