Files
DynamicDataDisplay/Charts/Isolines/IsolineTrackingGraph.xaml.cs
2024-02-23 00:46:06 -05:00

256 lines
6.2 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
{
/// <summary>
/// Draws one isoline line through mouse position.
/// </summary>
public partial class IsolineTrackingGraph : IsolineGraphBase
{
/// <summary>
/// Initializes a new instance of the <see cref="IsolineTrackingGraph"/> class.
/// </summary>
public IsolineTrackingGraph()
{
InitializeComponent();
}
private Style pathStyle = null;
/// <summary>
/// Gets or sets style, applied to line path.
/// </summary>
/// <value>The path style.</value>
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<Path> addedPaths = new List<Path>();
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;
}
}
}