From 7b56be0f3ac0a0257c10dc7ebe32c1fe95ea6253 Mon Sep 17 00:00:00 2001 From: Niels Martin Hansen Date: Mon, 18 Feb 2019 17:32:53 +0100 Subject: [PATCH] Codechange: Make a k-d tree index of towns --- projects/openttd_vs140.vcxproj | 1 + projects/openttd_vs140.vcxproj.filters | 3 ++ projects/openttd_vs141.vcxproj | 1 + projects/openttd_vs141.vcxproj.filters | 3 ++ projects/openttd_vs142.vcxproj | 1 + projects/openttd_vs142.vcxproj.filters | 3 ++ source.list | 1 + src/misc.cpp | 3 ++ src/saveload/afterload.cpp | 2 + src/saveload/town_sl.cpp | 1 + src/town.h | 3 ++ src/town_cmd.cpp | 69 ++++++++++++-------------- src/town_kdtree.h | 20 ++++++++ 13 files changed, 74 insertions(+), 37 deletions(-) create mode 100644 src/town_kdtree.h diff --git a/projects/openttd_vs140.vcxproj b/projects/openttd_vs140.vcxproj index 7643a693ef..1d53652168 100644 --- a/projects/openttd_vs140.vcxproj +++ b/projects/openttd_vs140.vcxproj @@ -680,6 +680,7 @@ + diff --git a/projects/openttd_vs140.vcxproj.filters b/projects/openttd_vs140.vcxproj.filters index c33036fcc7..b06f9576da 100644 --- a/projects/openttd_vs140.vcxproj.filters +++ b/projects/openttd_vs140.vcxproj.filters @@ -1128,6 +1128,9 @@ Header Files + + Header Files + Header Files diff --git a/projects/openttd_vs141.vcxproj b/projects/openttd_vs141.vcxproj index f7dcc166af..8d2a9dfc9b 100644 --- a/projects/openttd_vs141.vcxproj +++ b/projects/openttd_vs141.vcxproj @@ -680,6 +680,7 @@ + diff --git a/projects/openttd_vs141.vcxproj.filters b/projects/openttd_vs141.vcxproj.filters index c33036fcc7..b06f9576da 100644 --- a/projects/openttd_vs141.vcxproj.filters +++ b/projects/openttd_vs141.vcxproj.filters @@ -1128,6 +1128,9 @@ Header Files + + Header Files + Header Files diff --git a/projects/openttd_vs142.vcxproj b/projects/openttd_vs142.vcxproj index dc132eeb0a..56455c2f81 100644 --- a/projects/openttd_vs142.vcxproj +++ b/projects/openttd_vs142.vcxproj @@ -680,6 +680,7 @@ + diff --git a/projects/openttd_vs142.vcxproj.filters b/projects/openttd_vs142.vcxproj.filters index c33036fcc7..b06f9576da 100644 --- a/projects/openttd_vs142.vcxproj.filters +++ b/projects/openttd_vs142.vcxproj.filters @@ -1128,6 +1128,9 @@ Header Files + + Header Files + Header Files diff --git a/source.list b/source.list index d241e7e857..969d90b9cb 100644 --- a/source.list +++ b/source.list @@ -367,6 +367,7 @@ timetable.h toolbar_gui.h town.h town_type.h +town_kdtree.h townname_func.h townname_type.h track_func.h diff --git a/src/misc.cpp b/src/misc.cpp index 8151f2dd32..c3130d85bd 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -28,6 +28,7 @@ #include "core/pool_type.hpp" #include "game/game.hpp" #include "linkgraph/linkgraphschedule.h" +#include "town_kdtree.h" #include "safeguards.h" @@ -75,6 +76,8 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settin LinkGraphSchedule::Clear(); PoolBase::Clean(PT_NORMAL); + RebuildTownKdtree(); + ResetPersistentNewGRFData(); InitializeSound(); diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 87e870056f..07de260389 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -536,6 +536,8 @@ bool AfterLoadGame() GamelogTestRevision(); GamelogTestMode(); + RebuildTownKdtree(); + if (IsSavegameVersionBefore(SLV_98)) GamelogGRFAddList(_grfconfig); if (IsSavegameVersionBefore(SLV_119)) { diff --git a/src/saveload/town_sl.cpp b/src/saveload/town_sl.cpp index a31c886ec2..bc2ed8be6b 100644 --- a/src/saveload/town_sl.cpp +++ b/src/saveload/town_sl.cpp @@ -28,6 +28,7 @@ void RebuildTownCaches() { Town *town; InitializeBuildingCounts(); + RebuildTownKdtree(); /* Reset town population and num_houses */ FOR_ALL_TOWNS(town) { diff --git a/src/town.h b/src/town.h index 6cff5f7617..76c3b770d4 100644 --- a/src/town.h +++ b/src/town.h @@ -145,6 +145,9 @@ void UpdateAllTownVirtCoords(); void ShowTownViewWindow(TownID town); void ExpandTown(Town *t); +void RebuildTownKdtree(); + + /** * Action types that a company must ask permission for to a town authority. * @see CheckforTownRating diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 4aad76f448..e56cfd4174 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -38,6 +38,7 @@ #include "subsidy_func.h" #include "core/pool_func.hpp" #include "town.h" +#include "town_kdtree.h" #include "townname_func.h" #include "core/random_func.hpp" #include "core/backup_type.hpp" @@ -59,6 +60,20 @@ CargoTypes _town_cargoes_accepted; ///< Bitmap of all cargoes accepted by houses TownPool _town_pool("Town"); INSTANTIATE_POOL_METHODS(Town) + +TownKdtree _town_kdtree(&Kdtree_TownXYFunc); + +void RebuildTownKdtree() +{ + std::vector townids; + Town *town; + FOR_ALL_TOWNS(town) { + townids.push_back(town->index); + } + _town_kdtree.Build(townids.begin(), townids.end()); +} + + /** * Check if a town 'owns' a bridge. * Bridges to not directly have an owner, so we check the tiles adjacent to the bridge ends. @@ -366,30 +381,9 @@ static void AnimateTile_Town(TileIndex tile) */ static bool IsCloseToTown(TileIndex tile, uint dist) { - /* On a large map with many towns, it may be faster to check the surroundings of the tile. - * An iteration in TILE_AREA_LOOP() is generally 2 times faster than one in FOR_ALL_TOWNS(). */ - if (Town::GetNumItems() > (size_t) (dist * dist * 2)) { - const int tx = TileX(tile); - const int ty = TileY(tile); - TileArea tile_area = TileArea( - TileXY(max(0, tx - (int) dist), max(0, ty - (int) dist)), - TileXY(min(MapMaxX(), tx + (int) dist), min(MapMaxY(), ty + (int) dist)) - ); - TILE_AREA_LOOP(atile, tile_area) { - if (GetTileType(atile) == MP_HOUSE) { - Town *t = Town::GetByTile(atile); - if (DistanceManhattan(tile, t->xy) < dist) return true; - } - } - return false; - } - - const Town *t; - - FOR_ALL_TOWNS(t) { - if (DistanceManhattan(tile, t->xy) < dist) return true; - } - return false; + if (_town_kdtree.Count() == 0) return false; + Town *t = Town::Get(_town_kdtree.FindNearest(TileX(tile), TileY(tile))); + return DistanceManhattan(tile, t->xy) < dist; } /** @@ -1682,6 +1676,8 @@ static void DoCreateTown(Town *t, TileIndex tile, uint32 townnameparts, TownSize t->grow_counter = t->index % TOWN_GROWTH_TICKS; t->growth_rate = TownTicksToGameTicks(250); + _town_kdtree.Insert(t->index); + /* Set the default cargo requirement for town growth */ switch (_settings_game.game_creation.landscape) { case LT_ARCTIC: @@ -2092,6 +2088,9 @@ bool GenerateTowns(TownLayout layout) town_names.clear(); + /* Build the town k-d tree again to make sure it's well balanced */ + RebuildTownKdtree(); + if (current_number != 0) return true; /* If current_number is still zero at this point, it means that not a single town has been created. @@ -2867,7 +2866,10 @@ CommandCost CmdDeleteTown(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 } /* The town destructor will delete the other things related to the town. */ - if (flags & DC_EXEC) delete t; + if (flags & DC_EXEC) { + _town_kdtree.Remove(t->index); + delete t; + } return CommandCost(); } @@ -3392,19 +3394,12 @@ CommandCost CheckIfAuthorityAllowsNewStation(TileIndex tile, DoCommandFlag flags */ Town *CalcClosestTownFromTile(TileIndex tile, uint threshold) { - Town *t; - uint best = threshold; - Town *best_town = NULL; + if (Town::GetNumItems() == 0) return NULL; - FOR_ALL_TOWNS(t) { - uint dist = DistanceManhattan(tile, t->xy); - if (dist < best) { - best = dist; - best_town = t; - } - } - - return best_town; + TownID tid = _town_kdtree.FindNearest(TileX(tile), TileY(tile)); + Town *town = Town::Get(tid); + if (DistanceManhattan(tile, town->xy) < threshold) return town; + return NULL; } /** diff --git a/src/town_kdtree.h b/src/town_kdtree.h new file mode 100644 index 0000000000..ae6b9e4eac --- /dev/null +++ b/src/town_kdtree.h @@ -0,0 +1,20 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file town_kdtree.h Declarations for accessing the k-d tree of towns */ + +#ifndef TOWN_KDTREE_H +#define TOWN_KDTREE_H + +#include "core/kdtree.hpp" +#include "town.h" + +inline uint16 Kdtree_TownXYFunc(TownID tid, int dim) { return (dim == 0) ? TileX(Town::Get(tid)->xy) : TileY(Town::Get(tid)->xy); } +typedef Kdtree TownKdtree; +extern TownKdtree _town_kdtree; + +#endif