CPF/CPF/Thickness.cs
2023-11-21 23:05:03 +08:00

403 lines
14 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using CPF.Drawing;
using System.ComponentModel;
namespace CPF
{
/// <summary>
/// 表示四周的厚度,字符串格式 all、left,top,right,bottom
/// </summary>
[Description("表示四周的厚度不能用百分比格式all或者left,top,right,bottom")]
public struct Thickness : IEquatable<Thickness>
{
//-------------------------------------------------------------------
//
// Constructors
//
//-------------------------------------------------------------------
#region Constructors
/// <summary>
/// 表示四周的厚度 This constructur builds a Thickness with a specified value on every side.
/// </summary>
/// <param name="uniformLength">The specified uniform length.</param>
public Thickness(in float uniformLength)
{
_Left = _Top = _Right = _Bottom = uniformLength;
}
/// <summary>
/// This constructor builds a Thickness with the specified number of pixels on each side.
/// 表示四周的厚度
/// </summary>
/// <param name="left">The thickness for the left side.</param>
/// <param name="top">The thickness for the top side.</param>
/// <param name="right">The thickness for the right side.</param>
/// <param name="bottom">The thickness for the bottom side.</param>
public Thickness(in float left, in float top, in float right, in float bottom)
{
_Left = left;
_Top = top;
_Right = right;
_Bottom = bottom;
}
/// <summary>
/// Initializes a new instance of the <see cref="Thickness"/> structure.
/// </summary>
/// <param name="horizontal">The thickness on the left and right.</param>
/// <param name="vertical">The thickness on the top and bottom.</param>
public Thickness(in float horizontal, in float vertical)
{
_Left = _Right = horizontal;
_Top = _Bottom = vertical;
}
#endregion
/// <summary>
/// Top + Bottom
/// </summary>
public float Vertical
{
get { return _Top + _Bottom; }
}
/// <summary>
/// Right + Left
/// </summary>
public float Horizontal
{
get { return _Right + _Left; }
}
//-------------------------------------------------------------------
//
// Public Methods
//
//-------------------------------------------------------------------
#region Public Methods
/// <summary>
/// This function compares to the provided object for type and value equality.
/// </summary>
/// <param name="obj">Object to compare</param>
/// <returns>True if object is a Thickness and all sides of it are equal to this Thickness'.</returns>
public override bool Equals(object obj)
{
if (obj is Thickness)
{
Thickness otherObj = (Thickness)obj;
return (this == otherObj);
}
return (false);
}
/// <summary>
/// Compares this instance of Thickness with another instance.
/// </summary>
/// <param name="thickness">Thickness instance to compare.</param>
/// <returns><c>true</c>if this Thickness instance has the same value
/// and unit type as thickness.</returns>
public bool Equals(Thickness thickness)
{
return (this == thickness);
}
/// <summary>
/// This function returns a hash code.
/// </summary>
/// <returns>Hash code</returns>
public override int GetHashCode()
{
return _Left.GetHashCode() ^ _Top.GetHashCode() ^ _Right.GetHashCode() ^ _Bottom.GetHashCode();
}
/// <summary>
/// {Left},{Top},{Right},{Bottom}
/// </summary>
/// <returns>String conversion.</returns>
public override string ToString()
{
return $"{Left},{Top},{Right},{Bottom}";
}
/// <summary>
/// Parses a <see cref="Thickness"/> string.
/// </summary>
/// <param name="s">The string.</param>
/// <returns>The <see cref="Thickness"/>.</returns>
public static Thickness Parse(string s)
{
var parts = s.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Trim())
.ToList();
switch (parts.Count)
{
case 1:
var uniform = float.Parse(parts[0]);
return new Thickness(uniform);
case 2:
var horizontal = float.Parse(parts[0]);
var vertical = float.Parse(parts[1]);
return new Thickness(horizontal, vertical);
case 4:
var left = float.Parse(parts[0]);
var top = float.Parse(parts[1]);
var right = float.Parse(parts[2]);
var bottom = float.Parse(parts[3]);
return new Thickness(left, top, right, bottom);
}
throw new FormatException("Invalid Thickness.");
}
///// <summary>
///// Converts this Thickness object to a string.
///// </summary>
///// <returns>String conversion.</returns>
//internal string ToString(CultureInfo cultureInfo)
//{
// return ThicknessConverter.ToString(this, cultureInfo);
//}
internal bool IsZero
{
get
{
return FloatUtil.IsZero(Left)
&& FloatUtil.IsZero(Top)
&& FloatUtil.IsZero(Right)
&& FloatUtil.IsZero(Bottom);
}
}
internal bool IsUniform
{
get
{
return FloatUtil.AreClose(Left, Top)
&& FloatUtil.AreClose(Left, Right)
&& FloatUtil.AreClose(Left, Bottom);
}
}
/// <summary>
/// Verifies if this Thickness contains only valid values
/// The set of validity checks is passed as parameters.
/// </summary>
/// <param name='allowNegative'>allows negative values</param>
/// <param name='allowNaN'>allows Double.NaN</param>
/// <param name='allowPositiveInfinity'>allows Double.PositiveInfinity</param>
/// <param name='allowNegativeInfinity'>allows Double.NegativeInfinity</param>
/// <returns>Whether or not the thickness complies to the range specified</returns>
internal bool IsValid(bool allowNegative, bool allowNaN, bool allowPositiveInfinity, bool allowNegativeInfinity)
{
if (!allowNegative)
{
if (Left < 0d || Right < 0d || Top < 0d || Bottom < 0d)
return false;
}
if (!allowNaN)
{
if (FloatUtil.IsNaN(Left) || FloatUtil.IsNaN(Right) || FloatUtil.IsNaN(Top) || FloatUtil.IsNaN(Bottom))
return false;
}
if (!allowPositiveInfinity)
{
if (Double.IsPositiveInfinity(Left) || Double.IsPositiveInfinity(Right) || Double.IsPositiveInfinity(Top) || Double.IsPositiveInfinity(Bottom))
{
return false;
}
}
if (!allowNegativeInfinity)
{
if (Double.IsNegativeInfinity(Left) || Double.IsNegativeInfinity(Right) || Double.IsNegativeInfinity(Top) || Double.IsNegativeInfinity(Bottom))
{
return false;
}
}
return true;
}
/// <summary>
/// Compares two thicknesses for fuzzy equality. This function
/// helps compensate for the fact that float values can
/// acquire error when operated upon
/// </summary>
/// <param name='thickness'>The thickness to compare to this</param>
/// <returns>Whether or not the two points are equal</returns>
internal bool IsClose(Thickness thickness)
{
return (FloatUtil.AreClose(Left, thickness.Left)
&& FloatUtil.AreClose(Top, thickness.Top)
&& FloatUtil.AreClose(Right, thickness.Right)
&& FloatUtil.AreClose(Bottom, thickness.Bottom));
}
/// <summary>
/// Compares two thicknesses for fuzzy equality. This function
/// helps compensate for the fact that float values can
/// acquire error when operated upon
/// </summary>
/// <param name='thickness0'>The first thickness to compare</param>
/// <param name='thickness1'>The second thickness to compare</param>
/// <returns>Whether or not the two thicknesses are equal</returns>
static internal bool AreClose(Thickness thickness0, Thickness thickness1)
{
return thickness0.IsClose(thickness1);
}
#endregion
//-------------------------------------------------------------------
//
// Public Operators
//
//-------------------------------------------------------------------
#region Public Operators
/// <summary>
/// Overloaded operator to compare two Thicknesses for equality.
/// </summary>
/// <param name="t1">first Thickness to compare</param>
/// <param name="t2">second Thickness to compare</param>
/// <returns>True if all sides of the Thickness are equal, false otherwise</returns>
// SEEALSO
public static bool operator ==(Thickness t1, Thickness t2)
{
return ((t1._Left == t2._Left || (FloatUtil.IsNaN(t1._Left) && FloatUtil.IsNaN(t2._Left)))
&& (t1._Top == t2._Top || (FloatUtil.IsNaN(t1._Top) && FloatUtil.IsNaN(t2._Top)))
&& (t1._Right == t2._Right || (FloatUtil.IsNaN(t1._Right) && FloatUtil.IsNaN(t2._Right)))
&& (t1._Bottom == t2._Bottom || (FloatUtil.IsNaN(t1._Bottom) && FloatUtil.IsNaN(t2._Bottom)))
);
}
/// <summary>
/// Overloaded operator to compare two Thicknesses for inequality.
/// </summary>
/// <param name="t1">first Thickness to compare</param>
/// <param name="t2">second Thickness to compare</param>
/// <returns>False if all sides of the Thickness are equal, true otherwise</returns>
// SEEALSO
public static bool operator !=(Thickness t1, Thickness t2)
{
return (!(t1 == t2));
}
public static implicit operator Thickness(string n)
{
if (string.IsNullOrWhiteSpace(n))
{
throw new Exception("Thickness字符转换格式不对");
}
if (n.IndexOf(',') >= 0)
{
var temp = n.Split(',');
if (temp.Length < 4)
{
throw new Exception("Thickness字符转换格式不对");
}
try
{
return new Thickness(float.Parse(temp[0].Trim()), float.Parse(temp[1].Trim()), float.Parse(temp[2].Trim()), float.Parse(temp[3].Trim()));
}
catch (Exception)
{
throw new Exception("Thickness字符转换格式不对");
}
}
else
{
try
{
var nn = float.Parse(n);
return new Thickness(nn);
}
catch (Exception)
{
throw new Exception("Thickness字符转换格式不对");
}
}
}
#endregion
//-------------------------------------------------------------------
//
// Public Properties
//
//-------------------------------------------------------------------
#region Public Properties
/// <summary>This property is the Length on the thickness' left side</summary>
public float Left
{
get { return _Left; }
set { _Left = value; }
}
/// <summary>This property is the Length on the thickness' top side</summary>
public float Top
{
get { return _Top; }
set { _Top = value; }
}
/// <summary>This property is the Length on the thickness' right side</summary>
public float Right
{
get { return _Right; }
set { _Right = value; }
}
/// <summary>This property is the Length on the thickness' bottom side</summary>
public float Bottom
{
get { return _Bottom; }
set { _Bottom = value; }
}
#endregion
//-------------------------------------------------------------------
//
// INternal API
//
//-------------------------------------------------------------------
#region Internal API
internal Size Size
{
get
{
return new Size(_Left + _Right, _Top + _Bottom);
}
}
#endregion
//-------------------------------------------------------------------
//
// Private Fields
//
//-------------------------------------------------------------------
#region Private Fields
private float _Left;
private float _Top;
private float _Right;
private float _Bottom;
#endregion
}
}