mirror of
https://gitee.com/akwkevin/aistudio.-wpf.-diagram
synced 2026-03-03 00:00:57 +08:00
1087 lines
36 KiB
C#
1087 lines
36 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Windows;
|
|
|
|
namespace AIStudio.Wpf.DiagramDesigner.Geometrys
|
|
{
|
|
[Serializable]
|
|
public struct RectangleBase : IShape, IFormattable
|
|
{
|
|
public static RectangleBase Zero { get; } = new RectangleBase(0, 0, 0, 0, true);
|
|
|
|
public RectangleBase(double left, double top, double right, double bottom, bool lefttoprightbottom)
|
|
{
|
|
_x = left;
|
|
_y = top;
|
|
_width = Math.Abs(left - right);
|
|
_height = Math.Abs(top - bottom);
|
|
}
|
|
|
|
public RectangleBase(double x,double y,double width, double height)
|
|
{
|
|
if (width < 0 || height < 0)
|
|
{
|
|
throw new System.ArgumentException("Size_WidthAndHeightCannotBeNegative");
|
|
}
|
|
|
|
_x = x;
|
|
_y = y;
|
|
_width = width;
|
|
_height = height;
|
|
}
|
|
|
|
public RectangleBase(PointBase location, SizeBase size)
|
|
{
|
|
if (size.IsEmpty)
|
|
{
|
|
this = s_empty;
|
|
}
|
|
else
|
|
{
|
|
_x = location._x;
|
|
_y = location._y;
|
|
_width = size._width;
|
|
_height = size._height;
|
|
}
|
|
}
|
|
|
|
public RectangleBase(PointBase point1, PointBase point2)
|
|
{
|
|
_x = Math.Min(point1._x, point2._x);
|
|
_y = Math.Min(point1._y, point2._y);
|
|
|
|
// Max with 0 to prevent double weirdness from causing us to be (-epsilon..0)
|
|
_width = Math.Max(Math.Max(point1._x, point2._x) - _x, 0);
|
|
_height = Math.Max(Math.Max(point1._y, point2._y) - _y, 0);
|
|
}
|
|
|
|
public RectangleBase(SizeBase size)
|
|
{
|
|
if (size.IsEmpty)
|
|
{
|
|
this = s_empty;
|
|
}
|
|
else
|
|
{
|
|
_x = _y = 0;
|
|
_width = size.Width;
|
|
_height = size.Height;
|
|
}
|
|
}
|
|
|
|
public bool Overlap(RectangleBase r)
|
|
=> Left < r.Right && Right > r.Left && Top < r.Bottom && Bottom > r.Top;
|
|
|
|
public bool Intersects(RectangleBase r)
|
|
{
|
|
var thisX = Left;
|
|
var thisY = Top;
|
|
var thisW = Width;
|
|
var thisH = Height;
|
|
var rectX = r.Left;
|
|
var rectY = r.Top;
|
|
var rectW = r.Width;
|
|
var rectH = r.Height;
|
|
return rectX < thisX + thisW && thisX < rectX + rectW && rectY < thisY + thisH && thisY < rectY + rectH;
|
|
}
|
|
|
|
public RectangleBase InflateRectangle(double horizontal, double vertical)
|
|
=> new RectangleBase(Left - horizontal, Top - vertical, Right + horizontal, Bottom + vertical, true);
|
|
|
|
public RectangleBase UnionRectangle(RectangleBase r)
|
|
{
|
|
var x1 = Math.Min(Left, r.Left);
|
|
var x2 = Math.Max(Left + Width, r.Left + r.Width);
|
|
var y1 = Math.Min(Top, r.Top);
|
|
var y2 = Math.Max(Top + Height, r.Top + r.Height);
|
|
return new RectangleBase(x1, y1, x2, y2, true);
|
|
}
|
|
|
|
public bool ContainsPoint(PointBase point) => ContainsPoint(point.X, point.Y);
|
|
|
|
public bool ContainsPoint(double x, double y)
|
|
=> x >= Left && x <= Right && y >= Top && y <= Bottom;
|
|
|
|
public IEnumerable<PointBase> GetIntersectionsWithLine(LineBase line)
|
|
{
|
|
var borders = new[] {
|
|
new LineBase(NorthWest, NorthEast),
|
|
new LineBase(NorthEast, SouthEast),
|
|
new LineBase(SouthWest, SouthEast),
|
|
new LineBase(NorthWest, SouthWest)
|
|
};
|
|
|
|
for (var i = 0; i < borders.Length; i++)
|
|
{
|
|
var intersectionPt = borders[i].GetIntersection(line);
|
|
if (intersectionPt != null)
|
|
yield return intersectionPt.Value;
|
|
}
|
|
}
|
|
|
|
public PointBase Center => new PointBase(Left + Width / 2, Top + Height / 2);
|
|
public PointBase NorthEast => new PointBase(Right, Top);
|
|
public PointBase SouthEast => new PointBase(Right, Bottom);
|
|
public PointBase SouthWest => new PointBase(Left, Bottom);
|
|
public PointBase NorthWest => new PointBase(Left, Top);
|
|
public PointBase East => new PointBase(Right, Top + Height / 2);
|
|
public PointBase North => new PointBase(Left + Width / 2, Top);
|
|
public PointBase South => new PointBase(Left + Width / 2, Bottom);
|
|
public PointBase West => new PointBase(Left, Top + Height / 2);
|
|
|
|
//public bool Equals(Rectangle other)
|
|
//{
|
|
// return other != null && Left == other.Left && Right == other.Right && Top == other.Top &&
|
|
// Bottom == other.Bottom && Width == other.Width && Height == other.Height;
|
|
//}
|
|
|
|
//public override string ToString()
|
|
// => $"Rectangle(width={Width}, height={Height}, top={Top}, right={Right}, bottom={Bottom}, left={Left})";
|
|
|
|
#region Statics
|
|
|
|
/// <summary>
|
|
/// Empty - a static property which provides an Empty rectangle. X and Y are positive-infinity
|
|
/// and Width and Height are negative infinity. This is the only situation where Width or
|
|
/// Height can be negative.
|
|
/// </summary>
|
|
public static RectangleBase Empty
|
|
{
|
|
get
|
|
{
|
|
return s_empty;
|
|
}
|
|
}
|
|
|
|
#endregion Statics
|
|
|
|
#region Public Properties
|
|
|
|
/// <summary>
|
|
/// IsEmpty - this returns true if this rect is the Empty rectangle.
|
|
/// Note: If width or height are 0 this Rectangle still contains a 0 or 1 dimensional set
|
|
/// of points, so this method should not be used to check for 0 area.
|
|
/// </summary>
|
|
public bool IsEmpty
|
|
{
|
|
get
|
|
{
|
|
// The funny width and height tests are to handle NaNs
|
|
//Debug.Assert((!(_width < 0) && !(_height < 0)) || (this == Empty));
|
|
|
|
return _width < 0;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Location - The PointBase representing the origin of the Rectangle
|
|
/// </summary>
|
|
public PointBase Location
|
|
{
|
|
get
|
|
{
|
|
return new PointBase(_x, _y);
|
|
}
|
|
set
|
|
{
|
|
if (IsEmpty)
|
|
{
|
|
throw new System.InvalidOperationException("Rect_CannotModifyEmptyRect");
|
|
}
|
|
|
|
_x = value._x;
|
|
_y = value._y;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Size - The Size representing the area of the Rectangle
|
|
/// </summary>
|
|
public SizeBase Size
|
|
{
|
|
get
|
|
{
|
|
if (IsEmpty)
|
|
return SizeBase.Empty;
|
|
return new SizeBase(_width, _height);
|
|
}
|
|
set
|
|
{
|
|
if (value.IsEmpty)
|
|
{
|
|
this = s_empty;
|
|
}
|
|
else
|
|
{
|
|
if (IsEmpty)
|
|
{
|
|
throw new System.InvalidOperationException("Rect_CannotModifyEmptyRect");
|
|
}
|
|
|
|
_width = value._width;
|
|
_height = value._height;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal double _x;
|
|
/// <summary>
|
|
/// X - The X coordinate of the Location.
|
|
/// If this is the empty rectangle, the value will be positive infinity.
|
|
/// If this rect is Empty, setting this property is illegal.
|
|
/// </summary>
|
|
public double X
|
|
{
|
|
get
|
|
{
|
|
return _x;
|
|
}
|
|
set
|
|
{
|
|
if (IsEmpty)
|
|
{
|
|
throw new System.InvalidOperationException("Rect_CannotModifyEmptyRect");
|
|
}
|
|
|
|
_x = value;
|
|
}
|
|
}
|
|
|
|
internal double _y;
|
|
/// <summary>
|
|
/// Y - The Y coordinate of the Location
|
|
/// If this is the empty rectangle, the value will be positive infinity.
|
|
/// If this rect is Empty, setting this property is illegal.
|
|
/// </summary>
|
|
public double Y
|
|
{
|
|
get
|
|
{
|
|
return _y;
|
|
}
|
|
set
|
|
{
|
|
if (IsEmpty)
|
|
{
|
|
throw new System.InvalidOperationException("Rect_CannotModifyEmptyRect");
|
|
}
|
|
|
|
_y = value;
|
|
}
|
|
}
|
|
|
|
internal double _width;
|
|
/// <summary>
|
|
/// Width - The Width component of the Size. This cannot be set to negative, and will only
|
|
/// be negative if this is the empty rectangle, in which case it will be negative infinity.
|
|
/// If this rect is Empty, setting this property is illegal.
|
|
/// </summary>
|
|
public double Width
|
|
{
|
|
get
|
|
{
|
|
return _width;
|
|
}
|
|
set
|
|
{
|
|
if (IsEmpty)
|
|
{
|
|
throw new System.InvalidOperationException("Rect_CannotModifyEmptyRect");
|
|
}
|
|
|
|
if (value < 0)
|
|
{
|
|
throw new System.ArgumentException("Size_WidthCannotBeNegative");
|
|
}
|
|
|
|
_width = value;
|
|
}
|
|
}
|
|
|
|
internal double _height;
|
|
/// <summary>
|
|
/// Height - The Height component of the Size. This cannot be set to negative, and will only
|
|
/// be negative if this is the empty rectangle, in which case it will be negative infinity.
|
|
/// If this rect is Empty, setting this property is illegal.
|
|
/// </summary>
|
|
public double Height
|
|
{
|
|
get
|
|
{
|
|
return _height;
|
|
}
|
|
set
|
|
{
|
|
if (IsEmpty)
|
|
{
|
|
throw new System.InvalidOperationException("Rect_CannotModifyEmptyRect");
|
|
}
|
|
|
|
if (value < 0)
|
|
{
|
|
throw new System.ArgumentException("Size_HeightCannotBeNegative");
|
|
}
|
|
|
|
_height = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Left Property - This is a read-only alias for X
|
|
/// If this is the empty rectangle, the value will be positive infinity.
|
|
/// </summary>
|
|
public double Left
|
|
{
|
|
get
|
|
{
|
|
return _x;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Top Property - This is a read-only alias for Y
|
|
/// If this is the empty rectangle, the value will be positive infinity.
|
|
/// </summary>
|
|
public double Top
|
|
{
|
|
get
|
|
{
|
|
return _y;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Right Property - This is a read-only alias for X + Width
|
|
/// If this is the empty rectangle, the value will be negative infinity.
|
|
/// </summary>
|
|
public double Right
|
|
{
|
|
get
|
|
{
|
|
if (IsEmpty)
|
|
{
|
|
return Double.NegativeInfinity;
|
|
}
|
|
|
|
return _x + _width;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bottom Property - This is a read-only alias for Y + Height
|
|
/// If this is the empty rectangle, the value will be negative infinity.
|
|
/// </summary>
|
|
public double Bottom
|
|
{
|
|
get
|
|
{
|
|
if (IsEmpty)
|
|
{
|
|
return Double.NegativeInfinity;
|
|
}
|
|
|
|
return _y + _height;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// TopLeft Property - This is a read-only alias for the PointBase which is at X, Y
|
|
/// If this is the empty rectangle, the value will be positive infinity, positive infinity.
|
|
/// </summary>
|
|
public PointBase TopLeft
|
|
{
|
|
get
|
|
{
|
|
return new PointBase(Left, Top);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// TopRight Property - This is a read-only alias for the PointBase which is at X + Width, Y
|
|
/// If this is the empty rectangle, the value will be negative infinity, positive infinity.
|
|
/// </summary>
|
|
public PointBase TopRight
|
|
{
|
|
get
|
|
{
|
|
return new PointBase(Right, Top);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// BottomLeft Property - This is a read-only alias for the PointBase which is at X, Y + Height
|
|
/// If this is the empty rectangle, the value will be positive infinity, negative infinity.
|
|
/// </summary>
|
|
public PointBase BottomLeft
|
|
{
|
|
get
|
|
{
|
|
return new PointBase(Left, Bottom);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// BottomRight Property - This is a read-only alias for the PointBase which is at X + Width, Y + Height
|
|
/// If this is the empty rectangle, the value will be negative infinity, negative infinity.
|
|
/// </summary>
|
|
public PointBase BottomRight
|
|
{
|
|
get
|
|
{
|
|
return new PointBase(Right, Bottom);
|
|
}
|
|
}
|
|
#endregion Public Properties
|
|
|
|
#region Public Methods
|
|
|
|
/// <summary>
|
|
/// Contains - Returns true if the PointBase is within the rectangle, inclusive of the edges.
|
|
/// Returns false otherwise.
|
|
/// </summary>
|
|
/// <param name="point"> The point which is being tested </param>
|
|
/// <returns>
|
|
/// Returns true if the PointBase is within the rectangle.
|
|
/// Returns false otherwise
|
|
/// </returns>
|
|
public bool Contains(PointBase point)
|
|
{
|
|
return Contains(point._x, point._y);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains - Returns true if the PointBase represented by x,y is within the rectangle inclusive of the edges.
|
|
/// Returns false otherwise.
|
|
/// </summary>
|
|
/// <param name="x"> X coordinate of the point which is being tested </param>
|
|
/// <param name="y"> Y coordinate of the point which is being tested </param>
|
|
/// <returns>
|
|
/// Returns true if the PointBase represented by x,y is within the rectangle.
|
|
/// Returns false otherwise.
|
|
/// </returns>
|
|
public bool Contains(double x, double y)
|
|
{
|
|
if (IsEmpty)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return ContainsInternal(x, y);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains - Returns true if the Rectangle non-Empty and is entirely contained within the
|
|
/// rectangle, inclusive of the edges.
|
|
/// Returns false otherwise
|
|
/// </summary>
|
|
public bool Contains(RectangleBase rect)
|
|
{
|
|
if (IsEmpty || rect.IsEmpty)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return (_x <= rect._x &&
|
|
_y <= rect._y &&
|
|
_x + _width >= rect._x + rect._width &&
|
|
_y + _height >= rect._y + rect._height);
|
|
}
|
|
|
|
/// <summary>
|
|
/// IntersectsWith - Returns true if the Rectangle intersects with this rectangle
|
|
/// Returns false otherwise.
|
|
/// Note that if one edge is coincident, this is considered an intersection.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// Returns true if the Rectangle intersects with this rectangle
|
|
/// Returns false otherwise.
|
|
/// or Height
|
|
/// </returns>
|
|
/// <param name="rect"> Rectangle </param>
|
|
public bool IntersectsWith(RectangleBase rect)
|
|
{
|
|
if (IsEmpty || rect.IsEmpty)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return (rect.Left <= Right) &&
|
|
(rect.Right >= Left) &&
|
|
(rect.Top <= Bottom) &&
|
|
(rect.Bottom >= Top);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Intersect - Update this rectangle to be the intersection of this and rect
|
|
/// If either this or rect are Empty, the result is Empty as well.
|
|
/// </summary>
|
|
/// <param name="rect"> The rect to intersect with this </param>
|
|
public void Intersect(RectangleBase rect)
|
|
{
|
|
if (!this.IntersectsWith(rect))
|
|
{
|
|
//this = Empty;
|
|
}
|
|
else
|
|
{
|
|
double left = Math.Max(Left, rect.Left);
|
|
double top = Math.Max(Top, rect.Top);
|
|
|
|
// Max with 0 to prevent double weirdness from causing us to be (-epsilon..0)
|
|
_width = Math.Max(Math.Min(Right, rect.Right) - left, 0);
|
|
_height = Math.Max(Math.Min(Bottom, rect.Bottom) - top, 0);
|
|
|
|
_x = left;
|
|
_y = top;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Intersect - Return the result of the intersection of rect1 and rect2.
|
|
/// If either this or rect are Empty, the result is Empty as well.
|
|
/// </summary>
|
|
public static RectangleBase Intersect(RectangleBase rect1, RectangleBase rect2)
|
|
{
|
|
rect1.Intersect(rect2);
|
|
return rect1;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Union - Update this rectangle to be the union of this and rect.
|
|
/// </summary>
|
|
public void Union(RectangleBase rect)
|
|
{
|
|
if (IsEmpty)
|
|
{
|
|
//this = rect;
|
|
}
|
|
else if (!rect.IsEmpty)
|
|
{
|
|
double left = Math.Min(Left, rect.Left);
|
|
double top = Math.Min(Top, rect.Top);
|
|
|
|
|
|
// We need this check so that the math does not result in NaN
|
|
if ((rect.Width == Double.PositiveInfinity) || (Width == Double.PositiveInfinity))
|
|
{
|
|
_width = Double.PositiveInfinity;
|
|
}
|
|
else
|
|
{
|
|
// Max with 0 to prevent double weirdness from causing us to be (-epsilon..0)
|
|
double maxRight = Math.Max(Right, rect.Right);
|
|
_width = Math.Max(maxRight - left, 0);
|
|
}
|
|
|
|
// We need this check so that the math does not result in NaN
|
|
if ((rect.Height == Double.PositiveInfinity) || (Height == Double.PositiveInfinity))
|
|
{
|
|
_height = Double.PositiveInfinity;
|
|
}
|
|
else
|
|
{
|
|
// Max with 0 to prevent double weirdness from causing us to be (-epsilon..0)
|
|
double maxBottom = Math.Max(Bottom, rect.Bottom);
|
|
_height = Math.Max(maxBottom - top, 0);
|
|
}
|
|
|
|
_x = left;
|
|
_y = top;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Union - Return the result of the union of rect1 and rect2.
|
|
/// </summary>
|
|
public static RectangleBase Union(RectangleBase rect1, RectangleBase rect2)
|
|
{
|
|
rect1.Union(rect2);
|
|
return rect1;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Union - Update this rectangle to be the union of this and point.
|
|
/// </summary>
|
|
public void Union(PointBase point)
|
|
{
|
|
Union(new RectangleBase(point, point));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Union - Return the result of the union of rect and point.
|
|
/// </summary>
|
|
public static RectangleBase Union(RectangleBase rect, PointBase point)
|
|
{
|
|
rect.Union(new RectangleBase(point, point));
|
|
return rect;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Offset - translate the Location by the offset provided.
|
|
/// If this is Empty, this method is illegal.
|
|
/// </summary>
|
|
public void Offset(VectorBase offsetVector)
|
|
{
|
|
if (IsEmpty)
|
|
{
|
|
throw new System.InvalidOperationException("Rect_CannotCallMethod");
|
|
}
|
|
|
|
_x += offsetVector._x;
|
|
_y += offsetVector._y;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Offset - translate the Location by the offset provided
|
|
/// If this is Empty, this method is illegal.
|
|
/// </summary>
|
|
public void Offset(double offsetX, double offsetY)
|
|
{
|
|
if (IsEmpty)
|
|
{
|
|
throw new System.InvalidOperationException("Rect_CannotCallMethod");
|
|
}
|
|
|
|
_x += offsetX;
|
|
_y += offsetY;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Offset - return the result of offsetting rect by the offset provided
|
|
/// If this is Empty, this method is illegal.
|
|
/// </summary>
|
|
public static RectangleBase Offset(RectangleBase rect, VectorBase offsetVector)
|
|
{
|
|
rect.Offset(offsetVector.X, offsetVector.Y);
|
|
return rect;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Offset - return the result of offsetting rect by the offset provided
|
|
/// If this is Empty, this method is illegal.
|
|
/// </summary>
|
|
public static RectangleBase Offset(RectangleBase rect, double offsetX, double offsetY)
|
|
{
|
|
rect.Offset(offsetX, offsetY);
|
|
return rect;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Inflate - inflate the bounds by the size provided, in all directions
|
|
/// If this is Empty, this method is illegal.
|
|
/// </summary>
|
|
public void Inflate(SizeBase size)
|
|
{
|
|
Inflate(size._width, size._height);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Inflate - inflate the bounds by the size provided, in all directions.
|
|
/// If -width is > Width / 2 or -height is > Height / 2, this Rectangle becomes Empty
|
|
/// If this is Empty, this method is illegal.
|
|
/// </summary>
|
|
public void Inflate(double width, double height)
|
|
{
|
|
if (IsEmpty)
|
|
{
|
|
throw new System.InvalidOperationException("Rect_CannotCallMethod");
|
|
}
|
|
|
|
_x -= width;
|
|
_y -= height;
|
|
|
|
// Do two additions rather than multiplication by 2 to avoid spurious overflow
|
|
// That is: (A + 2 * B) != ((A + B) + B) if 2*B overflows.
|
|
// Note that multiplication by 2 might work in this case because A should start
|
|
// positive & be "clamped" to positive after, but consider A = Inf & B = -MAX.
|
|
_width += width;
|
|
_width += width;
|
|
_height += height;
|
|
_height += height;
|
|
|
|
// We catch the case of inflation by less than -width/2 or -height/2 here. This also
|
|
// maintains the invariant that either the Rectangle is Empty or _width and _height are
|
|
// non-negative, even if the user parameters were NaN, though this isn't strictly maintained
|
|
// by other methods.
|
|
if (!(_width >= 0 && _height >= 0))
|
|
{
|
|
this = s_empty;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Inflate - return the result of inflating rect by the size provided, in all directions
|
|
/// If this is Empty, this method is illegal.
|
|
/// </summary>
|
|
public static RectangleBase Inflate(RectangleBase rect, SizeBase size)
|
|
{
|
|
rect.Inflate(size._width, size._height);
|
|
return rect;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Inflate - return the result of inflating rect by the size provided, in all directions
|
|
/// If this is Empty, this method is illegal.
|
|
/// </summary>
|
|
public static RectangleBase Inflate(RectangleBase rect, double width, double height)
|
|
{
|
|
rect.Inflate(width, height);
|
|
return rect;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the bounds of the transformed rectangle.
|
|
/// The Empty Rectangle is not affected by this call.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// The rect which results from the transformation.
|
|
/// </returns>
|
|
/// <param name="rect"> The Rectangle to transform. </param>
|
|
/// <param name="matrix"> The Matrix by which to transform. </param>
|
|
//public static Rectangle Transform(Rectangle rect, Matrix matrix)
|
|
//{
|
|
// MatrixUtil.TransformRect(ref rect, ref matrix);
|
|
// return rect;
|
|
//}
|
|
|
|
/// <summary>
|
|
/// Updates rectangle to be the bounds of the original value transformed
|
|
/// by the matrix.
|
|
/// The Empty Rectangle is not affected by this call.
|
|
/// </summary>
|
|
/// <param name="matrix"> Matrix </param>
|
|
//public void Transform(Matrix matrix)
|
|
//{
|
|
// MatrixUtil.TransformRect(ref this, ref matrix);
|
|
//}
|
|
|
|
/// <summary>
|
|
/// Scale the rectangle in the X and Y directions
|
|
/// </summary>
|
|
/// <param name="scaleX"> The scale in X </param>
|
|
/// <param name="scaleY"> The scale in Y </param>
|
|
public void Scale(double scaleX, double scaleY)
|
|
{
|
|
if (IsEmpty)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_x *= scaleX;
|
|
_y *= scaleY;
|
|
_width *= scaleX;
|
|
_height *= scaleY;
|
|
|
|
// If the scale in the X dimension is negative, we need to normalize X and Width
|
|
if (scaleX < 0)
|
|
{
|
|
// Make X the left-most edge again
|
|
_x += _width;
|
|
|
|
// and make Width positive
|
|
_width *= -1;
|
|
}
|
|
|
|
// Do the same for the Y dimension
|
|
if (scaleY < 0)
|
|
{
|
|
// Make Y the top-most edge again
|
|
_y += _height;
|
|
|
|
// and make Height positive
|
|
_height *= -1;
|
|
}
|
|
}
|
|
|
|
#endregion Public Methods
|
|
|
|
#region Private Methods
|
|
|
|
/// <summary>
|
|
/// ContainsInternal - Performs just the "point inside" logic
|
|
/// </summary>
|
|
/// <returns>
|
|
/// bool - true if the point is inside the rect
|
|
/// </returns>
|
|
/// <param name="x"> The x-coord of the point to test </param>
|
|
/// <param name="y"> The y-coord of the point to test </param>
|
|
private bool ContainsInternal(double x, double y)
|
|
{
|
|
// We include points on the edge as "contained".
|
|
// We do "x - _width <= _x" instead of "x <= _x + _width"
|
|
// so that this check works when _width is PositiveInfinity
|
|
// and _x is NegativeInfinity.
|
|
return ((x >= _x) && (x - _width <= _x) &&
|
|
(y >= _y) && (y - _height <= _y));
|
|
}
|
|
|
|
static private RectangleBase CreateEmptyRect()
|
|
{
|
|
RectangleBase rect = new RectangleBase();
|
|
// We can't set these via the property setters because negatives widths
|
|
// are rejected in those APIs.
|
|
rect._x = Double.PositiveInfinity;
|
|
rect._y = Double.PositiveInfinity;
|
|
rect._width = Double.NegativeInfinity;
|
|
rect._height = Double.NegativeInfinity;
|
|
return rect;
|
|
}
|
|
|
|
#endregion Private Methods
|
|
|
|
#region Private Fields
|
|
|
|
private readonly static RectangleBase s_empty = CreateEmptyRect();
|
|
|
|
#endregion Private Fields
|
|
|
|
#region Internal Properties
|
|
|
|
|
|
/// <summary>
|
|
/// Creates a string representation of this object based on the current culture.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// A string representation of this object.
|
|
/// </returns>
|
|
public override string ToString()
|
|
{
|
|
// Delegate to the internal method which implements all ToString calls.
|
|
return ConvertToString(null /* format string */, null /* format provider */);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a string representation of this object based on the IFormatProvider
|
|
/// passed in. If the provider is null, the CurrentCulture is used.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// A string representation of this object.
|
|
/// </returns>
|
|
public string ToString(IFormatProvider provider)
|
|
{
|
|
// Delegate to the internal method which implements all ToString calls.
|
|
return ConvertToString(null /* format string */, provider);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a string representation of this object based on the format string
|
|
/// and IFormatProvider passed in.
|
|
/// If the provider is null, the CurrentCulture is used.
|
|
/// See the documentation for IFormattable for more information.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// A string representation of this object.
|
|
/// </returns>
|
|
string IFormattable.ToString(string format, IFormatProvider provider)
|
|
{
|
|
// Delegate to the internal method which implements all ToString calls.
|
|
return ConvertToString(format, provider);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a string representation of this object based on the format string
|
|
/// and IFormatProvider passed in.
|
|
/// If the provider is null, the CurrentCulture is used.
|
|
/// See the documentation for IFormattable for more information.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// A string representation of this object.
|
|
/// </returns>
|
|
internal string ConvertToString(string format, IFormatProvider provider)
|
|
{
|
|
if (IsEmpty)
|
|
{
|
|
return "Empty";
|
|
}
|
|
|
|
// Helper to get the numeric list separator for a given culture.
|
|
char separator = ',';
|
|
return String.Format(provider,
|
|
"{1:" + format + "}{0}{2:" + format + "}{0}{3:" + format + "}{0}{4:" + format + "}",
|
|
separator,
|
|
_x,
|
|
_y,
|
|
_width,
|
|
_height);
|
|
}
|
|
|
|
|
|
|
|
#endregion Internal Properties
|
|
|
|
#region Public Methods
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// Compares two Rectangle instances for exact equality.
|
|
/// Note that double values can acquire error when operated upon, such that
|
|
/// an exact comparison between two values which are logically equal may fail.
|
|
/// Furthermore, using this equality operator, Double.NaN is not equal to itself.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// bool - true if the two Rectangle instances are exactly equal, false otherwise
|
|
/// </returns>
|
|
/// <param name='rect1'>The first Rectangle to compare</param>
|
|
/// <param name='rect2'>The second Rectangle to compare</param>
|
|
public static bool operator ==(RectangleBase rect1, RectangleBase rect2)
|
|
{
|
|
return rect1.X == rect2.X &&
|
|
rect1.Y == rect2.Y &&
|
|
rect1.Width == rect2.Width &&
|
|
rect1.Height == rect2.Height;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Compares two Rectangle instances for exact inequality.
|
|
/// Note that double values can acquire error when operated upon, such that
|
|
/// an exact comparison between two values which are logically equal may fail.
|
|
/// Furthermore, using this equality operator, Double.NaN is not equal to itself.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// bool - true if the two Rectangle instances are exactly unequal, false otherwise
|
|
/// </returns>
|
|
/// <param name='rect1'>The first Rectangle to compare</param>
|
|
/// <param name='rect2'>The second Rectangle to compare</param>
|
|
public static bool operator !=(RectangleBase rect1, RectangleBase rect2)
|
|
{
|
|
return !(rect1 == rect2);
|
|
}
|
|
/// <summary>
|
|
/// Compares two Rectangle instances for object equality. In this equality
|
|
/// Double.NaN is equal to itself, unlike in numeric equality.
|
|
/// Note that double values can acquire error when operated upon, such that
|
|
/// an exact comparison between two values which
|
|
/// are logically equal may fail.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// bool - true if the two Rectangle instances are exactly equal, false otherwise
|
|
/// </returns>
|
|
/// <param name='rect1'>The first Rectangle to compare</param>
|
|
/// <param name='rect2'>The second Rectangle to compare</param>
|
|
public static bool Equals(RectangleBase rect1, RectangleBase rect2)
|
|
{
|
|
if (rect1.IsEmpty)
|
|
{
|
|
return rect2.IsEmpty;
|
|
}
|
|
else
|
|
{
|
|
return rect1.X.Equals(rect2.X) &&
|
|
rect1.Y.Equals(rect2.Y) &&
|
|
rect1.Width.Equals(rect2.Width) &&
|
|
rect1.Height.Equals(rect2.Height);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Equals - compares this Rectangle with the passed in object. In this equality
|
|
/// Double.NaN is equal to itself, unlike in numeric equality.
|
|
/// Note that double values can acquire error when operated upon, such that
|
|
/// an exact comparison between two values which
|
|
/// are logically equal may fail.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// bool - true if the object is an instance of Rectangle and if it's equal to "this".
|
|
/// </returns>
|
|
/// <param name='o'>The object to compare to "this"</param>
|
|
public override bool Equals(object o)
|
|
{
|
|
if ((null == o) || !(o is RectangleBase))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
RectangleBase value = (RectangleBase)o;
|
|
return RectangleBase.Equals(this, value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Equals - compares this Rectangle with the passed in object. In this equality
|
|
/// Double.NaN is equal to itself, unlike in numeric equality.
|
|
/// Note that double values can acquire error when operated upon, such that
|
|
/// an exact comparison between two values which
|
|
/// are logically equal may fail.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// bool - true if "value" is equal to "this".
|
|
/// </returns>
|
|
/// <param name='value'>The Rectangle to compare to "this"</param>
|
|
public bool Equals(RectangleBase value)
|
|
{
|
|
return RectangleBase.Equals(this, value);
|
|
}
|
|
/// <summary>
|
|
/// Returns the HashCode for this Rectangle
|
|
/// </summary>
|
|
/// <returns>
|
|
/// int - the HashCode for this Rectangle
|
|
/// </returns>
|
|
public override int GetHashCode()
|
|
{
|
|
if (IsEmpty)
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
// Perform field-by-field XOR of HashCodes
|
|
return X.GetHashCode() ^
|
|
Y.GetHashCode() ^
|
|
Width.GetHashCode() ^
|
|
Height.GetHashCode();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse - returns an instance converted from the provided string using
|
|
/// the culture "en-US"
|
|
/// <param name="source"> string with Rectangle data </param>
|
|
/// </summary>
|
|
//public static Rectangle Parse(string source)
|
|
//{
|
|
// IFormatProvider formatProvider = System.Windows.Markup.TypeConverterHelper.InvariantEnglishUS;
|
|
|
|
// TokenizerHelper th = new TokenizerHelper(source, formatProvider);
|
|
|
|
// Rectangle value;
|
|
|
|
// String firstToken = th.NextTokenRequired();
|
|
|
|
// // The token will already have had whitespace trimmed so we can do a
|
|
// // simple string compare.
|
|
// if (firstToken == "Empty")
|
|
// {
|
|
// value = Empty;
|
|
// }
|
|
// else
|
|
// {
|
|
// value = new Rectangle(
|
|
// Convert.ToDouble(firstToken, formatProvider),
|
|
// Convert.ToDouble(th.NextTokenRequired(), formatProvider),
|
|
// Convert.ToDouble(th.NextTokenRequired(), formatProvider),
|
|
// Convert.ToDouble(th.NextTokenRequired(), formatProvider));
|
|
// }
|
|
|
|
// // There should be no more tokens in this string.
|
|
// th.LastTokenRequired();
|
|
|
|
// return value;
|
|
//}
|
|
|
|
public static implicit operator RectangleBase(Rect rect)
|
|
{
|
|
return new RectangleBase(rect.Left, rect.Top, rect.Width, rect.Height);
|
|
}
|
|
|
|
public static implicit operator Rect(RectangleBase rect)
|
|
{
|
|
return new Rect(rect.Left, rect.Top, rect.Width, rect.Height);
|
|
}
|
|
|
|
#endregion Public Methods
|
|
|
|
}
|
|
}
|