From 13509c03a5b078e79f8460f0eb8ff4ab2e5e2c2f Mon Sep 17 00:00:00 2001
From: Arne Graf <ar.graf.cst@gmail.com>
Date: Thu, 3 May 2018 18:18:20 +0200
Subject: [PATCH] directional Escalators implemented; stopping on line bug: WIP

---
 IO/GeoFileParser.cpp           | 41 ++++++++++++++---
 IO/GeoFileParser.h             |  2 +-
 IO/IniFileParser.cpp           |  6 +++
 general/Configuration.h        | 17 +++++++
 geometry/SubRoom.cpp           | 63 ++++++++++++++++++++++++++
 geometry/SubRoom.h             | 24 ++++++++++
 math/VelocityModel.cpp         |  5 +++
 pedestrian/Pedestrian.cpp      |  2 +-
 routing/DirectionStrategy.cpp  | 10 +++++
 routing/DirectionStrategy.h    |  3 ++
 routing/ff_router/ffRouter.cpp | 82 +++++++++++++++++++++++++++++++++-
 routing/ff_router/ffRouter.h   |  2 +
 12 files changed, 248 insertions(+), 9 deletions(-)

diff --git a/IO/GeoFileParser.cpp b/IO/GeoFileParser.cpp
index 6d7350a5..6bf315cd 100644
--- a/IO/GeoFileParser.cpp
+++ b/IO/GeoFileParser.cpp
@@ -132,8 +132,8 @@ bool GeoFileParser::LoadGeometry(Building* building)
                SubRoom* subroom = nullptr;
 
                if (type=="stair" || type=="escalator" || type=="idle_escalator") {
-                    if (xSubRoom->FirstChildElement("up")==NULL) {
-                         Log->Write("ERROR:\t the attribute <up> and <down> are missing for the "+type);
+                    if (xSubRoom->FirstChildElement("up") == NULL) {
+                         Log->Write("ERROR:\t the attribute <up> and <down> are missing for the " + type);
                          Log->Write("ERROR:\t check your geometry file");
                          return false;
                     }
@@ -142,10 +142,39 @@ bool GeoFileParser::LoadGeometry(Building* building)
                     double down_x = xmltof(xSubRoom->FirstChildElement("down")->Attribute("py"), 0.0);
                     double down_y = xmltof(xSubRoom->FirstChildElement("down")->Attribute("py"), 0.0);
                     subroom = new Stair();
-                    ((Stair*) subroom)->SetUp(Point(up_x, up_y));
-                    ((Stair*) subroom)->SetDown(Point(down_x, down_y));
-               }
-               else {
+                    ((Stair *) subroom)->SetUp(Point(up_x, up_y));
+                    ((Stair *) subroom)->SetDown(Point(down_x, down_y));
+               } else if (type =="escalator_up") {
+                    if (xSubRoom->FirstChildElement("up") == NULL) {
+                         Log->Write("ERROR:\t the attribute <up> and <down> are missing for the " + type);
+                         Log->Write("ERROR:\t check your geometry file");
+                         return false;
+                    }
+                    double up_x = xmltof(xSubRoom->FirstChildElement("up")->Attribute("px"), 0.0);
+                    double up_y = xmltof(xSubRoom->FirstChildElement("up")->Attribute("py"), 0.0);
+                    double down_x = xmltof(xSubRoom->FirstChildElement("down")->Attribute("py"), 0.0);
+                    double down_y = xmltof(xSubRoom->FirstChildElement("down")->Attribute("py"), 0.0);
+                    subroom = new Escalator();
+                    ((Escalator *) subroom)->SetUp(Point(up_x, up_y));
+                    ((Escalator *) subroom)->SetDown(Point(down_x, down_y));
+                    ((Escalator*) subroom)->SetEscalatorUp();
+                    _configuration->set_has_directional_escalators(true);
+               } else if (type == "escalator_down") {
+                    if (xSubRoom->FirstChildElement("up") == NULL) {
+                         Log->Write("ERROR:\t the attribute <up> and <down> are missing for the " + type);
+                         Log->Write("ERROR:\t check your geometry file");
+                         return false;
+                    }
+                    double up_x = xmltof(xSubRoom->FirstChildElement("up")->Attribute("px"), 0.0);
+                    double up_y = xmltof(xSubRoom->FirstChildElement("up")->Attribute("py"), 0.0);
+                    double down_x = xmltof(xSubRoom->FirstChildElement("down")->Attribute("py"), 0.0);
+                    double down_y = xmltof(xSubRoom->FirstChildElement("down")->Attribute("py"), 0.0);
+                    subroom = new Escalator();
+                    ((Escalator *) subroom)->SetUp(Point(up_x, up_y));
+                    ((Escalator *) subroom)->SetDown(Point(down_x, down_y));
+                    ((Escalator*) subroom)->SetEscalatorDown();
+                    _configuration->set_has_directional_escalators(true);
+               } else {
                     //normal subroom or corridor
                     subroom = new NormalSubRoom();
                }
diff --git a/IO/GeoFileParser.h b/IO/GeoFileParser.h
index 83029e78..88329a54 100644
--- a/IO/GeoFileParser.h
+++ b/IO/GeoFileParser.h
@@ -40,7 +40,7 @@ public:
      virtual bool LoadTrafficInfo(Building* building) override;
 
 private:
-     const Configuration* _configuration;
+     Configuration* _configuration;
 
      bool LoadGeometry(Building* building);
 
diff --git a/IO/IniFileParser.cpp b/IO/IniFileParser.cpp
index 6fe93041..a611f8f3 100644
--- a/IO/IniFileParser.cpp
+++ b/IO/IniFileParser.cpp
@@ -1500,18 +1500,24 @@ bool IniFileParser::ParseStrategyNodeToObject(const TiXmlNode& strategyNode)
                     pExitStrategy = 9;
                     _exit_strat_number = 9;
                     _exit_strategy = std::shared_ptr<DirectionStrategy>(new DirectionSubLocalFloorfield());
+                    if(!ParseFfOpts(strategyNode)) {
+                        return false;
+                    };
+                    _config->set_dirSubLocal(dynamic_cast<DirectionSubLocalFloorfield*>(_exit_strategy.get()));
                     break;
                case 8:
                     _exit_strategy = std::shared_ptr<DirectionStrategy>(new DirectionLocalFloorfield());
                     if(!ParseFfOpts(strategyNode)) {
                          return false;
                     };
+                    _config->set_dirLocal(dynamic_cast<DirectionLocalFloorfield*>(_exit_strategy.get()));
                     break;
                case 9:
                     _exit_strategy = std::shared_ptr<DirectionStrategy>(new DirectionSubLocalFloorfield());
                     if(!ParseFfOpts(strategyNode)) {
                          return false;
                     };
+                    _config->set_dirSubLocal(dynamic_cast<DirectionSubLocalFloorfield*>(_exit_strategy.get()));
                     break;
                default:
                     _exit_strategy = std::shared_ptr<DirectionStrategy>(new DirectionMinSeperationShorterLine());
diff --git a/general/Configuration.h b/general/Configuration.h
index 88185ae3..476f86f7 100644
--- a/general/Configuration.h
+++ b/general/Configuration.h
@@ -44,6 +44,8 @@
 //This class provides a data container for all configuration parameters.
 
 class AgentsParameters;
+class DirectionSubLocalFloorfield;
+class DirectionLocalFloorfield;
 
 #ifdef _JPS_AS_A_SERVICE
 
@@ -117,9 +119,12 @@ public:
 
           //ff router
           _has_specific_goals = false;
+          _has_directional_escalators = false;
           _write_VTK_files = false;
           _exit_strat = 9;
           _write_VTK_files_direction = false;
+          _dirSubLocal = nullptr;
+          _dirLocal = nullptr;
 
 	  //for random numbers
           _rdGenerator=RandomNumberGenerator();
@@ -270,6 +275,9 @@ public:
 
      void set_has_specific_goals(bool has_specific_goals) { _has_specific_goals = has_specific_goals;}
 
+     bool get_has_directional_escalators() const { return _has_directional_escalators;}
+     void set_has_directional_escalators(bool has_directional_esc) {_has_directional_escalators = has_directional_esc;}
+
      void set_write_VTK_files(bool write_VTK_files) {_write_VTK_files = write_VTK_files;}
 
      bool get_write_VTK_files() const {return _write_VTK_files;}
@@ -278,6 +286,12 @@ public:
 
      int get_exit_strat() const {return _exit_strat;}
 
+     void set_dirSubLocal(DirectionSubLocalFloorfield* dir) {_dirSubLocal = dir;}
+     void set_dirLocal(DirectionLocalFloorfield* dir) {_dirLocal = dir;}
+
+     DirectionSubLocalFloorfield* get_dirSubLocal() const {return _dirSubLocal;}
+     DirectionLocalFloorfield* get_dirLocal() const {return _dirLocal;}
+
      const std::string& GetHostname() const { return _hostname; };
 
     void set_write_VTK_files_direction(bool write_VTK_files_direction) {_write_VTK_files_direction = write_VTK_files_direction;}
@@ -391,10 +405,13 @@ private:
 
      //ff router
      bool _has_specific_goals;
+     bool _has_directional_escalators;
      bool _write_VTK_files;
      bool _write_VTK_files_direction;
 
      int _exit_strat;
+     DirectionSubLocalFloorfield* _dirSubLocal;
+     DirectionLocalFloorfield* _dirLocal;
 
      std::string _hostname;
      std::string _trajectoriesFile;
diff --git a/geometry/SubRoom.cpp b/geometry/SubRoom.cpp
index ede9a6c8..f47d08cb 100644
--- a/geometry/SubRoom.cpp
+++ b/geometry/SubRoom.cpp
@@ -79,6 +79,33 @@ SubRoom::SubRoom()
      _boostPoly = polygon_type();
 }
 
+SubRoom::SubRoom(const SubRoom& orig) {
+    _id = orig._id;
+    _roomID=orig._roomID;
+    _walls = orig._walls;
+    _poly = orig._poly;
+    _poly_help_constatnt = orig._poly_help_constatnt;
+    _poly_help_multiple = orig._poly_help_multiple;
+    _obstacles=orig._obstacles;
+
+    _crossings = orig._crossings;
+    _transitions = orig._transitions;
+    _hlines = orig._hlines;
+
+    _planeEquation[0]=orig._planeEquation[0];
+    _planeEquation[1]=orig._planeEquation[1];
+    _planeEquation[2]=orig._planeEquation[2];
+    _cosAngleWithHorizontalPlane=orig._cosAngleWithHorizontalPlane;
+    _tanAngleWithHorizontalPlane=orig._tanAngleWithHorizontalPlane;
+    _minElevation=orig._minElevation;
+    _maxElevation=orig._maxElevation;
+
+    _goalIDs = orig._goalIDs;
+    _area = orig._area;
+    _uid = orig._uid;
+    _boostPoly = orig._boostPoly;
+}
+
 SubRoom::~SubRoom()
 {
      for (unsigned int i = 0; i < _obstacles.size(); i++)
@@ -200,6 +227,7 @@ void SubRoom::AddObstacle(Obstacle* obs)
 
 void SubRoom::AddGoalID(int ID)
 {
+     Log->Write("ERROR:\t Adding a Goal directly to a subroom is not allowed. Plz add Crossing/Transition instead!");
      if (std::find(_goalIDs.begin(), _goalIDs.end(), ID) != _goalIDs.end()) {
           Log->Write("WARNING: \tAdded existing GoalID to Subroom %d", this->GetSubRoomID());
           //if occurs, plz assert, that ID is a UID of any line of the goal and not a number given by the user (ar.graf)
@@ -833,6 +861,7 @@ NormalSubRoom::NormalSubRoom() : SubRoom()
 {
 }
 
+NormalSubRoom::NormalSubRoom(const NormalSubRoom& orig) : SubRoom(orig) {}
 
 NormalSubRoom::~NormalSubRoom()
 {
@@ -1138,6 +1167,12 @@ Stair::Stair() : NormalSubRoom()
      pDown = Point();
 }
 
+Stair::Stair(const Stair &orig) : NormalSubRoom(orig),
+pUp (orig.pUp),
+pDown (orig.pDown)
+{
+}
+
 
 Stair::~Stair()
 {
@@ -1443,5 +1478,33 @@ std::vector<Point> SubRoom::StartLLCorner(const std::vector<Point> &polygon)
 
 }
 
+/// Escalator
+
+Escalator::Escalator(): Stair() {
+
+}
+
+Escalator::Escalator(const Escalator& orig) : Stair(orig), isEscalator_Up(orig.isEscalator_Up)
+{}
+
+Escalator::~Escalator() {}
+
+// Setter-Funktionen
+void Escalator::SetEscalatorUp() {
+     isEscalator_Up = true;
+}
+void Escalator::SetEscalatorDown(){
+     isEscalator_Up = false;
+}
+
+// Getter-Funktionen
+bool Escalator::IsEscalatorUp() const {
+     return isEscalator_Up;
+}
+bool Escalator::IsEscalatorDown() const{
+     return !isEscalator_Up;
+}
+
+
 
 #endif // _SIMULATOR
diff --git a/geometry/SubRoom.h b/geometry/SubRoom.h
index f5e65907..0e2bd0a3 100644
--- a/geometry/SubRoom.h
+++ b/geometry/SubRoom.h
@@ -111,6 +111,7 @@ public:
       * Constructor
       */
      SubRoom();
+     SubRoom(const SubRoom& orig);
 
      /**
       * Destructor
@@ -451,5 +452,28 @@ public:
      virtual bool ConvertLineToPoly(const std::vector<Line*>& goals);
 };
 
+/************************************************************
+ Escalator
+************************************************************/
+
+class Escalator : public Stair {
+private:
+    bool isEscalator_Up;
+
+public:
+    Escalator();
+    Escalator(const Escalator& orig);
+    virtual ~Escalator();
+
+    // Setter-Funktionen
+    void SetEscalatorUp();
+    void SetEscalatorDown();
+
+    // Getter-Funktionen
+    bool IsEscalatorUp() const;
+    bool IsEscalatorDown() const;
+
+};
+
 #endif  /* _SUBROOM_H */
 
diff --git a/math/VelocityModel.cpp b/math/VelocityModel.cpp
index eccca319..a96cc841 100644
--- a/math/VelocityModel.cpp
+++ b/math/VelocityModel.cpp
@@ -312,6 +312,7 @@ Point VelocityModel::e0(Pedestrian* ped, Room* room) const
           if (desired_direction.NormSquare() < 0.25) {
               desired_direction = lastE0;
               ped->SetLastE0(lastE0);
+              Log->Write("%f    %f", desired_direction._x, desired_direction._y);
           }
 //          if (dist > 1*J_EPS_GOAL) {
 //               desired_direction = target - pos; //ped->GetV0(target);
@@ -325,6 +326,10 @@ Point VelocityModel::e0(Pedestrian* ped, Room* room) const
           ped->SetSmoothTurning();
           desired_direction = ped->GetV0();
       }
+      //Log->Write("%f    %f", desired_direction._x, desired_direction._y);
+      if (desired_direction.NormSquare() < 0.1) {
+          Log->Write("ERROR:\t desired_direction in VelocityModel::e0 is too small.");
+      }
       return desired_direction;
 }
 
diff --git a/pedestrian/Pedestrian.cpp b/pedestrian/Pedestrian.cpp
index 0fbcf1e3..1af2a5fb 100644
--- a/pedestrian/Pedestrian.cpp
+++ b/pedestrian/Pedestrian.cpp
@@ -230,7 +230,7 @@ void Pedestrian::SetTau(double tau)
 
 void Pedestrian::SetT(double T)
 {
-     _tau = T;
+     _T = T;
 }
 
 
diff --git a/routing/DirectionStrategy.cpp b/routing/DirectionStrategy.cpp
index 41650103..011a5a8c 100644
--- a/routing/DirectionStrategy.cpp
+++ b/routing/DirectionStrategy.cpp
@@ -381,6 +381,11 @@ double DirectionLocalFloorfield::GetDistance2Wall(Pedestrian* ped) const
      return _locffviafm.at(ped->GetRoomID())->getDistance2WallAt(ped->GetPos());
 }
 
+double DirectionLocalFloorfield::GetDistance2Target(Pedestrian* ped, int UID) {
+    int roomID = ped->GetRoomID();
+    return _locffviafm.at(roomID)->getCostToDestination(UID, ped->GetPos());
+}
+
 void DirectionLocalFloorfield::Init(Building* buildingArg, double stepsize,
                                     double threshold, bool useDistanceMap) {
      _stepsize = stepsize;
@@ -477,6 +482,11 @@ double DirectionSubLocalFloorfield::GetDistance2Wall(Pedestrian* ped) const
      return _locffviafm.at(ped->GetSubRoomUID())->getDistance2WallAt(ped->GetPos());
 }
 
+double DirectionSubLocalFloorfield::GetDistance2Target(Pedestrian* ped, int UID) {
+    int subroomUID = ped->GetSubRoomUID();
+    return _locffviafm.at(subroomUID)->getCostToDestination(UID, ped->GetPos());
+}
+
 void DirectionSubLocalFloorfield::Init(Building* buildingArg, double stepsize,
                                        double threshold, bool useDistanceMap) {
      _stepsize = stepsize;
diff --git a/routing/DirectionStrategy.h b/routing/DirectionStrategy.h
index 8f20cd07..219b7157 100644
--- a/routing/DirectionStrategy.h
+++ b/routing/DirectionStrategy.h
@@ -87,6 +87,7 @@ public:
     virtual Point GetDir2Wall(Pedestrian* ped) const;
     virtual double GetDistance2Wall(Pedestrian* ped) const;
 
+
 private:
      FloorfieldViaFM* _ffviafm;
      bool _initDone;
@@ -101,6 +102,7 @@ public:
      virtual Point GetTarget(Room* room, Pedestrian* ped) const;
      virtual Point GetDir2Wall(Pedestrian* ped) const;
      virtual double GetDistance2Wall(Pedestrian* ped) const;
+     virtual double GetDistance2Target(Pedestrian* ped, int UID);
 
 protected:
      std::map<int, UnivFFviaFM*> _locffviafm;
@@ -121,6 +123,7 @@ public:
      virtual Point GetTarget(Room* room, Pedestrian* ped) const;
      virtual Point GetDir2Wall(Pedestrian* ped) const;
      virtual double GetDistance2Wall(Pedestrian* ped) const;
+     virtual double GetDistance2Target(Pedestrian* ped, int UID);
 
 protected:
      std::map<int, UnivFFviaFM*> _locffviafm;
diff --git a/routing/ff_router/ffRouter.cpp b/routing/ff_router/ffRouter.cpp
index c85253cf..9190bd1e 100644
--- a/routing/ff_router/ffRouter.cpp
+++ b/routing/ff_router/ffRouter.cpp
@@ -141,6 +141,7 @@ bool FFRouter::Init(Building* building)
           }
      }
      //make unique
+     std::sort(_allDoorUIDs.begin(), _allDoorUIDs.end());
      _allDoorUIDs.erase( std::unique(_allDoorUIDs.begin(),_allDoorUIDs.end()), _allDoorUIDs.end());
 
      //cleanse maps
@@ -248,6 +249,40 @@ bool FFRouter::Init(Building* building)
           } // otherDoor
      } // roomAndCroTrVector
 
+     if (_config->get_has_directional_escalators()) {
+         _directionalEscalatorsUID.clear();
+         _penaltyList.clear();
+         for (auto room : building->GetAllRooms()) {
+             for (auto subroom : room.second->GetAllSubRooms()) {
+                 if ((subroom.second->GetType() == "escalator_up") || (subroom.second->GetType() == "escalator_down")) {
+                     _directionalEscalatorsUID.emplace_back(subroom.second->GetUID());
+                 }
+             }
+         }
+         for (int subUID : _directionalEscalatorsUID) {
+             Escalator* escalator = (Escalator*) building->GetSubRoomByUID(subUID);
+             std::vector<int> lineUIDs = escalator->GetAllGoalIDs();
+             assert(lineUIDs.size() == 2);
+             if (escalator->IsEscalatorUp()) {
+                 if (_CroTrByUID[lineUIDs[0]]->IsInLineSegment(escalator->GetUp())) {
+                     _penaltyList.emplace_back(std::make_pair(lineUIDs[0], lineUIDs[1]));
+                 } else {
+                     _penaltyList.emplace_back(std::make_pair(lineUIDs[1], lineUIDs[0]));
+                 }
+             } else { //IsEscalatorDown
+                 if (_CroTrByUID[lineUIDs[0]]->IsInLineSegment(escalator->GetUp())) {
+                     _penaltyList.emplace_back(std::make_pair(lineUIDs[1], lineUIDs[0]));
+                 } else {
+                     _penaltyList.emplace_back(std::make_pair(lineUIDs[0], lineUIDs[1]));
+                 }
+             }
+         }
+         for (auto key : _penaltyList) {
+             _distMatrix.erase(key);
+             _distMatrix.insert(std::make_pair(key, DBL_MAX));
+         }
+     }
+
      FloydWarshall();
 
      //debug output in file
@@ -340,6 +375,41 @@ bool FFRouter::ReInit()
                } //secondDoor(s)
           } //firstDoor(s)
      } //allRooms
+
+    if (_config->get_has_directional_escalators()) {
+        _directionalEscalatorsUID.clear();
+        _penaltyList.clear();
+        for (auto room : _building->GetAllRooms()) {
+            for (auto subroom : room.second->GetAllSubRooms()) {
+                if ((subroom.second->GetType() == "escalator_up") || (subroom.second->GetType() == "escalator_down")) {
+                    _directionalEscalatorsUID.emplace_back(subroom.second->GetUID());
+                }
+            }
+        }
+        for (int subUID : _directionalEscalatorsUID) {
+            Escalator* escalator = (Escalator*) _building->GetSubRoomByUID(subUID);
+            std::vector<int> lineUIDs = escalator->GetAllGoalIDs();
+            assert(lineUIDs.size() == 2);
+            if (escalator->IsEscalatorUp()) {
+                if (_CroTrByUID[lineUIDs[0]]->IsInLineSegment(escalator->GetUp())) {
+                    _penaltyList.emplace_back(std::make_pair(lineUIDs[0], lineUIDs[1]));
+                } else {
+                    _penaltyList.emplace_back(std::make_pair(lineUIDs[1], lineUIDs[0]));
+                }
+            } else { //IsEscalatorDown
+                if (_CroTrByUID[lineUIDs[0]]->IsInLineSegment(escalator->GetUp())) {
+                    _penaltyList.emplace_back(std::make_pair(lineUIDs[1], lineUIDs[0]));
+                } else {
+                    _penaltyList.emplace_back(std::make_pair(lineUIDs[0], lineUIDs[1]));
+                }
+            }
+        }
+        for (auto key : _penaltyList) {
+            _distMatrix.erase(key);
+            _distMatrix.insert(std::make_pair(key, DBL_MAX));
+        }
+    }
+
      FloydWarshall();
      _plzReInit = false;
      return true;
@@ -456,7 +526,14 @@ int FFRouter::FindExit(Pedestrian* p)
      for(int finalDoor : validFinalDoor) {
           //with UIDs, we can ask for shortest path
           for (int doorUID : DoorUIDsOfRoom) {
-               double locDistToDoor = _locffviafm[p->GetRoomID()]->getCostToDestination(doorUID, p->GetPos(), _mode);
+               //double locDistToDoor = _locffviafm[p->GetRoomID()]->getCostToDestination(doorUID, p->GetPos(), _mode);
+               double locDistToDoor = 0.;
+               if (_targetWithinSubroom) {
+                   locDistToDoor = _config->get_dirSubLocal()->GetDistance2Target(p, doorUID);
+               } else {
+                   locDistToDoor = _config->get_dirLocal()->GetDistance2Target(p, doorUID);
+               }
+
                if (locDistToDoor < -J_EPS) {     //for old ff: //this can happen, if the point is not reachable and therefore has init val -7
                     continue;
                }
@@ -472,6 +549,9 @@ int FFRouter::FindExit(Pedestrian* p)
                     if ((_distMatrix.at(key) + locDistToDoor) < minDist) {
                          minDist = _distMatrix.at(key) + locDistToDoor;
                          bestDoor = key.first; //doorUID
+                         if (locDistToDoor == 0.) {
+                             bestDoor = _pathsMatrix[key]; //@todo: @ar.graf: check this hack
+                         }
                          bestFinalDoor = key.second;
                     }
                }
diff --git a/routing/ff_router/ffRouter.h b/routing/ff_router/ffRouter.h
index e2ac697f..4da43c2f 100644
--- a/routing/ff_router/ffRouter.h
+++ b/routing/ff_router/ffRouter.h
@@ -192,6 +192,8 @@ protected:
      //std::map< std::pair<int, int> , SubRoom* > _subroomMatrix;
      std::vector<int>                         _allDoorUIDs;
      std::vector<int>                         _localShortestSafedPeds;
+     std::vector<int>                         _directionalEscalatorsUID;
+     std::vector<std::pair<int, int>>         _penaltyList;
      const Building*                          _building;
      std::map<int, UnivFFviaFM*>     _locffviafm; // the actual type might be CentrePointLocalFFViaFM
      FloorfieldViaFM*                         _globalFF;
-- 
GitLab