using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Research.DynamicDataDisplay.Charts.Isolines;
using Microsoft.Research.DynamicDataDisplay;
using Microsoft.Research.DynamicDataDisplay.Common;
namespace Microsoft.Research.DynamicDataDisplay.Charts
{
///
/// Draws one isoline line through mouse position.
///
public partial class IsolineTrackingGraph : IsolineGraphBase
{
///
/// Initializes a new instance of the class.
///
public IsolineTrackingGraph()
{
InitializeComponent();
}
private Style pathStyle = null;
///
/// Gets or sets style, applied to line path.
///
/// The path style.
public Style PathStyle
{
get { return pathStyle; }
set
{
pathStyle = value;
foreach (var path in addedPaths)
{
path.Style = pathStyle;
}
}
}
Point prevMousePos;
protected override void OnPlotterAttached()
{
UIElement parent = (UIElement)Parent;
parent.MouseMove += parent_MouseMove;
UpdateUIRepresentation();
}
protected override void OnPlotterDetaching()
{
UIElement parent = (UIElement)Parent;
parent.MouseMove -= parent_MouseMove;
}
private void parent_MouseMove(object sender, MouseEventArgs e)
{
Point mousePos = e.GetPosition(this);
if (mousePos != prevMousePos)
{
prevMousePos = mousePos;
UpdateUIRepresentation();
}
}
protected override void UpdateDataSource()
{
IsolineBuilder.DataSource = DataSource;
UpdateUIRepresentation();
}
protected override void OnViewportPropertyChanged(ExtendedPropertyChangedEventArgs e)
{
UpdateUIRepresentation();
}
private readonly List addedPaths = new List();
private Vector labelShift = new Vector(3, 3);
private void UpdateUIRepresentation()
{
if (Plotter2D == null)
return;
foreach (var path in addedPaths)
{
content.Children.Remove(path);
}
addedPaths.Clear();
if (DataSource == null)
{
labelGrid.Visibility = Visibility.Hidden;
return;
}
Rect output = Plotter2D.Viewport.Output;
Point mousePos = Mouse.GetPosition(this);
if (!output.Contains(mousePos)) return;
var transform = Plotter2D.Viewport.Transform;
Point visiblePoint = mousePos.ScreenToData(transform);
DataRect visible = Plotter2D.Viewport.Visible;
double isolineLevel;
if (Search(visiblePoint, out isolineLevel))
{
var collection = IsolineBuilder.BuildIsoline(isolineLevel);
string format = "G3";
if (Math.Abs(isolineLevel) < 1000)
format = "F";
textBlock.Text = isolineLevel.ToString(format);
double x = mousePos.X + labelShift.X;
if (x + labelGrid.ActualWidth > output.Right)
x = mousePos.X - labelShift.X - labelGrid.ActualWidth;
double y = mousePos.Y - labelShift.Y - labelGrid.ActualHeight;
if (y < output.Top)
y = mousePos.Y + labelShift.Y;
Canvas.SetLeft(labelGrid, x);
Canvas.SetTop(labelGrid, y);
foreach (LevelLine segment in collection.Lines)
{
StreamGeometry streamGeom = new StreamGeometry();
using (StreamGeometryContext context = streamGeom.Open())
{
Point startPoint = segment.StartPoint.DataToScreen(transform);
var otherPoints = segment.OtherPoints.DataToScreenAsList(transform);
context.BeginFigure(startPoint, false, false);
context.PolyLineTo(otherPoints, true, true);
}
Path path = new Path
{
Stroke = new SolidColorBrush(Palette.GetColor(segment.Value01)),
Data = streamGeom,
Style = pathStyle
};
content.Children.Add(path);
addedPaths.Add(path);
labelGrid.Visibility = Visibility.Visible;
Binding pathBinding = new Binding { Path = new PropertyPath("StrokeThickness"), Source = this };
path.SetBinding(Path.StrokeThicknessProperty, pathBinding);
}
}
else
{
labelGrid.Visibility = Visibility.Hidden;
}
}
int foundI = 0;
int foundJ = 0;
Quad foundQuad = null;
private bool Search(Point pt, out double foundVal)
{
var grid = DataSource.Grid;
foundVal = 0;
int width = DataSource.Width;
int height = DataSource.Height;
bool found = false;
int i = 0, j = 0;
for (i = 0; i < width - 1; i++)
{
for (j = 0; j < height - 1; j++)
{
Quad quad = new Quad(
grid[i, j],
grid[i, j + 1],
grid[i + 1, j + 1],
grid[i + 1, j]);
if (quad.Contains(pt))
{
found = true;
foundQuad = quad;
foundI = i;
foundJ = j;
break;
}
}
if (found) break;
}
if (!found)
{
foundQuad = null;
return false;
}
var data = DataSource.Data;
double x = pt.X;
double y = pt.Y;
Vector A = grid[i, j + 1].ToVector(); // @TODO: in common case add a sorting of points:
Vector B = grid[i + 1, j + 1].ToVector(); // maxA ___K___ B
Vector C = grid[i + 1, j].ToVector(); // | |
Vector D = grid[i, j].ToVector(); // M P N
double a = data[i, j + 1]; // | |
double b = data[i + 1, j + 1]; // В ___L____Сmin
double c = data[i + 1, j];
double d = data[i, j];
Vector K, L;
double k, l;
if (x >= A.X)
k = Interpolate(A, B, a, b, K = new Vector(x, GetY(A, B, x)));
else
k = Interpolate(D, A, d, a, K = new Vector(x, GetY(D, A, x)));
if (x >= C.X)
l = Interpolate(C, B, c, b, L = new Vector(x, GetY(C, B, x)));
else
l = Interpolate(D, C, d, c, L = new Vector(x, GetY(D, C, x)));
foundVal = Interpolate(L, K, l, k, new Vector(x, y));
return !Double.IsNaN(foundVal);
}
private double Interpolate(Vector v0, Vector v1, double u0, double u1, Vector a)
{
Vector l1 = a - v0;
Vector l = v1 - v0;
double res = (u1 - u0) / l.Length * l1.Length + u0;
return res;
}
private double GetY(Vector v0, Vector v1, double x)
{
double res = v0.Y + (v1.Y - v0.Y) / (v1.X - v0.X) * (x - v0.X);
return res;
}
}
}