-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
342 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> | ||
<s:Boolean x:Key="/Default/UserDictionary/Words/=decendant/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
using System.Collections; | ||
|
||
namespace TilesMath.Collections; | ||
|
||
/// <summary> | ||
/// A tile tree set that keeps a collection of tiles using leaf tiles. If all children of a tile are included only the parent is stored. | ||
/// </summary> | ||
public class TileTreeSet : IEnumerable<Tile> | ||
{ | ||
private readonly HashSet<Tile> _tiles = new(); | ||
|
||
/// <summary> | ||
/// Adds the given tile. | ||
/// </summary> | ||
/// <param name="tile">The tile.</param> | ||
/// <returns>True if the tile was added, false if it was already present.</returns> | ||
public bool Add(Tile tile) | ||
{ | ||
while (true) | ||
{ | ||
if (_tiles.Contains(tile)) return false; | ||
|
||
// add the tile. | ||
_tiles.Add(tile); | ||
|
||
// check if this leads to a new 'leaf'. | ||
var parent = tile.Parent; | ||
if (parent == null) | ||
{ | ||
// tile was added and it is now a new leaf. | ||
// this is the top level tile that was added. | ||
return true; | ||
} | ||
var hasAllChildren = parent.Value.Children.All(x => _tiles.Contains(x)); | ||
if (!hasAllChildren) | ||
{ | ||
// tile was added and it is now a new leaf. | ||
return true; | ||
} | ||
else | ||
{ | ||
// remove all the children, the leaf will cover them. | ||
_tiles.ExceptWith(parent.Value.Children); | ||
|
||
// the parent needs to be added. | ||
tile = parent.Value; | ||
} | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Checks if a tile is covered by this tree. | ||
/// </summary> | ||
/// <param name="tile">The tile.</param> | ||
/// <returns>True if the tile is covered, false otherwise.</returns> | ||
public bool Contains(Tile tile) | ||
{ | ||
return this.ContainsInternal(tile) != null; | ||
} | ||
|
||
private Tile? ContainsInternal(Tile tile) | ||
{ | ||
while (true) | ||
{ | ||
if (_tiles.Contains(tile)) return tile; | ||
|
||
// check parent. | ||
var parent = tile.Parent; | ||
if (parent == null) return null; | ||
|
||
tile = parent.Value; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Removes a tile from the set. | ||
/// </summary> | ||
/// <param name="tile">The tile to remove.</param> | ||
/// <returns>True if the tile was removed, false if not.</returns> | ||
public bool Remove(Tile tile) | ||
{ | ||
if (_tiles.Remove(tile)) return true; | ||
|
||
// find the parent that is there. | ||
var parent = this.ContainsInternal(tile); | ||
|
||
// compose blacklist of the entire parent queue. | ||
if (parent == null) return false; // tile is not in this set, no need to remove it. | ||
_tiles.Remove(parent.Value); // we are already sure this tile is not a leaf anymore. | ||
|
||
// add all new leaves one by one. | ||
_tiles.UnionWith(EnumerateTreeExceptAncestors(parent.Value)); | ||
return true; | ||
|
||
IEnumerable<Tile> EnumerateTreeExceptAncestors(Tile p) | ||
{ | ||
foreach (var child in p.Children) | ||
{ | ||
if (child == tile) continue; // the tile itself we do not want to add again. | ||
if (child.IsAncestor(tile)) | ||
{ | ||
// we do not want to add this ancestor again, but perhaps the children. | ||
foreach (var grandChild in EnumerateTreeExceptAncestors(child)) | ||
{ | ||
yield return grandChild; | ||
} | ||
} | ||
else | ||
{ | ||
yield return child; | ||
} | ||
} | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// True if the set is empty. | ||
/// </summary> | ||
public bool IsEmpty => _tiles.Count == 0; | ||
|
||
/// <summary> | ||
/// Gets the inverted set. | ||
/// </summary> | ||
/// <returns></returns> | ||
public TileTreeSet GetInvertedSet() | ||
{ | ||
// we start full and just remove all tiles in this set. | ||
var invertedSet = new TileTreeSet() { Tile.Create(0, 0, 0) }; | ||
foreach (var tile in _tiles) | ||
{ | ||
invertedSet.Remove(tile); | ||
} | ||
|
||
return invertedSet; | ||
} | ||
|
||
public IEnumerator<Tile> GetEnumerator() | ||
{ | ||
return _tiles.GetEnumerator(); | ||
} | ||
|
||
IEnumerator IEnumerable.GetEnumerator() | ||
{ | ||
return this.GetEnumerator(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
namespace TilesMath.Collections; | ||
|
||
public static class TileTreeSetExtensions | ||
{ | ||
/// <summary> | ||
/// Enumerates all the tiles in the set at the given zoom level. | ||
/// </summary> | ||
/// <param name="set">The set.</param> | ||
/// <param name="zoom">The zoom level.</param> | ||
/// <returns>An enumerable with all tiles covered by the set in the given zoom level.</returns> | ||
/// <exception cref="Exception">When tiles are found at a higher zoom level in the set it is not possible to enumerate.</exception> | ||
public static IEnumerable<Tile> ToEnumerableAtZoom(this TileTreeSet set, int zoom) | ||
{ | ||
foreach (var tile in set) | ||
{ | ||
if (tile.Zoom == zoom) | ||
{ | ||
yield return tile; | ||
} | ||
else if (tile.Zoom < zoom) | ||
{ | ||
foreach (var child in tile.ChildrenAtZoom(zoom)) | ||
{ | ||
yield return child; | ||
} | ||
} | ||
else | ||
{ | ||
throw new Exception( | ||
$"Cannot enumerate this set at {zoom}, found at tile at a higher zoom level: {tile.Zoom}"); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
using TilesMath.Collections; | ||
|
||
namespace TilesMath.Tests.Collections; | ||
|
||
public class TileTreeSetTests | ||
{ | ||
[Fact] | ||
public void TileTreeSet_NewSet_ShouldBeEmpty() | ||
{ | ||
var set = new TileTreeSet(); | ||
|
||
Assert.True(set.IsEmpty); | ||
} | ||
|
||
[Fact] | ||
public void TileTreeSet_OneTile_ShouldNotBeEmpty() | ||
{ | ||
var set = new TileTreeSet { Tile.Create(1025, 4511, 14) }; | ||
|
||
Assert.False(set.IsEmpty); | ||
} | ||
|
||
[Fact] | ||
public void TileTreeSet_OneTile_ShouldEnumerateOneTile() | ||
{ | ||
var set = new TileTreeSet { Tile.Create(1025, 4511, 14) }; | ||
|
||
var leaves = set.ToList(); | ||
Assert.Single(leaves); | ||
Assert.Equal(Tile.Create(1025, 4511, 14), leaves[0]); | ||
} | ||
|
||
[Fact] | ||
public void TileTreeSet_AllChildren_OneZoomLower_ShouldEnumerateOneLeaf() | ||
{ | ||
var set = new TileTreeSet(); | ||
var expectedLeaf = Tile.Create(102, 451, 13); | ||
foreach (var tile in expectedLeaf.Children) | ||
{ | ||
set.Add(tile); | ||
} | ||
|
||
var leaves = set.ToList(); | ||
Assert.Single(leaves); | ||
Assert.Equal(expectedLeaf, leaves[0]); | ||
} | ||
|
||
[Fact] | ||
public void TileTreeSet_AllChildren_ThreeZoomsLower_ShouldEnumerateOneLeaf() | ||
{ | ||
var set = new TileTreeSet(); | ||
var expectedLeaf = Tile.Create(2, 4, 4); | ||
foreach (var tile in expectedLeaf.ChildrenAtZoom(7)) | ||
{ | ||
set.Add(tile); | ||
} | ||
|
||
var leaves = set.ToList(); | ||
Assert.Single(leaves); | ||
Assert.Equal(expectedLeaf, leaves[0]); | ||
} | ||
|
||
[Fact] | ||
public void TileTreeSet_SetWithTileZero_ShouldContainAllTiles() | ||
{ | ||
var set = new TileTreeSet { Tile.Create(0, 0, 0) }; | ||
|
||
Assert.True(set.Contains(Tile.Create(2, 4, 4))); | ||
Assert.True(set.Contains(Tile.Create(102, 451, 13))); | ||
Assert.True(set.Contains(Tile.Create(1025, 4511, 14))); | ||
} | ||
|
||
[Fact] | ||
public void TileTreeSet_SetWithTileZero_RemoveChildTile_ShouldContainAllExceptRemovedTile() | ||
{ | ||
var set = new TileTreeSet { Tile.Create(0, 0, 0) }; | ||
|
||
var removedTile = Tile.Create(1, 1, 1); | ||
set.Remove(removedTile); | ||
|
||
Assert.True(set.Contains(Tile.Create(0, 0, 1))); | ||
Assert.True(set.Contains(Tile.Create(1, 0, 1))); | ||
Assert.True(set.Contains(Tile.Create(0, 1, 1))); | ||
Assert.False(set.Contains(removedTile)); | ||
} | ||
|
||
[Fact] | ||
public void TileTreeSet_SetWithTileZero_RemoveGranChildTile_ShouldContainAllExceptRemovedTile() | ||
{ | ||
var set = new TileTreeSet { Tile.Create(0, 0, 0) }; | ||
|
||
var removedTile = Tile.Create(2, 2, 2); | ||
set.Remove(removedTile); | ||
|
||
Assert.True(set.Contains(Tile.Create(0, 0, 1))); | ||
Assert.True(set.Contains(Tile.Create(1, 0, 1))); | ||
Assert.True(set.Contains(Tile.Create(0, 1, 1))); | ||
foreach (var leaf in removedTile.Parent.Value.Children.Where(x => x != removedTile)) | ||
Check warning on line 98 in test/TilesMath.Tests/Collections/TileTreeSetTests.cs GitHub Actions / build
|
||
{ | ||
Assert.True(set.Contains(leaf)); | ||
} | ||
Assert.False(set.Contains(removedTile)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
namespace TilesMath.Tests; | ||
|
||
public class TileExtensionTests | ||
{ | ||
[Fact] | ||
public void Tile_IsAncestor_WhenParent_ShouldBeTrue() | ||
{ | ||
var tile = Tile.Create(1025, 4511, 14); | ||
Assert.True(tile.Parent != null && tile.Parent.Value.IsAncestor(tile)); | ||
} | ||
|
||
[Fact] | ||
public void Tile_IsAncestor_WhenGranParent_ShouldBeTrue() | ||
{ | ||
var tile = Tile.Create(1025, 4511, 14); | ||
Assert.True(tile.Parent?.Parent is not null && tile.Parent.Value.Parent.Value.IsAncestor(tile)); | ||
} | ||
} |
Oops, something went wrong.