201 lines
6.2 KiB
C#
201 lines
6.2 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|