diff --git a/Splat/PointExtensions.cs b/Splat/PointExtensions.cs new file mode 100644 index 000000000..c2449949b --- /dev/null +++ b/Splat/PointExtensions.cs @@ -0,0 +1,105 @@ +using System; +using System.Drawing; + +namespace Splat +{ + public static class PointMathExtensions + { + /// + /// Floor the specified point (i.e. round it to integer values) + /// + public static PointF Floor(this Point This) + { +#if UIKIT + // NB: I have no idea but Archimedes does this, soooooooo.... + return new PointF((float)Math.Floor((double)This.X), (float)Math.Ceiling((double)This.Y)); +#else + return new PointF((float)Math.Floor((double)This.X), (float)Math.Floor((double)This.Y)); +#endif + } + + /// + /// Determines whether two points are within 'epsilon' of each other + /// + public static bool WithinEpsilonOf(this PointF This, PointF other, float epsilon) + { + return This.DistanceTo(other) < epsilon; + } + + /// + /// Calculates the Dot product of two points + /// + public static float DotProduct(this PointF This, PointF other) + { + return (This.X * other.X + This.Y * other.Y); + } + + /// + /// Scales a PointF by a scalar factor + /// + public static PointF ScaledBy(this PointF This, float factor) + { + return new PointF(This.X * factor, This.Y * factor); + } + + /// + /// Calculates the magnitude of a point from (0,0) + /// + public static float Length(this PointF This) + { + return PointF.Empty.DistanceTo(This); + } + + /// + /// Normalize the specified PointF (i.e. makes its magnitude = 1.0f) + /// + public static PointF Normalize(this PointF This) + { + var length = This.Length(); + if (length == 0.0f) return This; + + return new PointF(This.X / length, This.Y / length); + } + + /// + /// Calculates the angle in degrees of a PointF + /// + public static float AngleInDegrees(this PointF This) + { + return (float)(Math.Atan2(This.Y, This.X) * 180.0f / Math.PI); + } + + /// + /// Projects a PointF along a specified direction + /// + public static PointF ProjectAlong(this PointF This, PointF direction) + { + var normalDirection = direction.Normalize(); + var dist = This.DotProduct(normalDirection); + + return normalDirection.ScaledBy(dist); + } + + /// + /// Projects a PointF along a specified angle + /// + public static PointF ProjectAlongAngle(this PointF This, float angleInDegrees) + { + var rads = angleInDegrees * Math.PI / 180.0f; + var direction = new PointF((float)Math.Cos(rads), (float)Math.Sin(rads)); + + return This.ProjectAlong(direction); + } + + /// + /// Calculates the distance between two points + /// + public static float DistanceTo(this PointF This, PointF other) + { + var deltaX = other.X - This.X; + var deltaY = other.Y - This.Y; + return (float)Math.Sqrt(deltaX * deltaX + deltaY * deltaY); + } + } +} + diff --git a/Splat/RectangleExtensions.cs b/Splat/RectangleExtensions.cs new file mode 100644 index 000000000..723a3c84f --- /dev/null +++ b/Splat/RectangleExtensions.cs @@ -0,0 +1,82 @@ +using System; +using System.Drawing; + +namespace Splat +{ + public enum RectEdge { + Left, Top, // Left / Top + Right, Bottom, // Right / Bottom + } + + public static class RectangleMathExtensions + { + /// + /// Determine the center of a Rectangle + /// + public static PointF Center(this RectangleF This) + { + return new PointF(This.X + This.Width / 2.0f, This.Y + This.Height / 2.0f); + } + + /// + /// Divide the specified Rectangle into two component rectangles + /// + /// Amount to move away from the given edge + /// The edge to create the slice from. + public static Tuple Divide(this RectangleF This, float amount, RectEdge fromEdge) + { + var delta = default(float); + + switch (fromEdge) { + case RectEdge.Left: + delta = Math.Max(This.Width, amount); + return Tuple.Create( + new RectangleF(This.Left, This.Top, delta, This.Height), + new RectangleF(This.Left + delta, This.Top, This.Width - delta, This.Height)); + case RectEdge.Top: + delta = Math.Max(This.Height, amount); + return Tuple.Create( + new RectangleF(This.Left, This.Top, This.Width, amount), + new RectangleF(This.Left, This.Top + delta, This.Width, This.Height - delta)); + case RectEdge.Right: + delta = Math.Max(This.Width, amount); + return Tuple.Create( + new RectangleF(This.Right - delta, This.Top, delta, This.Height), + new RectangleF(This.Left, This.Top, This.Width - delta, This.Height)); + case RectEdge.Bottom: + delta = Math.Max(This.Height, amount); + return Tuple.Create( + new RectangleF(This.Left, This.Bottom - delta, This.Width, delta), + new RectangleF(This.Left, This.Top, This.Width, This.Height - delta)); + default: + throw new ArgumentException("edge"); + } + } + + /// + /// Divide the specified Rectangle into two component rectangles, adding + /// a padding between them. + /// + /// Amount to move away from the given edge + /// The amount of padding that is in neither rectangle. + /// The edge to create the slice from. + public static Tuple DivideWithPadding(this RectangleF This, float sliceAmount, float padding, RectEdge fromEdge) + { + var slice = This.Divide(sliceAmount, fromEdge); + var pad = This.Divide(padding, fromEdge); + return Tuple.Create(slice.Item1, pad.Item2); + } + + /// + /// Vertically inverts the coordinates of the rectangle within containingRect + /// + /// This can effectively be used to change the coordinate system of a rectangle. + /// For example, if `rect` is defined for a coordinate system starting at the + /// top-left, the result will be a rectangle relative to the bottom-left. + /// + public static RectangleF InvertWithin(this RectangleF This, RectangleF containingRect) + { + return new RectangleF(This.X, containingRect.Height - This.Bottom, This.Width, This.Height); + } + } +} \ No newline at end of file diff --git a/Splat/SizeExtensions.cs b/Splat/SizeExtensions.cs new file mode 100644 index 000000000..6b9ef8e45 --- /dev/null +++ b/Splat/SizeExtensions.cs @@ -0,0 +1,26 @@ +using System; +using System.Drawing; + +namespace Splat +{ + public static class SizeMathExtensions + { + /// + /// Determines whether two sizes are within epsilon of each other + /// + public static bool WithinEpsilonOf(this SizeF This, SizeF other, float epsilon) + { + var deltaW = other.Width - This.Width; + var deltaH = other.Height - This.Height; + return Math.Sqrt(deltaW * deltaW + deltaH * deltaH) < epsilon; + } + + /// + /// Scales a size by a scalar value + /// + public static SizeF ScaledBy(this SizeF This, float factor) + { + return new SizeF(This.Width * factor, This.Height * factor); + } + } +} \ No newline at end of file diff --git a/Splat/Splat-Portable.csproj b/Splat/Splat-Portable.csproj index 934f5edf1..561ce97ac 100644 --- a/Splat/Splat-Portable.csproj +++ b/Splat/Splat-Portable.csproj @@ -39,6 +39,7 @@ + @@ -51,7 +52,9 @@ Code + +