/// <summary> /// Two cells overlap if their bounding rectangle overlap by more than 50% of their area. /// </summary> /// <param name="rectangle1">The bounding rectangle of the first cell</param> /// <param name="rectangle2">The bounding rectangle of the second cell</param> /// <returns>Returns true if either rectangle overlaps the other by more than 50% of its surface.</returns> protected override bool Overlap(Rectangle rectangle1, Rectangle rectangle2) { // The rationale is that we want to provide a rectangular view over // cells of various shape, that might be embedded in a same square. Decimal overlapArea = rectangle1.Overlap(rectangle2); return Math.Max((overlapArea / rectangle1.Area()), (overlapArea / rectangle2.Area())) >= (decimal)0.5; }
/// <summary> /// Determines the area change from the first rectangle to the second. /// </summary> /// <param name="r1">The first rectangle.</param> /// <param name="r2">The second rectangle.</param> /// <returns>A percentage indicating area change.</returns> public static decimal AreaChangeAsPercent(Rectangle r1, Rectangle r2) { var minWidth = Math.Min(r1.Width, r2.Width); var minHeight = Math.Min(r1.Height, r2.Height); var maxWidth = Math.Max(r1.Width, r2.Width); var maxHeight = Math.Max(r1.Height, r2.Height); var startingArea = r1.Area(); long areaDelta = 0; if (r1.Width != r2.Width) areaDelta += ((maxWidth - minWidth) * minHeight); if (r1.Height != r2.Height) areaDelta += ((maxHeight - minHeight) * minWidth); if ((r1.Height > r2.Height && r1.Width > r2.Width) || (r2.Height > r1.Height && r2.Width > r1.Width)) areaDelta += ((maxHeight - minHeight) * (maxWidth - minWidth)); return areaDelta == 0 ? 0 : ((decimal)areaDelta / startingArea) * 100; }
/// <summary> /// Check whether a region in an image matches an expected color. /// Does a simplistic average on each color channel and includes and inbuilt tolerance parameter. /// A repeating pattern can fool this test. /// </summary> /// <example> /// ImageAssert.ImageRegionIsColor(new Rectangle(10, 20, 100, 200), Color.FromArgb(41, 42, 31), actualImage, 1.0); /// </example> /// <param name="region">The rectangle within the image to test.</param> /// <param name="expectedColor">The single color you expect the region to have</param> /// <param name="actualImage">The image to test</param> /// <param name="tolerance">The tolerance allowed for each color channel</param> public static void ImageRegionIsColor(this Assert assert, System.Drawing.Rectangle region, System.Drawing.Color expectedColor, Bitmap actualImage, double tolerance = 0.0) { var width = region.Width; var area = region.Area(); var red = new int[area]; var green = new int[area]; var blue = new int[area]; var indices = new HashSet <int>(); for (var x = region.Left; x < region.Right; x++) { for (var y = region.Top; y < region.Bottom; y++) { var color = actualImage.GetPixel(x, y); var i = x - region.Left; var j = y - region.Top; var index0 = (i * region.Height) + j; if (indices.Contains(index0)) { Debugger.Break(); } indices.Add(index0); red [index0] = color.R; green[index0] = color.G; blue [index0] = color.B; } } var averageRed = red.Average(); var averageBlue = blue.Average(); var averageGreen = green.Average(); Assert.IsTrue( Math.Abs(averageRed - expectedColor.R) <= tolerance && Math.Abs(averageGreen - expectedColor.G) <= tolerance && Math.Abs(averageBlue - expectedColor.B) <= tolerance, $"Region {region} is not expected color {expectedColor} - actual averages: R={averageRed:F20}, G={averageGreen:F20}, B={averageBlue:F20}"); }
/// <summary> /// Returns the difference between two rectangles as a string description. /// </summary> /// <param name="r1">The first rectangle.</param> /// <param name="r2">The second rectangle.</param> /// <param name="percentageChange">The change as a percentage.</param> /// <returns>A string description.</returns> private static string GetLocationChanges(Rectangle r1, Rectangle r2, out decimal percentageChange) { var sb = new System.Text.StringBuilder(); if (r1.Width != r2.Width) { sb.AppendLine(string.Format("Element width {0} by {1} pixels", r1.Width > r2.Width ? "decreased" : "increased", Math.Abs(r1.Width - r2.Width))); } if (r1.Height != r2.Height) { sb.AppendLine(string.Format("Element height {0} by {1} pixels", r1.Height > r2.Height ? "decreased" : "increased", Math.Abs(r1.Height - r2.Height))); } if (r1.X != r2.X) { sb.AppendLine(string.Format("Element moved {0} {1} pixels", r1.X > r2.X ? "left" : "right", Math.Abs(r1.X - r2.X))); } if (r1.Y != r2.Y) { sb.AppendLine(string.Format("Element moved {0} {1} pixels", r1.Y > r2.Y ? "up" : "down", Math.Abs(r1.Y - r2.Y))); } var commonArea = Rectangle.Intersect(r1, r2).Area(); percentageChange = commonArea > 0 ? (commonArea / (decimal)r1.Area()) * 100 : 100; return sb.ToString(); }
/// <summary> /// Calculates the area difference between two rectangles. /// </summary> /// <param name="r1">The first rectangle.</param> /// <param name="r2">The second rectangle.</param> /// <returns>A long value.</returns> public static long AreaDifferenceBetweenRectangles(Rectangle r1, Rectangle r2) { return Math.Abs(r1.Area() - r2.Area()); }