diff options
Diffstat (limited to 'Software/Visual_Studio/Tango.RemoteDesktop/Quantization/Utility.cs')
| -rw-r--r-- | Software/Visual_Studio/Tango.RemoteDesktop/Quantization/Utility.cs | 2531 |
1 files changed, 2531 insertions, 0 deletions
diff --git a/Software/Visual_Studio/Tango.RemoteDesktop/Quantization/Utility.cs b/Software/Visual_Studio/Tango.RemoteDesktop/Quantization/Utility.cs new file mode 100644 index 000000000..74119e9c5 --- /dev/null +++ b/Software/Visual_Studio/Tango.RemoteDesktop/Quantization/Utility.cs @@ -0,0 +1,2531 @@ +///////////////////////////////////////////////////////////////////////////////// +// Paint.NET // +// Copyright (C) dotPDN LLC, Rick Brewster, Tom Jackson, and contributors. // +// Portions Copyright (C) Microsoft Corporation. All Rights Reserved. // +// See src/Resources/Files/License.txt for full licensing and attribution // +// details. // +// . // +///////////////////////////////////////////////////////////////////////////////// + +using Microsoft.Win32; +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Globalization; +using System.IO; +using System.Net; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; +using System.Text; +using System.Threading; + +namespace Tango.RemoteDesktop.Quantization +{ + /// <summary> + /// Defines miscellaneous constants and static functions. + /// </summary> + /// // TODO: refactor into mini static classes + internal sealed class Utility + { + private Utility() + { + } + + internal static bool IsNumber(float x) + { + return x >= float.MinValue && x <= float.MaxValue; + } + + internal static bool IsNumber(double x) + { + return x >= double.MinValue && x <= double.MaxValue; + } + + internal static int Min(int val0, params int[] vals) + { + int min = val0; + + for (int i = 0; i < vals.Length; ++i) + { + if (vals[i] < min) + { + min = vals[i]; + } + } + + return min; + } + + internal static int Max(int val0, params int[] vals) + { + int max = val0; + + for (int i = 0; i < vals.Length; ++i) + { + if (vals[i] > max) + { + max = vals[i]; + } + } + + return max; + } + + public static PointF[] GetRgssOffsets(int quality) + { + unsafe + { + int sampleCount = quality * quality; + PointF[] samplesArray = new PointF[sampleCount]; + + fixed (PointF* pSamplesArray = samplesArray) + { + GetRgssOffsets(pSamplesArray, sampleCount, quality); + } + + return samplesArray; + } + } + + public static unsafe void GetRgssOffsets(PointF* samplesArray, int sampleCount, int quality) + { + if (sampleCount < 1) + { + throw new ArgumentOutOfRangeException("sampleCount", "sampleCount must be [0, int.MaxValue]"); + } + + if (sampleCount != quality * quality) + { + throw new ArgumentOutOfRangeException("sampleCount != (quality * quality)"); + } + + if (sampleCount == 1) + { + samplesArray[0] = new PointF(0.0f, 0.0f); + } + else + { + for (int i = 0; i < sampleCount; ++i) + { + double y = (i + 1d) / (sampleCount + 1d); + double x = y * quality; + + x -= (int)x; + + samplesArray[i] = new PointF((float)(x - 0.5d), (float)(y - 0.5d)); + } + } + } + + public static bool IsObsolete(Type type, bool inherit) + { + object[] attrs = type.GetCustomAttributes(typeof(ObsoleteAttribute), inherit); + return (attrs.Length != 0); + } + + public static void DrawDropShadow1px(Graphics g, Rectangle rect) + { + Brush b0 = new SolidBrush(Color.FromArgb(15, Color.Black)); + Brush b1 = new SolidBrush(Color.FromArgb(47, Color.Black)); + Pen p2 = new Pen(Color.FromArgb(63, Color.Black)); + + g.FillRectangle(b0, rect.Left, rect.Top, 1, 1); + g.FillRectangle(b1, rect.Left + 1, rect.Top, 1, 1); + g.FillRectangle(b1, rect.Left, rect.Top + 1, 1, 1); + + g.FillRectangle(b0, rect.Right - 1, rect.Top, 1, 1); + g.FillRectangle(b1, rect.Right - 2, rect.Top, 1, 1); + g.FillRectangle(b1, rect.Right - 1, rect.Top + 1, 1, 1); + + g.FillRectangle(b0, rect.Left, rect.Bottom - 1, 1, 1); + g.FillRectangle(b1, rect.Left + 1, rect.Bottom - 1, 1, 1); + g.FillRectangle(b1, rect.Left, rect.Bottom - 2, 1, 1); + + g.FillRectangle(b0, rect.Right - 1, rect.Bottom - 1, 1, 1); + g.FillRectangle(b1, rect.Right - 2, rect.Bottom - 1, 1, 1); + g.FillRectangle(b1, rect.Right - 1, rect.Bottom - 2, 1, 1); + + g.DrawLine(p2, rect.Left + 2, rect.Top, rect.Right - 3, rect.Top); + g.DrawLine(p2, rect.Left, rect.Top + 2, rect.Left, rect.Bottom - 3); + g.DrawLine(p2, rect.Left + 2, rect.Bottom - 1, rect.Right - 3, rect.Bottom - 1); + g.DrawLine(p2, rect.Right - 1, rect.Top + 2, rect.Right - 1, rect.Bottom - 3); + + b0.Dispose(); + b0 = null; + b1.Dispose(); + b1 = null; + p2.Dispose(); + p2 = null; + } + + public static void DrawColorRectangle(Graphics g, Rectangle rect, Color color, bool drawBorder) + { + int inflateAmt = drawBorder ? -2 : 0; + Rectangle colorRectangle = Rectangle.Inflate(rect, inflateAmt, inflateAmt); + Brush colorBrush = new LinearGradientBrush(colorRectangle, Color.FromArgb(255, color), color, 90.0f, false); + HatchBrush backgroundBrush = new HatchBrush(HatchStyle.LargeCheckerBoard, Color.FromArgb(191, 191, 191), Color.FromArgb(255, 255, 255)); + + if (drawBorder) + { + g.DrawRectangle(Pens.Black, rect.Left, rect.Top, rect.Width - 1, rect.Height - 1); + g.DrawRectangle(Pens.White, rect.Left + 1, rect.Top + 1, rect.Width - 3, rect.Height - 3); + } + + PixelOffsetMode oldPOM = g.PixelOffsetMode; + g.PixelOffsetMode = PixelOffsetMode.Half; + g.FillRectangle(backgroundBrush, colorRectangle); + g.FillRectangle(colorBrush, colorRectangle); + g.PixelOffsetMode = oldPOM; + + backgroundBrush.Dispose(); + colorBrush.Dispose(); + } + + public static Size ComputeThumbnailSize(Size originalSize, int maxEdgeLength) + { + Size thumbSize; + + if (originalSize.Width > originalSize.Height) + { + int longSide = Math.Min(originalSize.Width, maxEdgeLength); + thumbSize = new Size(longSide, Math.Max(1, (originalSize.Height * longSide) / originalSize.Width)); + } + else if (originalSize.Height > originalSize.Width) + { + int longSide = Math.Min(originalSize.Height, maxEdgeLength); + thumbSize = new Size(Math.Max(1, (originalSize.Width * longSide) / originalSize.Height), longSide); + } + else // if (docSize.Width == docSize.Height) + { + int longSide = Math.Min(originalSize.Width, maxEdgeLength); + thumbSize = new Size(longSide, longSide); + } + + return thumbSize; + } + + public static Font CreateFont(string name, float size, FontStyle style) + { + Font returnFont; + + try + { + returnFont = new Font(name, size, style); + } + + catch (Exception) + { + returnFont = new Font(FontFamily.GenericSansSerif, size); + } + + return returnFont; + } + + public static Font CreateFont(string name, float size, string backupName, float backupSize, FontStyle style) + { + Font returnFont; + + try + { + returnFont = new Font(name, size, style); + } + + catch (Exception) + { + returnFont = CreateFont(backupName, backupSize, style); + } + + return returnFont; + } + + public static readonly Color TransparentKey = Color.FromArgb(192, 192, 192); + + private static bool allowGCFullCollect = true; + public static bool AllowGCFullCollect + { + get + { + return allowGCFullCollect; + } + + set + { + allowGCFullCollect = value; + } + } + + public static void GCFullCollect() + { + if (AllowGCFullCollect) + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + GC.WaitForPendingFinalizers(); + } + } + + private static int defaultSimplificationFactor = 50; + public static int DefaultSimplificationFactor + { + get + { + return defaultSimplificationFactor; + } + + set + { + defaultSimplificationFactor = value; + } + } + + public static void SplitRectangle(Rectangle rect, Rectangle[] rects) + { + int height = rect.Height; + + for (int i = 0; i < rects.Length; ++i) + { + Rectangle newRect = Rectangle.FromLTRB(rect.Left, + rect.Top + ((height * i) / rects.Length), + rect.Right, + rect.Top + ((height * (i + 1)) / rects.Length)); + + rects[i] = newRect; + } + } + + public static long TicksToMs(long ticks) + { + return ticks / 10000; + } + + public static string GetStaticName(Type type) + { + PropertyInfo pi = type.GetProperty("StaticName", BindingFlags.Static | BindingFlags.Public | BindingFlags.GetProperty); + return (string)pi.GetValue(null, null); + } + + public static readonly float[][] Identity5x5F = new float[][] { + new float[] { 1, 0, 0, 0, 0 }, + new float[] { 0, 1, 0, 0, 0 }, + new float[] { 0, 0, 1, 0, 0 }, + new float[] { 0, 0, 0, 1, 0 }, + new float[] { 0, 0, 0, 0, 1 } + }; + + public static readonly ColorMatrix IdentityColorMatrix = new ColorMatrix(Identity5x5F); + + [ThreadStatic] + private static Matrix identityMatrix = null; + public static Matrix IdentityMatrix + { + get + { + if (identityMatrix == null) + { + identityMatrix = new Matrix(); + identityMatrix.Reset(); + } + + return identityMatrix; + } + } + + /// <summary> + /// Rounds an integer to the smallest power of 2 that is greater + /// than or equal to it. + /// </summary> + public static int Log2RoundUp(int x) + { + if (x == 0) + { + return 1; + } + + if (x == 1) + { + return 1; + } + + return 1 << (1 + HighestBit(x - 1)); + } + + private static int HighestBit(int x) + { + if (x == 0) + { + return 0; + } + + int b = 0; + int hi = 0; + + while (b <= 30) + { + if ((x & (1 << b)) != 0) + { + hi = b; + } + + ++b; + } + + return hi; + } + + private int CountBits(int x) + { + uint y = (uint)x; + int count = 0; + + for (int bit = 0; bit < 32; ++bit) + { + if ((y & ((uint)1 << bit)) != 0) + { + ++count; + } + } + + return count; + } + + public static string RemoveSpaces(string s) + { + StringBuilder sb = new StringBuilder(); + + foreach (char c in s) + { + if (!char.IsWhiteSpace(c)) + { + sb.Append(c); + } + } + + return sb.ToString(); + } + + public static int Max(int[,] array) + { + int max = int.MinValue; + + for (int i = array.GetLowerBound(0); i <= array.GetUpperBound(0); ++i) + { + for (int j = array.GetLowerBound(1); j <= array.GetUpperBound(1); ++j) + { + if (array[i,j] > max) + { + max = array[i,j]; + } + } + } + + return max; + } + + public static int Sum(int[][] array) + { + int sum = 0; + + for (int i = 0; i < array.Length; ++i) + { + int[] row = array[i]; + + for (int j = 0; j < row.Length; ++j) + { + sum += row[j]; + } + } + + return sum; + } + + public static Icon ImageToIcon(Image image) + { + return ImageToIcon(image, Utility.TransparentKey); + } + + public static Icon ImageToIcon(Image image, bool disposeImage) + { + return ImageToIcon(image, Utility.TransparentKey, disposeImage); + } + + public static Icon ImageToIcon(Image image, Color seeThru) + { + return ImageToIcon(image, seeThru, false); + } + + /// <summary> + /// Converts an Image to an Icon. + /// </summary> + /// <param name="image">The Image to convert to an icon. Must be an appropriate icon size (32x32, 16x16, etc).</param> + /// <param name="seeThru">The color that will be treated as transparent in the icon.</param> + /// <param name="disposeImage">Whether or not to dispose the passed-in Image.</param> + /// <returns>An Icon representation of the Image.</returns> + public static Icon ImageToIcon(Image image, Color seeThru, bool disposeImage) + { + Bitmap bitmap = new Bitmap(image); + + for (int y = 0; y < bitmap.Height; ++y) + { + for (int x = 0; x < bitmap.Width; ++x) + { + if (bitmap.GetPixel(x, y) == seeThru) + { + bitmap.SetPixel(x, y, Color.FromArgb(0)); + } + } + } + + Icon icon = Icon.FromHandle(bitmap.GetHicon()); + bitmap.Dispose(); + + if (disposeImage) + { + image.Dispose(); + } + + return icon; + } + + public static Icon BitmapToIcon(Bitmap bitmap, bool disposeBitmap) + { + Icon icon = Icon.FromHandle(bitmap.GetHicon()); + + if (disposeBitmap) + { + bitmap.Dispose(); + } + + return icon; + } + + public static Point GetRectangleCenter(Rectangle rect) + { + return new Point((rect.Left + rect.Right) / 2, (rect.Top + rect.Bottom) / 2); + } + + public static PointF GetRectangleCenter(RectangleF rect) + { + return new PointF((rect.Left + rect.Right) / 2, (rect.Top + rect.Bottom) / 2); + } + + public static Scanline[] GetRectangleScans(Rectangle rect) + { + Scanline[] scans = new Scanline[rect.Height]; + + for (int y = 0; y < rect.Height; ++y) + { + scans[y] = new Scanline(rect.X, rect.Y + y, rect.Width); + } + + return scans; + } + + public static Scanline[] GetRegionScans(Rectangle[] region) + { + int scanCount = 0; + + for (int i = 0; i < region.Length; ++i) + { + scanCount += region[i].Height; + } + + Scanline[] scans = new Scanline[scanCount]; + int scanIndex = 0; + + foreach (Rectangle rect in region) + { + for (int y = 0; y < rect.Height; ++y) + { + scans[scanIndex] = new Scanline(rect.X, rect.Y + y, rect.Width); + ++scanIndex; + } + } + + return scans; + } + + public static Rectangle[] ScanlinesToRectangles(Scanline[] scans) + { + return ScanlinesToRectangles(scans, 0, scans.Length); + } + + public static Rectangle[] ScanlinesToRectangles(Scanline[] scans, int startIndex, int length) + { + Rectangle[] rects = new Rectangle[length]; + + for (int i = 0; i < length; ++i) + { + Scanline scan = scans[i + startIndex]; + rects[i] = new Rectangle(scan.X, scan.Y, scan.Length, 1); + } + + return rects; + } + + public static RectangleF RectangleFromCenter(PointF center, float halfSize) + { + RectangleF ret = new RectangleF(center.X, center.Y, 0, 0); + ret.Inflate(halfSize, halfSize); + return ret; + } + + public static List<PointF> PointListToPointFList(List<Point> ptList) + { + List<PointF> ret = new List<PointF>(ptList.Count); + + for (int i = 0; i < ptList.Count; ++i) + { + ret.Add((PointF)ptList[i]); + } + + return ret; + } + + public static PointF[] PointArrayToPointFArray(Point[] ptArray) + { + PointF[] ret = new PointF[ptArray.Length]; + + for (int i = 0; i < ret.Length; ++i) + { + ret[i] = (PointF)ptArray[i]; + } + + return ret; + } + + public static Rectangle[] InflateRectangles(Rectangle[] rects, int amount) + { + Rectangle[] inflated = new Rectangle[rects.Length]; + + for (int i = 0; i < rects.Length; ++i) + { + inflated[i] = Rectangle.Inflate(rects[i], amount, amount); + } + + return inflated; + } + + public static void InflateRectanglesInPlace(Rectangle[] rects, int amount) + { + for (int i = 0; i < rects.Length; ++i) + { + rects[i].Inflate(amount, amount); + } + } + + public static RectangleF[] InflateRectangles(RectangleF[] rectsF, int amount) + { + RectangleF[] inflated = new RectangleF[rectsF.Length]; + + for (int i = 0; i < rectsF.Length; ++i) + { + inflated[i] = RectangleF.Inflate(rectsF[i], amount, amount); + } + + return inflated; + } + + public static void InflateRectanglesInPlace(RectangleF[] rectsF, float amount) + { + for (int i = 0; i < rectsF.Length; ++i) + { + rectsF[i].Inflate(amount, amount); + } + } + + public static Rectangle PointsToConstrainedRectangle(Point a, Point b) + { + Rectangle rect = Utility.PointsToRectangle(a, b); + int minWH = Math.Min(rect.Width, rect.Height); + + rect.Width = minWH; + rect.Height = minWH; + + if (rect.Y != a.Y) + { + rect.Location = new Point(rect.X, a.Y - minWH); + } + + if (rect.X != a.X) + { + rect.Location = new Point(a.X - minWH, rect.Y); + } + + return rect; + } + + public static RectangleF PointsToConstrainedRectangle(PointF a, PointF b) + { + RectangleF rect = Utility.PointsToRectangle(a, b); + float minWH = Math.Min(rect.Width, rect.Height); + + rect.Width = minWH; + rect.Height = minWH; + + if (rect.Y != a.Y) + { + rect.Location = new PointF(rect.X, a.Y - minWH); + } + + if (rect.X != a.X) + { + rect.Location = new PointF(a.X - minWH, rect.Y); + } + + return rect; + } + + /// <summary> + /// Takes two points and creates a bounding rectangle from them. + /// </summary> + /// <param name="a">One corner of the rectangle.</param> + /// <param name="b">The other corner of the rectangle.</param> + /// <returns>A Rectangle instance that bounds the two points.</returns> + public static Rectangle PointsToRectangle(Point a, Point b) + { + int x = Math.Min(a.X, b.X); + int y = Math.Min(a.Y, b.Y); + int width = Math.Abs(a.X - b.X) + 1; + int height = Math.Abs(a.Y - b.Y) + 1; + + return new Rectangle(x, y, width, height); + } + + public static RectangleF PointsToRectangle(PointF a, PointF b) + { + float x = Math.Min(a.X, b.X); + float y = Math.Min(a.Y, b.Y); + float width = Math.Abs(a.X - b.X) + 1; + float height = Math.Abs(a.Y - b.Y) + 1; + + return new RectangleF(x, y, width, height); + } + + public static Rectangle PointsToRectangleExclusive(Point a, Point b) + { + int x = Math.Min(a.X, b.X); + int y = Math.Min(a.Y, b.Y); + int width = Math.Abs(a.X - b.X); + int height = Math.Abs(a.Y - b.Y); + + return new Rectangle(x, y, width, height); + } + + public static RectangleF PointsToRectangleExclusive(PointF a, PointF b) + { + float x = Math.Min(a.X, b.X); + float y = Math.Min(a.Y, b.Y); + float width = Math.Abs(a.X - b.X); + float height = Math.Abs(a.Y - b.Y); + + return new RectangleF(x, y, width, height); + } + + public static RectangleF[] PointsToRectangles(PointF[] pointsF) + { + if (pointsF.Length == 0) + { + return new RectangleF[] { }; + } + + if (pointsF.Length == 1) + { + return new RectangleF[] { new RectangleF(pointsF[0].X, pointsF[0].Y, 1, 1) }; + } + + RectangleF[] rectsF = new RectangleF[pointsF.Length - 1]; + + for (int i = 0; i < pointsF.Length - 1; ++i) + { + rectsF[i] = PointsToRectangle(pointsF[i], pointsF[i + 1]); + } + + return rectsF; + } + + public static Rectangle[] PointsToRectangles(Point[] points) + { + if (points.Length == 0) + { + return new Rectangle[] { }; + } + + if (points.Length == 1) + { + return new Rectangle[] { new Rectangle(points[0].X, points[0].Y, 1, 1) }; + } + + Rectangle[] rects = new Rectangle[points.Length - 1]; + + for (int i = 0; i < points.Length - 1; ++i) + { + rects[i] = PointsToRectangle(points[i], points[i + 1]); + } + + return rects; + } + + /// <summary> + /// Converts a RectangleF to RectangleF by rounding down the Location and rounding + /// up the Size. + /// </summary> + public static Rectangle RoundRectangle(RectangleF rectF) + { + float left = (float)Math.Floor(rectF.Left); + float top = (float)Math.Floor(rectF.Top); + float right = (float)Math.Ceiling(rectF.Right); + float bottom = (float)Math.Ceiling(rectF.Bottom); + + return Rectangle.Truncate(RectangleF.FromLTRB(left, top, right, bottom)); + } + + public static Stack Reverse(Stack reverseMe) + { + Stack reversed = new Stack(); + + foreach (object o in reverseMe) + { + reversed.Push(o); + } + + return reversed; + } + + public static void SerializeObjectToStream(object graph, Stream stream) + { + new BinaryFormatter().Serialize(stream, graph); + } + + public static object DeserializeObjectFromStream(Stream stream) + { + return new BinaryFormatter().Deserialize(stream); + } + + [Obsolete("Use rect.Contains() instead", true)] + public static bool IsPointInRectangle(Point pt, Rectangle rect) + { + return rect.Contains(pt); + } + + [Obsolete("Use rect.Contains() instead", true)] + public static bool IsPointInRectangle(int x, int y, Rectangle rect) + { + return rect.Contains(x, y); + } + + public static Bitmap FullCloneBitmap(Bitmap cloneMe) + { + Bitmap bitmap = new Bitmap(cloneMe.Width, cloneMe.Height, cloneMe.PixelFormat); + + using (Graphics g = Graphics.FromImage(bitmap)) + { + g.DrawImage(cloneMe, 0, 0, cloneMe.Width, cloneMe.Height); + } + + return bitmap; + } + + /// <summary> + /// Allows you to find the bounding box for a "region" that is described as an + /// array of bounding boxes. + /// </summary> + /// <param name="rectsF">The "region" you want to find a bounding box for.</param> + /// <returns>A RectangleF structure that surrounds the Region.</returns> + public static RectangleF GetRegionBounds(RectangleF[] rectsF, int startIndex, int length) + { + if (rectsF.Length == 0) + { + return RectangleF.Empty; + } + + float left = rectsF[startIndex].Left; + float top = rectsF[startIndex].Top; + float right = rectsF[startIndex].Right; + float bottom = rectsF[startIndex].Bottom; + + for (int i = startIndex + 1; i < startIndex + length; ++i) + { + RectangleF rectF = rectsF[i]; + + if (rectF.Left < left) + { + left = rectF.Left; + } + + if (rectF.Top < top) + { + top = rectF.Top; + } + + if (rectF.Right > right) + { + right = rectF.Right; + } + + if (rectF.Bottom > bottom) + { + bottom = rectF.Bottom; + } + } + + return RectangleF.FromLTRB(left, top, right, bottom); + } + + public static RectangleF GetTraceBounds(PointF[] pointsF, int startIndex, int length) + { + if (pointsF.Length == 0) + { + return RectangleF.Empty; + } + + float left = pointsF[startIndex].X; + float top = pointsF[startIndex].Y; + float right = 1 + pointsF[startIndex].X; + float bottom = 1 + pointsF[startIndex].Y; + + for (int i = startIndex + 1; i < startIndex + length; ++i) + { + PointF pointF = pointsF[i]; + + if (pointF.X < left) + { + left = pointF.X; + } + + if (pointF.Y < top) + { + top = pointF.Y; + } + + if (pointF.X > right) + { + right = pointF.X; + } + + if (pointF.Y > bottom) + { + bottom = pointF.Y; + } + } + + return RectangleF.FromLTRB(left, top, right, bottom); + } + + public static Rectangle GetTraceBounds(Point[] points, int startIndex, int length) + { + if (points.Length == 0) + { + return Rectangle.Empty; + } + + int left = points[startIndex].X; + int top = points[startIndex].Y; + int right = 1 + points[startIndex].X; + int bottom = 1 + points[startIndex].Y; + + for (int i = startIndex + 1; i < startIndex + length; ++i) + { + Point point = points[i]; + + if (point.X < left) + { + left = point.X; + } + + if (point.Y < top) + { + top = point.Y; + } + + if (point.X > right) + { + right = point.X; + } + + if (point.Y > bottom) + { + bottom = point.Y; + } + } + + return Rectangle.FromLTRB(left, top, right, bottom); + } + + /// <summary> + /// Allows you to find the bounding box for a "region" that is described as an + /// array of bounding boxes. + /// </summary> + /// <param name="rectsF">The "region" you want to find a bounding box for.</param> + /// <returns>A RectangleF structure that surrounds the Region.</returns> + public static Rectangle GetRegionBounds(Rectangle[] rects, int startIndex, int length) + { + if (rects.Length == 0) + { + return Rectangle.Empty; + } + + int left = rects[startIndex].Left; + int top = rects[startIndex].Top; + int right = rects[startIndex].Right; + int bottom = rects[startIndex].Bottom; + + for (int i = startIndex + 1; i < startIndex + length; ++i) + { + Rectangle rect = rects[i]; + + if (rect.Left < left) + { + left = rect.Left; + } + + if (rect.Top < top) + { + top = rect.Top; + } + + if (rect.Right > right) + { + right = rect.Right; + } + + if (rect.Bottom > bottom) + { + bottom = rect.Bottom; + } + } + + return Rectangle.FromLTRB(left, top, right, bottom); + } + + public static RectangleF GetRegionBounds(RectangleF[] rectsF) + { + return GetRegionBounds(rectsF, 0, rectsF.Length); + } + + public static Rectangle GetRegionBounds(Rectangle[] rects) + { + return GetRegionBounds(rects, 0, rects.Length); + } + + private static float DistanceSquared(RectangleF[] rectsF, int indexA, int indexB) + { + PointF centerA = new PointF(rectsF[indexA].Left + (rectsF[indexA].Width / 2), rectsF[indexA].Top + (rectsF[indexA].Height / 2)); + PointF centerB = new PointF(rectsF[indexB].Left + (rectsF[indexB].Width / 2), rectsF[indexB].Top + (rectsF[indexB].Height / 2)); + + return ((centerA.X - centerB.X) * (centerA.X - centerB.X)) + + ((centerA.Y - centerB.Y) * (centerA.Y - centerB.Y)); + } + + public static Rectangle[] SimplifyRegion(Rectangle[] rects, int complexity) + { + if (complexity == 0 || rects.Length < complexity) + { + return (Rectangle[])rects.Clone(); + } + + Rectangle[] boxes = new Rectangle[complexity]; + + for (int i = 0; i < complexity; ++i) + { + int startIndex = (i * rects.Length) / complexity; + int length = Math.Min(rects.Length, ((i + 1) * rects.Length) / complexity) - startIndex; + boxes[i] = GetRegionBounds(rects, startIndex, length); + } + + return boxes; + } + + + public static RectangleF[] SimplifyTrace(PointF[] pointsF, int complexity) + { + if (complexity == 0 || + (pointsF.Length - 1) < complexity) + { + return PointsToRectangles(pointsF); + } + + RectangleF[] boxes = new RectangleF[complexity]; + int parLength = pointsF.Length - 1; // "(points as Rectangles).Length" + + for (int i = 0; i < complexity; ++i) + { + int startIndex = (i * parLength) / complexity; + int length = Math.Min(parLength, ((i + 1) * parLength) / complexity) - startIndex; + boxes[i] = GetTraceBounds(pointsF, startIndex, length + 1); + } + + return boxes; + } + + public static RectangleF[] SimplifyTrace(PointF[] pointsF) + { + return SimplifyTrace(pointsF, defaultSimplificationFactor); + } + + public static Rectangle[] SimplifyAndInflateRegion(Rectangle[] rects, int complexity, int inflationAmount) + { + Rectangle[] simplified = SimplifyRegion(rects, complexity); + + for (int i = 0; i < simplified.Length; ++i) + { + simplified[i].Inflate(inflationAmount, inflationAmount); + } + + return simplified; + } + + public static Rectangle[] SimplifyAndInflateRegion(Rectangle[] rects) + { + return SimplifyAndInflateRegion(rects, defaultSimplificationFactor, 1); + } + + public static RectangleF[] TranslateRectangles(RectangleF[] rectsF, PointF offset) + { + RectangleF[] retRectsF = new RectangleF[rectsF.Length]; + int i = 0; + + foreach (RectangleF rectF in rectsF) + { + retRectsF[i] = new RectangleF(rectF.X + offset.X, rectF.Y + offset.Y, rectF.Width, rectF.Height); + ++i; + } + + return retRectsF; + } + + public static Rectangle[] TranslateRectangles(Rectangle[] rects, int dx, int dy) + { + Rectangle[] retRects = new Rectangle[rects.Length]; + + for (int i = 0; i < rects.Length; ++i) + { + retRects[i] = new Rectangle(rects[i].X + dx, rects[i].Y + dy, rects[i].Width, rects[i].Height); + } + + return retRects; + } + + public static void TranslatePointsInPlace(PointF[] ptsF, float dx, float dy) + { + for (int i = 0; i < ptsF.Length; ++i) + { + ptsF[i].X += dx; + ptsF[i].Y += dy; + } + } + + public static void TranslatePointsInPlace(Point[] pts, int dx, int dy) + { + for (int i = 0; i < pts.Length; ++i) + { + pts[i].X += dx; + pts[i].Y += dy; + } + } + + public static Rectangle[] TruncateRectangles(RectangleF[] rectsF) + { + Rectangle[] rects = new Rectangle[rectsF.Length]; + + for (int i = 0; i < rectsF.Length; ++i) + { + rects[i] = Rectangle.Truncate(rectsF[i]); + } + + return rects; + } + + public static Point[] TruncatePoints(PointF[] pointsF) + { + Point[] points = new Point[pointsF.Length]; + + for (int i = 0; i < pointsF.Length; ++i) + { + points[i] = Point.Truncate(pointsF[i]); + } + + return points; + } + + public static Point[] RoundPoints(PointF[] pointsF) + { + Point[] points = new Point[pointsF.Length]; + + for (int i = 0; i < pointsF.Length; ++i) + { + points[i] = Point.Round(pointsF[i]); + } + + return points; + } + + /// <summary> + /// The Sutherland-Hodgman clipping alrogithm. + /// http://ezekiel.vancouver.wsu.edu/~cs442/lectures/clip/clip/index.html + /// + /// # Clipping a convex polygon to a convex region (e.g., rectangle) will always produce a convex polygon (or no polygon if completely outside the clipping region). + /// # Clipping a concave polygon to a rectangle may produce several polygons (see figure above) or, as the following algorithm does, produce a single, possibly degenerate, polygon. + /// # Divide and conquer: Clip entire polygon against a single edge (i.e., half-plane). Repeat for each edge in the clipping region. + /// + /// The input is a sequence of vertices: {v0, v1, ... vn} given as an array of Points + /// the result is a sequence of vertices, given as an array of Points. This result may have + /// less than, equal, more than, or 0 vertices. + /// </summary> + /// <param name="vertices"></param> + /// <returns></returns> + public static List<PointF> SutherlandHodgman(RectangleF bounds, List<PointF> v) + { + List<PointF> p1 = SutherlandHodgmanOneAxis(bounds, RectangleEdge.Left, v); + List<PointF> p2 = SutherlandHodgmanOneAxis(bounds, RectangleEdge.Right, p1); + List<PointF> p3 = SutherlandHodgmanOneAxis(bounds, RectangleEdge.Top, p2); + List<PointF> p4 = SutherlandHodgmanOneAxis(bounds, RectangleEdge.Bottom, p3); + + return p4; + } + + private enum RectangleEdge + { + Left, + Right, + Top, + Bottom + } + + private static List<PointF> SutherlandHodgmanOneAxis(RectangleF bounds, RectangleEdge edge, List<PointF> v) + { + if (v.Count == 0) + { + return new List<PointF>(); + } + + List<PointF> polygon = new List<PointF>(); + + PointF s = v[v.Count - 1]; + + for (int i = 0; i < v.Count; ++i) + { + PointF p = v[i]; + bool pIn = IsInside(bounds, edge, p); + bool sIn = IsInside(bounds, edge, s); + + if (sIn && pIn) + { + // case 1: inside -> inside + polygon.Add(p); + } + else if (sIn && !pIn) + { + // case 2: inside -> outside + polygon.Add(LineIntercept(bounds, edge, s, p)); + } + else if (!sIn && !pIn) + { + // case 3: outside -> outside + // emit nothing + } + else if (!sIn && pIn) + { + // case 4: outside -> inside + polygon.Add(LineIntercept(bounds, edge, s, p)); + polygon.Add(p); + } + + s = p; + } + + return polygon; + } + + private static bool IsInside(RectangleF bounds, RectangleEdge edge, PointF p) + { + switch (edge) + { + case RectangleEdge.Left: + return !(p.X < bounds.Left); + + case RectangleEdge.Right: + return !(p.X >= bounds.Right); + + case RectangleEdge.Top: + return !(p.Y < bounds.Top); + + case RectangleEdge.Bottom: + return !(p.Y >= bounds.Bottom); + + default: + throw new InvalidEnumArgumentException("edge"); + } + } + + private static Point LineIntercept(Rectangle bounds, RectangleEdge edge, Point a, Point b) + { + if (a == b) + { + return a; + } + + switch (edge) + { + case RectangleEdge.Bottom: + if (b.Y == a.Y) + { + throw new ArgumentException("no intercept found"); + } + + return new Point(a.X + (((b.X - a.X) * (bounds.Bottom - a.Y)) / (b.Y - a.Y)), bounds.Bottom); + + case RectangleEdge.Left: + if (b.X == a.X) + { + throw new ArgumentException("no intercept found"); + } + + return new Point(bounds.Left, a.Y + (((b.Y - a.Y) * (bounds.Left - a.X)) / (b.X - a.X))); + + case RectangleEdge.Right: + if (b.X == a.X) + { + throw new ArgumentException("no intercept found"); + } + + return new Point(bounds.Right, a.Y + (((b.Y - a.Y) * (bounds.Right - a.X)) / (b.X - a.X))); + + case RectangleEdge.Top: + if (b.Y == a.Y) + { + throw new ArgumentException("no intercept found"); + } + + return new Point(a.X + (((b.X - a.X) * (bounds.Top - a.Y)) / (b.Y - a.Y)), bounds.Top); + } + + throw new ArgumentException("no intercept found"); + } + + private static PointF LineIntercept(RectangleF bounds, RectangleEdge edge, PointF a, PointF b) + { + if (a == b) + { + return a; + } + + switch (edge) + { + case RectangleEdge.Bottom: + if (b.Y == a.Y) + { + throw new ArgumentException("no intercept found"); + } + + return new PointF(a.X + (((b.X - a.X) * (bounds.Bottom - a.Y)) / (b.Y - a.Y)), bounds.Bottom); + + case RectangleEdge.Left: + if (b.X == a.X) + { + throw new ArgumentException("no intercept found"); + } + + return new PointF(bounds.Left, a.Y + (((b.Y - a.Y) * (bounds.Left - a.X)) / (b.X - a.X))); + + case RectangleEdge.Right: + if (b.X == a.X) + { + throw new ArgumentException("no intercept found"); + } + + return new PointF(bounds.Right, a.Y + (((b.Y - a.Y) * (bounds.Right - a.X)) / (b.X - a.X))); + + case RectangleEdge.Top: + if (b.Y == a.Y) + { + throw new ArgumentException("no intercept found"); + } + + return new PointF(a.X + (((b.X - a.X) * (bounds.Top - a.Y)) / (b.Y - a.Y)), bounds.Top); + } + + throw new ArgumentException("no intercept found"); + } + + public static Point[] GetLinePoints(Point first, Point second) + { + Point[] coords = null; + + int x1 = first.X; + int y1 = first.Y; + int x2 = second.X; + int y2 = second.Y; + int dx = x2 - x1; + int dy = y2 - y1; + int dxabs = Math.Abs(dx); + int dyabs = Math.Abs(dy); + int px = x1; + int py = y1; + int sdx = Math.Sign(dx); + int sdy = Math.Sign(dy); + int x = 0; + int y = 0; + + if (dxabs > dyabs) + { + coords = new Point[dxabs + 1]; + + for (int i = 0; i <= dxabs; i++) + { + y += dyabs; + + if (y >= dxabs) + { + y -= dxabs; + py += sdy; + } + + coords[i] = new Point(px, py); + px += sdx; + } + } + else + // had to add in this cludge for slopes of 1 ... wasn't drawing half the line + if (dxabs == dyabs) + { + coords = new Point[dxabs + 1]; + + for (int i = 0; i <= dxabs; i++) + { + coords[i] = new Point(px, py); + px += sdx; + py += sdy; + } + } + else + { + coords = new Point[dyabs + 1]; + + for (int i = 0; i <= dyabs; i++) + { + x += dxabs; + + if (x >= dyabs) + { + x -= dyabs; + px += sdx; + } + + coords[i] = new Point(px, py); + py += sdy; + } + } + + return coords; + } + + public static long GetTimeMs() + { + return Utility.TicksToMs(DateTime.Now.Ticks); + } + + /// <summary> + /// Returns the Distance between two points + /// </summary> + public static float Distance(PointF a, PointF b) + { + return Magnitude(new PointF(a.X - b.X, a.Y - b.Y)); + } + + /// <summary> + /// Returns the Magnitude (distance to origin) of a point + /// </summary> + // TODO: In v4.0 codebase, turn this into an extension method + public static float Magnitude(PointF p) + { + return (float)Math.Sqrt(p.X * p.X + p.Y * p.Y); + } + + // TODO: In v4.0 codebase, turn this into an extension method + public static double Clamp(double x, double min, double max) + { + if (x < min) + { + return min; + } + else if (x > max) + { + return max; + } + else + { + return x; + } + } + + // TODO: In v4.0 codebase, turn this into an extension method + public static float Clamp(float x, float min, float max) + { + if (x < min) + { + return min; + } + else if (x > max) + { + return max; + } + else + { + return x; + } + } + + // TODO: In v4.0 codebase, turn this into an extension method + public static int Clamp(int x, int min, int max) + { + if (x < min) + { + return min; + } + else if (x > max) + { + return max; + } + else + { + return x; + } + } + + public static byte ClampToByte(double x) + { + if (x > 255) + { + return 255; + } + else if (x < 0) + { + return 0; + } + else + { + return (byte)x; + } + } + + public static byte ClampToByte(float x) + { + if (x > 255) + { + return 255; + } + else if (x < 0) + { + return 0; + } + else + { + return (byte)x; + } + } + + public static byte ClampToByte(int x) + { + if (x > 255) + { + return 255; + } + else if (x < 0) + { + return 0; + } + else + { + return (byte)x; + } + } + + public static float Lerp(float from, float to, float frac) + { + return (from + frac * (to - from)); + } + + public static double Lerp(double from, double to, double frac) + { + return (from + frac * (to - from)); + } + + public static PointF Lerp(PointF from, PointF to, float frac) + { + return new PointF(Lerp(from.X, to.X, frac), Lerp(from.Y, to.Y, frac)); + } + + public static int ColorDifference(ColorBgra a, ColorBgra b) + { + return (int)Math.Ceiling(Math.Sqrt(ColorDifferenceSquared(a, b))); + } + + public static int ColorDifferenceSquared(ColorBgra a, ColorBgra b) + { + int diffSq = 0, tmp; + + tmp = a.R - b.R; + diffSq += tmp * tmp; + tmp = a.G - b.G; + diffSq += tmp * tmp; + tmp = a.B - b.B; + diffSq += tmp * tmp; + + return diffSq / 3; + } + + /// <summary> + /// Reads a 16-bit unsigned integer from a Stream in little-endian format. + /// </summary> + /// <param name="stream"></param> + /// <returns>-1 on failure, else the 16-bit unsigned integer that was read.</returns> + public static int ReadUInt16(Stream stream) + { + int byte1 = stream.ReadByte(); + + if (byte1 == -1) + { + return -1; + } + + int byte2 = stream.ReadByte(); + + if (byte2 == -1) + { + return -1; + } + + return byte1 + (byte2 << 8); + } + + public static void WriteUInt16(Stream stream, UInt16 word) + { + stream.WriteByte((byte)(word & 0xff)); + stream.WriteByte((byte)(word >> 8)); + } + + public static void WriteUInt24(Stream stream, int uint24) + { + stream.WriteByte((byte)(uint24 & 0xff)); + stream.WriteByte((byte)((uint24 >> 8) & 0xff)); + stream.WriteByte((byte)((uint24 >> 16) & 0xff)); + } + + public static void WriteUInt32(Stream stream, UInt32 uint32) + { + stream.WriteByte((byte)(uint32 & 0xff)); + stream.WriteByte((byte)((uint32 >> 8) & 0xff)); + stream.WriteByte((byte)((uint32 >> 16) & 0xff)); + stream.WriteByte((byte)((uint32 >> 24) & 0xff)); + } + + /// <summary> + /// Reads a 24-bit unsigned integer from a Stream in little-endian format. + /// </summary> + /// <param name="stream"></param> + /// <returns>-1 on failure, else the 24-bit unsigned integer that was read.</returns> + public static int ReadUInt24(Stream stream) + { + int byte1 = stream.ReadByte(); + + if (byte1 == -1) + { + return -1; + } + + int byte2 = stream.ReadByte(); + + if (byte2 == -1) + { + return -1; + } + + int byte3 = stream.ReadByte(); + + if (byte3 == -1) + { + return -1; + } + + return byte1 + (byte2 << 8) + (byte3 << 16); + } + + /// <summary> + /// Reads a 32-bit unsigned integer from a Stream in little-endian format. + /// </summary> + /// <param name="stream"></param> + /// <returns>-1 on failure, else the 32-bit unsigned integer that was read.</returns> + public static long ReadUInt32(Stream stream) + { + int byte1 = stream.ReadByte(); + + if (byte1 == -1) + { + return -1; + } + + int byte2 = stream.ReadByte(); + + if (byte2 == -1) + { + return -1; + } + + int byte3 = stream.ReadByte(); + + if (byte3 == -1) + { + return -1; + } + + int byte4 = stream.ReadByte(); + + if (byte4 == -1) + { + return -1; + } + + return unchecked((long)((uint)(byte1 + (byte2 << 8) + (byte3 << 16) + (byte4 << 24)))); + } + + public static int ReadFromStream(Stream input, byte[] buffer, int offset, int count) + { + int totalBytesRead = 0; + + while (totalBytesRead < count) + { + int bytesRead = input.Read(buffer, offset + totalBytesRead, count - totalBytesRead); + + if (bytesRead == 0) + { + throw new IOException("ran out of data"); + } + + totalBytesRead += bytesRead; + } + + return totalBytesRead; + } + + public static long CopyStream(Stream input, Stream output, long maxBytes) + { + long bytesCopied = 0; + byte[] buffer = new byte[4096]; + + while (true) + { + int bytesRead = input.Read(buffer, 0, buffer.Length); + + if (bytesRead == 0) + { + break; + } + else + { + int bytesToCopy; + + if (maxBytes != -1 && (bytesCopied + bytesRead) > maxBytes) + { + bytesToCopy = (int)(maxBytes - bytesCopied); + } + else + { + bytesToCopy = bytesRead; + } + + output.Write(buffer, 0, bytesRead); + bytesCopied += bytesToCopy; + + if (bytesToCopy != bytesRead) + { + break; + } + } + } + + return bytesCopied; + } + + public static long CopyStream(Stream input, Stream output) + { + return CopyStream(input, output, -1); + } + + private struct Edge + { + public int miny; // int + public int maxy; // int + public int x; // fixed point: 24.8 + public int dxdy; // fixed point: 24.8 + + public Edge(int miny, int maxy, int x, int dxdy) + { + this.miny = miny; + this.maxy = maxy; + this.x = x; + this.dxdy = dxdy; + } + } + + public static Scanline[] GetScans(Point[] vertices) + { + return GetScans(vertices, 0, vertices.Length); + } + + public static Scanline[] GetScans(Point[] vertices, int startIndex, int length) + { + if (length > vertices.Length - startIndex) + { + throw new ArgumentException("out of bounds: length > vertices.Length - startIndex"); + } + + int ymax = 0; + + // Build edge table + Edge[] edgeTable = new Edge[length]; + int edgeCount = 0; + + for (int i = startIndex; i < startIndex + length; ++i) + { + Point top = vertices[i]; + Point bottom = vertices[(((i + 1) - startIndex) % length) + startIndex]; + int dy; + + if (top.Y > bottom.Y) + { + Point temp = top; + top = bottom; + bottom = temp; + } + + dy = bottom.Y - top.Y; + + if (dy != 0) + { + edgeTable[edgeCount] = new Edge(top.Y, bottom.Y, top.X << 8, (((bottom.X - top.X) << 8) / dy)); + ymax = Math.Max(ymax, bottom.Y); + ++edgeCount; + } + } + + // Sort edge table by miny + for (int i = 0; i < edgeCount - 1; ++i) + { + int min = i; + + for (int j = i + 1; j < edgeCount; ++j) + { + if (edgeTable[j].miny < edgeTable[min].miny) + { + min = j; + } + } + + if (min != i) + { + Edge temp = edgeTable[min]; + edgeTable[min] = edgeTable[i]; + edgeTable[i] = temp; + } + } + + // Compute how many scanlines we will be emitting + int scanCount = 0; + int activeLow = 0; + int activeHigh = 0; + int yscan1 = edgeTable[0].miny; + + // we assume that edgeTable[0].miny == yscan + while (activeHigh < edgeCount - 1 && + edgeTable[activeHigh + 1].miny == yscan1) + { + ++activeHigh; + } + + while (yscan1 <= ymax) + { + // Find new edges where yscan == miny + while (activeHigh < edgeCount - 1 && + edgeTable[activeHigh + 1].miny == yscan1) + { + ++activeHigh; + } + + int count = 0; + for (int i = activeLow; i <= activeHigh; ++i) + { + if (edgeTable[i].maxy > yscan1) + { + ++count; + } + } + + scanCount += count / 2; + ++yscan1; + + // Remove edges where yscan == maxy + while (activeLow < edgeCount - 1 && + edgeTable[activeLow].maxy <= yscan1) + { + ++activeLow; + } + + if (activeLow > activeHigh) + { + activeHigh = activeLow; + } + } + + // Allocate scanlines that we'll return + Scanline[] scans = new Scanline[scanCount]; + + // Active Edge Table (AET): it is indices into the Edge Table (ET) + int[] active = new int[edgeCount]; + int activeCount = 0; + int yscan2 = edgeTable[0].miny; + int scansIndex = 0; + + // Repeat until both the ET and AET are empty + while (yscan2 <= ymax) + { + // Move any edges from the ET to the AET where yscan == miny + for (int i = 0; i < edgeCount; ++i) + { + if (edgeTable[i].miny == yscan2) + { + active[activeCount] = i; + ++activeCount; + } + } + + // Sort the AET on x + for (int i = 0; i < activeCount - 1; ++i) + { + int min = i; + + for (int j = i + 1; j < activeCount; ++j) + { + if (edgeTable[active[j]].x < edgeTable[active[min]].x) + { + min = j; + } + } + + if (min != i) + { + int temp = active[min]; + active[min] = active[i]; + active[i] = temp; + } + } + + // For each pair of entries in the AET, fill in pixels between their info + for (int i = 0; i < activeCount; i += 2) + { + Edge el = edgeTable[active[i]]; + Edge er = edgeTable[active[i + 1]]; + int startx = (el.x + 0xff) >> 8; // ceil(x) + int endx = er.x >> 8; // floor(x) + + scans[scansIndex] = new Scanline(startx, yscan2, endx - startx); + ++scansIndex; + } + + ++yscan2; + + // Remove from the AET any edge where yscan == maxy + int k = 0; + while (k < activeCount && activeCount > 0) + { + if (edgeTable[active[k]].maxy == yscan2) + { + // remove by shifting everything down one + for (int j = k + 1; j < activeCount; ++j) + { + active[j - 1] = active[j]; + } + + --activeCount; + } + else + { + ++k; + } + } + + // Update x for each entry in AET + for (int i = 0; i < activeCount; ++i) + { + edgeTable[active[i]].x += edgeTable[active[i]].dxdy; + } + } + + return scans; + } + + public static PointF TransformOnePoint(Matrix matrix, PointF ptF) + { + PointF[] ptFs = new PointF[1] { ptF }; + matrix.TransformPoints(ptFs); + return ptFs[0]; + } + + public static PointF TransformOneVector(Matrix matrix, PointF ptF) + { + PointF[] ptFs = new PointF[1] { ptF }; + matrix.TransformVectors(ptFs); + return ptFs[0]; + } + + public static PointF NormalizeVector(PointF vecF) + { + float magnitude = Magnitude(vecF); + vecF.X /= magnitude; + vecF.Y /= magnitude; + return vecF; + } + + public static PointF NormalizeVector2(PointF vecF) + { + float magnitude = Magnitude(vecF); + + if (magnitude == 0) + { + vecF.X = 0; + vecF.Y = 0; + } + else + { + vecF.X /= magnitude; + vecF.Y /= magnitude; + } + + return vecF; + } + + public static void NormalizeVectors(PointF[] vecsF) + { + for (int i = 0; i < vecsF.Length; ++i) + { + vecsF[i] = NormalizeVector(vecsF[i]); + } + } + + public static PointF RotateVector(PointF vecF, float angleDelta) + { + angleDelta *= (float)( Math.PI / 180.0); + float vecFLen = Magnitude(vecF); + float vecFAngle = angleDelta + (float)Math.Atan2(vecF.Y, vecF.X); + vecF.X = (float)Math.Cos(vecFAngle); + vecF.Y = (float)Math.Sin(vecFAngle); + return vecF; + } + + public static void RotateVectors(PointF[] vecFs, float angleDelta) + { + for (int i = 0; i < vecFs.Length; ++i) + { + vecFs[i] = RotateVector(vecFs[i], angleDelta); + } + } + + public static PointF MultiplyVector(PointF vecF, float scalar) + { + return new PointF(vecF.X * scalar, vecF.Y * scalar); + } + + public static PointF AddVectors(PointF a, PointF b) + { + return new PointF(a.X + b.X, a.Y + b.Y); + } + + public static PointF SubtractVectors(PointF lhs, PointF rhs) + { + return new PointF(lhs.X - rhs.X, lhs.Y - rhs.Y); + } + + public static PointF NegateVector(PointF v) + { + return new PointF(-v.X, -v.Y); + } + + public static float GetAngleOfTransform(Matrix matrix) + { + PointF[] pts = new PointF[] { new PointF(1.0f, 0.0f) }; + matrix.TransformVectors(pts); + double atan2 = Math.Atan2(pts[0].Y, pts[0].X); + double angle = atan2 * (180.0f / Math.PI); + + return (float)angle; + } + + public static bool IsTransformFlipped(Matrix matrix) + { + PointF ptX = new PointF(1.0f, 0.0f); + PointF ptXT = Utility.TransformOneVector(matrix, ptX); + double atan2X = Math.Atan2(ptXT.Y, ptXT.X); + double angleX = atan2X * (180.0 / Math.PI); + + PointF ptY = new PointF(0.0f, 1.0f); + PointF ptYT = Utility.TransformOneVector(matrix, ptY); + double atan2Y = Math.Atan2(ptYT.Y, ptYT.X); + double angleY = (atan2Y * (180.0 / Math.PI)) - 90.0; + + while (angleX < 0) + { + angleX += 360; + } + + while (angleY < 0) + { + angleY += 360; + } + + double angleDelta = Math.Abs(angleX - angleY); + + return angleDelta > 1.0 && angleDelta < 359.0; + } + + /// <summary> + /// Calculates the dot product of two vectors. + /// </summary> + public static float DotProduct(PointF lhs, PointF rhs) + { + return lhs.X * rhs.X + lhs.Y * rhs.Y; + } + + /// <summary> + /// Calculates the orthogonal projection of y on to u. + /// yhat = u * ((y dot u) / (u dot u)) + /// z = y - yhat + /// Section 6.2 (pg. 381) of Linear Algebra and its Applications, Second Edition, by David C. Lay + /// </summary> + /// <param name="y">The vector to decompose</param> + /// <param name="u">The non-zero vector to project y on to</param> + /// <param name="yhat">The orthogonal projection of y onto u</param> + /// <param name="yhatLen">The length of yhat such that yhat = yhatLen * u</param> + /// <param name="z">The component of y orthogonal to u</param> + /// <remarks> + /// As a special case, if u=(0,0) the results are all zero. + /// </remarks> + public static void GetProjection(PointF y, PointF u, out PointF yhat, out float yhatLen, out PointF z) + { + if (u.X == 0 && u.Y == 0) + { + yhat = new PointF(0, 0); + yhatLen = 0; + z = new PointF(0, 0); + } + else + { + float yDotU = DotProduct(y, u); + float uDotU = DotProduct(u, u); + yhatLen = yDotU / uDotU; + yhat = MultiplyVector(u, yhatLen); + z = SubtractVectors(y, yhat); + } + } + + public static int GreatestCommonDivisor(int a, int b) + { + int r; + + if (a < b) + { + r = a; + a = b; + b = r; + } + + do + { + r = a % b; + a = b; + b = r; + } while (r != 0); + + return a; + } + + public static void Swap(ref int a, ref int b) + { + int t; + + t = a; + a = b; + b = t; + } + + public static void Swap<T>(ref T a, ref T b) + { + T t; + + t = a; + a = b; + b = t; + } + + private static byte[] DownloadSmallFile(Uri uri, WebProxy proxy) + { + WebRequest request = WebRequest.Create(uri); + + if (proxy != null) + { + request.Proxy = proxy; + } + + request.Timeout = 5000; + WebResponse response = request.GetResponse(); + Stream stream = response.GetResponseStream(); + + try + { + byte[] buffer = new byte[8192]; + int offset = 0; + + while (offset < buffer.Length) + { + int bytesRead = stream.Read(buffer, offset, buffer.Length - offset); + + if (bytesRead == 0) + { + byte[] smallerBuffer = new byte[offset + bytesRead]; + + for (int i = 0; i < offset + bytesRead; ++i) + { + smallerBuffer[i] = buffer[i]; + } + + buffer = smallerBuffer; + } + + offset += bytesRead; + } + + return buffer; + } + + finally + { + if (stream != null) + { + stream.Close(); + stream = null; + } + + if (response != null) + { + response.Close(); + response = null; + } + } + } + + public static T[] RepeatArray<T>(T[] array, int repeatCount) + { + T[] returnArray = new T[repeatCount * array.Length]; + + for (int i = 0; i < repeatCount; ++i) + { + for (int j = 0; j < array.Length; ++j) + { + int index = (i * array.Length) + j; + returnArray[index] = array[j]; + } + } + + return returnArray; + } + + public static byte FastScaleByteByByte(byte a, byte b) + { + int r1 = a * b + 0x80; + int r2 = ((r1 >> 8) + r1) >> 8; + return (byte)r2; + } + + public static int FastDivideShortByByte(ushort n, byte d) + { + int i = d * 3; + uint m = masTable[i]; + uint a = masTable[i + 1]; + uint s = masTable[i + 2]; + + uint nTimesMPlusA = unchecked((n * m) + a); + uint shifted = nTimesMPlusA >> (int)s; + int r = (int)shifted; + + return r; + } + + // i = z * 3; + // (x / z) = ((x * masTable[i]) + masTable[i + 1]) >> masTable[i + 2) + private static readonly uint[] masTable = + { + 0x00000000, 0x00000000, 0, // 0 + 0x00000001, 0x00000000, 0, // 1 + 0x00000001, 0x00000000, 1, // 2 + 0xAAAAAAAB, 0x00000000, 33, // 3 + 0x00000001, 0x00000000, 2, // 4 + 0xCCCCCCCD, 0x00000000, 34, // 5 + 0xAAAAAAAB, 0x00000000, 34, // 6 + 0x49249249, 0x49249249, 33, // 7 + 0x00000001, 0x00000000, 3, // 8 + 0x38E38E39, 0x00000000, 33, // 9 + 0xCCCCCCCD, 0x00000000, 35, // 10 + 0xBA2E8BA3, 0x00000000, 35, // 11 + 0xAAAAAAAB, 0x00000000, 35, // 12 + 0x4EC4EC4F, 0x00000000, 34, // 13 + 0x49249249, 0x49249249, 34, // 14 + 0x88888889, 0x00000000, 35, // 15 + 0x00000001, 0x00000000, 4, // 16 + 0xF0F0F0F1, 0x00000000, 36, // 17 + 0x38E38E39, 0x00000000, 34, // 18 + 0xD79435E5, 0xD79435E5, 36, // 19 + 0xCCCCCCCD, 0x00000000, 36, // 20 + 0xC30C30C3, 0xC30C30C3, 36, // 21 + 0xBA2E8BA3, 0x00000000, 36, // 22 + 0xB21642C9, 0x00000000, 36, // 23 + 0xAAAAAAAB, 0x00000000, 36, // 24 + 0x51EB851F, 0x00000000, 35, // 25 + 0x4EC4EC4F, 0x00000000, 35, // 26 + 0x97B425ED, 0x97B425ED, 36, // 27 + 0x49249249, 0x49249249, 35, // 28 + 0x8D3DCB09, 0x00000000, 36, // 29 + 0x88888889, 0x00000000, 36, // 30 + 0x42108421, 0x42108421, 35, // 31 + 0x00000001, 0x00000000, 5, // 32 + 0x3E0F83E1, 0x00000000, 35, // 33 + 0xF0F0F0F1, 0x00000000, 37, // 34 + 0x75075075, 0x75075075, 36, // 35 + 0x38E38E39, 0x00000000, 35, // 36 + 0x6EB3E453, 0x6EB3E453, 36, // 37 + 0xD79435E5, 0xD79435E5, 37, // 38 + 0x69069069, 0x69069069, 36, // 39 + 0xCCCCCCCD, 0x00000000, 37, // 40 + 0xC7CE0C7D, 0x00000000, 37, // 41 + 0xC30C30C3, 0xC30C30C3, 37, // 42 + 0x2FA0BE83, 0x00000000, 35, // 43 + 0xBA2E8BA3, 0x00000000, 37, // 44 + 0x5B05B05B, 0x5B05B05B, 36, // 45 + 0xB21642C9, 0x00000000, 37, // 46 + 0xAE4C415D, 0x00000000, 37, // 47 + 0xAAAAAAAB, 0x00000000, 37, // 48 + 0x5397829D, 0x00000000, 36, // 49 + 0x51EB851F, 0x00000000, 36, // 50 + 0xA0A0A0A1, 0x00000000, 37, // 51 + 0x4EC4EC4F, 0x00000000, 36, // 52 + 0x9A90E7D9, 0x9A90E7D9, 37, // 53 + 0x97B425ED, 0x97B425ED, 37, // 54 + 0x94F2094F, 0x94F2094F, 37, // 55 + 0x49249249, 0x49249249, 36, // 56 + 0x47DC11F7, 0x47DC11F7, 36, // 57 + 0x8D3DCB09, 0x00000000, 37, // 58 + 0x22B63CBF, 0x00000000, 35, // 59 + 0x88888889, 0x00000000, 37, // 60 + 0x4325C53F, 0x00000000, 36, // 61 + 0x42108421, 0x42108421, 36, // 62 + 0x41041041, 0x41041041, 36, // 63 + 0x00000001, 0x00000000, 6, // 64 + 0xFC0FC0FD, 0x00000000, 38, // 65 + 0x3E0F83E1, 0x00000000, 36, // 66 + 0x07A44C6B, 0x00000000, 33, // 67 + 0xF0F0F0F1, 0x00000000, 38, // 68 + 0x76B981DB, 0x00000000, 37, // 69 + 0x75075075, 0x75075075, 37, // 70 + 0xE6C2B449, 0x00000000, 38, // 71 + 0x38E38E39, 0x00000000, 36, // 72 + 0x381C0E07, 0x381C0E07, 36, // 73 + 0x6EB3E453, 0x6EB3E453, 37, // 74 + 0x1B4E81B5, 0x00000000, 35, // 75 + 0xD79435E5, 0xD79435E5, 38, // 76 + 0x3531DEC1, 0x00000000, 36, // 77 + 0x69069069, 0x69069069, 37, // 78 + 0xCF6474A9, 0x00000000, 38, // 79 + 0xCCCCCCCD, 0x00000000, 38, // 80 + 0xCA4587E7, 0x00000000, 38, // 81 + 0xC7CE0C7D, 0x00000000, 38, // 82 + 0x3159721F, 0x00000000, 36, // 83 + 0xC30C30C3, 0xC30C30C3, 38, // 84 + 0xC0C0C0C1, 0x00000000, 38, // 85 + 0x2FA0BE83, 0x00000000, 36, // 86 + 0x2F149903, 0x00000000, 36, // 87 + 0xBA2E8BA3, 0x00000000, 38, // 88 + 0xB81702E1, 0x00000000, 38, // 89 + 0x5B05B05B, 0x5B05B05B, 37, // 90 + 0x2D02D02D, 0x2D02D02D, 36, // 91 + 0xB21642C9, 0x00000000, 38, // 92 + 0xB02C0B03, 0x00000000, 38, // 93 + 0xAE4C415D, 0x00000000, 38, // 94 + 0x2B1DA461, 0x2B1DA461, 36, // 95 + 0xAAAAAAAB, 0x00000000, 38, // 96 + 0xA8E83F57, 0xA8E83F57, 38, // 97 + 0x5397829D, 0x00000000, 37, // 98 + 0xA57EB503, 0x00000000, 38, // 99 + 0x51EB851F, 0x00000000, 37, // 100 + 0xA237C32B, 0xA237C32B, 38, // 101 + 0xA0A0A0A1, 0x00000000, 38, // 102 + 0x9F1165E7, 0x9F1165E7, 38, // 103 + 0x4EC4EC4F, 0x00000000, 37, // 104 + 0x27027027, 0x27027027, 36, // 105 + 0x9A90E7D9, 0x9A90E7D9, 38, // 106 + 0x991F1A51, 0x991F1A51, 38, // 107 + 0x97B425ED, 0x97B425ED, 38, // 108 + 0x2593F69B, 0x2593F69B, 36, // 109 + 0x94F2094F, 0x94F2094F, 38, // 110 + 0x24E6A171, 0x24E6A171, 36, // 111 + 0x49249249, 0x49249249, 37, // 112 + 0x90FDBC09, 0x90FDBC09, 38, // 113 + 0x47DC11F7, 0x47DC11F7, 37, // 114 + 0x8E78356D, 0x8E78356D, 38, // 115 + 0x8D3DCB09, 0x00000000, 38, // 116 + 0x23023023, 0x23023023, 36, // 117 + 0x22B63CBF, 0x00000000, 36, // 118 + 0x44D72045, 0x00000000, 37, // 119 + 0x88888889, 0x00000000, 38, // 120 + 0x8767AB5F, 0x8767AB5F, 38, // 121 + 0x4325C53F, 0x00000000, 37, // 122 + 0x85340853, 0x85340853, 38, // 123 + 0x42108421, 0x42108421, 37, // 124 + 0x10624DD3, 0x00000000, 35, // 125 + 0x41041041, 0x41041041, 37, // 126 + 0x10204081, 0x10204081, 35, // 127 + 0x00000001, 0x00000000, 7, // 128 + 0x0FE03F81, 0x00000000, 35, // 129 + 0xFC0FC0FD, 0x00000000, 39, // 130 + 0xFA232CF3, 0x00000000, 39, // 131 + 0x3E0F83E1, 0x00000000, 37, // 132 + 0xF6603D99, 0x00000000, 39, // 133 + 0x07A44C6B, 0x00000000, 34, // 134 + 0xF2B9D649, 0x00000000, 39, // 135 + 0xF0F0F0F1, 0x00000000, 39, // 136 + 0x077975B9, 0x00000000, 34, // 137 + 0x76B981DB, 0x00000000, 38, // 138 + 0x75DED953, 0x00000000, 38, // 139 + 0x75075075, 0x75075075, 38, // 140 + 0x3A196B1F, 0x00000000, 37, // 141 + 0xE6C2B449, 0x00000000, 39, // 142 + 0xE525982B, 0x00000000, 39, // 143 + 0x38E38E39, 0x00000000, 37, // 144 + 0xE1FC780F, 0x00000000, 39, // 145 + 0x381C0E07, 0x381C0E07, 37, // 146 + 0xDEE95C4D, 0x00000000, 39, // 147 + 0x6EB3E453, 0x6EB3E453, 38, // 148 + 0xDBEB61EF, 0x00000000, 39, // 149 + 0x1B4E81B5, 0x00000000, 36, // 150 + 0x36406C81, 0x00000000, 37, // 151 + 0xD79435E5, 0xD79435E5, 39, // 152 + 0xD62B80D7, 0x00000000, 39, // 153 + 0x3531DEC1, 0x00000000, 37, // 154 + 0xD3680D37, 0x00000000, 39, // 155 + 0x69069069, 0x69069069, 38, // 156 + 0x342DA7F3, 0x00000000, 37, // 157 + 0xCF6474A9, 0x00000000, 39, // 158 + 0xCE168A77, 0xCE168A77, 39, // 159 + 0xCCCCCCCD, 0x00000000, 39, // 160 + 0xCB8727C1, 0x00000000, 39, // 161 + 0xCA4587E7, 0x00000000, 39, // 162 + 0xC907DA4F, 0x00000000, 39, // 163 + 0xC7CE0C7D, 0x00000000, 39, // 164 + 0x634C0635, 0x00000000, 38, // 165 + 0x3159721F, 0x00000000, 37, // 166 + 0x621B97C3, 0x00000000, 38, // 167 + 0xC30C30C3, 0xC30C30C3, 39, // 168 + 0x60F25DEB, 0x00000000, 38, // 169 + 0xC0C0C0C1, 0x00000000, 39, // 170 + 0x17F405FD, 0x17F405FD, 36, // 171 + 0x2FA0BE83, 0x00000000, 37, // 172 + 0xBD691047, 0xBD691047, 39, // 173 + 0x2F149903, 0x00000000, 37, // 174 + 0x5D9F7391, 0x00000000, 38, // 175 + 0xBA2E8BA3, 0x00000000, 39, // 176 + 0x5C90A1FD, 0x5C90A1FD, 38, // 177 + 0xB81702E1, 0x00000000, 39, // 178 + 0x5B87DDAD, 0x5B87DDAD, 38, // 179 + 0x5B05B05B, 0x5B05B05B, 38, // 180 + 0xB509E68B, 0x00000000, 39, // 181 + 0x2D02D02D, 0x2D02D02D, 37, // 182 + 0xB30F6353, 0x00000000, 39, // 183 + 0xB21642C9, 0x00000000, 39, // 184 + 0x1623FA77, 0x1623FA77, 36, // 185 + 0xB02C0B03, 0x00000000, 39, // 186 + 0xAF3ADDC7, 0x00000000, 39, // 187 + 0xAE4C415D, 0x00000000, 39, // 188 + 0x15AC056B, 0x15AC056B, 36, // 189 + 0x2B1DA461, 0x2B1DA461, 37, // 190 + 0xAB8F69E3, 0x00000000, 39, // 191 + 0xAAAAAAAB, 0x00000000, 39, // 192 + 0x15390949, 0x00000000, 36, // 193 + 0xA8E83F57, 0xA8E83F57, 39, // 194 + 0x15015015, 0x15015015, 36, // 195 + 0x5397829D, 0x00000000, 38, // 196 + 0xA655C439, 0xA655C439, 39, // 197 + 0xA57EB503, 0x00000000, 39, // 198 + 0x5254E78F, 0x00000000, 38, // 199 + 0x51EB851F, 0x00000000, 38, // 200 + 0x028C1979, 0x00000000, 33, // 201 + 0xA237C32B, 0xA237C32B, 39, // 202 + 0xA16B312F, 0x00000000, 39, // 203 + 0xA0A0A0A1, 0x00000000, 39, // 204 + 0x4FEC04FF, 0x00000000, 38, // 205 + 0x9F1165E7, 0x9F1165E7, 39, // 206 + 0x27932B49, 0x00000000, 37, // 207 + 0x4EC4EC4F, 0x00000000, 38, // 208 + 0x9CC8E161, 0x00000000, 39, // 209 + 0x27027027, 0x27027027, 37, // 210 + 0x9B4C6F9F, 0x00000000, 39, // 211 + 0x9A90E7D9, 0x9A90E7D9, 39, // 212 + 0x99D722DB, 0x00000000, 39, // 213 + 0x991F1A51, 0x991F1A51, 39, // 214 + 0x4C346405, 0x00000000, 38, // 215 + 0x97B425ED, 0x97B425ED, 39, // 216 + 0x4B809701, 0x4B809701, 38, // 217 + 0x2593F69B, 0x2593F69B, 37, // 218 + 0x12B404AD, 0x12B404AD, 36, // 219 + 0x94F2094F, 0x94F2094F, 39, // 220 + 0x25116025, 0x25116025, 37, // 221 + 0x24E6A171, 0x24E6A171, 37, // 222 + 0x24BC44E1, 0x24BC44E1, 37, // 223 + 0x49249249, 0x49249249, 38, // 224 + 0x91A2B3C5, 0x00000000, 39, // 225 + 0x90FDBC09, 0x90FDBC09, 39, // 226 + 0x905A3863, 0x905A3863, 39, // 227 + 0x47DC11F7, 0x47DC11F7, 38, // 228 + 0x478BBCED, 0x00000000, 38, // 229 + 0x8E78356D, 0x8E78356D, 39, // 230 + 0x46ED2901, 0x46ED2901, 38, // 231 + 0x8D3DCB09, 0x00000000, 39, // 232 + 0x2328A701, 0x2328A701, 37, // 233 + 0x23023023, 0x23023023, 37, // 234 + 0x45B81A25, 0x45B81A25, 38, // 235 + 0x22B63CBF, 0x00000000, 37, // 236 + 0x08A42F87, 0x08A42F87, 35, // 237 + 0x44D72045, 0x00000000, 38, // 238 + 0x891AC73B, 0x00000000, 39, // 239 + 0x88888889, 0x00000000, 39, // 240 + 0x10FEF011, 0x00000000, 36, // 241 + 0x8767AB5F, 0x8767AB5F, 39, // 242 + 0x86D90545, 0x00000000, 39, // 243 + 0x4325C53F, 0x00000000, 38, // 244 + 0x85BF3761, 0x85BF3761, 39, // 245 + 0x85340853, 0x85340853, 39, // 246 + 0x10953F39, 0x10953F39, 36, // 247 + 0x42108421, 0x42108421, 38, // 248 + 0x41CC9829, 0x41CC9829, 38, // 249 + 0x10624DD3, 0x00000000, 36, // 250 + 0x828CBFBF, 0x00000000, 39, // 251 + 0x41041041, 0x41041041, 38, // 252 + 0x81848DA9, 0x00000000, 39, // 253 + 0x10204081, 0x10204081, 36, // 254 + 0x80808081, 0x00000000, 39 // 255 + }; + } +} |
