using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary; using System.Windows; namespace Microsoft.Research.DynamicDataDisplay.Charts.Isolines { /// /// Isoline's grid cell /// internal interface ICell { Vector LeftTop { get; } Vector LeftBottom { get; } Vector RightTop { get; } Vector RightBottom { get; } } internal sealed class IrregularCell : ICell { public IrregularCell(Vector leftBottom, Vector rightBottom, Vector rightTop, Vector leftTop) { this.leftBottom = leftBottom; this.rightBottom = rightBottom; this.rightTop = rightTop; this.leftTop = leftTop; } public IrregularCell(Point lb, Point rb, Point rt, Point lt) { leftTop = lt.ToVector(); leftBottom = lb.ToVector(); rightTop = rt.ToVector(); rightBottom = rb.ToVector(); } #region ICell Members private readonly Vector leftTop; public Vector LeftTop { get { return leftTop; } } private readonly Vector leftBottom; public Vector LeftBottom { get { return leftBottom; } } private readonly Vector rightTop; public Vector RightTop { get { return rightTop; } } private readonly Vector rightBottom; public Vector RightBottom { get { return rightBottom; } } #endregion #region Sides public Vector LeftSide { get { return (leftBottom + leftTop) / 2; } } public Vector RightSide { get { return (rightBottom + rightTop) / 2; } } public Vector TopSide { get { return (leftTop + rightTop) / 2; } } public Vector BottomSide { get { return (leftBottom + rightBottom) / 2; } } #endregion public Point Center { get { return ((LeftSide + RightSide) / 2).ToPoint(); } } public IrregularCell GetSubRect(SubCell sub) { switch (sub) { case SubCell.LeftBottom: return new IrregularCell(LeftBottom, BottomSide, Center.ToVector(), LeftSide); case SubCell.LeftTop: return new IrregularCell(LeftSide, Center.ToVector(), TopSide, LeftTop); case SubCell.RightBottom: return new IrregularCell(BottomSide, RightBottom, RightSide, Center.ToVector()); case SubCell.RightTop: default: return new IrregularCell(Center.ToVector(), RightSide, RightTop, TopSide); } } } internal enum SubCell { LeftBottom = 0, LeftTop = 1, RightBottom = 2, RightTop = 3 } internal class ValuesInCell { double min = Double.MaxValue, max = Double.MinValue; /// Initializes values in four corners of cell /// /// /// /// /// Some or all values can be NaN. That means that value is not specified (misssing) public ValuesInCell(double leftBottom, double rightBottom, double rightTop, double leftTop) { this.leftTop = leftTop; this.leftBottom = leftBottom; this.rightTop = rightTop; this.rightBottom = rightBottom; // Find max and min values (with respect to possible NaN values) if (!Double.IsNaN(leftTop)) { if (min > leftTop) min = leftTop; if (max < leftTop) max = leftTop; } if (!Double.IsNaN(leftBottom)) { if (min > leftBottom) min = leftBottom; if (max < leftBottom) max = leftBottom; } if (!Double.IsNaN(rightTop)) { if (min > rightTop) min = rightTop; if (max < rightTop) max = rightTop; } if (!Double.IsNaN(rightBottom)) { if (min > rightBottom) min = rightBottom; if (max < rightBottom) max = rightBottom; } left = (leftTop + leftBottom) / 2; bottom = (leftBottom + rightBottom) / 2; right = (rightTop + rightBottom) / 2; top = (rightTop + leftTop) / 2; } public ValuesInCell(double leftBottom, double rightBottom, double rightTop, double leftTop, double missingValue) { DebugVerify.IsNotNaN(leftBottom); DebugVerify.IsNotNaN(rightBottom); DebugVerify.IsNotNaN(rightTop); DebugVerify.IsNotNaN(leftTop); // Copy values and find min and max with respect to possible missing values if (leftTop != missingValue) { this.leftTop = leftTop; if (min > leftTop) min = leftTop; if (max < leftTop) max = leftTop; } else this.leftTop = Double.NaN; if (leftBottom != missingValue) { this.leftBottom = leftBottom; if (min > leftBottom) min = leftBottom; if (max < leftBottom) max = leftBottom; } else this.leftBottom = Double.NaN; if (rightTop != missingValue) { this.rightTop = rightTop; if (min > rightTop) min = rightTop; if (max < rightTop) max = rightTop; } else this.rightTop = Double.NaN; if (rightBottom != missingValue) { this.rightBottom = rightBottom; if (min > rightBottom) min = rightBottom; if (max < rightBottom) max = rightBottom; } else this.rightBottom = Double.NaN; left = (this.leftTop + this.leftBottom) / 2; bottom = (this.leftBottom + this.rightBottom) / 2; right = (this.rightTop + this.rightBottom) / 2; top = (this.rightTop + this.leftTop) / 2; /* if (leftTop != missingValue && ) { if (leftBottom != missingValue) left = (leftTop + leftBottom) / 2; else left = Double.NaN; if (rightTop != missingValue) top = (leftTop + rightTop) / 2; else top = Double.NaN; } if (rightBottom != missingValue) { if (leftBottom != missingValue) bottom = (leftBottom + rightBottom) / 2; else bottom = Double.NaN; if (rightTop != missingValue) right = (rightTop + rightBottom) / 2; else right = Double.NaN; }*/ } /*internal bool ValueBelongTo(double value) { IEnumerable values = new double[] { leftTop, leftBottom, rightTop, rightBottom }; return !(values.All(v => v > value) || values.All(v => v < value)); }*/ internal bool ValueBelongTo(double value) { return (min <= value && value <= max); } #region Edges [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly double leftTop; public double LeftTop { get { return leftTop; } } [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly double leftBottom; public double LeftBottom { get { return leftBottom; } } [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly double rightTop; public double RightTop { get { return rightTop; } } [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly double rightBottom; public double RightBottom { get { return rightBottom; } } #endregion #region Sides & center [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly double left; public double Left { get { return left; } } [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly double right; public double Right { get { return right; } } [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly double top; public double Top { get { return top; } } [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly double bottom; public double Bottom { get { return bottom; } } public double Center { get { return (Left + Right) * 0.5; } } #endregion #region SubCells public ValuesInCell LeftTopCell { get { return new ValuesInCell(Left, Center, Top, LeftTop); } } public ValuesInCell RightTopCell { get { return new ValuesInCell(Center, Right, RightTop, Top); } } public ValuesInCell RightBottomCell { get { return new ValuesInCell(Bottom, RightBottom, Right, Center); } } public ValuesInCell LeftBottomCell { get { return new ValuesInCell(LeftBottom, Bottom, Center, Left); } } public ValuesInCell GetSubCell(SubCell subCell) { switch (subCell) { case SubCell.LeftBottom: return LeftBottomCell; case SubCell.LeftTop: return LeftTopCell; case SubCell.RightBottom: return RightBottomCell; case SubCell.RightTop: default: return RightTopCell; } } #endregion /// /// Returns bitmask of comparison of values at cell corners with reference value. /// Corresponding bit is set to one if value at cell corner is greater than reference value. /// a------b /// | Cell | /// d------c /// /// Value at corner (see figure) /// Value at corner (see figure) /// Value at corner (see figure) /// Value at corner (see figure) /// Reference value /// Bitmask public CellBitmask GetCellValue(double value) { CellBitmask n = CellBitmask.None; if (!Double.IsNaN(leftTop) && leftTop > value) n |= CellBitmask.LeftTop; if (!Double.IsNaN(leftBottom) && leftBottom > value) n |= CellBitmask.LeftBottom; if (!Double.IsNaN(rightBottom) && rightBottom > value) n |= CellBitmask.RightBottom; if (!Double.IsNaN(rightTop) && rightTop > value) n |= CellBitmask.RightTop; return n; } } }