public Line(Vertex v1, Vertex v2) : this() { V1 = v1; V2 = v2; if (Point) { return; } if (!Orthogonal) { throw new ArgumentException("Must be an orthogonal line."); } int x1 = V1.X, x2 = V2.X, y1 = V1.Y, y2 = V2.Y; if (Vertical && y1 > y2) { V1 = new Vertex(x1, y2); V2 = new Vertex(x2, y1); return; } if (!Horizontal || x1 <= x2) { return; } V1 = new Vertex(x2, y1); V2 = new Vertex(x1, y2); }
/// <summary> /// This maps all the polygon "diagonals" between concave vertices. /// </summary> private static HashSet<Line> FindDiagonals(List<Vertex> allConcaves, List<Line> lines) { HashSet<Line> diagonals = new HashSet<Line>(); foreach (Vertex concave in allConcaves) { Vertex reference = concave; List<Vertex> axisNeighbors = allConcaves.Where(cv => cv.X == reference.X || cv.Y == reference.Y).ToList(); axisNeighbors.Remove(reference); var leftNeighbors = axisNeighbors.Where(an => an.X < reference.X).OrderBy(an => an.X); Vertex leftVertex = new Vertex(Int32.MinValue, Int32.MinValue); if (leftNeighbors.Any()) { leftVertex = leftNeighbors.Last(); } Vertex rightVertex = new Vertex(Int32.MinValue, Int32.MinValue); var rightNeighbors = axisNeighbors.Where(an => an.X > reference.X).OrderBy(an => an.X); if (rightNeighbors.Any()) { rightVertex = rightNeighbors.First(); } var topNeighbors = axisNeighbors.Where(an => an.Y > reference.Y).OrderBy(an => an.Y); Vertex topVertex = new Vertex(Int32.MinValue, Int32.MinValue); if (topNeighbors.Any()) { topVertex = topNeighbors.First(); } var bottomNeighbors = axisNeighbors.Where(an => an.Y < reference.Y).OrderBy(an => an.Y); Vertex bottomVertex = new Vertex(Int32.MinValue, Int32.MinValue); if (bottomNeighbors.Any()) { bottomVertex = bottomNeighbors.Last(); } Line testLine; if (TestLineIntersection(reference, leftVertex, out testLine, lines)) { diagonals.Add(testLine); } if (TestLineIntersection(reference, rightVertex, out testLine, lines)) { diagonals.Add(testLine); } if (TestLineIntersection(reference, topVertex, out testLine, lines)) { diagonals.Add(testLine); } if (TestLineIntersection(reference, bottomVertex, out testLine, lines)) { diagonals.Add(testLine); } } return diagonals; }
public bool Intersects(Line l2) { int determinant = A * l2.B - l2.A * B; if (determinant == 0) { return false; } int x1 = ( l2.B * C - B * l2.C ) / determinant; int y1 = ( A * l2.C - l2.A * C ) / determinant; Vertex test = new Vertex(x1, y1); return Contains(test) && l2.Contains(test); }
/// <summary> /// Checks to see if this line contains a <see cref="Vertex"/> /// within it. Since we're dealing with integer coordinate /// orthogonal lines, this is pretty easy. /// </summary> public bool Contains(Vertex v) { int x1 = V1.X, x2 = V2.X, y1 = V1.Y, y2 = V2.Y; int swap; if (x1 > x2) { swap = x1; x1 = x2; x2 = swap; } if (y1 <= y2) { return v.X >= x1 && v.X <= x2 && v.Y >= y1 && v.Y <= y2; } swap = y1; y1 = y2; y2 = swap; return v.X >= x1 && v.X <= x2 && v.Y >= y1 && v.Y <= y2; }
public bool Equals(Vertex other) { return X == other.X && Y == other.Y; }
/// <summary> /// This processes polygons to determine where to slice them in order to find rectangles. /// </summary> private static void ProcessAreas(Dictionary<int, List<Cell>> areas) { Queue<List<Cell>> areasQueue = new Queue<List<Cell>>(); foreach (var pair in areas) { areasQueue.Enqueue(pair.Value); } while (areasQueue.Count > 0) { if (_showStatus) { Console.WriteLine("{0} area{1} to process.", areasQueue.Count, areasQueue.Count == 1 ? "" : "s"); } var newArea = areasQueue.Dequeue(); newArea = ResetArea(newArea); var lines = GetEdgeLines(newArea); HashSet<Vertex> concaves = new HashSet<Vertex>(); foreach (Vertex concaveVertex in newArea.SelectMany(cell => cell.ConcaveVertices)) { concaves.Add(concaveVertex); } List<Vertex> allConcaves = concaves.OrderBy(v => v.Y).ThenBy(v => v.X).ToList(); // We're done with this rectangle, store it. if (allConcaves.Count == 0) { StoreArea(newArea); continue; } // Slice on a diagonal and enqueue the results var diagonals = FindDiagonals(allConcaves, lines); if (diagonals.Count > 0) { int max = diagonals.Max(d => d.Length); Line l = diagonals.First(d => d.Length == max); List<Cell> newCells = newArea.ToList(); Slice(newCells, l); var possibles = MapCellsToAreas(newCells); foreach (var possible in possibles) { if (_showStatus) { Console.WriteLine("Enqueueing...."); } areasQueue.Enqueue(possible.Value); } continue; } // Take a concave, find a valid closest wall, and slice to it. if (allConcaves.Count > 0) { Vertex bad1 = new Vertex(int.MaxValue, int.MaxValue); Vertex bad2 = new Vertex(int.MaxValue - 1, int.MaxValue); Vertex bad3 = new Vertex(int.MaxValue, int.MaxValue - 1); Vertex v = allConcaves.First(); var vLines = lines.Where(l => l.Vertical && !(l.V1==v||l.V2==v)).OrderBy(l => Math.Abs(l.V1.X - v.X)).ToList(); var hLines = lines.Where(l => l.Horizontal && !(l.V1==v||l.V2==v)).OrderBy(l => Math.Abs(l.V1.Y - v.Y)).ToList(); Line hLineTest = new Line(); foreach (var hLine in hLines) { Vertex v2 = new Vertex(v.X, hLine.V1.Y); if (TestLineIntersection(v, v2, out hLineTest, lines)) { hLineTest = new Line(v, v2); break; } hLineTest = new Line(bad1, bad2); } Line vLineTest = new Line(); foreach (Vertex v2 in vLines.Select(vLine => new Vertex(vLine.V1.X, v.Y))) { if (TestLineIntersection(v, v2, out vLineTest, lines)) { vLineTest = new Line(v, v2); break; } vLineTest = new Line(bad1, bad3); } int vLineDist1 = Math.Abs(v.X - vLineTest.V1.X); int vLineDist2 = Math.Abs(v.X - vLineTest.V2.X); int vLineDist = vLineDist1 < vLineDist2 ? vLineDist2 : vLineDist1; int hLineDist1 = Math.Abs(v.Y - hLineTest.V1.Y); int hLineDist2 = Math.Abs(v.Y - hLineTest.V2.Y); int hLineDist = hLineDist1 < hLineDist2 ? hLineDist2 : hLineDist1; Line slicer = vLineDist < hLineDist ? vLineTest : hLineTest; List<Cell> newCells = newArea.ToList(); Slice(newCells, slicer); var possibles = MapCellsToAreas(newCells); foreach (var possible in possibles) { if (_showStatus) { Console.WriteLine("Enqueueing...."); } areasQueue.Enqueue(possible.Value); } } } }
/// <summary> /// Checks to see if a theoretical line between two vertices will intersect with a list of lines. /// </summary> /// <param name="reference">Starting vertex</param> /// <param name="testVertex">Ending vertex</param> /// <param name="test">Line to modify in return</param> /// <param name="lines">List of lines to test</param> /// <returns>True if intersections.</returns> private static bool TestLineIntersection(Vertex reference, Vertex testVertex, out Line test, List<Line> lines) { test = default ( Line ); if (testVertex.X == Int32.MinValue && testVertex.Y == Int32.MinValue) { return false; } Line testing = new Line(reference, testVertex); if (testing.Point) { return false; } // Find lines that may contain the test line List<Line> testOverLines = testing.Horizontal ? lines.Where(li => li.Horizontal && li.V1.Y == testing.V1.Y).ToList() : lines.Where(li => li.Vertical && li.V1.X == testing.V1.X).ToList(); if (testOverLines.Any(tol => tol.Contains(testing))) { return false; } // Test to see if the line contains any edge lines if (testOverLines.Any(testing.Contains)) { return false; } // Find lines that may intersect List<Line> testCrossLines = testing.Horizontal ? lines.Where(li => li.Vertical && li.V1.X > testing.V1.X && li.V1.X < testing.V2.X).ToList() : lines.Where(li => li.Horizontal && li.V1.Y > testing.V1.Y && li.V1.Y < testing.V2.Y).ToList(); // No lines between. We pass. if (testCrossLines.Count == 0) { test = testing; return true; } // Nothing intersects. We pass. if (testCrossLines.Any(tcl => tcl.Intersects(testing))) { return false; } test = testing; return true; }