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