Initial Commit

This commit is contained in:
2024-02-23 00:46:06 -05:00
commit 2bbedc0178
470 changed files with 46035 additions and 0 deletions

View File

@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Research.DynamicDataDisplay.Charts;
namespace Microsoft.Research.DynamicDataDisplay.Common.Auxiliary
{
internal static class ArrayExtensions
{
internal static T Last<T>(this T[] array) {
return array[array.Length - 1];
}
internal static T[] CreateArray<T>(int length, T defaultValue)
{
T[] res = new T[length];
for (int i = 0; i < res.Length; i++)
{
res[i] = defaultValue;
}
return res;
}
internal static IEnumerable<Range<T>> GetPairs<T>(this IList<T> array)
{
if (array == null)
throw new ArgumentNullException("array");
for (int i = 0; i < array.Count - 1; i++)
{
yield return new Range<T>(array[i], array[i + 1]);
}
}
}
}

View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Data;
using System.Windows;
namespace Microsoft.Research.DynamicDataDisplay.Common.Auxiliary
{
public static class BindingHelper
{
public static Binding CreateAttachedPropertyBinding(DependencyProperty attachedProperty)
{
return new Binding { Path = new PropertyPath("(0)", attachedProperty) };
}
}
}

View File

@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Windows;
using Microsoft.Research.DynamicDataDisplay.Common;
namespace Microsoft.Research.DynamicDataDisplay
{
public static class BoundsHelper
{
/// <summary>Computes bounding rectangle for sequence of points</summary>
/// <param name="points">Points sequence</param>
/// <returns>Minimal axis-aligned bounding rectangle</returns>
public static DataRect GetViewportBounds(IEnumerable<Point> viewportPoints)
{
DataRect bounds = DataRect.Empty;
double xMin = Double.PositiveInfinity;
double xMax = Double.NegativeInfinity;
double yMin = Double.PositiveInfinity;
double yMax = Double.NegativeInfinity;
foreach (Point p in viewportPoints)
{
xMin = Math.Min(xMin, p.X);
xMax = Math.Max(xMax, p.X);
yMin = Math.Min(yMin, p.Y);
yMax = Math.Max(yMax, p.Y);
}
// were some points in collection
if (!Double.IsInfinity(xMin))
{
bounds = DataRect.Create(xMin, yMin, xMax, yMax);
}
return bounds;
}
public static DataRect GetViewportBounds(IEnumerable<Point> dataPoints, DataTransform transform)
{
return GetViewportBounds(dataPoints.DataToViewport(transform));
}
}
}

View File

@@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
namespace Microsoft.Research.DynamicDataDisplay
{
public static class BrushHelper
{
/// <summary>
/// Creates a SolidColorBrush with random hue of its color.
/// </summary>
/// <returns>A SolicColorBrush with random hue of its color.</returns>
public static SolidColorBrush CreateBrushWithRandomHue()
{
return new SolidColorBrush { Color = ColorHelper.CreateColorWithRandomHue() };
}
/// <summary>
/// Makes SolidColorBrush transparent.
/// </summary>
/// <param name="brush">The brush.</param>
/// <param name="alpha">The alpha, [0..255]</param>
/// <returns></returns>
public static SolidColorBrush MakeTransparent(this SolidColorBrush brush, int alpha)
{
Color color = brush.Color;
color.A = (byte)alpha;
return new SolidColorBrush(color);
}
/// <summary>
/// Makes SolidColorBrush transparent.
/// </summary>
/// <param name="brush">The brush.</param>
/// <param name="alpha">The alpha, [0.0 .. 1.0].</param>
/// <returns></returns>
public static SolidColorBrush MakeTransparent(this SolidColorBrush brush, double opacity)
{
return MakeTransparent(brush, (int)(opacity * 255));
}
public static SolidColorBrush ChangeLightness(this SolidColorBrush brush, double lightnessFactor)
{
Color color = brush.Color;
HsbColor hsbColor = HsbColor.FromArgbColor(color);
hsbColor.Brightness *= lightnessFactor;
if (hsbColor.Brightness > 1.0) hsbColor.Brightness = 1.0;
SolidColorBrush result = new SolidColorBrush(hsbColor.ToArgbColor());
return result;
}
public static SolidColorBrush ChangeSaturation(this SolidColorBrush brush, double saturationFactor)
{
Color color = brush.Color;
HsbColor hsbColor = HsbColor.FromArgbColor(color);
hsbColor.Saturation *= saturationFactor;
if (hsbColor.Saturation > 1.0) hsbColor.Saturation = 1.0;
SolidColorBrush result = new SolidColorBrush(hsbColor.ToArgbColor());
return result;
}
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
namespace Microsoft.Research.DynamicDataDisplay.Common.Auxiliary
{
public static class ColorExtensions
{
public static Color MakeTransparent(this Color color, int alpha)
{
color.A = (byte)alpha;
return color;
}
public static Color MakeTransparent(this Color color, double opacity)
{
return MakeTransparent(color, (int)(255 * opacity));
}
}
}

View File

@@ -0,0 +1,104 @@
using System;
using System.Windows.Media;
using System.Diagnostics.CodeAnalysis;
namespace Microsoft.Research.DynamicDataDisplay
{
public static class ColorHelper
{
private readonly static Random random = new Random();
/// <summary>
/// Creates color from HSB color space with random hue and saturation and brighness equal to 1.
/// </summary>
/// <returns></returns>
public static Color CreateColorWithRandomHue()
{
double hue = random.NextDouble() * 360;
HsbColor hsbColor = new HsbColor(hue, 1, 1);
return hsbColor.ToArgbColor();
}
public static Color[] CreateRandomColors(int colorNum)
{
double startHue = random.NextDouble() * 360;
Color[] res = new Color[colorNum];
double hueStep = 360.0 / colorNum;
for (int i = 0; i < res.Length; i++)
{
double hue = startHue + i * hueStep;
res[i] = new HsbColor(hue, 1, 1).ToArgbColor();
}
return res;
}
/// <summary>
/// Creates color with fully random hue and slightly random saturation and brightness.
/// </summary>
/// <returns></returns>
public static Color CreateRandomHsbColor()
{
double h = random.NextDouble() * 360;
double s = random.NextDouble() * 0.5 + 0.5;
double b = random.NextDouble() * 0.25 + 0.75;
return new HsbColor(h, s, b).ToArgbColor();
}
/// <summary>
/// Creates color with random hue.
/// </summary>
/// <param name="saturation">The saturation, [0..1].</param>
/// <param name="brightness">The brightness, [0..1]</param>
/// <returns></returns>
public static Color CreateColorWithRandomHue(double saturation, double brightness)
{
double h = random.NextDouble() * 360;
return new HsbColor(h, saturation, brightness).ToArgbColor();
}
/// <summary>
/// Creates brush with random hue.
/// </summary>
/// <param name="saturation">The saturation, [0..1].</param>
/// <param name="brightness">The brightness, [0..1].</param>
/// <returns></returns>
public static Brush CreateBrushWithRandomHue(double saturation, double brightness)
{
Color color = CreateColorWithRandomHue(saturation, brightness);
return new SolidColorBrush(color);
}
/// <summary>
/// Gets the random color (this property is created to use it from Xaml).
/// </summary>
/// <value>The random color.</value>
public static Color RandomColor
{
get { return CreateRandomHsbColor(); }
}
/// <summary>
/// Gets the random brush.
/// </summary>
/// <value>The random brush.</value>
public static SolidColorBrush RandomBrush
{
get { return new SolidColorBrush(CreateRandomHsbColor()); }
}
public static int ToArgb(this Color color)
{
int result =
color.A << 24 |
color.R << 16 |
color.G << 8 |
color.B;
return result;
}
}
}

View File

@@ -0,0 +1,76 @@
using System.Collections.Generic;
using System.Windows;
using System;
using Microsoft.Research.DynamicDataDisplay.Common;
namespace Microsoft.Research.DynamicDataDisplay
{
public static class CoordinateUtilities
{
public static Rect RectZoom(Rect rect, double ratio)
{
return RectZoom(rect, rect.GetCenter(), ratio);
}
public static Rect RectZoom(Rect rect, double horizontalRatio, double verticalRatio)
{
return RectZoom(rect, rect.GetCenter(), horizontalRatio, verticalRatio);
}
public static Rect RectZoom(Rect rect, Point zoomCenter, double ratio)
{
return RectZoom(rect, zoomCenter, ratio, ratio);
}
public static Rect RectZoom(Rect rect, Point zoomCenter, double horizontalRatio, double verticalRatio)
{
Rect res = new Rect();
res.X = zoomCenter.X - (zoomCenter.X - rect.X) * horizontalRatio;
res.Y = zoomCenter.Y - (zoomCenter.Y - rect.Y) * verticalRatio;
res.Width = rect.Width * horizontalRatio;
res.Height = rect.Height * verticalRatio;
return res;
}
public static DataRect RectZoom(DataRect rect, double ratio)
{
return RectZoom(rect, rect.GetCenter(), ratio);
}
public static DataRect RectZoom(DataRect rect, double horizontalRatio, double verticalRatio)
{
return RectZoom(rect, rect.GetCenter(), horizontalRatio, verticalRatio);
}
public static DataRect RectZoom(DataRect rect, Point zoomCenter, double ratio)
{
return RectZoom(rect, zoomCenter, ratio, ratio);
}
public static DataRect RectZoom(DataRect rect, Point zoomCenter, double horizontalRatio, double verticalRatio)
{
DataRect res = new DataRect();
res.XMin = zoomCenter.X - (zoomCenter.X - rect.XMin) * horizontalRatio;
res.YMin = zoomCenter.Y - (zoomCenter.Y - rect.YMin) * verticalRatio;
res.Width = rect.Width * horizontalRatio;
res.Height = rect.Height * verticalRatio;
return res;
}
public static DataRect RectZoomX(DataRect rect, Point zoomCenter, double ratio)
{
DataRect res = rect;
res.XMin = zoomCenter.X - (zoomCenter.X - rect.XMin) * ratio;
res.Width = rect.Width * ratio;
return res;
}
public static DataRect RectZoomY(DataRect rect, Point zoomCenter, double ratio)
{
DataRect res = rect;
res.YMin = zoomCenter.Y - (zoomCenter.Y - rect.YMin) * ratio;
res.Height = rect.Height * ratio;
return res;
}
}
}

View File

@@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using Microsoft.Research.DynamicDataDisplay.Common;
namespace Microsoft.Research.DynamicDataDisplay
{
public static class DataRectExtensions
{
internal static bool IsNaN(this DataRect rect)
{
return !rect.IsEmpty &&
(
rect.XMin.IsNaN() ||
rect.YMin.IsNaN() ||
rect.XMax.IsNaN() ||
rect.YMax.IsNaN()
);
}
public static Point GetCenter(this DataRect rect)
{
return new Point(rect.XMin + rect.Width * 0.5, rect.YMin + rect.Height * 0.5);
}
public static DataRect Zoom(this DataRect rect, Point to, double ratio)
{
return CoordinateUtilities.RectZoom(rect, to, ratio);
}
public static DataRect ZoomOutFromCenter(this DataRect rect, double ratio)
{
return CoordinateUtilities.RectZoom(rect, rect.GetCenter(), ratio);
}
public static DataRect ZoomInToCenter(this DataRect rect, double ratio)
{
return CoordinateUtilities.RectZoom(rect, rect.GetCenter(), 1 / ratio);
}
public static DataRect ZoomX(this DataRect rect, Point to, double ratio)
{
return CoordinateUtilities.RectZoomX(rect, to, ratio);
}
public static DataRect ZoomY(this DataRect rect, Point to, double ratio)
{
return CoordinateUtilities.RectZoomY(rect, to, ratio);
}
public static double GetSquare(this DataRect rect)
{
if (rect.IsEmpty)
return 0;
return rect.Width * rect.Height;
}
/// <summary>
/// Determines whether one DataRect is close to another DataRect.
/// </summary>
/// <param name="rect1">The rect1.</param>
/// <param name="rect2">The rect2.</param>
/// <param name="difference">The difference.</param>
/// <returns>
/// <c>true</c> if [is close to] [the specified rect1]; otherwise, <c>false</c>.
/// </returns>
public static bool IsCloseTo(this DataRect rect1, DataRect rect2, double difference)
{
DataRect intersection = DataRect.Intersect(rect1, rect2);
double square1 = rect1.GetSquare();
double square2 = rect2.GetSquare();
double intersectionSquare = intersection.GetSquare();
bool areClose = MathHelper.AreClose(square1, intersectionSquare, difference) &&
MathHelper.AreClose(square2, intersectionSquare, difference);
return areClose;
}
}
}

View File

@@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Microsoft.Research.DynamicDataDisplay.Common.DataSearch
{
internal sealed class GenericSearcher1d<TCollection, TMember> where TMember : IComparable<TMember>
{
private readonly Func<TCollection, TMember> selector;
private readonly IList<TCollection> collection;
public GenericSearcher1d(IList<TCollection> collection, Func<TCollection, TMember> selector)
{
if (collection == null)
throw new ArgumentNullException("collection");
if (selector == null)
throw new ArgumentNullException("selector");
this.collection = collection;
this.selector = selector;
}
public SearchResult1d SearchBetween(TMember x)
{
return SearchBetween(x, SearchResult1d.Empty);
}
public SearchResult1d SearchBetween(TMember x, SearchResult1d result)
{
if (collection.Count == 0)
return SearchResult1d.Empty;
int lastIndex = collection.Count - 1;
if (x.CompareTo(selector(collection[0])) < 0)
return SearchResult1d.Empty;
else if (selector(collection[lastIndex]).CompareTo(x) < 0)
return SearchResult1d.Empty;
int startIndex = !result.IsEmpty ? Math.Min(result.Index, lastIndex) : 0;
// searching ascending
if (selector(collection[startIndex]).CompareTo(x) < 0)
{
for (int i = startIndex + 1; i <= lastIndex; i++)
if (selector(collection[i]).CompareTo(x) >= 0)
return new SearchResult1d { Index = i - 1 };
}
else // searching descending
{
for (int i = startIndex - 1; i >= 0; i--)
if (selector(collection[i]).CompareTo(x) <= 0)
return new SearchResult1d { Index = i };
}
throw new InvalidOperationException("Should not appear here.");
}
public SearchResult1d SearchFirstLess(TMember x)
{
if (collection.Count == 0)
return SearchResult1d.Empty;
SearchResult1d result = SearchResult1d.Empty;
for (int i = 0; i < collection.Count; i++)
{
if (selector(collection[i]).CompareTo(x) >= 0)
{
result.Index = i;
break;
}
}
return result;
}
public SearchResult1d SearchGreater(TMember x)
{
if (collection.Count == 0)
return SearchResult1d.Empty;
SearchResult1d result = SearchResult1d.Empty;
for (int i = collection.Count - 1; i >= 0; i--)
{
if (selector(collection[i]).CompareTo(x) <= 0)
{
result.Index = i;
break;
}
}
return result;
}
}
}

View File

@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Microsoft.Research.DynamicDataDisplay.Common.DataSearch
{
internal struct SearchResult1d
{
public static SearchResult1d Empty
{
get { return new SearchResult1d { Index = -1 }; }
}
public int Index { get; internal set; }
public bool IsEmpty
{
get { return Index == -1; }
}
public override string ToString()
{
if (IsEmpty)
return "Empty";
return String.Format("Index = {0}", Index);
}
}
}

View File

@@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
namespace Microsoft.Research.DynamicDataDisplay.Common.DataSearch
{
internal class SortedXSearcher1d
{
private readonly IList<Point> collection;
public SortedXSearcher1d(IList<Point> collection)
{
if (collection == null)
throw new ArgumentNullException("collection");
this.collection = collection;
}
public SearchResult1d SearchXBetween(double x)
{
return SearchXBetween(x, SearchResult1d.Empty);
}
public SearchResult1d SearchXBetween(double x, SearchResult1d result)
{
if (collection.Count == 0)
return SearchResult1d.Empty;
int lastIndex = collection.Count - 1;
if (x < collection[0].X)
return SearchResult1d.Empty;
else if (collection[lastIndex].X < x)
return SearchResult1d.Empty;
int startIndex = !result.IsEmpty ? Math.Min(result.Index, lastIndex) : 0;
// searching ascending
if (collection[startIndex].X < x)
{
for (int i = startIndex + 1; i <= lastIndex; i++)
if (collection[i].X >= x)
return new SearchResult1d { Index = i - 1 };
}
else // searching descending
{
for (int i = startIndex - 1; i >= 0; i--)
if (collection[i].X <= x)
return new SearchResult1d { Index = i };
}
throw new InvalidOperationException("Should not appear here.");
}
}
}

View File

@@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Windows;
namespace Microsoft.Research.DynamicDataDisplay.Common.Auxiliary
{
internal static class DebugVerify
{
[Conditional("DEBUG")]
[DebuggerStepThrough]
public static void Is(bool condition)
{
if (!condition)
{
throw new ArgumentException(Strings.Exceptions.AssertionFailed);
}
}
[Conditional("DEBUG")]
[DebuggerStepThrough]
public static void IsNotNaN(double d)
{
DebugVerify.Is(!Double.IsNaN(d));
}
[Conditional("DEBUG")]
[DebuggerStepThrough]
public static void IsNotNaN(Vector vec)
{
DebugVerify.IsNotNaN(vec.X);
DebugVerify.IsNotNaN(vec.Y);
}
[Conditional("DEBUG")]
[DebuggerStepThrough]
public static void IsNotNaN(Point point)
{
DebugVerify.IsNotNaN(point.X);
DebugVerify.IsNotNaN(point.Y);
}
[Conditional("DEBUG")]
[DebuggerStepThrough]
public static void IsFinite(double d)
{
DebugVerify.Is(!Double.IsInfinity(d) && !(Double.IsNaN(d)));
}
[Conditional("DEBUG")]
[DebuggerStepThrough]
public static void IsNotNull(object obj)
{
DebugVerify.Is(obj != null);
}
}
}

View File

@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Threading;
namespace Microsoft.Research.DynamicDataDisplay.Common.Auxiliary
{
public static class DependencyObjectExtensions
{
public static T GetValueSync<T>(this DependencyObject d, DependencyProperty property)
{
object value = null;
d.Dispatcher.Invoke(() => { value = d.GetValue(property); }, DispatcherPriority.Send);
return (T)value;
}
}
}

View File

@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Research.DynamicDataDisplay.Charts.Isolines;
namespace Microsoft.Research.DynamicDataDisplay.Common.Auxiliary
{
internal static class DictionaryExtensions
{
internal static void Add<TKey, TValue>(this Dictionary<TKey, TValue> dict, TValue value, params TKey[] keys)
{
foreach (var key in keys)
{
dict.Add(key, value);
}
}
internal static void Add(this Dictionary<int, Edge> dict, Edge value, params CellBitmask[] keys)
{
foreach (var key in keys)
{
dict.Add((int)key, value);
}
}
}
}

View File

@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Threading;
namespace Microsoft.Research.DynamicDataDisplay.Common.Auxiliary
{
public static class DispatcherExtensions
{
public static DispatcherOperation BeginInvoke(this Dispatcher dispatcher, Action action)
{
return dispatcher.BeginInvoke((Delegate)action);
}
public static DispatcherOperation BeginInvoke(this Dispatcher dispatcher, Action action, DispatcherPriority priority)
{
return dispatcher.BeginInvoke(action, priority);
}
public static void Invoke(this Dispatcher dispatcher, Action action, DispatcherPriority priority)
{
dispatcher.Invoke(action, priority);
}
}
}

View File

@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace Microsoft.Research.DynamicDataDisplay.Common.Auxiliary
{
public sealed class DisposableTimer : IDisposable
{
private bool isActive = true;
private readonly string name;
Stopwatch timer;
public DisposableTimer(string name) : this(name, true) { }
public DisposableTimer(string name, bool isActive)
{
this.name = name;
this.isActive = isActive;
if (isActive)
{
timer = Stopwatch.StartNew();
Trace.WriteLine(name + ": started " + DateTime.Now.TimeOfDay);
}
}
#region IDisposable Members
public void Dispose()
{
//#if DEBUG
if (isActive)
{
var duration = timer.ElapsedMilliseconds;
Trace.WriteLine(name + ": elapsed " + duration + " ms.");
timer.Stop();
}
//#endif
}
#endregion
}
}

View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
namespace Microsoft.Research.DynamicDataDisplay.Common.Auxiliary
{
public static class DoubleCollectionHelper
{
public static DoubleCollection Create(params double[] collection)
{
return new DoubleCollection(collection);
}
}
}

View File

@@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.ComponentModel;
using System.Collections.Specialized;
using System.Windows;
using Microsoft.Research.DynamicDataDisplay.Common;
namespace Microsoft.Research.DynamicDataDisplay
{
public static class EventExtensions
{
[DebuggerStepThrough]
[DebuggerHidden]
public static void Raise<T>(this EventHandler<T> @event, object sender, T args) where T : EventArgs
{
if (@event != null)
{
@event(sender, args);
}
}
[DebuggerStepThrough]
[DebuggerHidden]
public static void Raise(this EventHandler @event, object sender)
{
if (@event != null)
{
@event(sender, EventArgs.Empty);
}
}
[DebuggerStepThrough]
[DebuggerHidden]
public static void Raise(this EventHandler @event, object sender, EventArgs args)
{
if (@event != null)
{
@event(sender, args);
}
}
[DebuggerStepThrough]
[DebuggerHidden]
public static void Raise(this PropertyChangedEventHandler @event, object sender, string propertyName)
{
if (@event != null)
{
@event(sender, new PropertyChangedEventArgs(propertyName));
}
}
/// <summary>
/// Raises the specified event with Reset action.
/// </summary>
/// <param name="event">The event.</param>
/// <param name="sender">The sender.</param>
[DebuggerStepThrough]
[DebuggerHidden]
public static void Raise(this NotifyCollectionChangedEventHandler @event, object sender)
{
if (@event != null)
{
@event(sender, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
[DebuggerStepThrough]
[DebuggerHidden]
public static void Raise(this NotifyCollectionChangedEventHandler @event, object sender, NotifyCollectionChangedAction action)
{
if (@event != null)
{
@event(sender, new NotifyCollectionChangedEventArgs(action));
}
}
[DebuggerStepThrough]
[DebuggerHidden]
public static void Raise(this NotifyCollectionChangedEventHandler @event, object sender, NotifyCollectionChangedEventArgs e)
{
if (e == null)
throw new ArgumentNullException("e");
if (@event != null)
{
@event(sender, e);
}
}
[DebuggerStepThrough]
[DebuggerHidden]
public static void RaiseRoutedEvent(this UIElement sender, RoutedEvent routedEvent)
{
sender.RaiseEvent(new RoutedEventArgs(routedEvent));
}
/// <summary>
/// Raises the specified value changed event.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TValue">The type of the value.</typeparam>
/// <param name="event">The event.</param>
/// <param name="sender">The sender of event.</param>
/// <param name="prevValue">The previous value.</param>
/// <param name="currValue">The current value.</param>
[DebuggerStepThrough]
[DebuggerHidden]
public static void Raise<TValue>(this EventHandler<ValueChangedEventArgs<TValue>> @event, object sender, TValue prevValue, TValue currValue)
{
if (@event != null)
{
ValueChangedEventArgs<TValue> args = new ValueChangedEventArgs<TValue>(prevValue, currValue);
@event(sender, args);
}
}
/// <summary>
/// Raises the specified value changed event.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TValue">The type of the value.</typeparam>
/// <param name="event">The event.</param>
/// <param name="sender">The sender of event.</param>
/// <param name="prevValue">The previous value.</param>
/// <param name="currValue">The current value.</param>
[DebuggerStepThrough]
[DebuggerHidden]
public static void Raise<TValue>(this EventHandler<ValueChangedEventArgs<TValue>> @event, object sender, object prevValue, object currValue)
{
if (@event != null)
{
ValueChangedEventArgs<TValue> args = new ValueChangedEventArgs<TValue>((TValue)prevValue, (TValue)currValue);
@event(sender, args);
}
}
}
}

View File

@@ -0,0 +1,301 @@
using System;
using System.Windows.Media;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
namespace Microsoft.Research.DynamicDataDisplay
{
/// <summary>
/// Represents color in Hue Saturation Brightness color space.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Hsb")]
[DebuggerDisplay("HSBColor A={Alpha} H={Hue} S={Saturation} B={Brightness}")]
public struct HsbColor
{
private double hue;
private double saturation;
private double brightness;
private double alpha;
/// <summary>Hue; [0, 360]</summary>
public double Hue
{
get { return hue; }
set
{
if (value < 0)
value = 360 - value;
hue = value % 360;
}
}
/// <summary>Saturation; [0, 1]</summary>
public double Saturation
{
get { return saturation; }
set { saturation = value; }
}
/// <summary>Brightness; [0, 1]</summary>
public double Brightness
{
get { return brightness; }
set { brightness = value; }
}
/// <summary>Alpha; [0, 1]</summary>
public double Alpha
{
get { return alpha; }
set { alpha = value; }
}
/// <summary>
/// Initializes a new instance of the <see cref="HSBColor"/> struct.
/// </summary>
/// <param name="hue">The hue; [0; 360]</param>
/// <param name="saturation">The saturation; [0, 1]</param>
/// <param name="brightness">The brightness; [0, 1]</param>
public HsbColor(double hue, double saturation, double brightness)
{
this.hue = hue;
this.saturation = saturation;
this.brightness = brightness;
alpha = 1;
}
/// <summary>
/// Initializes a new instance of the <see cref="HSBColor"/> struct.
/// </summary>
/// <param name="hue">The hue; [0, 360]</param>
/// <param name="saturation">The saturation; [0, 1]</param>
/// <param name="brightness">The brightness; [0, 1]</param>
/// <param name="alpha">The alpha; [0, 1]</param>
public HsbColor(double hue, double saturation, double brightness, double alpha)
{
this.hue = hue;
this.saturation = saturation;
this.brightness = brightness;
this.alpha = alpha;
}
/// <summary>
/// Creates HSBColor from the ARGB color.
/// </summary>
/// <param name="color">The color.</param>
/// <returns></returns>
public static HsbColor FromArgbColor(Color color)
{
double limit255 = 255;
double r = color.R / limit255;
double g = color.G / limit255;
double b = color.B / limit255;
double max = Math.Max(Math.Max(r, g), b);
double min = Math.Min(Math.Min(r, g), b);
double len = max - min;
double brightness = max; // 0.5 * (max + min);
double sat;
double hue;
if (max == 0 || len == 0)
{
sat = hue = 0;
}
else
{
sat = len / max;
if (r == max)
{
hue = (g - b) / len;
}
else if (g == max)
{
hue = 2 + (b - r) / len;
}
else
{
hue = 4 + (r - g) / len;
}
}
hue *= 60;
if (hue < 0)
hue += 360;
HsbColor res = new HsbColor();
res.hue = hue;
res.saturation = sat;
res.brightness = brightness;
res.alpha = color.A / limit255;
return res;
}
public static HsbColor FromArgb(int argb)
{
byte a = (byte)(argb >> 24);
byte r = (byte)((argb >> 16) & 0xFF);
byte g = (byte)((argb >> 8) & 0xFF);
byte b = (byte)(argb & 0xFF);
return FromArgbColor(Color.FromArgb(a, r, g, b));
}
/// <summary>
/// Converts HSBColor to ARGB color space.
/// </summary>
/// <returns></returns>
public Color ToArgbColor()
{
double r = 0.0;
double g = 0.0;
double b = 0.0;
double hue = this.hue % 360.0;
if (saturation == 0.0)
{
r = g = b = brightness;
}
else
{
double smallHue = hue / 60.0;
int smallHueInt = (int)Math.Floor(smallHue);
double smallHueFrac = smallHue - smallHueInt;
double val1 = brightness * (1.0 - saturation);
double val2 = brightness * (1.0 - (saturation * smallHueFrac));
double val3 = brightness * (1.0 - (saturation * (1.0 - smallHueFrac)));
switch (smallHueInt)
{
case 0:
r = brightness;
g = val3;
b = val1;
break;
case 1:
r = val2;
g = brightness;
b = val1;
break;
case 2:
r = val1;
g = brightness;
b = val3;
break;
case 3:
r = val1;
g = val2;
b = brightness;
break;
case 4:
r = val3;
g = val1;
b = brightness;
break;
case 5:
r = brightness;
g = val1;
b = val2;
break;
}
}
return Color.FromArgb(
(byte)(Math.Round(alpha * 255)),
(byte)(Math.Round(r * 255)),
(byte)(Math.Round(g * 255)),
(byte)(Math.Round(b * 255)));
}
public int ToArgb()
{
return ToArgbColor().ToArgb();
}
/// <summary>
/// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
/// </summary>
/// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
public override bool Equals(object obj)
{
if (obj is HsbColor)
{
HsbColor c = (HsbColor)obj;
return (c.alpha == alpha &&
c.brightness == brightness &&
c.hue == hue &&
c.saturation == saturation);
}
else
return false;
}
/// <summary>
/// Returns a hash code for this instance.
/// </summary>
/// <returns>
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
/// </returns>
public override int GetHashCode()
{
return alpha.GetHashCode() ^
brightness.GetHashCode() ^
hue.GetHashCode() ^
saturation.GetHashCode();
}
/// <summary>
/// Implements the operator ==.
/// </summary>
/// <param name="first">The first.</param>
/// <param name="second">The second.</param>
/// <returns>The result of the operator.</returns>
public static bool operator ==(HsbColor first, HsbColor second)
{
return (first.alpha == second.alpha &&
first.brightness == second.brightness &&
first.hue == second.hue &&
first.saturation == second.saturation);
}
/// <summary>
/// Implements the operator !=.
/// </summary>
/// <param name="first">The first.</param>
/// <param name="second">The second.</param>
/// <returns>The result of the operator.</returns>
public static bool operator !=(HsbColor first, HsbColor second)
{
return (first.alpha != second.alpha ||
first.brightness != second.brightness ||
first.hue != second.hue ||
first.saturation != second.saturation);
}
}
public static class ColorExtensions
{
/// <summary>
/// Converts the ARGB color to the HSB color.
/// </summary>
/// <param name="color">The color.</param>
/// <returns></returns>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Hsb")]
public static HsbColor ToHsbColor(this Color color)
{
return HsbColor.FromArgbColor(color);
}
}
}

View File

@@ -0,0 +1,146 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Research.DynamicDataDisplay.Charts;
using Microsoft.Research.DynamicDataDisplay.DataSources;
using System.Windows;
using System.Diagnostics.CodeAnalysis;
namespace Microsoft.Research.DynamicDataDisplay.Common.Auxiliary
{
public static class IDataSource2DExtensions
{
public static Range<double> GetMinMax(this double[,] data)
{
data.VerifyNotNull("data");
int width = data.GetLength(0);
int height = data.GetLength(1);
Verify.IsTrueWithMessage(width > 0, Strings.Exceptions.ArrayWidthShouldBePositive);
Verify.IsTrueWithMessage(height > 0, Strings.Exceptions.ArrayHeightShouldBePositive);
double min = data[0, 0];
double max = data[0, 0];
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
if (data[x, y] < min)
min = data[x, y];
if (data[x, y] > max)
max = data[x, y];
}
}
Range<double> res = new Range<double>(min, max);
return res;
}
public static Range<double> GetMinMax(this double[,] data, double missingValue)
{
data.VerifyNotNull("data");
int width = data.GetLength(0);
int height = data.GetLength(1);
Verify.IsTrueWithMessage(width > 0, Strings.Exceptions.ArrayWidthShouldBePositive);
Verify.IsTrueWithMessage(height > 0, Strings.Exceptions.ArrayHeightShouldBePositive);
double min = Double.MaxValue;
double max = Double.MinValue;
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
if (data[x, y] != missingValue && data[x, y] < min)
min = data[x, y];
if (data[x, y] != missingValue && data[x, y] > max)
max = data[x, y];
}
}
Range<double> res = new Range<double>(min, max);
return res;
}
public static Range<double> GetMinMax(this IDataSource2D<double> dataSource)
{
dataSource.VerifyNotNull("dataSource");
return GetMinMax(dataSource.Data);
}
public static Range<double> GetMinMax(this IDataSource2D<double> dataSource, double missingValue)
{
dataSource.VerifyNotNull("dataSource");
return GetMinMax(dataSource.Data, missingValue);
}
public static Range<double> GetMinMax(this IDataSource2D<double> dataSource, DataRect area)
{
if (dataSource == null)
throw new ArgumentNullException("dataSource");
double min = Double.PositiveInfinity;
double max = Double.NegativeInfinity;
int width = dataSource.Width;
int height = dataSource.Height;
var grid = dataSource.Grid;
var data = dataSource.Data;
for (int ix = 0; ix < width; ix++)
{
for (int iy = 0; iy < height; iy++)
{
if (area.Contains(grid[ix, iy]))
{
var value = data[ix, iy];
if (value < min)
min = value;
if (value > max)
max = value;
}
}
}
if (min < max)
return new Range<double>(min, max);
else
return new Range<double>();
}
public static Rect GetGridBounds(this Point[,] grid)
{
double minX = grid[0, 0].X;
double maxX = minX;
double minY = grid[0, 0].Y;
double maxY = minY;
int width = grid.GetLength(0);
int height = grid.GetLength(1);
for (int ix = 0; ix < width; ix++)
{
for (int iy = 0; iy < height; iy++)
{
Point pt = grid[ix, iy];
double x = pt.X;
double y = pt.Y;
if (x < minX) minX = x;
if (x > maxX) maxX = x;
if (y < minY) minY = y;
if (y > maxY) maxY = y;
}
}
return new Rect(new Point(minX, minY), new Point(maxX, maxY));
}
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
public static Rect GetGridBounds<T>(this IDataSource2D<T> dataSource) where T : struct
{
return dataSource.Grid.GetGridBounds();
}
}
}

View File

@@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
namespace Microsoft.Research.DynamicDataDisplay.Common.Auxiliary
{
internal static class IEnumerableExtensions
{
public static bool CountGreaterOrEqual<T>(this IEnumerable<T> enumerable, int count)
{
int counter = 0;
using (var enumerator = enumerable.GetEnumerator())
{
while (counter < count && enumerator.MoveNext())
{
counter++;
}
}
return counter == count;
}
public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> source, int maxCount)
{
using (var enumerator = new FixedEnumeratorWrapper<T>(source.GetEnumerator()))
{
do
{
var enumerable = new FixedEnumerable<T>(enumerator);
yield return enumerable.Take(maxCount);
}
while (enumerator.CanMoveNext);
}
}
private sealed class FixedEnumeratorWrapper<T> : IEnumerator<T>
{
private readonly IEnumerator<T> enumerator;
public FixedEnumeratorWrapper(IEnumerator<T> enumerator)
{
this.enumerator = enumerator;
}
#region IEnumerator<T> Members
public T Current
{
get { return enumerator.Current; }
}
#endregion
#region IDisposable Members
public void Dispose()
{
//enumerator.Dispose();
}
#endregion
#region IEnumerator Members
object IEnumerator.Current
{
get { throw new NotImplementedException(); }
}
private bool canMoveNext = false;
public bool CanMoveNext
{
get { return canMoveNext; }
}
public bool MoveNext()
{
canMoveNext = enumerator.MoveNext();
return canMoveNext;
}
public void Reset()
{
enumerator.Reset();
}
#endregion
}
private sealed class FixedEnumerable<T> : IEnumerable<T>
{
private readonly IEnumerator<T> enumerator;
public FixedEnumerable(IEnumerator<T> enumerator)
{
this.enumerator = enumerator;
}
#region IEnumerable<T> Members
public IEnumerator<T> GetEnumerator()
{
return enumerator;
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotSupportedException();
}
#endregion
}
}
}

View File

@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Microsoft.Research.DynamicDataDisplay
{
public static class IListExtensions
{
public static void AddMany<T>(this IList<T> collection, IEnumerable<T> addingItems)
{
foreach (var item in addingItems)
{
collection.Add(item);
}
}
public static void AddMany<T>(this IList<T> collection, params T[] children)
{
foreach (var child in children)
{
collection.Add(child);
}
}
public static void RemoveAll<T>(this IList<T> collection, Type type)
{
var children = collection.Where(el => type.IsAssignableFrom(el.GetType())).ToArray();
foreach (var child in children)
{
collection.Remove((T)child);
}
}
public static void RemoveAll<T, TDelete>(this IList<T> collection)
{
var children = collection.OfType<TDelete>().ToArray();
foreach (var child in children)
{
collection.Remove((T)(object)child);
}
}
}
}

View File

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Microsoft.Research.DynamicDataDisplay
{
public static class IPlotterElementExtensions
{
public static void RemoveFromPlotter(this IPlotterElement element)
{
if (element == null)
throw new ArgumentNullException("element");
if (element.Plotter != null)
{
element.Plotter.Children.Remove(element);
}
}
public static void AddToPlotter(this IPlotterElement element, Plotter plotter)
{
if (element == null)
throw new ArgumentNullException("element");
if (plotter == null)
throw new ArgumentNullException("plotter");
plotter.Children.Add(element);
}
}
}

View File

@@ -0,0 +1,21 @@
using System.Collections.Generic;
using System.Windows;
namespace Microsoft.Research.DynamicDataDisplay
{
public static class IPointCollectionExtensions
{
public static DataRect GetBounds(this IEnumerable<Point> points)
{
return BoundsHelper.GetViewportBounds(points);
}
public static IEnumerable<Point> Skip(this IList<Point> points, int skipCount)
{
for (int i = skipCount; i < points.Count; i++)
{
yield return points[i];
}
}
}
}

View File

@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
namespace Microsoft.Research.DynamicDataDisplay
{
internal static class ListExtensions
{
/// <summary>
/// Gets last element of list.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list"></param>
/// <returns></returns>
internal static T GetLast<T>(this List<T> list)
{
if (list == null) throw new ArgumentNullException("list");
if (list.Count == 0) throw new InvalidOperationException(Strings.Exceptions.CannotGetLastElement);
return list[list.Count - 1];
}
internal static void ForEach<T>(this IEnumerable<T> source, Action<T> action) {
if (action == null)
throw new ArgumentNullException("action");
if (source == null)
throw new ArgumentNullException("source");
foreach (var item in source)
{
action(item);
}
}
}
}

View File

@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Windows;
namespace Microsoft.Research.DynamicDataDisplay
{
public static class ListGenerator {
public static IEnumerable<Point> GeneratePoints(int length, Func<int, Point> generator) {
for (int i = 0; i < length; i++) {
yield return generator(i);
}
}
public static IEnumerable<Point> GeneratePoints(int length, Func<int, double> x, Func<int, double> y) {
for (int i = 0; i < length; i++) {
yield return new Point(x(i), y(i));
}
}
public static IEnumerable<T> Generate<T>(int length, Func<int, T> generator) {
for (int i = 0; i < length; i++) {
yield return generator(i);
}
}
}
}

View File

@@ -0,0 +1,53 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Markup;
using System.Resources;
namespace Microsoft.Research.DynamicDataDisplay.MarkupExtensions
{
/// <summary>
/// Represents a markup extension, which allows to get an access to application resource files.
/// </summary>
[MarkupExtensionReturnType(typeof(string))]
public class ResourceExtension : MarkupExtension
{
/// <summary>
/// Initializes a new instance of the <see cref="ResourceExtension"/> class.
/// </summary>
public ResourceExtension() { }
private string resourceKey;
//[ConstructorArgument("resourceKey")]
public string ResourceKey
{
get { return resourceKey; }
set
{
if (resourceKey == null)
throw new ArgumentNullException("resourceKey");
resourceKey = value;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="ResourceExtension"/> class.
/// </summary>
/// <param name="resourceKey">The resource key.</param>
public ResourceExtension(string resourceKey)
{
if (resourceKey == null)
throw new ArgumentNullException("resourceKey");
this.resourceKey = resourceKey;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return Strings.UIResources.ResourceManager.GetString(resourceKey);
}
}
}

View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Data;
using System.Windows;
namespace Microsoft.Research.DynamicDataDisplay.MarkupExtensions
{
public class SelfBinding : Binding
{
public SelfBinding()
{
RelativeSource = new RelativeSource { Mode = RelativeSourceMode.Self };
}
public SelfBinding(string propertyPath)
: this()
{
Path = new PropertyPath(propertyPath);
}
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Data;
namespace Microsoft.Research.DynamicDataDisplay.MarkupExtensions
{
public class TemplateBinding : Binding
{
public TemplateBinding()
{
RelativeSource = new RelativeSource { Mode = RelativeSourceMode.TemplatedParent };
}
public TemplateBinding(string propertyPath)
: this()
{
Path = new System.Windows.PropertyPath(propertyPath);
}
}
}

View File

@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Markup;
using System.Windows;
using System.ComponentModel;
namespace Microsoft.Research.DynamicDataDisplay.MarkupExtensions
{
[EditorBrowsable(EditorBrowsableState.Never)]
public class XbapConditionalExpression : MarkupExtension
{
public XbapConditionalExpression() { }
public XbapConditionalExpression(object value)
{
this.Value = value;
}
[ConstructorArgument("value")]
public object Value { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
#if RELEASEXBAP
return null;
#else
return ((ResourceDictionary)Application.LoadComponent(new Uri("/DynamicDataDisplay;component/Themes/Generic.xaml", UriKind.Relative)))[Value];
#endif
}
}
}

View File

@@ -0,0 +1,91 @@
using System;
using System.Windows;
namespace Microsoft.Research.DynamicDataDisplay
{
public static class MathHelper
{
public static long Clamp(long value, long min, long max)
{
return Math.Max(min, Math.Min(value, max));
}
public static double Clamp(double value, double min, double max)
{
return Math.Max(min, Math.Min(value, max));
}
/// <summary>Clamps specified value to [0,1]</summary>
/// <param name="d">Value to clamp</param>
/// <returns>Value in range [0,1]</returns>
public static double Clamp(double value)
{
return Math.Max(0, Math.Min(value, 1));
}
public static int Clamp(int value, int min, int max)
{
return Math.Max(min, Math.Min(value, max));
}
public static Rect CreateRectByPoints(double xMin, double yMin, double xMax, double yMax)
{
return new Rect(new Point(xMin, yMin), new Point(xMax, yMax));
}
public static double Interpolate(double start, double end, double ratio)
{
return start * (1 - ratio) + end * ratio;
}
public static double RadiansToDegrees(this double radians)
{
return radians * 180 / Math.PI;
}
public static double DegreesToRadians(this double degrees)
{
return degrees / 180 * Math.PI;
}
/// <summary>
/// Converts vector into angle.
/// </summary>
/// <param name="vector">The vector.</param>
/// <returns>Angle in degrees.</returns>
public static double ToAngle(this Vector vector)
{
return Math.Atan2(-vector.Y, vector.X).RadiansToDegrees();
}
public static Point ToPoint(this Vector v)
{
return new Point(v.X, v.Y);
}
public static bool IsNaN(this double d)
{
return Double.IsNaN(d);
}
public static bool IsNotNaN(this double d)
{
return !Double.IsNaN(d);
}
public static bool IsFinite(this double d)
{
return !Double.IsNaN(d) && !Double.IsInfinity(d);
}
public static bool IsInfinite(this double d)
{
return Double.IsInfinity(d);
}
public static bool AreClose(double d1, double d2, double diffRatio)
{
return Math.Abs(d1 / d2 - 1) < diffRatio;
}
}
}

View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
namespace Microsoft.Research.DynamicDataDisplay.Common.Auxiliary
{
internal static class MenuItemExtensions
{
public static MenuItem FindChildByHeader(this MenuItem parent, string header)
{
return parent.Items.OfType<MenuItem>().Where(subMenu => subMenu.Header.Equals(header)).FirstOrDefault();
}
}
}

View File

@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
namespace Microsoft.Research.DynamicDataDisplay.Common.Auxiliary
{
internal static class ObservableCollectionHelper
{
public static void ApplyChanges<T>(this ObservableCollection<T> collection, NotifyCollectionChangedEventArgs args)
{
if (args.NewItems != null)
{
int startingIndex = args.NewStartingIndex;
var newItems = args.NewItems;
for (int i = 0; i < newItems.Count; i++)
{
T addedItem = (T)newItems[i];
collection.Insert(startingIndex + i, addedItem);
}
}
if (args.OldItems != null)
{
for (int i = 0; i < args.OldItems.Count; i++)
{
T removedItem = (T)args.OldItems[i];
collection.Remove(removedItem);
}
}
}
}
}

View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Research.DynamicDataDisplay.Charts;
namespace Microsoft.Research.DynamicDataDisplay.Common.Auxiliary
{
internal static class PlacementExtensions
{
public static bool IsBottomOrTop(this AxisPlacement placement)
{
return placement == AxisPlacement.Bottom || placement == AxisPlacement.Top;
}
}
}

View File

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Research.DynamicDataDisplay.Common;
using System.Windows.Threading;
namespace Microsoft.Research.DynamicDataDisplay
{
public static class PlotterChildrenCollectionExtensions
{
public static void RemoveAll<T>(this PlotterChildrenCollection children)
{
var childrenToDelete = children.OfType<T>().ToList();
foreach (var child in childrenToDelete)
{
children.Remove(child as IPlotterElement);
}
}
public static void BeginAdd(this PlotterChildrenCollection children, IPlotterElement child)
{
children.Plotter.Dispatcher.BeginInvoke(((Action)(() => { children.Add(child); })), DispatcherPriority.Send);
}
public static void BeginRemove(this PlotterChildrenCollection children, IPlotterElement child)
{
children.Plotter.Dispatcher.BeginInvoke(((Action)(() => { children.Remove(child); })), DispatcherPriority.Send);
}
}
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Microsoft.Research.DynamicDataDisplay.Common.Auxiliary
{
public static class PlotterExtensions
{
public static void AddChild(this Plotter plotter, IPlotterElement child)
{
plotter.Children.Add(child);
}
}
}

View File

@@ -0,0 +1,19 @@
using System.Windows;
using System;
using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary;
namespace Microsoft.Research.DynamicDataDisplay
{
public static class PointExtensions
{
public static Vector ToVector(this Point pt)
{
return new Vector(pt.X, pt.Y);
}
public static bool IsFinite(this Point pt)
{
return pt.X.IsFinite() && pt.Y.IsFinite();
}
}
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Microsoft.Research.DynamicDataDisplay.Common.Auxiliary
{
public static class RandomExtensions
{
public static double NextDouble(this Random rnd, double min, double max)
{
return min + (max - min) * rnd.NextDouble();
}
}
}

View File

@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using Microsoft.Research.DynamicDataDisplay.Charts;
namespace Microsoft.Research.DynamicDataDisplay
{
public static class RangeExtensions
{
public static double GetLength(this Range<Point> range)
{
Point p1 = range.Min;
Point p2 = range.Max;
return (p1 - p2).Length;
}
public static double GetLength(this Range<double> range)
{
return range.Max - range.Min;
}
}
}

View File

@@ -0,0 +1,67 @@
using System.Windows;
using Microsoft.Research.DynamicDataDisplay.Common;
using System.Diagnostics;
namespace Microsoft.Research.DynamicDataDisplay
{
public static class RectExtensions
{
public static Point GetCenter(this Rect rect)
{
return new Point(rect.Left + rect.Width * 0.5, rect.Top + rect.Height * 0.5);
}
public static Rect FromCenterSize(Point center, Size size)
{
return FromCenterSize(center, size.Width, size.Height);
}
public static Rect FromCenterSize(Point center, double width, double height)
{
Rect res = new Rect(center.X - width / 2, center.Y - height / 2, width, height);
return res;
}
public static Rect Zoom(this Rect rect, Point to, double ratio)
{
return CoordinateUtilities.RectZoom(rect, to, ratio);
}
public static Rect ZoomOutFromCenter(this Rect rect, double ratio)
{
return CoordinateUtilities.RectZoom(rect, rect.GetCenter(), ratio);
}
public static Rect ZoomInToCenter(this Rect rect, double ratio)
{
return CoordinateUtilities.RectZoom(rect, rect.GetCenter(), 1 / ratio);
}
public static Int32Rect ToInt32Rect(this Rect rect)
{
Int32Rect intRect = new Int32Rect(
(int)rect.X,
(int)rect.Y,
(int)rect.Width,
(int)rect.Height);
return intRect;
}
[DebuggerStepThrough]
public static DataRect ToDataRect(this Rect rect)
{
return new DataRect(rect);
}
internal static bool IsNaN(this Rect rect)
{
return !rect.IsEmpty && (
rect.X.IsNaN() ||
rect.Y.IsNaN() ||
rect.Width.IsNaN() ||
rect.Height.IsNaN()
);
}
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace Microsoft.Research.DynamicDataDisplay.Common.Auxiliary
{
internal static class ResourcePoolExtensions
{
public static T GetOrCreate<T>(this ResourcePool<T> pool) where T : new()
{
T instance = pool.Get();
if (instance == null)
{
instance = new T();
}
return instance;
}
}
}

View File

@@ -0,0 +1,205 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media.Imaging;
using System.Windows;
using System.Windows.Media;
using System.IO;
using System.Diagnostics;
using System.Windows.Threading;
using System.Windows.Shapes;
namespace Microsoft.Research.DynamicDataDisplay.Common.Auxiliary
{
internal static class ScreenshotHelper
{
/// <summary>Gets the encoder by extension</summary>
/// <param name="extension">The extension</param>
/// <returns>BitmapEncoder object</returns>
internal static BitmapEncoder GetEncoderByExtension(string extension)
{
switch (extension)
{
case "bmp":
return new BmpBitmapEncoder();
case "jpg":
return new JpegBitmapEncoder();
case "gif":
return new GifBitmapEncoder();
case "png":
return new PngBitmapEncoder();
case "tiff":
return new TiffBitmapEncoder();
case "wmp":
return new WmpBitmapEncoder();
default:
throw new ArgumentException(Strings.Exceptions.CannotDetermineImageTypeByExtension, "extension");
}
}
/// <summary>Creates the screenshot of entire plotter element</summary>
/// <returns></returns>
internal static BitmapSource CreateScreenshot(UIElement uiElement, Int32Rect screenshotSource)
{
Window window = Window.GetWindow(uiElement);
if (window == null)
{
return CreateElementScreenshot(uiElement);
}
Size size = window.RenderSize;
//double dpiCoeff = 32 / SystemParameters.CursorWidth;
//int dpi = (int)(dpiCoeff * 96);
double dpiCoeff = 1;
int dpi = 96;
RenderTargetBitmap bmp = new RenderTargetBitmap(
(int)(size.Width * dpiCoeff), (int)(size.Height * dpiCoeff),
dpi, dpi, PixelFormats.Default);
// white background
Rectangle whiteRect = new Rectangle { Width = size.Width, Height = size.Height, Fill = Brushes.White };
whiteRect.Measure(size);
whiteRect.Arrange(new Rect(size));
bmp.Render(whiteRect);
// the very element
bmp.Render(uiElement);
CroppedBitmap croppedBmp = new CroppedBitmap(bmp, screenshotSource);
return croppedBmp;
}
private static BitmapSource CreateElementScreenshot(UIElement uiElement)
{
bool measureValid = uiElement.IsMeasureValid;
if (!measureValid)
{
double width = 300;
double height = 300;
FrameworkElement frElement = uiElement as FrameworkElement;
if (frElement != null)
{
if (!Double.IsNaN(frElement.Width))
width = frElement.Width;
if (!Double.IsNaN(frElement.Height))
height = frElement.Height;
}
Size size = new Size(width, height);
uiElement.Measure(size);
uiElement.Arrange(new Rect(size));
}
RenderTargetBitmap bmp = new RenderTargetBitmap(
(int)uiElement.RenderSize.Width, (int)uiElement.RenderSize.Height,
96, 96, PixelFormats.Default);
// this is waiting for dispatcher to perform measure, arrange and render passes
uiElement.Dispatcher.Invoke(((Action)(() => { })), DispatcherPriority.Background);
Size elementSize = uiElement.DesiredSize;
// white background
Rectangle whiteRect = new Rectangle { Width = elementSize.Width, Height = elementSize.Height, Fill = Brushes.White };
whiteRect.Measure(elementSize);
whiteRect.Arrange(new Rect(elementSize));
bmp.Render(whiteRect);
bmp.Render(uiElement);
return bmp;
}
private static Dictionary<BitmapSource, string> pendingBitmaps = new Dictionary<BitmapSource, string>();
internal static void SaveBitmapToStream(BitmapSource bitmap, Stream stream, string fileExtension)
{
if (bitmap == null)
throw new ArgumentNullException("bitmap");
if (stream == null)
throw new ArgumentNullException("stream");
if (String.IsNullOrEmpty(fileExtension))
throw new ArgumentException(Strings.Exceptions.ExtensionCannotBeNullOrEmpty, fileExtension);
BitmapEncoder encoder = ScreenshotHelper.GetEncoderByExtension(fileExtension);
encoder.Frames.Add(BitmapFrame.Create(bitmap, null, new BitmapMetadata(fileExtension.Trim('.')), null));
encoder.Save(stream);
}
internal static void SaveBitmapToFile(BitmapSource bitmap, string filePath)
{
if (String.IsNullOrEmpty(filePath))
throw new ArgumentException(Strings.Exceptions.FilePathCannotbeNullOrEmpty, "filePath");
if (bitmap.IsDownloading)
{
pendingBitmaps[bitmap] = filePath;
bitmap.DownloadCompleted += OnBitmapDownloadCompleted;
return;
}
string dirPath = System.IO.Path.GetDirectoryName(filePath);
if (!String.IsNullOrEmpty(dirPath) && !Directory.Exists(dirPath))
{
Directory.CreateDirectory(dirPath);
}
bool fileExistedBefore = File.Exists(filePath);
try
{
using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write))
{
string extension = System.IO.Path.GetExtension(filePath).TrimStart('.');
SaveBitmapToStream(bitmap, fs, extension);
}
}
catch (ArgumentException)
{
if (!fileExistedBefore && File.Exists(filePath))
{
try
{
File.Delete(filePath);
}
catch { }
}
}
catch (IOException exc)
{
Debug.WriteLine("Exception while saving bitmap to file: " + exc.Message);
}
}
public static void SaveStreamToFile(Stream stream, string filePath)
{
string dirPath = System.IO.Path.GetDirectoryName(filePath);
if (!String.IsNullOrEmpty(dirPath) && !Directory.Exists(dirPath))
{
Directory.CreateDirectory(dirPath);
}
using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write))
{
string extension = System.IO.Path.GetExtension(filePath).TrimStart('.');
if (stream.CanSeek)
stream.Seek(0, SeekOrigin.Begin);
stream.CopyTo(fs);
}
stream.Dispose();
}
private static void OnBitmapDownloadCompleted(object sender, EventArgs e)
{
BitmapSource bmp = (BitmapSource)sender;
bmp.DownloadCompleted -= OnBitmapDownloadCompleted;
string filePath = pendingBitmaps[bmp];
pendingBitmaps.Remove(bmp);
SaveBitmapToFile(bmp, filePath);
}
}
}

View File

@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
namespace Microsoft.Research.DynamicDataDisplay.Common.Auxiliary
{
internal static class SizeExtensions
{
private const double sizeRatio = 1e-7;
public static bool EqualsApproximately(this Size size1, Size size2)
{
bool widthEquals = Math.Abs(size1.Width - size2.Width) / size1.Width < sizeRatio;
bool heightEquals = Math.Abs(size1.Height - size2.Height) / size1.Height < sizeRatio;
return widthEquals && heightEquals;
}
}
}

View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
namespace Microsoft.Research.DynamicDataDisplay.Common.Auxiliary
{
internal static class SizeHelper
{
public static Size CreateInfiniteSize()
{
return new Size(Double.PositiveInfinity, Double.PositiveInfinity);
}
}
}

View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace Microsoft.Research.DynamicDataDisplay.Common.Auxiliary
{
public static class StreamExtensions
{
public static void CopyTo(this Stream input, Stream output)
{
byte[] buffer = new byte[32768];
while (true)
{
int read = input.Read(buffer, 0, buffer.Length);
if (read <= 0)
return;
output.Write(buffer, 0, read);
}
}
}
}

View File

@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Microsoft.Research.DynamicDataDisplay.Common.Auxiliary
{
internal static class StringExtensions
{
public static string Format(this string formatString, object param)
{
return String.Format(formatString, param);
}
public static string Format(this string formatString, object param1, object param2)
{
return String.Format(formatString, param1, param2);
}
}
}

View File

@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Windows.Threading;
namespace Microsoft.Research.DynamicDataDisplay.Common.Auxiliary
{
public static class TaskExtensions
{
/// <summary>
/// Logs exceptions that occur during task execution.
/// </summary>
/// <param name="task">The task.</param>
/// <returns></returns>
public static Task WithExceptionLogging(this Task task)
{
return task.ContinueWith(t =>
{
var exception = t.Exception;
if (exception != null)
{
if (exception.InnerException != null)
exception = (AggregateException)exception.InnerException;
Debug.WriteLine("Failure in async task: " + exception.Message);
}
});
}
/// <summary>
/// Rethrows exceptions thrown during task execution in thespecified dispatcher thread.
/// </summary>
/// <param name="task">The task.</param>
/// <param name="dispatcher">The dispatcher.</param>
/// <returns></returns>
public static Task WithExceptionThrowingInDispatcher(this Task task, Dispatcher dispatcher)
{
return task.ContinueWith(t =>
{
dispatcher.BeginInvoke(() =>
{
throw t.Exception;
}, DispatcherPriority.Send);
});
}
}
}

View File

@@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media.Media3D;
namespace Microsoft.Research.DynamicDataDisplay.Common.Auxiliary
{
public static class TriangleMath
{
public static bool TriangleContains(Point a, Point b, Point c, Point m)
{
double a0 = a.X - c.X;
double a1 = b.X - c.X;
double a2 = a.Y - c.Y;
double a3 = b.Y - c.Y;
if (AreClose(a0 * a3, a1 * a2))
{
// determinant is too close to zero => apexes are on one line
Vector ab = a - b;
Vector ac = a - c;
Vector bc = b - c;
Vector ax = a - m;
Vector bx = b - m;
bool res = AreClose(ab.X * ax.Y, ab.Y * ax.X) && !AreClose(ab.LengthSquared, 0) ||
AreClose(ac.X * ax.Y, ac.Y * ax.X) && !AreClose(ac.LengthSquared, 0) ||
AreClose(bc.X * bx.Y, bc.Y * bx.X) && !AreClose(bc.LengthSquared, 0);
return res;
}
else
{
double b1 = m.X - c.X;
double b2 = m.Y - c.Y;
// alpha, beta and gamma - are baricentric coordinates of v
// in triangle with apexes a, b and c
double beta = (b2 / a2 * a0 - b1) / (a3 / a2 * a0 - a1);
double alpha = (b1 - a1 * beta) / a0;
double gamma = 1 - beta - alpha;
return alpha >= 0 && beta >= 0 && gamma >= 0;
}
}
private const double eps = 0.00001;
private static bool AreClose(double x, double y)
{
return Math.Abs(x - y) < eps;
}
public static Vector3D GetBaricentricCoordinates(Point a, Point b, Point c, Point m)
{
double Sac = GetSquare(a, c, m);
double Sbc = GetSquare(b, c, m);
double Sab = GetSquare(a, b, m);
double sum = (Sab + Sac + Sbc) / 3;
return new Vector3D(Sbc / sum, Sac / sum, Sab / sum);
}
public static double GetSquare(Point a, Point b, Point c)
{
double ab = (a - b).Length;
double ac = (a - c).Length;
double bc = (b - c).Length;
double p = 0.5 * (ab + ac + bc); // half of perimeter
return Math.Sqrt(p * (p - ab) * (p - ac) * (p - bc));
}
}
}

View File

@@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Diagnostics;
namespace Microsoft.Research.DynamicDataDisplay
{
public sealed class ValueStore : CustomTypeDescriptor, INotifyPropertyChanged
{
public ValueStore(params string[] propertiesNames)
{
foreach (var propertyName in propertiesNames)
{
this[propertyName] = "";
}
}
private Dictionary<string, object> cache = new Dictionary<string, object>();
public object this[string propertyName]
{
get { return cache[propertyName]; }
set { SetValue(propertyName, value); }
}
public ValueStore SetValue(string propertyName, object value)
{
cache[propertyName] = value;
PropertyChanged.Raise(this, propertyName);
return this;
}
private PropertyDescriptorCollection collection;
public override PropertyDescriptorCollection GetProperties()
{
PropertyDescriptor[] propertyDescriptors = new PropertyDescriptor[cache.Count];
var keys = cache.Keys.ToArray();
for (int i = 0; i < keys.Length; i++)
{
propertyDescriptors[i] = new ValueStorePropertyDescriptor(keys[i]);
}
collection = new PropertyDescriptorCollection(propertyDescriptors);
return collection;
}
private sealed class ValueStorePropertyDescriptor : PropertyDescriptor
{
private readonly string name;
public ValueStorePropertyDescriptor(string name)
: base(name, null)
{
this.name = name;
}
public override bool CanResetValue(object component)
{
return false;
}
public override Type ComponentType
{
get { return typeof(ValueStore); }
}
public override object GetValue(object component)
{
ValueStore store = (ValueStore)component;
return store[name];
}
public override bool IsReadOnly
{
get { return false; }
}
public override Type PropertyType
{
get { return typeof(string); }
}
public override void ResetValue(object component)
{
}
public override void SetValue(object component, object value)
{
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
}

View File

@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Data;
using System.Globalization;
namespace Microsoft.Research.DynamicDataDisplay
{
public sealed class ValueStoreConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
ValueStore store = (ValueStore)value;
string key = (string)parameter;
return store[key];
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
#endregion
}
}

View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
namespace Microsoft.Research.DynamicDataDisplay.Common.Auxiliary
{
internal static class VectorExtensions
{
public static Point ToPoint(this Vector vector)
{
return new Point(vector.X, vector.Y);
}
}
}

View File

@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace Microsoft.Research.DynamicDataDisplay.Common.Auxiliary
{
internal static class Verify
{
[DebuggerStepThrough]
public static void IsTrue(this bool condition)
{
if (!condition)
{
throw new ArgumentException(Strings.Exceptions.AssertionFailedSearch);
}
}
[DebuggerStepThrough]
public static void IsTrue(this bool condition, string paramName)
{
if (!condition)
{
throw new ArgumentException(Strings.Exceptions.AssertionFailedSearch, paramName);
}
}
public static void IsTrueWithMessage(this bool condition, string message)
{
if (!condition)
throw new ArgumentException(message);
}
[DebuggerStepThrough]
public static void AssertNotNull(object obj)
{
Verify.IsTrue(obj != null);
}
public static void VerifyNotNull(this object obj, string paramName)
{
if (obj == null)
throw new ArgumentNullException(paramName);
}
public static void VerifyNotNull(this object obj)
{
VerifyNotNull(obj, "value");
}
[DebuggerStepThrough]
public static void AssertIsNotNaN(this double d)
{
Verify.IsTrue(!Double.IsNaN(d));
}
[DebuggerStepThrough]
public static void AssertIsFinite(this double d)
{
Verify.IsTrue(!Double.IsInfinity(d) && !(Double.IsNaN(d)));
}
}
}

View File

@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media;
namespace Microsoft.Research.DynamicDataDisplay.Common.Auxiliary
{
internal static class VisualTreeHelperHelper
{
public static DependencyObject GetParent(DependencyObject target, int depth)
{
DependencyObject parent = target;
do
{
parent = VisualTreeHelper.GetParent(parent);
if (parent == null)
{
break;
}
} while (--depth > 0);
return parent;
}
}
}