/// <summary> /// Use <param>chords</param> to construct Bipartite Graph Nodes and create a dictionary that maps Node -> Chords /// (so we can easily get which chords were 'selected' from the Max Independent Set later). /// </summary> /// <param name="chords">List of chords in polygon.</param> /// <returns>A dictionary that maps Bipartite Graph Nodes to Chords.</returns> private static Dictionary <BipartiteGraphNode, Chord> _ConvertChordsToNodes(IReadOnlyList <Chord> chords) { var bipartiteNodeToChords = new Dictionary <BipartiteGraphNode, Chord>(); for (int i = 0; i < chords.Count; i++) { Chord chord = chords[i]; var connectedNodeIDs = new List <int>(); for (int j = 0; j < chords.Count; j++) { Chord comparisonChord = chords[j]; if (j == i || comparisonChord.direction == chord.direction) { continue; } if (GeometryFuncs.DoSegmentsIntersect(chord.a, chord.b, comparisonChord.a, comparisonChord.b)) { //chord B is connected to chord A IFF they intersect, B != A, and they have different orientations connectedNodeIDs.Add(j); } } BipartiteGraphNode.BipartiteSide side = (chord.direction == Chord.Direction.Vertical) ? BipartiteGraphNode.BipartiteSide.Left : BipartiteGraphNode.BipartiteSide.Right; var bipartiteGraphNode = new BipartiteGraphNode(i, connectedNodeIDs, side); bipartiteNodeToChords.Add(bipartiteGraphNode, chord); } return(bipartiteNodeToChords); }
/// <summary> /// Checks if a segment between two vertexes is a valid chord, which relies on the following conditions: /// 0. The segment is not in the chords array already (with/without reversed points) /// 1. The vertices are different /// 2. The segment is vertical or horizontal /// 3. The segment does not CONTAIN a part of the polygon's outer perimeter OR hole perimeter(s). /// 4 WHICH I FORGOT. The segment does not intersect any part of the perimeter /// 5 WHICH I ALSO FORGOT. The segment is actually within the polygon. /// </summary> /// <param name="pointA"></param> /// <param name="pointB"></param> /// <param name="allIsoPerims"></param> /// <param name="chords"></param> /// <returns></returns> private static bool _IsChordValid(Vector2 pointA, Vector2 pointB, List <Vector2>[] allIsoPerims, IEnumerable <Chord> chords) { if (chords.Any(chord => (chord.a == pointA && chord.b == pointB) || (chord.a == pointB && chord.b == pointA))) { //if not already in chords array return(false); } if (pointA == pointB) { return(false); //if vertices are different } if (pointA.x != pointB.x && pointA.y != pointB.y) { return(false); //if the segment is vertical or horizontal } Vector2 midpoint = (pointB - pointA) / 2 + pointA; for (int i = 0; i < allIsoPerims.Length; i++) { List <Vector2> perims = allIsoPerims[i]; if (i == 0) { //midpoint not in poly if (!GeometryFuncs.IsPointInPoly(midpoint, perims.ToArray())) { return(false); } } else { //midpoint in hole if (GeometryFuncs.IsPointInPoly(midpoint, perims.ToArray())) { return(false); } } for (int j = 0; j < perims.Count - 1; j++) //i < perims.Count - 1 because perims[0] = perims[last] { //if segment does not contain a part of the polygon's perimeter(s) Vector2 perimVertexA = perims[j]; Vector2 perimVertexB = perims[j + 1]; if (GeometryFuncs.DoSegmentsOverlap(pointA, pointB, perimVertexA, perimVertexB)) { //segment confirmed to contain part of polygon's perimeter(s) return(false); } if (perimVertexA != pointA && perimVertexA != pointB && perimVertexB != pointA && perimVertexB != pointB) { if (GeometryFuncs.DoSegmentsIntersect(pointA, pointB, perimVertexA, perimVertexB)) { //segment intersects part of perimeter return(false); } } } } return(true); }
/// <summary> /// Given some segment represented by <param>segmentA</param> and <param>segmentB</param> that is not parallel to /// the segment represented by <param>origin</param> and <param>overextension</param> and if necessary, cuts down /// the overextension to the segment IFF the segment cuts origin->overextension. /// If the two input segments do not intersect just returns overextension. /// </summary> /// <param name="origin">Concave vertex being extended.</param> /// <param name="overextension">Extension that should reach outside the polygon.</param> /// <param name="segmentA"></param> /// <param name="segmentB">Segment that is being used to cut the overextension.</param> /// <returns>A coordinate between origin and overextension closer to the origin IFF segment cuts origin->overextension, /// or overextension if the segment does not intersect origin->overextension.</returns> private static Vector2 _CutExtensionWithSegment(Vector2 origin, Vector2 overextension, Vector2 segmentA, Vector2 segmentB) { if (!GeometryFuncs.DoSegmentsIntersect(origin, overextension, segmentA, segmentB)) { return(overextension); } Vector2 cutExtension = overextension; if (origin.x == overextension.x) { //VERTICAL cutExtension.y = segmentA.y; } else if (origin.y == overextension.y) { //HORIZONTAL cutExtension.x = segmentA.x; } return(cutExtension); }