← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~widelands-dev/widelands/overlapping_workareas into lp:widelands

 

Benedikt Straub has proposed merging lp:~widelands-dev/widelands/overlapping_workareas into lp:widelands.

Commit message:
Indicate overlapping workareas when placing buildings

Requested reviews:
  Widelands Developers (widelands-dev)

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/overlapping_workareas/+merge/366623

When placing a productionsite, only own conflicting productionsites and constructionsites for productionsites will be indicated.
When placing a militarysite or port, own and enemy conflicting milsites and ports and constructionsites for milsites and ports will be indicated.

Some restructuring and efficiency tweaks to workarea drawing code to improve performance.
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/overlapping_workareas into lp:widelands.
=== modified file 'src/graphic/color.cc'
--- src/graphic/color.cc	2019-02-23 11:00:49 +0000
+++ src/graphic/color.cc	2019-04-28 16:58:57 +0000
@@ -59,6 +59,13 @@
 	a = 255;
 }
 
+RGBAColor::RGBAColor(uint32_t hex)
+	: r((hex & 0xff0000) >> 16),
+	  g((hex & 0xff00) >> 8),
+	  b((hex & 0xff)),
+	  a((hex & 0xff000000) >> 24) {
+}
+
 std::string RGBAColor::hex_value() const {
 	return (boost::format("%02x%02x%02x%02x>") % int(r) % int(g) % int(b) % int(a)).str();
 }

=== modified file 'src/graphic/color.h'
--- src/graphic/color.h	2019-02-23 11:00:49 +0000
+++ src/graphic/color.h	2019-04-28 16:58:57 +0000
@@ -49,6 +49,7 @@
 
 struct RGBAColor {
 	RGBAColor(uint8_t R, uint8_t G, uint8_t B, uint8_t A);
+	RGBAColor(uint32_t);
 	RGBAColor(const RGBAColor& other) = default;
 
 	// Initializes the color to black.

=== modified file 'src/graphic/gl/workarea_program.cc'
--- src/graphic/gl/workarea_program.cc	2019-04-25 21:48:17 +0000
+++ src/graphic/gl/workarea_program.cc	2019-04-28 16:58:57 +0000
@@ -65,12 +65,21 @@
       RGBAColor(0, 0, 127, kWorkareaTransparency),    // Inner circle
    };
 static inline RGBAColor apply_color(RGBAColor c1, RGBAColor c2) {
+	if (c1.a == 0 && c2.a == 0) {
+		return RGBAColor((c1.r + c2.r) / 2, (c1.g + c2.g) / 2, (c1.b + c2.b) / 2, 0);
+	}
 	uint8_t r = (c1.r * c1.a + c2.r * c2.a) / (c1.a + c2.a);
 	uint8_t g = (c1.g * c1.a + c2.g * c2.a) / (c1.a + c2.a);
 	uint8_t b = (c1.b * c1.a + c2.b * c2.a) / (c1.a + c2.a);
 	uint8_t a = (c1.a + c2.a) / 2;
 	return RGBAColor(r, g, b, a);
 }
+static inline RGBAColor apply_color_special(RGBAColor base, RGBAColor special) {
+	uint8_t r = (base.r * base.a + special.r * 255) / (base.a + 255);
+	uint8_t g = (base.g * base.a + special.g * 255) / (base.a + 255);
+	uint8_t b = (base.b * base.a + special.b * 255) / (base.a + 255);
+	return RGBAColor(r, g, b, special.a);
+}
 
 void WorkareaProgram::add_vertex(const FieldsToDraw::Field& field, RGBAColor overlay) {
 	vertices_.emplace_back();
@@ -104,31 +113,43 @@
 		// Down triangle.
 		if (field.bln_index != FieldsToDraw::kInvalidIndex) {
 			RGBAColor color(0, 0, 0, 0);
-			for (const std::map<Widelands::TCoords<>, uint8_t>& wa_map : workarea) {
-				const auto it =
-				   wa_map.find(Widelands::TCoords<>(field.fcoords, Widelands::TriangleIndex::D));
-				if (it != wa_map.end()) {
-					color = apply_color(color, workarea_colors[it->second]);
+			for (const WorkareasEntry& wa_map : workarea) {
+				for (const WorkareaPreviewData& data : wa_map) {
+					if (data.coords == Widelands::TCoords<>(field.fcoords, Widelands::TriangleIndex::D)) {
+						RGBAColor color_to_apply = workarea_colors[data.index];
+						if (data.use_special_coloring) {
+							color_to_apply = apply_color_special(color_to_apply, RGBAColor(data.special_coloring));
+						}
+						color = apply_color(color, color_to_apply);
+					}
 				}
 			}
-			add_vertex(fields_to_draw.at(current_index), color);
-			add_vertex(fields_to_draw.at(field.bln_index), color);
-			add_vertex(fields_to_draw.at(field.brn_index), color);
+			if (color.a > 0) {
+				add_vertex(fields_to_draw.at(current_index), color);
+				add_vertex(fields_to_draw.at(field.bln_index), color);
+				add_vertex(fields_to_draw.at(field.brn_index), color);
+			}
 		}
 
 		// Right triangle.
 		if (field.rn_index != FieldsToDraw::kInvalidIndex) {
 			RGBAColor color(0, 0, 0, 0);
-			for (const std::map<Widelands::TCoords<>, uint8_t>& wa_map : workarea) {
-				const auto it =
-				   wa_map.find(Widelands::TCoords<>(field.fcoords, Widelands::TriangleIndex::R));
-				if (it != wa_map.end()) {
-					color = apply_color(color, workarea_colors[it->second]);
+			for (const WorkareasEntry& wa_map : workarea) {
+				for (const WorkareaPreviewData& data : wa_map) {
+					if (data.coords == Widelands::TCoords<>(field.fcoords, Widelands::TriangleIndex::R)) {
+						RGBAColor color_to_apply = workarea_colors[data.index];
+						if (data.use_special_coloring) {
+							color_to_apply = apply_color_special(color_to_apply, RGBAColor(data.special_coloring));
+						}
+						color = apply_color(color, color_to_apply);
+					}
 				}
 			}
-			add_vertex(fields_to_draw.at(current_index), color);
-			add_vertex(fields_to_draw.at(field.brn_index), color);
-			add_vertex(fields_to_draw.at(field.rn_index), color);
+			if (color.a > 0) {
+				add_vertex(fields_to_draw.at(current_index), color);
+				add_vertex(fields_to_draw.at(field.brn_index), color);
+				add_vertex(fields_to_draw.at(field.rn_index), color);
+			}
 		}
 	}
 

=== modified file 'src/logic/map.cc'
--- src/logic/map.cc	2019-04-26 05:33:44 +0000
+++ src/logic/map.cc	2019-04-28 16:58:57 +0000
@@ -2387,4 +2387,30 @@
 	return influence;
 }
 
+std::set<Coords> Map::to_set(Area<Coords> area) const {
+	std::set<Coords> result;
+	MapRegion<Area<Coords>> mr(*this, area);
+	do {
+		result.insert(mr.location());
+	} while (mr.advance(*this));
+	return result;
+}
+
+// Returns all triangles whose corners are all in the given area
+std::set<TCoords<Coords>> Map::triangles_in_region(std::set<Coords> area) const {
+	std::set<TCoords<Coords>> result;
+	for (const Coords& c : area) {
+		if (!area.count(br_n(c))) {
+			continue;
+		}
+		if (area.count(r_n(c))) {
+			result.insert(TCoords<Coords>(c, TriangleIndex::R));
+		}
+		if (area.count(bl_n(c))) {
+			result.insert(TCoords<Coords>(c, TriangleIndex::D));
+		}
+	}
+	return result;
+}
+
 }  // namespace Widelands

=== modified file 'src/logic/map.h'
--- src/logic/map.h	2019-04-24 07:09:29 +0000
+++ src/logic/map.h	2019-04-28 16:58:57 +0000
@@ -333,6 +333,7 @@
 
 	// Field logic
 	static MapIndex get_index(const Coords&, int16_t width);
+	MapIndex get_index(const Coords&) const;
 	MapIndex max_index() const {
 		return width_ * height_;
 	}
@@ -382,6 +383,9 @@
 	void get_neighbour(const FCoords&, Direction dir, FCoords*) const;
 	FCoords get_neighbour(const FCoords&, Direction dir) const;
 
+	std::set<Coords> to_set(Area<Coords> area) const;
+	std::set<TCoords<Coords>> triangles_in_region(std::set<Coords> area) const;
+
 	// Pathfinding
 	int32_t findpath(Coords instart,
 	                 Coords inend,
@@ -593,6 +597,10 @@
 	return c.y * width + c.x;
 }
 
+inline MapIndex Map::get_index(const Coords& c) const {
+	return get_index(c, width_);
+}
+
 inline Field& Map::operator[](MapIndex const i) const {
 	return fields_[i];
 }

=== modified file 'src/logic/map_objects/tribes/tribes.cc'
--- src/logic/map_objects/tribes/tribes.cc	2019-02-23 11:00:49 +0000
+++ src/logic/map_objects/tribes/tribes.cc	2019-04-28 16:58:57 +0000
@@ -425,4 +425,13 @@
 	}
 }
 
+uint32_t Tribes::get_largest_workarea() const {
+	uint32_t largest = 0;
+	for (DescriptionIndex di = 0; di < buildings_->size(); ++di) {
+		for (const auto& pair : buildings_->get(di).workarea_info()) {
+			largest = std::max(largest, pair.first);
+		}
+	}
+	return largest;
+}
 }  // namespace Widelands

=== modified file 'src/logic/map_objects/tribes/tribes.h'
--- src/logic/map_objects/tribes/tribes.h	2019-02-23 11:00:49 +0000
+++ src/logic/map_objects/tribes/tribes.h	2019-04-28 16:58:57 +0000
@@ -143,6 +143,8 @@
 	/// Complete the Description objects' information with data from other Description objects.
 	void postload();
 
+	uint32_t get_largest_workarea() const;
+
 private:
 	void postload_calculate_trainingsites_proportions();
 

=== modified file 'src/logic/widelands_geometry.h'
--- src/logic/widelands_geometry.h	2019-03-11 14:45:04 +0000
+++ src/logic/widelands_geometry.h	2019-04-28 16:58:57 +0000
@@ -21,9 +21,8 @@
 #define WL_LOGIC_WIDELANDS_GEOMETRY_H
 
 #include <cmath>
-#include <map>
-#include <set>
 #include <tuple>
+#include <vector>
 
 #include <stdint.h>
 
@@ -157,6 +156,25 @@
 };
 }  // namespace Widelands
 
-using Workareas = std::set<std::map<Widelands::TCoords<>, uint8_t>>;
+struct WorkareaPreviewData {
+	WorkareaPreviewData(Widelands::TCoords<> c, uint8_t i)
+		: coords(c), index(i), use_special_coloring(false), special_coloring(0) {
+	}
+	WorkareaPreviewData(Widelands::TCoords<> c, uint8_t i, uint32_t col)
+		: coords(c), index(i), use_special_coloring(true), special_coloring(col) {
+	}
+	WorkareaPreviewData()
+		: coords(Widelands::TCoords<>(Widelands::Coords::null(), Widelands::TriangleIndex::D)),
+		  index(0), use_special_coloring(false), special_coloring(0) {
+	}
+	WorkareaPreviewData& operator=(const WorkareaPreviewData&) = default;
+
+	Widelands::TCoords<> coords;
+	uint8_t index;
+	bool use_special_coloring;
+	uint32_t special_coloring;
+};
+using WorkareasEntry = std::vector<WorkareaPreviewData>;
+using Workareas = std::vector<WorkareasEntry>;
 
 #endif  // end of include guard: WL_LOGIC_WIDELANDS_GEOMETRY_H

=== modified file 'src/wui/actionconfirm.cc'
--- src/wui/actionconfirm.cc	2019-02-23 11:00:49 +0000
+++ src/wui/actionconfirm.cc	2019-04-28 16:58:57 +0000
@@ -246,7 +246,7 @@
 	if (building && iaplayer().can_act(building->owner().player_number()) &&
 	    (building->get_playercaps() & Widelands::Building::PCap_Dismantle)) {
 		game.send_player_dismantle(*todismantle);
-		iaplayer().hide_workarea(building->get_position());
+		iaplayer().hide_workarea(building->get_position(), false);
 	}
 
 	die();

=== modified file 'src/wui/buildingwindow.cc'
--- src/wui/buildingwindow.cc	2019-03-25 15:39:52 +0000
+++ src/wui/buildingwindow.cc	2019-04-28 16:58:57 +0000
@@ -512,7 +512,7 @@
 		return;  // already hidden, nothing to be done
 	}
 
-	igbase()->hide_workarea(building_position_);
+	igbase()->hide_workarea(building_position_, false);
 	showing_workarea_ = false;
 	if (configure_button) {
 		configure_workarea_button();

=== modified file 'src/wui/fieldaction.cc'
--- src/wui/fieldaction.cc	2019-02-23 11:00:49 +0000
+++ src/wui/fieldaction.cc	2019-04-28 16:58:57 +0000
@@ -193,6 +193,8 @@
 	                       bool repeating = false);
 	void reset_mouse_and_die();
 
+	void clear_overlapping_workareas();
+
 	Widelands::Player* player_;
 	const Widelands::Map& map_;
 
@@ -202,6 +204,7 @@
 	bool fastclick_;  // if true, put the mouse over first button in first tab
 	uint32_t best_tab_;
 	bool showing_workarea_preview_;
+	std::set<Widelands::Coords> overlapping_workareas_;
 
 	/// Variables to use with attack dialog.
 	AttackBox* attack_box_;
@@ -256,8 +259,10 @@
 }
 
 FieldActionWindow::~FieldActionWindow() {
-	if (showing_workarea_preview_)
-		ibase().hide_workarea(node_);
+	if (showing_workarea_preview_) {
+		ibase().hide_workarea(node_, false);
+	}
+	clear_overlapping_workareas();
 	ibase().set_sel_freeze(false);
 	delete attack_box_;
 }
@@ -268,6 +273,13 @@
 		die();
 }
 
+void FieldActionWindow::clear_overlapping_workareas() {
+	for (const Widelands::Coords& c : overlapping_workareas_) {
+		ibase().hide_workarea(c, true);
+	}
+	overlapping_workareas_.clear();
+}
+
 /*
 ===============
 Initialize after buttons have been registered.
@@ -683,17 +695,99 @@
 
 void FieldActionWindow::building_icon_mouse_out(Widelands::DescriptionIndex) {
 	if (showing_workarea_preview_) {
-		ibase().hide_workarea(node_);
+		ibase().hide_workarea(node_, false);
+		clear_overlapping_workareas();
 		showing_workarea_preview_ = false;
 	}
 }
 
 void FieldActionWindow::building_icon_mouse_in(const Widelands::DescriptionIndex idx) {
 	if (!showing_workarea_preview_) {
-		const WorkareaInfo& workarea_info =
-		   player_->tribe().get_building_descr(Widelands::DescriptionIndex(idx))->workarea_info();
+		assert(overlapping_workareas_.empty());
+		const Widelands::BuildingDescr& descr = *player_->tribe().get_building_descr(idx);
+		const WorkareaInfo& workarea_info = descr.workarea_info();
 		ibase().show_workarea(workarea_info, node_);
 		showing_workarea_preview_ = true;
+
+		const Widelands::Map& map = ibase().egbase().map();
+		uint32_t workarea_radius = 0;
+		for (const auto& pair : workarea_info) {
+			workarea_radius = std::max(workarea_radius, pair.first);
+		}
+		if (workarea_radius == 0) {
+			return;
+		}
+		std::set<Widelands::TCoords<>> main_region = map.triangles_in_region(
+				map.to_set(Widelands::Area<>(node_, workarea_radius)));
+
+		Widelands::MapRegion<Widelands::Area<Widelands::FCoords>> mr(map, Widelands::Area<Widelands::FCoords>(
+				node_, workarea_radius + ibase().egbase().tribes().get_largest_workarea()));
+		do {
+			if (player_->vision(map.get_index(mr.location())) > 1) {
+				if (Widelands::BaseImmovable* imm = mr.location().field->get_immovable()) {
+					const Widelands::MapObjectType imm_type = imm->descr().type();
+					if (imm_type < Widelands::MapObjectType::BUILDING) {
+						// We are not interested in trees and pebbles
+						continue;
+					}
+					const Widelands::BuildingDescr* d = nullptr;
+					if (descr.type() == Widelands::MapObjectType::PRODUCTIONSITE) {
+						if (imm->get_owner() != player_) {
+							continue;
+						}
+						if (imm_type != Widelands::MapObjectType::PRODUCTIONSITE) {
+							if (imm_type != Widelands::MapObjectType::CONSTRUCTIONSITE) {
+								continue;
+							}
+							upcast(Widelands::ConstructionSite, cs, imm);
+							d = cs->get_info().becomes;
+							if (d->type() != Widelands::MapObjectType::PRODUCTIONSITE) {
+								continue;
+							}
+						}
+					} else if (descr.type() == Widelands::MapObjectType::WAREHOUSE ||
+							descr.type() == Widelands::MapObjectType::MILITARYSITE) {
+						if (imm_type != Widelands::MapObjectType::MILITARYSITE &&
+								imm_type != Widelands::MapObjectType::WAREHOUSE) {
+							if (imm_type != Widelands::MapObjectType::CONSTRUCTIONSITE) {
+								continue;
+							}
+							upcast(Widelands::ConstructionSite, cs, imm);
+							d = cs->get_info().becomes;
+							if (d->type() != Widelands::MapObjectType::WAREHOUSE &&
+									d->type() != Widelands::MapObjectType::MILITARYSITE) {
+								continue;
+							}
+						}
+					}
+					upcast(Widelands::Building, bld, imm);
+					if (bld->get_position() != mr.location()) {
+						// Don't count big buildings more than once
+						continue;
+					}
+					if (!d) {
+						d = &bld->descr();
+					}
+					const WorkareaInfo& wa = d->workarea_info();
+					uint32_t wa_radius = 0;
+					for (const auto& pair : wa) {
+						wa_radius = std::max(wa_radius, pair.first);
+					}
+					if (wa_radius == 0) {
+						continue;
+					}
+					if (map.calc_distance(node_, mr.location()) <= workarea_radius + wa_radius) {
+						std::map<Widelands::TCoords<>, uint32_t> colors;
+						for (const Widelands::TCoords<>& t : map.triangles_in_region(
+								map.to_set(Widelands::Area<>(mr.location(), wa_radius)))) {
+							colors[t] = main_region.count(t) ? 0xbf7f0000 : 0x3fffffff;
+						}
+						ibase().show_workarea(wa, mr.location(), colors);
+						overlapping_workareas_.insert(mr.location());
+					}
+				}
+			}
+		} while (mr.advance(map));
 	}
 }
 

=== modified file 'src/wui/interactive_base.cc'
--- src/wui/interactive_base.cc	2019-04-25 06:31:33 +0000
+++ src/wui/interactive_base.cc	2019-04-28 16:58:57 +0000
@@ -101,6 +101,7 @@
      chat_overlay_(new ChatOverlay(this, 10, 25, get_w() / 2, get_h() - 25)),
      toolbar_(this, 0, 0, UI::Box::Horizontal),
      quick_navigation_(&map_view_),
+     workareas_cache_(nullptr),
      egbase_(the_egbase),
 #ifndef NDEBUG  //  not in releases
      display_flags_(dfDebug),
@@ -196,14 +197,19 @@
 bool InteractiveBase::has_workarea_preview(const Widelands::Coords& coords,
                                            const Widelands::Map* map) const {
 	if (!map) {
-		return workarea_previews_.count(coords) == 1;
+		for (const auto& it : workarea_previews_) {
+			if (it->coords == coords) {
+				return true;
+			}
+		}
+		return false;
 	}
-	for (const auto& pair : workarea_previews_) {
+	for (const auto& it : workarea_previews_) {
 		uint32_t radius = 0;
-		for (const auto& p : *pair.second) {
-			radius = std::max(radius, p.first);
+		for (const auto& wa : *it->info) {
+			radius = std::max(radius, wa.first);
 		}
-		if (map->calc_distance(coords, pair.first) <= radius) {
+		if (map->calc_distance(coords, it->coords) <= radius) {
 			return true;
 		}
 	}
@@ -304,9 +310,17 @@
 void InteractiveBase::on_buildhelp_changed(bool /* value */) {
 }
 
-// Show the given workareas at the given coords and returns the overlay job id associated
+// Show the given workareas at the given coords
+void InteractiveBase::show_workarea(const WorkareaInfo& workarea_info,
+                                    Widelands::Coords coords,
+                                    std::map<Widelands::TCoords<>, uint32_t>& extra_data) {
+	workarea_previews_.insert(std::unique_ptr<WorkareaPreview>(new WorkareaPreview{coords, &workarea_info, extra_data}));
+	workareas_cache_.reset(nullptr);
+}
+
 void InteractiveBase::show_workarea(const WorkareaInfo& workarea_info, Widelands::Coords coords) {
-	workarea_previews_[coords] = &workarea_info;
+	std::map<Widelands::TCoords<>, uint32_t> empty;
+	show_workarea(workarea_info, coords, empty);
 }
 
 /* Helper function to get the correct index for graphic/gl/workarea_program.cc::workarea_colors .
@@ -350,73 +364,100 @@
 	}
 }
 
-Workareas InteractiveBase::get_workarea_overlays(const Widelands::Map& map) const {
-	Workareas result_set;
-	for (const auto& wa_pair : workarea_previews_) {
-		std::map<Coords, uint8_t> intermediate_result;
-		const Coords& coords = wa_pair.first;
-		const WorkareaInfo* workarea_info = wa_pair.second;
-		intermediate_result[coords] = 0;
-		WorkareaInfo::size_type wa_index;
-		switch (workarea_info->size()) {
-		case 0:
-			continue;  // no workarea
-		case 1:
-			wa_index = 5;
-			break;
-		case 2:
-			wa_index = 3;
-			break;
-		case 3:
-			wa_index = 0;
-			break;
-		default:
-			throw wexception(
-			   "Encountered unexpected WorkareaInfo size %i", static_cast<int>(workarea_info->size()));
-		}
-
-		Widelands::HollowArea<> hollow_area(Widelands::Area<>(coords, 0), 0);
-
-		// Iterate through the work areas, from building to its enhancement
-		WorkareaInfo::const_iterator it = workarea_info->begin();
-		for (; it != workarea_info->end(); ++it) {
-			hollow_area.radius = it->first;
-			Widelands::MapHollowRegion<> mr(map, hollow_area);
-			do {
-				intermediate_result[mr.location()] = wa_index;
-			} while (mr.advance(map));
-			wa_index++;
-			hollow_area.hole_radius = hollow_area.radius;
-		}
-
-		std::map<TCoords<>, uint8_t> result;
-		for (const auto& pair : intermediate_result) {
-			Coords c;
-			map.get_brn(pair.first, &c);
-			const auto brn = intermediate_result.find(c);
-			if (brn == intermediate_result.end()) {
-				continue;
-			}
-			map.get_bln(pair.first, &c);
-			const auto bln = intermediate_result.find(c);
-			map.get_rn(pair.first, &c);
-			const auto rn = intermediate_result.find(c);
-			if (bln != intermediate_result.end()) {
-				result[TCoords<>(pair.first, Widelands::TriangleIndex::D)] =
-				   workarea_max(pair.second, brn->second, bln->second);
-			}
-			if (rn != intermediate_result.end()) {
-				result[TCoords<>(pair.first, Widelands::TriangleIndex::R)] =
-				   workarea_max(pair.second, brn->second, rn->second);
-			}
-		}
-		result_set.emplace(result);
-	}
-	return result_set;
-}
-
-void InteractiveBase::hide_workarea(const Widelands::Coords& coords) {
-	workarea_previews_.erase(coords);
+Workareas InteractiveBase::get_workarea_overlays(const Widelands::Map& map) {
+	if (!workareas_cache_) {
+		workareas_cache_.reset(new Workareas());
+		for (const auto& it : workarea_previews_) {
+			workareas_cache_->push_back(get_workarea_overlay(map, *it));
+		}
+	}
+	return Workareas(*workareas_cache_);
+}
+
+// static
+WorkareasEntry InteractiveBase::get_workarea_overlay(const Widelands::Map& map, const WorkareaPreview& workarea) {
+	std::map<Coords, uint8_t> intermediate_result;
+	const Coords& coords = workarea.coords;
+	const WorkareaInfo* workarea_info = workarea.info;
+	intermediate_result[coords] = 0;
+	WorkareaInfo::size_type wa_index;
+	switch (workarea_info->size()) {
+	case 0:
+		return WorkareasEntry();  // no workarea
+	case 1:
+		wa_index = 5;
+		break;
+	case 2:
+		wa_index = 3;
+		break;
+	case 3:
+		wa_index = 0;
+		break;
+	default:
+		throw wexception(
+		   "Encountered unexpected WorkareaInfo size %i", static_cast<int>(workarea_info->size()));
+	}
+
+	Widelands::HollowArea<> hollow_area(Widelands::Area<>(coords, 0), 0);
+
+	// Iterate through the work areas, from building to its enhancement
+	WorkareaInfo::const_iterator it = workarea_info->begin();
+	for (; it != workarea_info->end(); ++it) {
+		hollow_area.radius = it->first;
+		Widelands::MapHollowRegion<> mr(map, hollow_area);
+		do {
+			intermediate_result[mr.location()] = wa_index;
+		} while (mr.advance(map));
+		wa_index++;
+		hollow_area.hole_radius = hollow_area.radius;
+	}
+
+	WorkareasEntry result;
+	for (const auto& pair : intermediate_result) {
+		Coords c;
+		map.get_brn(pair.first, &c);
+		const auto brn = intermediate_result.find(c);
+		if (brn == intermediate_result.end()) {
+			continue;
+		}
+		map.get_bln(pair.first, &c);
+		const auto bln = intermediate_result.find(c);
+		map.get_rn(pair.first, &c);
+		const auto rn = intermediate_result.find(c);
+		if (bln != intermediate_result.end()) {
+			TCoords<> tc(pair.first, Widelands::TriangleIndex::D);
+			WorkareaPreviewData wd(tc, workarea_max(pair.second, brn->second, bln->second));
+			for (const auto& p : workarea.data) {
+				if (p.first == tc) {
+					wd = WorkareaPreviewData(tc, wd.index, p.second);
+					break;
+				}
+			}
+			result.push_back(wd);
+		}
+		if (rn != intermediate_result.end()) {
+			TCoords<> tc(pair.first, Widelands::TriangleIndex::R);
+			WorkareaPreviewData wd(tc, workarea_max(pair.second, brn->second, rn->second));
+			for (const auto& p : workarea.data) {
+				if (p.first == tc) {
+					wd = WorkareaPreviewData(tc, wd.index, p.second);
+					break;
+				}
+			}
+			result.push_back(wd);
+		}
+	}
+	return result;
+}
+
+void InteractiveBase::hide_workarea(const Widelands::Coords& coords, bool is_additional) {
+	for (auto it = workarea_previews_.begin(); it != workarea_previews_.end(); ++it) {
+		if (it->get()->coords == coords && (is_additional ^ it->get()->data.empty())) {
+			workarea_previews_.erase(it);
+			workareas_cache_.reset(nullptr);
+			return;
+		}
+	}
 }
 
 /**

=== modified file 'src/wui/interactive_base.h'
--- src/wui/interactive_base.h	2019-04-25 06:31:33 +0000
+++ src/wui/interactive_base.h	2019-04-28 16:58:57 +0000
@@ -46,6 +46,12 @@
 class EdgeOverlayManager;
 class UniqueWindowHandler;
 
+struct WorkareaPreview {
+	Widelands::Coords coords;
+	const WorkareaInfo* info;
+	std::map<Widelands::TCoords<>, uint32_t> data;
+};
+
 /**
  * This is used to represent the code that InteractivePlayer and
  * EditorInteractive share.
@@ -84,7 +90,10 @@
 	}
 
 	void show_workarea(const WorkareaInfo& workarea_info, Widelands::Coords coords);
-	void hide_workarea(const Widelands::Coords& coords);
+	void show_workarea(const WorkareaInfo& workarea_info,
+	                   Widelands::Coords coords,
+	                   std::map<Widelands::TCoords<>, uint32_t>& extra_data);
+	void hide_workarea(const Widelands::Coords& coords, bool is_additional);
 
 	//  point of view for drawing
 	virtual Widelands::Player* get_player() const = 0;
@@ -231,7 +240,8 @@
 	TextToDraw get_text_to_draw() const;
 
 	// Returns the current overlays for the work area previews.
-	Workareas get_workarea_overlays(const Widelands::Map& map) const;
+	Workareas get_workarea_overlays(const Widelands::Map& map);
+	static WorkareasEntry get_workarea_overlay(const Widelands::Map&, const WorkareaPreview&);
 
 	// Returns the 'BuildhelpOverlay' for 'caps' or nullptr if there is no help
 	// to be displayed on this field.
@@ -287,9 +297,9 @@
 	MiniMap::Registry minimap_registry_;
 	QuickNavigation quick_navigation_;
 
-	// The currently enabled work area previews. They are keyed by the
-	// coordinate that the building that shows the work area is positioned.
-	std::map<Widelands::Coords, const WorkareaInfo*> workarea_previews_;
+	// The currently enabled work area previews
+	std::unordered_set<std::unique_ptr<WorkareaPreview>> workarea_previews_;
+	std::unique_ptr<Workareas> workareas_cache_;
 
 	RoadBuildingOverlays road_building_overlays_;
 


Follow ups