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; } } } }