using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Media;
namespace Microsoft.Research.DynamicDataDisplay
{
///
/// Base class for all data transforms.
/// Defines methods to transform point from data coordinate system to viewport coordinates and vice versa.
/// Derived class should be immutable; to perform any changes a new new instance with different parameters should be created.
///
public abstract class DataTransform
{
///
/// Transforms the point in data coordinates to viewport coordinates.
///
/// The point in data coordinates.
/// Transformed point in viewport coordinates.
public abstract Point DataToViewport(Point pt);
///
/// Transforms the point in viewport coordinates to data coordinates.
///
/// The point in viewport coordinates.
/// Transformed point in data coordinates.
public abstract Point ViewportToData(Point pt);
private static readonly DataRect defaultDomain = DataRect.Empty;
///
/// Gets the data domain of this dataTransform.
///
/// The data domain of this dataTransform.
public virtual DataRect DataDomain { get { return defaultDomain; } }
}
///
/// Represents identity data transform, which applies no transformation.
/// is by default in CoordinateTransform.
///
public sealed class IdentityTransform : DataTransform
{
///
/// Initializes a new instance of the class.
///
public IdentityTransform() { }
///
/// Transforms the point in data coordinates to viewport coordinates.
///
/// The point in data coordinates.
///
public override Point DataToViewport(Point pt)
{
return pt;
}
///
/// Transforms the point in viewport coordinates to data coordinates.
///
/// The point in viewport coordinates.
///
public override Point ViewportToData(Point pt)
{
return pt;
}
}
///
/// Represents a logarithmic transform of y-values of points.
///
public sealed class Log10YTransform : DataTransform
{
///
/// Initializes a new instance of the class.
///
public Log10YTransform() { }
///
/// Transforms the point in data coordinates to viewport coordinates.
///
/// The point in data coordinates.
///
public override Point DataToViewport(Point pt)
{
double y = pt.Y;
if (y < 0)
y = Double.MinValue;
else
y = Math.Log10(y);
return new Point(pt.X, y);
}
///
/// Transforms the point in viewport coordinates to data coordinates.
///
/// The point in viewport coordinates.
///
public override Point ViewportToData(Point pt)
{
return new Point(pt.X, Math.Pow(10, pt.Y));
}
///
/// Gets the data domain of this dataTransform.
///
/// The data domain of this dataTransform.
public override DataRect DataDomain
{
get { return DataDomains.YPositive; }
}
}
///
/// Represents a logarithmic transform of x-values of points.
///
public sealed class Log10XTransform : DataTransform
{
///
/// Initializes a new instance of the class.
///
public Log10XTransform() { }
///
/// Transforms the point in data coordinates to viewport coordinates.
///
/// The point in data coordinates.
///
public override Point DataToViewport(Point pt)
{
double x = pt.X;
if (x < 0)
x = Double.MinValue;
else
x = Math.Log10(x);
return new Point(x, pt.Y);
}
///
/// Transforms the point in viewport coordinates to data coordinates.
///
/// The point in viewport coordinates.
///
public override Point ViewportToData(Point pt)
{
return new Point(Math.Pow(10, pt.X), pt.Y);
}
///
/// Gets the data domain.
///
/// The data domain.
public override DataRect DataDomain
{
get { return DataDomains.XPositive; }
}
}
///
/// Represents a mercator transform, used in maps.
/// Transforms y coordinates.
///
public sealed class MercatorTransform : DataTransform
{
///
/// Initializes a new instance of the class.
///
public MercatorTransform()
{
CalcScale(maxLatitude);
}
///
/// Initializes a new instance of the class.
///
/// The maximal latitude.
public MercatorTransform(double maxLatitude)
{
this.maxLatitude = maxLatitude;
CalcScale(maxLatitude);
}
private void CalcScale(double maxLatitude)
{
double maxLatDeg = maxLatitude;
double maxLatRad = maxLatDeg * Math.PI / 180;
scale = maxLatDeg / Math.Log(Math.Tan(maxLatRad / 2 + Math.PI / 4));
}
private double scale;
///
/// Gets the scale.
///
/// The scale.
public double Scale
{
get { return scale; }
}
private double maxLatitude = 85;
///
/// Gets the maximal latitude.
///
/// The max latitude.
public double MaxLatitude
{
get { return maxLatitude; }
}
///
/// Transforms the point in data coordinates to viewport coordinates.
///
/// The point in data coordinates.
///
public sealed override Point DataToViewport(Point pt)
{
double y = pt.Y;
if (-maxLatitude <= y && y <= maxLatitude)
{
y = scale * Math.Log(Math.Tan(Math.PI * (pt.Y + 90) / 360));
}
return new Point(pt.X, y);
}
///
/// Transforms the point in viewport coordinates to data coordinates.
///
/// The point in viewport coordinates.
///
public sealed override Point ViewportToData(Point pt)
{
double y = pt.Y;
if (-maxLatitude <= y && y <= maxLatitude)
{
double e = Math.Exp(y / scale);
y = 360 * Math.Atan(e) / Math.PI - 90;
}
return new Point(pt.X, y);
}
}
///
/// Represents transform from polar coordinate system to rectangular coordinate system.
///
public sealed class PolarToRectTransform : DataTransform
{
///
/// Initializes a new instance of the class.
///
public PolarToRectTransform() { }
///
/// Transforms the point in data coordinates to viewport coordinates.
///
/// The point in data coordinates.
///
public override Point DataToViewport(Point pt)
{
double r = pt.X;
double phi = pt.Y;
double x = r * Math.Cos(phi);
double y = r * Math.Sin(phi);
return new Point(x, y);
}
///
/// Transforms the point in viewport coordinates to data coordinates.
///
/// The point in viewport coordinates.
///
public override Point ViewportToData(Point pt)
{
double x = pt.X;
double y = pt.Y;
double r = Math.Sqrt(x * x + y * y);
double phi = Math.Atan2(y, x);
return new Point(r, phi);
}
}
///
/// Represents a data transform which applies rotation around specified center at specified angle.
///
public sealed class RotateDataTransform : DataTransform
{
///
/// Initializes a new instance of the class.
///
/// The angle in radians.
public RotateDataTransform(double angleInRadians)
{
this.center = new Point();
this.angle = angleInRadians;
}
///
/// Initializes a new instance of the class.
///
/// The angle in radians.
/// The center of rotation.
public RotateDataTransform(double angleInRadians, Point center)
{
this.center = center;
this.angle = angleInRadians;
}
private readonly Point center;
///
/// Gets the center of rotation.
///
/// The center.
public Point Center
{
get { return center; }
}
private readonly double angle;
///
/// Gets the rotation angle.
///
/// The angle.
public double Angle
{
get { return angle; }
}
///
/// Transforms the point in data coordinates to viewport coordinates.
///
/// The point in data coordinates.
///
public override Point DataToViewport(Point pt)
{
return Transform(pt, angle);
}
///
/// Transforms the point in viewport coordinates to data coordinates.
///
/// The point in viewport coordinates.
///
public override Point ViewportToData(Point pt)
{
return Transform(pt, -angle);
}
private Point Transform(Point pt, double angle)
{
Vector vec = pt - center;
double currAngle = Math.Atan2(vec.Y, vec.X);
currAngle += angle;
Vector rotatedVec = new Vector(Math.Cos(currAngle), Math.Sin(currAngle)) * vec.Length;
return center + rotatedVec;
}
}
///
/// Represents data transform performed by multiplication on given matrix.
///
public sealed class MatrixDataTransform : DataTransform
{
///
/// Initializes a new instance of the class.
///
/// The transform matrix.
public MatrixDataTransform(Matrix matrix)
{
this.matrix = matrix;
this.invertedMatrix = matrix;
invertedMatrix.Invert();
}
private readonly Matrix matrix;
private readonly Matrix invertedMatrix;
///
/// Transforms the point in data coordinates to viewport coordinates.
///
/// The point in data coordinates.
///
public override Point DataToViewport(Point pt)
{
return matrix.Transform(pt);
}
///
/// Transforms the point in viewport coordinates to data coordinates.
///
/// The point in viewport coordinates.
///
public override Point ViewportToData(Point pt)
{
return invertedMatrix.Transform(pt);
}
}
///
/// Represents a chain of transforms which are being applied consequently.
///
public sealed class CompositeDataTransform : DataTransform
{
///
/// Initializes a new instance of the class.
///
/// The transforms.
public CompositeDataTransform(params DataTransform[] transforms)
{
if (transforms == null)
throw new ArgumentNullException("transforms");
foreach (var transform in transforms)
{
if (transform == null)
throw new ArgumentNullException("transforms", Strings.Exceptions.EachTransformShouldNotBeNull);
}
this.transforms = transforms;
}
///
/// Initializes a new instance of the class.
///
/// The transforms.
public CompositeDataTransform(IEnumerable transforms)
{
if (transforms == null)
throw new ArgumentNullException("transforms");
this.transforms = transforms;
}
private readonly IEnumerable transforms;
///
/// Gets the transforms.
///
/// The transforms.
public IEnumerable Transforms
{
get { return transforms; }
}
///
/// Transforms the point in data coordinates to viewport coordinates.
///
/// The point in data coordinates.
///
public override Point DataToViewport(Point pt)
{
foreach (var transform in transforms)
{
pt = transform.DataToViewport(pt);
}
return pt;
}
///
/// Transforms the point in viewport coordinates to data coordinates.
///
/// The point in viewport coordinates.
///
public override Point ViewportToData(Point pt)
{
foreach (var transform in transforms.Reverse())
{
pt = transform.ViewportToData(pt);
}
return pt;
}
}
///
/// Represents a data transform, performed by given lambda function.
///
public sealed class LambdaDataTransform : DataTransform
{
///
/// Initializes a new instance of the class.
///
/// The data to viewport transform delegate.
/// The viewport to data transform delegate.
public LambdaDataTransform(Func dataToViewport, Func viewportToData)
{
if (dataToViewport == null)
throw new ArgumentNullException("dataToViewport");
if (viewportToData == null)
throw new ArgumentNullException("viewportToData");
this.dataToViewport = dataToViewport;
this.viewportToData = viewportToData;
}
private readonly Func dataToViewport;
///
/// Gets the data to viewport transform delegate.
///
/// The data to viewport func.
public Func DataToViewportFunc
{
get { return dataToViewport; }
}
private readonly Func viewportToData;
///
/// Gets the viewport to data transform delegate.
///
/// The viewport to data func.
public Func ViewportToDataFunc
{
get { return viewportToData; }
}
///
/// Transforms the point in data coordinates to viewport coordinates.
///
/// The point in data coordinates.
///
public override Point DataToViewport(Point pt)
{
return dataToViewport(pt);
}
///
/// Transforms the point in viewport coordinates to data coordinates.
///
/// The point in viewport coordinates.
///
public override Point ViewportToData(Point pt)
{
return viewportToData(pt);
}
}
///
/// Contains default data transforms.
///
public static class DataTransforms
{
private static readonly IdentityTransform identity = new IdentityTransform();
///
/// Gets the default identity data transform.
///
/// The identity data transform.
public static IdentityTransform Identity
{
get
{
return identity;
}
}
}
}