I think we have some room here to get rid of code duplication.

Diff comments:

> 
> === 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
> @@ -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) {

You could pull out a common function here that takes the TriangleIndex as 
parameters. It could be a lambda function, because it's not being used anywhere 
else, like this:

auto do_something = [this](const Field& field, Widelands::TriangleIndex 
triangle_index) { // do something];

> +                             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_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 {

I think that we should calculate it only once during postload() and store it 
into a variable.

> +     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/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
> @@ -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;

What makes a coloring special, the overlap? I think we should rename this.

> +     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/fieldaction.cc'
> --- src/wui/fieldaction.cc    2019-02-23 11:00:49 +0000
> +++ src/wui/fieldaction.cc    2019-04-28 16:58:57 +0000
> @@ -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) {

Why not just check for imm_type == Widelands::MapObjectType::CONSTRUCTIONSITE 
here and lose the inner if?

> +                                                     if (imm_type != 
> Widelands::MapObjectType::CONSTRUCTIONSITE) {
> +                                                             continue;
> +                                                     }
> +                                                     
> upcast(Widelands::ConstructionSite, cs, imm);
> +                                                     d = 
> cs->get_info().becomes;
> +                                                     if (d->type() != 
> Widelands::MapObjectType::PRODUCTIONSITE) {

I think we can assume that this is not false, because we checked for 
descr.type() == Widelands::MapObjectType::PRODUCTIONSITE above. So, we can skip 
the whole upcast and becomes check and shift it below where we assign the 
building description that we'll use for the workarea.

> +                                                             continue;
> +                                                     }
> +                                             }
> +                                     } else if (descr.type() == 
> Widelands::MapObjectType::WAREHOUSE ||
> +                                                     descr.type() == 
> Widelands::MapObjectType::MILITARYSITE) {
> +                                             if (imm_type != 
> Widelands::MapObjectType::MILITARYSITE &&

Use same simplification as above. We could even do the check whether it's a 
constructionsite below where we assign the building description that we'll use 
for the workarea.

> +                                                             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
> @@ -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_) {

"it" is a misleading name, because this is not an iterator, but a <key, entry> 
pair.

>               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;
>               }
>       }
> @@ -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

Did you intend to make this static? If not, get rid of the comment.

> +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;
> +             }
> +     }
>  }
>  
>  /**


-- 
https://code.launchpad.net/~widelands-dev/widelands/overlapping_workareas/+merge/366623
Your team Widelands Developers is requested to review the proposed merge of 
lp:~widelands-dev/widelands/overlapping_workareas into lp:widelands.

_______________________________________________
Mailing list: https://launchpad.net/~widelands-dev
Post to     : widelands-dev@lists.launchpad.net
Unsubscribe : https://launchpad.net/~widelands-dev
More help   : https://help.launchpad.net/ListHelp

Reply via email to