/// <summary> /// <para>Checks if a sector is mergeable with other assuming that the other is positioned in at positive direction ( right, bottom or both ).</para> /// <para>Therefore, if a sector if mergeable with other, according to this method the same sectors inverted will not be mergeables.</para> /// </summary> /// <typeparam name="T">Type of the quadtree matrix values.</typeparam> /// <param name="original">Original sector.</param> /// <param name="other">Other sector.</param> /// <param name="get">Getter of the quadtree matrix values.</param> /// <param name="equals">Comparator of quadtree matrix elements. Must return true if a value is compared with himself.</param> /// <returns>If the original sector is mergeable with other assuming that the other is positioned in at positive direction ( right, bottom or both ).</returns> public static bool AreMergeableNotInvertible <T>(QuadTreeSector original, QuadTreeSector other, Func <int, int, T> get, Func <T, T, bool> equals) { bool notContained = original.Initial != other.Initial && original.Final != other.Final && original.Initial != other.Final && original.Final != other.Initial; bool horitzontalMatch = original.Final.x == other.Initial.x - 1 && original.Final.y == other.Final.y && original.Initial.y == other.Initial.y; bool verticalMatch = original.Final.y == other.Final.y - 1 && original.Final.x == other.Final.x && original.Initial.x == other.Initial.x; bool equalsValues = equals(get(other.Initial.x, other.Initial.y), get(original.Initial.x, original.Initial.y)); return(notContained && (horitzontalMatch || verticalMatch) && equalsValues); }
/// <summary> /// Checks a quadtree sector. Test if the sector is equal and calls MathFunctions.QuadTree if it isn't. /// </summary> /// <typeparam name="T">Type of the matrix values.</typeparam> /// <param name="sector">Sector to check.</param> /// <param name="get">Getter of the matrix values.</param> /// <param name="equals">Comparator of matrix elements. Must return true if a value is compared with himself.</param> /// <param name="mergeSectors">If the sectors will be merged. Used to pass it to MathFunctions.QuadTree if is called.</param> /// <returns>List of the obtained sectors.</returns> private static List <QuadTreeSector> QuadTreeSectorCheck <T>(QuadTreeSector sector, Func <int, int, T> get, Func <T, T, bool> equals, bool mergeSectors) { List <QuadTreeSector> result = new List <QuadTreeSector>(); if (IsEqualQuadTreeSector(sector, get, equals)) { result.Add(sector); } else { result.AddRange(SplitAndMerge(get, equals, sector, mergeSectors)); } return(result); }
/// <summary> /// Check if a quadtree sector is completly equal. For that the first position is compared with all the other positions. /// </summary> /// <typeparam name="T">Type of the matrix values.</typeparam> /// <param name="sector">Sector to check.</param> /// <param name="get">Getter of the matrix values.</param> /// <param name="equals">Comparator of matrix elements. Must return true if a value is compared with himself.</param> /// <returns>If the sector is completely equal.</returns> private static bool IsEqualQuadTreeSector <T>(QuadTreeSector sector, Func <int, int, T> get, Func <T, T, bool> equals) { bool equalGroup = true; T lastElement = get(sector.Initial.x, sector.Initial.y); T currentElement; for (int col = sector.Initial.x; col <= sector.Final.x && equalGroup; col++) { for (int row = sector.Initial.y; row <= sector.Final.y && equalGroup; row++) { currentElement = get(col, row); equalGroup &= equals(lastElement, currentElement); } } return(equalGroup); }
/// <summary> /// Tries a merge between the current sector and other. If the merge is not possible, throws a Exception. /// </summary> /// <typeparam name="T">Type of the quadtree matrix values.</typeparam> /// <param name="other">Other sector.</param> /// <param name="get">Getter of the quadtree matrix values.</param> /// <param name="equals">Comparator of quadtree matrix elements. Must return true if a value is compared with himself.</param> /// <returns>The merged sector if is possible. Else, throws a exception.</returns> public QuadTreeSector TryMerge <T>(QuadTreeSector other, Func <int, int, T> get, Func <T, T, bool> equals) { QuadTreeSector result; if (AreMergeableNotInvertible(this, other, get, equals)) { result = new QuadTreeSector(this.Initial, other.Final); } else if (AreMergeableNotInvertible(other, this, get, equals)) { result = new QuadTreeSector(other.Initial, this.Final); } else { throw new Exception($"Impossible merge sector {this} with {other}"); } return(result); }
/// <summary> /// Merge a list of quadtree sectors with other list of sectors of a positive position ( more to the right or down ). /// </summary> /// <typeparam name="T">Type of the matrix values.</typeparam> /// <param name="baseSectors">Original list of sectors. Will be modified adding new sectors ( merged or not ).</param> /// <param name="greatersSectors">Sectors to try to merge or, instead, add to baseSectors.</param> /// <param name="get">Getter of the matrix values.</param> /// <param name="equals">Comparator of matrix elements. Must return true if a value is compared with himself.</param> private static void MergeQuadTreeSectorsPositive <T>(List <QuadTreeSector> baseSectors, List <QuadTreeSector> greatersSectors, Func <int, int, T> get, Func <T, T, bool> equals) { QuadTreeSector baseSector; bool isMerged; foreach (QuadTreeSector greaterSector in greatersSectors) { isMerged = false; for (int j = 0; j < baseSectors.Count && !isMerged; j++) { baseSector = baseSectors[j]; if (QuadTreeSector.AreMergeableNotInvertible(baseSector, greaterSector, get, equals)) { baseSectors[j] = new QuadTreeSector(baseSector.Initial, greaterSector.Final); isMerged = true; } } if (!isMerged) { baseSectors.Add(greaterSector); } } }
/// <summary> /// <para>Calculates the quadtree sectors of a square matrix sector with a power of two dimensions ( columns and rows ).</para> /// <para>Is recursive.</para> /// </summary> /// <typeparam name="T">Type of the matrix values.</typeparam> /// <param name="get">Getter of the matrix values.</param> /// <param name="equals">Comparator of matrix elements. Must return true if a value is compared with himself.</param> /// <param name="sector">Sector to apply the quadtree algorithm.</param> /// <param name="mergeSectors">If is true : some of the mergeable sectors ( equals and have a common size ) will be merged. Implies an additional cost but fewer sectors will be generated.</param> /// <returns>List of the obtained sectors.</returns> public static List <QuadTreeSector> SplitAndMerge <T>(Func <int, int, T> get, Func <T, T, bool> equals, QuadTreeSector sector, bool mergeSectors) { List <QuadTreeSector> result = new List <QuadTreeSector>(); List <QuadTreeSector> currentSectors = new List <QuadTreeSector>(); int firstCol = sector.Initial.x; int firstRow = sector.Initial.y; Vector2Int sectorSize = sector.Size; // If not square sector if (sectorSize.x != sectorSize.y) { throw new Exception($"ERROR QuadTree : Sector [ {sector} ] isn't a square"); } else if (!IsPowerOfTwo(sectorSize.x)) { throw new Exception($"ERROR QuadTree : Sector size [ {sectorSize.x} ] isn't power of two"); } // If only one tile else if (sectorSize.x == 1) { result.Add(sector); } // Else, check the four sectors else { int midSectorColumns = (sectorSize.x - 1) / 2; int midSectorRows = (sectorSize.y - 1) / 2; int midCol = firstCol + midSectorColumns; int midRow = firstRow + midSectorRows; int finalCol = sector.Final.x; int finalRow = sector.Final.y; // Top-left result.AddRange(QuadTreeSectorCheck(new QuadTreeSector(firstCol, firstRow, midCol, midRow), get, equals, mergeSectors)); // Top-right currentSectors = QuadTreeSectorCheck(new QuadTreeSector(midCol + 1, firstRow, finalCol, midRow), get, equals, mergeSectors); if (mergeSectors) { MergeQuadTreeSectorsPositive(result, currentSectors, get, equals); } else { result.AddRange(currentSectors); } // Bottom-left currentSectors = QuadTreeSectorCheck(new QuadTreeSector(firstCol, midRow + 1, midCol, finalRow), get, equals, mergeSectors); if (mergeSectors) { MergeQuadTreeSectorsPositive(result, currentSectors, get, equals); } else { result.AddRange(currentSectors); } // Bottom-right ( not mergeable with Top-left ) currentSectors = QuadTreeSectorCheck(new QuadTreeSector(midCol + 1, midRow + 1, finalCol, finalRow), get, equals, mergeSectors); if (mergeSectors) { MergeQuadTreeSectorsPositive(result, currentSectors, get, equals); } else { result.AddRange(currentSectors); } } return(result); }