diff --git a/CMakeLists.txt b/CMakeLists.txt index 340148b033f34a2309a9f5982682f9bf20532253..ba10b6f055486d571f022d8745e6054745a9e1a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ #-------------------------------------------------------------------------- cmake_minimum_required(VERSION 3.1 FATAL_ERROR) + set(CMAKE_LEGACY_CYGWIN_WIN32 0) IF (POLICY CMP0054) cmake_policy(SET CMP0054 NEW) @@ -41,7 +42,7 @@ message(STATUS "JPSCORE_VERSION: " ${JPSCORE_VERSION}) add_definitions("-DJPSCORE_VERSION=\"${JPSCORE_VERSION}\"") if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - set(warnings "-Wall -Wextra") + set(warnings "-Wall -Wextra -") # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") set(warnings "/W4 /WX /EHsc") @@ -54,6 +55,8 @@ endif () set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${warnings}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${warnings}") +message(STATUS "!BUILD_TESTING: " ${BUILD_TESTING}) + if (NOT BUILD_TESTING) set(BUILD_TESTING OFF) # test units & python tests are not generated. endif (NOT BUILD_TESTING) @@ -69,24 +72,43 @@ if (NOT CMAKE_EXPORT_COMPILE_COMMANDS) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) endif (NOT CMAKE_EXPORT_COMPILE_COMMANDS) -set(CTEST_BUILD_FLAGS "-j") +# Get number of processors. Mac is not supported +if (NOT DEFINED PROCESSOR_COUNT) +# Unknown: +set(PROCESSOR_COUNT 0) + +# Linux: +set(cpuinfo_file "/proc/cpuinfo") +if (EXISTS "${cpuinfo_file}") +file(STRINGS "${cpuinfo_file}" procs REGEX "^processor.: [0-9]+$") +list(LENGTH procs PROCESSOR_COUNT) +endif (EXISTS "${cpuinfo_file}") + +# Windows: +if (WIN32) +set(PROCESSOR_COUNT "$ENV{NUMBER_OF_PROCESSORS}") +endif (WIN32) +endif (NOT DEFINED PROCESSOR_COUNT) + +if (PROCESSOR_COUNT) +message( STATUS "PROCESSOR_COUNT: " ${PROCESSOR_COUNT}) +# set(CTEST_BUILD_FLAGS "-j${PROCESSOR_COUNT}") +message(STATUS "PROCESSOR_COUNT: 1") + +set(CTEST_BUILD_FLAGS "-j0") +endif (PROCESSOR_COUNT) if (NOT CMAKE_BUILD_TYPE) - # set (CMAKE_BUILD_TYPE Release) - set(CMAKE_BUILD_TYPE Debug) +# set (CMAKE_BUILD_TYPE Release) +set(CMAKE_BUILD_TYPE Debug) endif (NOT CMAKE_BUILD_TYPE) message(STATUS "CMAKE_BUILD_TYPE: " ${CMAKE_BUILD_TYPE}) if(NOT USE_DUAL_ABI) - set (USE_DUAL_ABI FALSE) +set (USE_DUAL_ABI FALSE) endif() #------------------ set important directories -------------------- -if (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY) - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin) -endif() - -if(NOT EXECUTABLE_OUTPUT_PATH) - set(EXECUTABLE_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/bin") -endif() +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin) +set(EXECUTABLE_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/bin") set(LIBRARY_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/lib/${CMAKE_BUILD_TYPE}) set(CMAKE_TEST_DIR ${CMAKE_SOURCE_DIR}/Utest) # Debug messages @@ -100,134 +122,142 @@ message(STATUS "") message(STATUS "Platform: ") message(STATUS " Host: " ${CMAKE_HOST_SYSTEM_NAME} ${CMAKE_HOST_SYSTEM_VERSION} ${CMAKE_HOST_SYSTEM_PROCESSOR}) if (CMAKE_CROSSCOMPILING) - message(STATUS " Target: " ${CMAKE_SYSTEM_NAME} ${CMAKE_SYSTEM_VERSION} ${CMAKE_SYSTEM_PROCESSOR}) +message(STATUS " Target: " ${CMAKE_SYSTEM_NAME} ${CMAKE_SYSTEM_VERSION} ${CMAKE_SYSTEM_PROCESSOR}) endif () message(STATUS " CMake: " ${CMAKE_VERSION}) message(STATUS " CMake generator: " ${CMAKE_GENERATOR}) message(STATUS " CMake build tool: " ${CMAKE_BUILD_TOOL}) if (MSVC) - message(STATUS " MSVC: " ${MSVC_VERSION}) +message(STATUS " MSVC: " ${MSVC_VERSION}) #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ZLIB_WINAPI) endif () if (CMAKE_GENERATOR MATCHES Xcode) - message(STATUS " Xcode: " ${XCODE_VERSION}) +message(STATUS " Xcode: " ${XCODE_VERSION}) endif () if (NOT CMAKE_GENERATOR MATCHES "Xcode|Visual Studio") - message(STATUS " Configuration: " ${CMAKE_BUILD_TYPE}) +message(STATUS " Configuration: " ${CMAKE_BUILD_TYPE}) endif () message(STATUS "") -find_package(Git QUIET) # no need for this msg. It comes from cmake.findgit() +# message( STATUS "CMAKE_CURRENT_SOURCE_DIR: " ${CMAKE_CURRENT_SOURCE_DIR} ) +# message( STATUS "CMAKE_RUNTIME_OUTPUT_DIRECTORY: " ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ) +# message( STATUS "EXECUTABLE_OUTPUT_PATH: " ${EXECUTABLE_OUTPUT_PATH} ) +# message( STATUS "CMAKE_VERBOSE_MAKEFILE: " ${CMAKE_VERBOSE_MAKEFILE} ) + +find_package(Git REQUIRED) # no need for this msg. It comes from cmake.findgit() find_program(GIT_SCM git DOC "Git version control") mark_as_advanced(GIT_SCM) find_file(GITDIR NAMES .git PATHS ${CMAKE_SOURCE_DIR} NO_DEFAULT_PATH) if (GIT_SCM AND GITDIR) - # the commit's SHA1, and whether the building workspace was dirty or not - # describe --match=NeVeRmAtCh --always --tags --abbrev=40 --dirty - execute_process(COMMAND - "${GIT_EXECUTABLE}" --no-pager describe --tags --always --dirty - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" - OUTPUT_VARIABLE GIT_SHA1 - ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - - # branch - execute_process( - COMMAND "${GIT_EXECUTABLE}" rev-parse --abbrev-ref HEAD - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" - OUTPUT_VARIABLE GIT_BRANCH - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - - # the date of the commit - execute_process(COMMAND - "${GIT_EXECUTABLE}" log -1 --format=%ad --date=local - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" - OUTPUT_VARIABLE GIT_DATE - ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - - # the subject of the commit - execute_process(COMMAND - "${GIT_EXECUTABLE}" log -1 --format=%s - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" - OUTPUT_VARIABLE GIT_COMMIT_SUBJECT - ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) -else() - message(STATUS "Not in a git repo") - endif() - string(REGEX REPLACE "\#" - "" GIT_COMMIT_SUBJECT - ${GIT_COMMIT_SUBJECT}) +# the commit's SHA1, and whether the building workspace was dirty or not +# describe --match=NeVeRmAtCh --always --tags --abbrev=40 --dirty +execute_process(COMMAND +"${GIT_EXECUTABLE}" --no-pager describe --tags --always --dirty +WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" +OUTPUT_VARIABLE GIT_SHA1 +ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + +# branch +execute_process( +COMMAND "${GIT_EXECUTABLE}" rev-parse --abbrev-ref HEAD +WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" +OUTPUT_VARIABLE GIT_BRANCH +OUTPUT_STRIP_TRAILING_WHITESPACE +) + +# the date of the commit +execute_process(COMMAND +"${GIT_EXECUTABLE}" log -1 --format=%ad --date=local +WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" +OUTPUT_VARIABLE GIT_DATE +ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + +# the subject of the commit +execute_process(COMMAND +"${GIT_EXECUTABLE}" log -1 --format=%s +WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" +OUTPUT_VARIABLE GIT_COMMIT_SUBJECT +ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + + -# even if not in a git repository we need to define these add_definitions("-DGIT_COMMIT_HASH=\"${GIT_SHA1}\"") add_definitions("-DGIT_COMMIT_DATE=\"${GIT_DATE}\"") add_definitions("-DGIT_COMMIT_SUBJECT=\"${GIT_COMMIT_SUBJECT}\"") add_definitions("-DGIT_BRANCH=\"${GIT_BRANCH}\"") +else() +message(STATUS "Not in a git repo") +endif() + # add a target to generate API documentation with Doxygen find_package(Doxygen) if (DOXYGEN_FOUND) - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/doc/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) - add_custom_target(doc - ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - COMMENT "Generating API documentation with Doxygen" VERBATIM) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) +add_custom_target(doc +${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile +WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} +COMMENT "Generating API documentation with Doxygen" VERBATIM +) endif (DOXYGEN_FOUND) #http://stackoverflow.com/questions/1487752/how-do-i-instruct-cmake-to-look-for-libraries-installed-by-macports if (APPLE) - # Detect if the "port" command is valid on this system; if so, return full path - execute_process(COMMAND which port RESULT_VARIABLE DETECT_MACPORTS OUTPUT_VARIABLE MACPORTS_PREFIX ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - if (${DETECT_MACPORTS} EQUAL 0) - # "/opt/local/bin/port" doesn't have libs, so we get the parent directory - get_filename_component(MACPORTS_PREFIX ${MACPORTS_PREFIX} DIRECTORY) - # "/opt/local/bin" doesn't have libs, so we get the parent directory - get_filename_component(MACPORTS_PREFIX ${MACPORTS_PREFIX} DIRECTORY) - # "/opt/local" is where MacPorts lives, add `/lib` suffix and link - link_directories(${MACPORTS_PREFIX}/lib) - message(STATUS "Macports detected: ${MACPORTS_PREFIX}/lib") - # SET(CMAKE_SYSTEM_NAME Darwin) - # # Add MacPorts - # INCLUDE_DIRECTORIES(/opt/local/include) - - # LINK_DIRECTORIES(/opt/local/lib) - - else () - # Recommendation, also add a "brew --prefix" custom command to detect a homebrew build environment - execute_process(COMMAND brew --prefix RESULT_VARIABLE DETECT_BREW OUTPUT_VARIABLE BREW_PREFIX ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - if (${DETECT_BREW} EQUAL 0) - link_directories(${BREW_PREFIX}/lib) - message(STATUS "Brew detected: ${BREW_PREFIX}") - endif () - endif () +# Detect if the "port" command is valid on this system; if so, return full path +execute_process(COMMAND which port RESULT_VARIABLE DETECT_MACPORTS OUTPUT_VARIABLE MACPORTS_PREFIX ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) +if (${DETECT_MACPORTS} EQUAL 0) +# "/opt/local/bin/port" doesn't have libs, so we get the parent directory +get_filename_component(MACPORTS_PREFIX ${MACPORTS_PREFIX} DIRECTORY) +# "/opt/local/bin" doesn't have libs, so we get the parent directory +get_filename_component(MACPORTS_PREFIX ${MACPORTS_PREFIX} DIRECTORY) +# "/opt/local" is where MacPorts lives, add `/lib` suffix and link +link_directories(${MACPORTS_PREFIX}/lib) +message(STATUS "Macports detected: ${MACPORTS_PREFIX}/lib") +# SET(CMAKE_SYSTEM_NAME Darwin) +# # Add MacPorts +# INCLUDE_DIRECTORIES(/opt/local/include) + +# LINK_DIRECTORIES(/opt/local/lib) + +else () +# Recommendation, also add a "brew --prefix" custom command to detect a homebrew build environment +execute_process(COMMAND brew --prefix RESULT_VARIABLE DETECT_BREW OUTPUT_VARIABLE BREW_PREFIX ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) +if (${DETECT_BREW} EQUAL 0) +link_directories(${BREW_PREFIX}/lib) +message(STATUS "Brew detected: ${BREW_PREFIX}") +endif () +endif () + + endif (APPLE) if (Boost_NO_SYSTEM_PATHS) - set(Boost_NO_SYSTEM_PATHS ON) - set(BOOST_INCLUDE_DIRS "${BOOST_ROOT}/include") - set(BOOST_LIBRARY_DIRS "${BOOST_ROOT}/stage/lib") - set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} ${BOOST_ROOT}) - set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} ${BOOST_LIBRARY_DIRS}) +set(Boost_NO_SYSTEM_PATHS ON) +set(BOOST_INCLUDE_DIRS "${BOOST_ROOT}/include") +set(BOOST_LIBRARY_DIRS "${BOOST_ROOT}/stage/lib") +set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} ${BOOST_ROOT}) +set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} ${BOOST_LIBRARY_DIRS}) endif (Boost_NO_SYSTEM_PATHS) # in case boost is a non-default location # SET(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} "C:/win32libs/boost") # SET(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} "C:/win32libs/boost/lib") +#find_package(Boost REQUIRED) # find the correct OpenMP flag FIND_PACKAGE(OpenMP) if (OPENMP_FOUND) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") else (OPENMP_FOUND) - if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp") - # somehow find_package(openmp) does not work properly with clang - else (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - message(STATUS "Disabling OpenMP support") - endif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") +#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp") +# somehow find_package(openmp) does not work properly with clang +else (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") +message(STATUS "Disabling OpenMP support") +endif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") endif (OPENMP_FOUND) #statically link all gcc stuffs @@ -235,42 +265,41 @@ endif (OPENMP_FOUND) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}") #boost -if(MSVC) - find_package(Boost COMPONENTS QUIET) -else() - find_package(Boost COMPONENTS REQUIRED) +find_package(Boost COMPONENTS timer chrono system filesystem unit_test_framework REQUIRED) -endif() if(AIROUTER) - #CGAL - find_package(CGAL REQUIRED) # for AI router +#CGAL +find_package(CGAL REQUIRED) # for AI router endif() # test all cpp-files in Utest if (BUILD_TESTING OR BUILD_CPPUNIT_TEST) - IF (CMAKE_COMPILER_IS_GNUCXX) - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fprofile-arcs -ftest-coverage") - IF (USE_DUAL_ABI) - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_GLIBCXX_USE_CXX11_ABI=0") - ENDIF(USE_DUAL_ABI) - set(CMAKE_EXE_LINKER_FLAGS "-fprofile-arcs -ftest-coverage") - set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake_modules/") - INCLUDE(CodeCoverage) - set(ENABLE_COVERAGE ON) - SETUP_TARGET_FOR_COVERAGE( - cov # Name for custom target. - ctest # Name of the test driver executable that runs the tests. - # NOTE! This should always have a ZERO as exit code - # otherwise the coverage generation will not complete. - coverage # Name of output directory. - ) - endif (CMAKE_COMPILER_IS_GNUCXX) - file(GLOB_RECURSE test_files "${CMAKE_TEST_DIR}/*.cpp") - # file(GLOB test_py_files "${CMAKE_TEST_DIR}/*/runtest*.py") + +find_package(Boost COMPONENTS timer chrono system filesystem unit_test_framework REQUIRED) +IF (CMAKE_COMPILER_IS_GNUCXX) +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fprofile-arcs -ftest-coverage") +IF (USE_DUAL_ABI) +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_GLIBCXX_USE_CXX11_ABI=0") +ENDIF(USE_DUAL_ABI) +set(CMAKE_EXE_LINKER_FLAGS "-fprofile-arcs -ftest-coverage") +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake_modules/") +INCLUDE(CodeCoverage) +set(ENABLE_COVERAGE ON) +SETUP_TARGET_FOR_COVERAGE( +cov # Name for custom target. +ctest # Name of the test driver executable that runs the tests. +# NOTE! This should always have a ZERO as exit code +# otherwise the coverage generation will not complete. +coverage # Name of output directory. +) + +endif (CMAKE_COMPILER_IS_GNUCXX) +file(GLOB_RECURSE test_files "${CMAKE_TEST_DIR}/*.cpp") +# file(GLOB test_py_files "${CMAKE_TEST_DIR}/*/runtest*.py") endif (BUILD_TESTING OR BUILD_CPPUNIT_TEST) if (BUILD_TESTING) - file(GLOB_RECURSE test_py_files "${CMAKE_TEST_DIR}/*runtest_*.py") +file(GLOB_RECURSE test_py_files "${CMAKE_TEST_DIR}/*runtest_*.py") endif (BUILD_TESTING) # add sources and headers @@ -278,6 +307,10 @@ set(source_files Simulation.cpp general/ArgumentParser.cpp IO/progress_bar.cpp +tinyxml/tinystr.cpp +tinyxml/tinyxml.cpp +tinyxml/tinyxmlerror.cpp +tinyxml/tinyxmlparser.cpp geometry/Building.cpp geometry/Line.cpp @@ -334,11 +367,23 @@ routing/ff_router/FloorfieldViaFM.cpp #routing/ff_router/FFKit.cpp routing/ff_router/UnivFFviaFM.cpp -#global_shortest +#floorfield +routing/ff_router_trips/ffRouterTrips.cpp +#routing/ff_router/LocalFloorfieldViaFM.cpp +routing/ff_router_trips/FloorfieldViaFM.cpp +#routing/ff_router/FFKit.cpp +routing/ff_router_trips/UnivFFviaFM.cpp + + #global_shortest routing/global_shortest/AccessPoint.cpp routing/global_shortest/GlobalRouter.cpp routing/global_shortest/DTriangulation.cpp +#global_shortest +routing/global_shortest_trips/AccessPoint.cpp +routing/global_shortest_trips/GlobalRouterTrips.cpp +routing/global_shortest_trips/DTriangulation.cpp + #general routing/DirectionStrategy.cpp routing/Router.cpp @@ -372,13 +417,21 @@ routing/smoke_router/cognitiveMap/cogmapoutputhandler.cpp routing/smoke_router/cognitiveMap/landmarknetwork.cpp routing/smoke_router/Brain.cpp routing/smoke_router/cognitiveMap/internnavigationnetwork.cpp +visiLibity/source_code/visilibity.cpp - +poly2tri/common/shapes.cpp +poly2tri/sweep/sweep_context.cpp +poly2tri/sweep/advancing_front.cpp +poly2tri/sweep/sweep.cpp +poly2tri/sweep/cdt.cpp events/EventManager.cpp events/Event.cpp +geometry/Trips.cpp +routing/trips_router/TripsRouter.cpp + forms/jpscore.rc -math/KrauszModel.cpp IO/Trips.cpp IO/Trips.h) +math/KrauszModel.cpp ) set(THIRD_PARTY_SRC tinyxml/tinystr.cpp @@ -418,6 +471,7 @@ JPSfire/B_walking_speed/WalkingSpeed.cpp JPSfire/C_toxicity_analysis/ToxicityAnalysis.cpp JPSfire/C_toxicity_analysis/ToxicityOutputhandler.cpp ) + set(header_files #floorfield @@ -429,6 +483,15 @@ routing/ff_router/FloorfieldViaFM.h #routing/ff_router/FFKit.h routing/ff_router/UnivFFviaFM.h +#floorfield +routing/ff_router_trips/ffRouterTrips.h +routing/ff_router_trips/RectGrid.h +routing/ff_router_trips/Trial.h +#routing/ff_router/LocalFloorfieldViaFM.h +routing/ff_router_trips/FloorfieldViaFM.h +#routing/ff_router/FFKit.h +routing/ff_router_trips/UnivFFviaFM.h + #general routing/DirectionStrategy.h routing/DummyRouter.h @@ -443,6 +506,11 @@ routing/global_shortest/GlobalRouter.h routing/global_shortest/AccessPoint.h routing/global_shortest/DTriangulation.h +#global_shortest trips +routing/global_shortest_trips/AccessPoint.h +routing/global_shortest_trips/GlobalRouterTrips.h +routing/global_shortest_trips/DTriangulation.h + #quickest routing/quickest/QuickestPathRouter.h @@ -470,6 +538,7 @@ routing/smoke_router/cognitiveMap/cogmapoutputhandler.h routing/smoke_router/cognitiveMap/landmarknetwork.h routing/smoke_router/Brain.h routing/smoke_router/cognitiveMap/internnavigationnetwork.h +visiLibity/source_code/visilibity.hpp pedestrian/Pedestrian.h pedestrian/PedDistributor.h @@ -487,6 +556,8 @@ voronoi-boost/VoronoiPositionGenerator.h mpi/LCGrid.h +tinyxml/tinyxml.h +tinyxml/tinystr.h general/ArgumentParser.h general/Configuration.h @@ -523,9 +594,22 @@ math/GradientModel.h math/VelocityModel.h math/OperationalModel.h + + +poly2tri/poly2tri.h +poly2tri/common/shapes.h +poly2tri/sweep/cdt.h +poly2tri/common/utils.h +poly2tri/sweep/sweep_context.h +poly2tri/sweep/advancing_front.h +poly2tri/sweep/sweep.h events/EventManager.h events/Event.h -) + + geometry/Trips.h + routing/trips_router/TripsRouter.h + + ) set(JPSFIRE_HDR JPSfire/generic/FDSMesh.h @@ -535,8 +619,8 @@ JPSfire/A_smoke_sensor/SmokeSensor.h JPSfire/B_walking_speed/WalkingSpeed.h JPSfire/C_toxicity_analysis/ToxicityAnalysis.h JPSfire/C_toxicity_analysis/ToxicityOutputhandler.h - ) + SET(AIROUTER_SRC #AI router routing/ai_router/AIRouter.cpp @@ -553,7 +637,24 @@ routing/ai_router/perception/visibleenvironment.cpp routing/ai_router/perception/cgalgeometry.cpp routing/ai_router/perception/sign.cpp routing/ai_router/Cortex.cpp + +#AI router trips +routing/ai_router_trips/AIRouterTrips.cpp +routing/ai_router_trips/BrainStorage.cpp +routing/ai_router_trips/cognitiveMap/associations.cpp +routing/ai_router_trips/cognitiveMap/connection.cpp +routing/ai_router_trips/cognitiveMap/cognitivemap.cpp +routing/ai_router_trips/cognitiveMap/landmark.cpp +routing/ai_router_trips/cognitiveMap/region.cpp +routing/ai_router_trips/cognitiveMap/landmarknetwork.cpp +routing/ai_router_trips/cognitiveMap/internnavigationnetwork.cpp +routing/ai_router_trips/perception/visualsystem.cpp +routing/ai_router_trips/perception/visibleenvironment.cpp +routing/ai_router_trips/perception/cgalgeometry.cpp +routing/ai_router_trips/perception/sign.cpp +routing/ai_router_trips/Cortex.cpp ) + SET(AIROUTER_HDR #AI router routing/ai_router/AIRouter.h @@ -570,6 +671,22 @@ routing/ai_router/perception/visibleenvironment.h routing/ai_router/perception/cgalgeometry.h routing/ai_router/perception/sign.h routing/ai_router/Cortex.h + +#AI router trips +routing/ai_router_trips/AIRouterTrips.h +routing/ai_router_trips/BrainStorage.h +routing/ai_router_trips/cognitiveMap/associations.h +routing/ai_router_trips/cognitiveMap/connection.cpp +routing/ai_router_trips/cognitiveMap/cognitivemap.h +routing/ai_router_trips/cognitiveMap/landmark.h +routing/ai_router_trips/cognitiveMap/region.h +routing/ai_router_trips/cognitiveMap/landmarknetwork.h +routing/ai_router_trips/cognitiveMap/internnavigationnetwork.h +routing/ai_router_trips/perception/visualsystem.h +routing/ai_router_trips/perception/visibleenvironment.h +routing/ai_router_trips/perception/cgalgeometry.h +routing/ai_router_trips/perception/sign.h +routing/ai_router_trips/Cortex.h ) message(STATUS "AIROUTER: ${AIROUTER}") diff --git a/IO/GeoFileParser.cpp b/IO/GeoFileParser.cpp index 4dce1fdbb619d4b1354e3b19cf2f3f3bf2f69754..42407da6c875f9ec731359771d6ffa1fc1f44581 100644 --- a/IO/GeoFileParser.cpp +++ b/IO/GeoFileParser.cpp @@ -482,34 +482,36 @@ bool GeoFileParser::LoadRoutingInfo(Building* building) std::cout << waitingArea->toString() << std::endl; } +// building->AddTrip(trips); + _configuration->GetRoutingEngine()->AddTrip(trips); + - std::cout << trips; } //load routes - TiXmlNode* xTripsNode = xRootNode->FirstChild("routing")->FirstChild("routes"); - - if (xTripsNode) - for (TiXmlElement* trip = xTripsNode->FirstChildElement("route"); trip; - trip = trip->NextSiblingElement("route")) { - - double id = xmltof(trip->Attribute("id"), -1); - if (id==-1) { - Log->Write("ERROR:\t id missing for trip"); - return false; - } - std::string sTrip = trip->FirstChild()->ValueStr(); - std::vector<std::string> vTrip; - vTrip.clear(); - - char* str = (char*) sTrip.c_str(); - char* p = strtok(str, ":"); - while (p) { - vTrip.push_back(xmltoa(p)); - p = strtok(NULL, ":"); - } - _configuration->GetRoutingEngine()->AddTrip(vTrip); - } +// TiXmlNode* xTripsNode = xRootNode->FirstChild("routing")->FirstChild("routes"); +// +// if (xTripsNode) +// for (TiXmlElement* trip = xTripsNode->FirstChildElement("route"); trip; +// trip = trip->NextSiblingElement("route")) { +// +// double id = xmltof(trip->Attribute("id"), -1); +// if (id==-1) { +// Log->Write("ERROR:\t id missing for trip"); +// return false; +// } +// std::string sTrip = trip->FirstChild()->ValueStr(); +// std::vector<std::string> vTrip; +// vTrip.clear(); +// +// char* str = (char*) sTrip.c_str(); +// char* p = strtok(str, ":"); +// while (p) { +// vTrip.push_back(xmltoa(p)); +// p = strtok(NULL, ":"); +// } +// _configuration->GetRoutingEngine()->AddTrip(vTrip); +// } Log->Write("INFO:\tdone with loading extra routing information"); return true; } diff --git a/IO/GeoFileParser.h b/IO/GeoFileParser.h index 07de711703763d13de7b00acc1892df5c38ae547..ef4023e6bed20bf82a60935570d3d4ea0ffebaf9 100644 --- a/IO/GeoFileParser.h +++ b/IO/GeoFileParser.h @@ -25,7 +25,7 @@ #include "../general/Configuration.h" #include "../geometry/Building.h" #include "../geometry/GeometryReader.h" -#include "Trips.h" +#include "../geometry/Trips.h" //TODO: the class name GeoFileParser is misleading as the ``geometry'' file contains among others also relations (transitions) //TODO: between geometries/rooms. Probably, EnvironmentFileParser would be better, still parts of the environment are diff --git a/IO/IniFileParser.cpp b/IO/IniFileParser.cpp index 45523695040df9fdb526d0e3019e18a54f920192..ade857ea97c239f5acc7a37f596d404bdd0d7904 100644 --- a/IO/IniFileParser.cpp +++ b/IO/IniFileParser.cpp @@ -40,10 +40,13 @@ #include "../math/GradientModel.h" #include "../math/VelocityModel.h" #include "../routing/global_shortest/GlobalRouter.h" +#include "../routing/global_shortest_trips/GlobalRouterTrips.h" #include "../routing/quickest/QuickestPathRouter.h" #include "../routing/smoke_router/SmokeRouter.h" #include "../routing/ai_router/AIRouter.h" #include "../routing/ff_router/ffRouter.h" +#include "../routing/ff_router_trips/ffRouterTrips.h" +#include "../routing/trips_router/TripsRouter.h" /* https://stackoverflow.com/questions/38530981/output-compiler-version-in-a-c-program#38531037 */ std::string ver_string(int a, int b, int c) { @@ -1134,6 +1137,13 @@ bool IniFileParser::ParseRoutingStrategies(TiXmlNode* routingNode, TiXmlNode* ag Router *r = new GlobalRouter(id, ROUTING_GLOBAL_SHORTEST); _config->GetRoutingEngine()->AddRouter(r); } + else if ((strategy == "global_shortest_trips") && + (std::find(usedRouter.begin(), usedRouter.end(), id) != usedRouter.end()) ) { + //pRoutingStrategies.push_back(make_pair(id, ROUTING_GLOBAL_SHORTEST)); + Router *r = new GlobalRouterTrips(id, ROUTING_GLOBAL_SHORTEST); + _config->GetRoutingEngine()->AddRouter(r); + } + else if ((strategy == "quickest") && (std::find(usedRouter.begin(), usedRouter.end(), id) != usedRouter.end()) ) { //pRoutingStrategies.push_back(make_pair(id, ROUTING_QUICKEST)); @@ -1169,14 +1179,30 @@ bool IniFileParser::ParseRoutingStrategies(TiXmlNode* routingNode, TiXmlNode* ag exit(EXIT_FAILURE); #endif } - else if ((strategy == "ff_global_shortest") && + else if ((strategy == "AI_trips") && + (std::find(usedRouter.begin(), usedRouter.end(), id) != usedRouter.end()) ) { + #ifdef AIROUTER + Router *r = new AIRouterTrips(id, ROUTING_AI_TRIPS); + _config->GetRoutingEngine()->AddRouter(r); + + Log->Write("\nINFO: \tUsing AIRouter Trips"); + ///Parsing additional options + if (!ParseAIOpts(e)) + return false; + #else + std::cerr << "\nCan not use AI Router. Rerun cmake with option -DAIROUTER=true and recompile.\n"; + exit(EXIT_FAILURE); + #endif + } + + else if ((strategy == "ff_global_shortest_trips") && (std::find(usedRouter.begin(), usedRouter.end(), id) != usedRouter.end()) ) { //pRoutingStrategies.push_back(make_pair(id, ROUTING_FF_GLOBAL_SHORTEST)); - Router *r = new FFRouter(id, ROUTING_FF_GLOBAL_SHORTEST, hasSpecificGoals, _config); + Router *r = new FFRouterTrips(id, ROUTING_FF_GLOBAL_SHORTEST, hasSpecificGoals, _config); _config->GetRoutingEngine()->AddRouter(r); if ((_exit_strat_number == 8) || (_exit_strat_number == 9)){ - Log->Write("\nINFO: \tUsing FF Global Shortest Router"); + Log->Write("\nINFO: \tUsing FF Global Shortest Router Trips"); } else { Log->Write("\nWARNING: \tExit Strategy Number is not 8 or 9!!!"); @@ -1188,6 +1214,26 @@ bool IniFileParser::ParseRoutingStrategies(TiXmlNode* routingNode, TiXmlNode* ag return false; } } + else if ((strategy == "ff_global_shortest") && + (std::find(usedRouter.begin(), usedRouter.end(), id) != usedRouter.end()) ) { + //pRoutingStrategies.push_back(make_pair(id, ROUTING_FF_GLOBAL_SHORTEST)); + Router *r = new FFRouter(id, ROUTING_FF_GLOBAL_SHORTEST, hasSpecificGoals, _config); + _config->GetRoutingEngine()->AddRouter(r); + + if ((_exit_strat_number == 8) || (_exit_strat_number == 9)){ + Log->Write("\nINFO: \tUsing FF Global Shortest Router"); + } + else { + Log->Write("\nWARNING: \tExit Strategy Number is not 8 or 9!!!"); + // config object holds default values, so we omit any set operations + } + + ///Parsing additional options + if (!ParseFfRouterOps(e, ROUTING_FF_GLOBAL_SHORTEST)) { + return false; + } + } + else if ((strategy == "ff_local_shortest") && (std::find(usedRouter.begin(), usedRouter.end(), id) != usedRouter.end()) ) { //pRoutingStrategies.push_back(make_pair(id, ROUTING_FF_GLOBAL_SHORTEST)); @@ -1221,6 +1267,12 @@ bool IniFileParser::ParseRoutingStrategies(TiXmlNode* routingNode, TiXmlNode* ag return false; } } + else if ((strategy == "trips") && + (std::find(usedRouter.begin(), usedRouter.end(), id) != usedRouter.end()) ) { + Router *r = new TripsRouter(id, ROUTING_TRIPS, _config); + _config->GetRoutingEngine()->AddRouter(r); + } + else if (std::find(usedRouter.begin(), usedRouter.end(), id) != usedRouter.end()) { Log->Write("ERROR: \twrong value for routing strategy [%s]!!!\n", strategy.c_str()); diff --git a/Simulation.cpp b/Simulation.cpp index eac581c262b956c3ba26c1c2bc3876b55786fa5d..15b9befd15ffc50db58553e7e19434687b6466a2 100644 --- a/Simulation.cpp +++ b/Simulation.cpp @@ -299,6 +299,12 @@ void Simulation::UpdateRoutesAndLocations() const map<int, Goal*>& goals = _building->GetAllGoals(); auto allRooms = _building->GetAllRooms(); +// for (signed int p = 0; p < allPeds.size(); ++p) { +// Pedestrian* ped = allPeds[p]; +// +// std::cout << "FinalDestination of [" << ped->GetID() << "] in (" << ped->GetRoomID() << ", " << ped->GetSubRoomID() << "/" << ped->GetSubRoomUID() << "): " << ped->GetFinalDestination() << std::endl; +// } + #pragma omp parallel for shared(pedsToRemove, allRooms) for (signed int p = 0; p < allPeds.size(); ++p) { auto ped = allPeds[p]; @@ -307,8 +313,7 @@ void Simulation::UpdateRoutesAndLocations() //set the new room if needed if ((ped->GetFinalDestination() == FINAL_DEST_OUT) - && (room->GetCaption() == "outside")) { - + && (room->GetCaption() == "outside")) { //TODO Hier aendern fuer inside goals? #pragma omp critical(Simulation_Update_pedsToRemove) pedsToRemove.insert(ped); } else if ((ped->GetFinalDestination() != FINAL_DEST_OUT) diff --git a/Simulation.h b/Simulation.h index 518f5e264ed9a04f7643958c1ad660c6765ca737..9449b4972b0a3590c102baf791ed785becfe5294 100644 --- a/Simulation.h +++ b/Simulation.h @@ -41,6 +41,7 @@ #include "math/OperationalModel.h" #include "math/ODESolver.h" #include "routing/global_shortest/GlobalRouter.h" +#include "routing/global_shortest_trips/GlobalRouterTrips.h" #include "routing/quickest/QuickestPathRouter.h" #include "routing/DirectionStrategy.h" #include "routing/RoutingEngine.h" diff --git a/general/ArgumentParser.cpp b/general/ArgumentParser.cpp index e6838806cfe7d8c5f0dfb742587c348512823dd7..f8fb75d26d3f613f933fee40e09f66f742a83022 100644 --- a/general/ArgumentParser.cpp +++ b/general/ArgumentParser.cpp @@ -43,6 +43,7 @@ #include "ArgumentParser.h" #include "../pedestrian/AgentsParameters.h" #include "../routing/global_shortest/GlobalRouter.h" +#include "../routing/global_shortest_trips/GlobalRouterTrips.h" #include "../routing/quickest/QuickestPathRouter.h" #include "../routing/smoke_router/SmokeRouter.h" #include "../IO/IniFileParser.h" diff --git a/general/Macros.h b/general/Macros.h index 1efd970f527945ca5bdebbe0c92c4ff7dab966bc..11ca4b777e50ae4dc5ef2d7e82f3be5d640c4561 100644 --- a/general/Macros.h +++ b/general/Macros.h @@ -128,10 +128,12 @@ enum RoutingStrategy { ROUTING_FROM_FILE, ROUTING_SMOKE, ROUTING_AI, + ROUTING_AI_TRIPS, ROUTING_FLOORFIELD, ROUTING_FF_GLOBAL_SHORTEST, ROUTING_FF_LOCAL_SHORTEST, ROUTING_FF_QUICKEST, + ROUTING_TRIPS, ROUTING_UNDEFINED =-1 }; diff --git a/IO/Trips.cpp b/geometry/Trips.cpp similarity index 91% rename from IO/Trips.cpp rename to geometry/Trips.cpp index c7d02c7bcb87c8ef9bc19308fe8ddd8823cbd94d..5f16cf0dd554c235fca5e61b6d6251180f95116c 100644 --- a/IO/Trips.cpp +++ b/geometry/Trips.cpp @@ -3,7 +3,6 @@ // #include "Trips.h" -//#include <random> #include <boost/random/mersenne_twister.hpp> #include <boost/random/discrete_distribution.hpp> @@ -39,7 +38,8 @@ VertexItr Trips::getGoal(int id) for (boost::tie(vi, vi_end) = boost::vertices(trips); vi != vi_end; ++vi) { if(trips[*vi].getID() == id) return vi; } - return vi_end;} + return vi_end; +} void Trips::addConnection(int sourceId, int destinationId, EdgeProperty& weight) { @@ -87,12 +87,6 @@ int Trips::getNextGoal(int id) } // Random number generator -// std::random_device rd; -// std::mt19937 gen(rd()); -// -// std::default_random_engine generator; -// std::discrete_distribution<double> distribution (weights.begin(), weights.end()); - boost::mt19937 gen; boost::random::discrete_distribution<> dist(weights.begin(), weights.end()); diff --git a/IO/Trips.h b/geometry/Trips.h similarity index 100% rename from IO/Trips.h rename to geometry/Trips.h diff --git a/routing/Router.cpp b/routing/Router.cpp index 1c74795ca5bb241172e50912aa5616df01f016ea..a3b8224d3f47c49e9f70dfb9ac286100331a4f27 100644 --- a/routing/Router.cpp +++ b/routing/Router.cpp @@ -94,3 +94,7 @@ RoutingStrategy Router::GetStrategy() const return _strategy; } +void Router::SetTrips(const Trips& trips){ + _trips = trips; + std::cout << _trips << std::endl; +} diff --git a/routing/Router.h b/routing/Router.h index 8bb99544a02eaa5b7b323a5f2b6d7bd555dc89d8..132b8781cac29c6244e74385cf753370d485efca 100644 --- a/routing/Router.h +++ b/routing/Router.h @@ -34,7 +34,7 @@ #include <vector> #include "../general/Macros.h" -#include "../IO/Trips.h" +#include "../geometry/Trips.h" class Building; class Pedestrian; @@ -140,6 +140,8 @@ public: */ virtual bool ParseAdditionalParameters(){return true;}; + void SetTrips(const Trips& trips); + }; #endif /* _ROUTING_H */ diff --git a/routing/RoutingEngine.cpp b/routing/RoutingEngine.cpp index 10001c9331adefd22879fd99139e5aecdad67395..287797055546c37bb5913b8fd4643e849fed7dec 100644 --- a/routing/RoutingEngine.cpp +++ b/routing/RoutingEngine.cpp @@ -28,6 +28,7 @@ #include "RoutingEngine.h" #include "../pedestrian/Pedestrian.h" +#include "../geometry/Trips.h" using namespace std; @@ -68,19 +69,19 @@ void RoutingEngine::AddRouter(Router* router) } _routersCollection.push_back(router); } - -const vector<string>& RoutingEngine::GetTrip(int index) const -{ - if ((index >= 0) && (index < (int) _tripsCollection.size())) - return _tripsCollection[index]; - else { - char tmp[CLENGTH]; - sprintf(tmp, "ERROR: \tWrong 'index' [%d] > [%d] in Routing::GetTrip()", - index, int(_tripsCollection.size())); - Log->Write(tmp); - exit(EXIT_FAILURE); - } -} +// +//const vector<string>& RoutingEngine::GetTrip(int index) const +//{ +// if ((index >= 0) && (index < (int) _tripsCollection.size())) +// return _tripsCollection[index]; +// else { +// char tmp[CLENGTH]; +// sprintf(tmp, "ERROR: \tWrong 'index' [%d] > [%d] in Routing::GetTrip()", +// index, int(_tripsCollection.size())); +// Log->Write(tmp); +// exit(EXIT_FAILURE); +// } +//} const std::vector<Router*> RoutingEngine::GetAvailableRouters() const { @@ -110,9 +111,14 @@ Router* RoutingEngine::GetRouter(int id) const return /*(Router*)*/ nullptr; } -void RoutingEngine::AddTrip(vector<string> trip) +void RoutingEngine::AddTrip(Trips trip) { - _tripsCollection.push_back(trip); +// _tripsCollection.push_back(trip); +// _tripsCollection = trip; + for(Router* router:_routersCollection) + { + router->SetTrips(trip); + } } bool RoutingEngine::Init(Building* building) diff --git a/routing/RoutingEngine.h b/routing/RoutingEngine.h index 3b39d723c5ffc8ec59463f286ea5f5e28bbbb7b3..94fac8e77b44a56b5b4734c656da47e546490659 100644 --- a/routing/RoutingEngine.h +++ b/routing/RoutingEngine.h @@ -36,6 +36,7 @@ class Pedestrian; +class Trips; class RoutingEngine { public: @@ -60,14 +61,14 @@ public: * Add a new trip to the system. Individual pedestrian can be assigned a particular trip. * @param trip */ - void AddTrip(std::vector<std::string> trip); + void AddTrip(Trips trip); - /** - * Return a trip/route with the particular id - * @param id, the trip id - * @return the corresponding trip - */ - const std::vector<std::string>& GetTrip(int id) const; +// /** +// * Return a trip/route with the particular id +// * @param id, the trip id +// * @return the corresponding trip +// */ +// const std::vector<std::string>& GetTrip(int id) const; /** * @return all available routers @@ -111,7 +112,7 @@ private: /// collections of all routers used std::vector<Router*> _routersCollection; /// collection of all trips/routes - std::vector<std::vector<std::string> >_tripsCollection; + Trips _tripsCollection; }; #endif /* ROUTINGENGINE_H_ */ diff --git a/routing/ai_router_trips/BrainStorage.cpp b/routing/ai_router_trips/BrainStorage.cpp new file mode 100644 index 0000000000000000000000000000000000000000..21fdaa52bc993b335b3a9ab7e184bec295231d56 --- /dev/null +++ b/routing/ai_router_trips/BrainStorage.cpp @@ -0,0 +1,383 @@ +/** + * \file CognitiveMapStorage.cpp + * \date Feb 1, 2014 + * \version v0.7 + * \copyright <2009-2015> Forschungszentrum Jülich GmbH. All rights reserved. + * + * \section License + * This file is part of JuPedSim. + * + * JuPedSim is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * JuPedSim 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 Lesser General Public License + * along with JuPedSim. If not, see <http://www.gnu.org/licenses/>. + * + * \section Description + * Cognitive Map Storage + * + * + **/ + + +#include "BrainStorage.h" +#include "cognitiveMap/internnavigationnetwork.h" + +#include "../../tinyxml/tinyxml.h" +#include <memory> + +#include "../../geometry/Building.h" +#include "../../pedestrian/Pedestrian.h" +//#include "NavigationGraph.h" + + +AIBrainStorage::AIBrainStorage(const Building * const b, const std::string &cogMapFiles, const std::string &signFiles) + : _building(b),_visibleEnv(VisibleEnvironment(b)),_cogMapFiles(cogMapFiles),_signFiles(signFiles) +{ + + //Set signs + ParseSigns(); + +} + +Cortex* AIBrainStorage::operator[] (BStorageKeyType key) +{ + BStorageType::iterator it = _corteces.find(key); + if(it == _corteces.end()) { + CreateCortex(key); + } + + return _corteces[key].get(); +} + +void AIBrainStorage::ParseCogMap(BStorageKeyType ped) +{ + + _regions.clear(); + + //create filename + + int groupId = ped->GetGroup(); + if (_cogMapFiles=="") + return; + std::string cMFileName=_cogMapFiles; + cMFileName.replace(cMFileName.size()-4,4,std::to_string(groupId)+".xml"); + + std::string cogMapFilenameWithPath = _building->GetProjectRootDir() + cMFileName; + + Log->Write(cogMapFilenameWithPath); + TiXmlDocument doccogMap(cogMapFilenameWithPath); + if (!doccogMap.LoadFile()) { + Log->Write("ERROR: \t%s", doccogMap.ErrorDesc()); + Log->Write("\t could not parse the cognitive map file"); + Log->Write("Cognitive map not specified"); + return; + } + + TiXmlElement* xRootNode = doccogMap.RootElement(); + if( ! xRootNode ) { + Log->Write("ERROR:\tRoot element does not exist"); + Log->Write("Cognitive map not specified"); + return; + } + + if( xRootNode->ValueStr () != "cognitiveMap" ) { + Log->Write("ERROR:\tRoot element value is not 'cognitiveMap'."); + Log->Write("Cognitive map not specified"); + return; + } + if(xRootNode->Attribute("unit")) + if(std::string(xRootNode->Attribute("unit")) != "m") { + Log->Write("ERROR:\tOnly the unit m (meters) is supported. \n\tYou supplied [%s]",xRootNode->Attribute("unit")); + Log->Write("Cognitive map not specified"); + return; + } + + double version = xmltof(xRootNode->Attribute("version"), -1); + + if (version < std::stod(JPS_OLD_VERSION) ) { + Log->Write(" \tWrong geometry version!"); + Log->Write(" \tOnly version >= %s supported",JPS_VERSION); + Log->Write(" \tPlease update the version of your geometry file to %s",JPS_VERSION); + Log->Write("Cognitive map not specified"); + return; + } + + //processing the regions node + TiXmlNode* xRegionsNode = xRootNode->FirstChild("regions"); + if (!xRegionsNode) { + Log->Write("ERROR: \tCognitive map file without region definition!"); + Log->Write("Cognitive map not specified"); + return; + } + + for(TiXmlElement* xRegion = xRegionsNode->FirstChildElement("region"); xRegion; + xRegion = xRegion->NextSiblingElement("region")) + { + + std::string idR = xmltoa(xRegion->Attribute("id"), "-1"); + std::string captionR = xmltoa(xRegion->Attribute("caption")); + std::string pxinmapR = xmltoa(xRegion->Attribute("px"),"-1"); + std::string pyinmapR = xmltoa(xRegion->Attribute("py"),"-1"); + std::string aR = xmltoa(xRegion->Attribute("a"),"-1"); + std::string bR = xmltoa(xRegion->Attribute("b"),"-1"); + + AIRegion region (Point(std::stod(pxinmapR),std::stod(pyinmapR))); + region.SetId(std::stoi(idR)); + + region.SetCaption(captionR); + region.SetPosInMap(Point(std::stod(pxinmapR),std::stod(pyinmapR))); + region.SetA(std::stod(aR)); + region.SetB(std::stod(bR)); + + + + //processing the landmarks node + TiXmlNode* xLandmarksNode = xRegion->FirstChild("landmarks"); + if (!xLandmarksNode) { + Log->Write("ERROR: \tCognitive map file without landmark definition!"); + Log->Write("No landmarks specified"); + return; + } + + + + for(TiXmlElement* xLandmark = xLandmarksNode->FirstChildElement("landmark"); xLandmark; + xLandmark = xLandmark->NextSiblingElement("landmark")) + { + + std::string id = xmltoa(xLandmark->Attribute("id"), "-1"); + std::string caption = xmltoa(xLandmark->Attribute("caption")); + std::string type = xmltoa(xLandmark->Attribute("type"),"-1"); + std::string roomId = xmltoa(xLandmark->Attribute("subroom1_id"),"-1"); + std::string pxreal = xmltoa(xLandmark->Attribute("pxreal"),"-1"); + std::string pyreal = xmltoa(xLandmark->Attribute("pyreal"),"-1"); + std::string pxinmap = xmltoa(xLandmark->Attribute("px"),"-1"); + std::string pyinmap = xmltoa(xLandmark->Attribute("py"),"-1"); + std::string a = xmltoa(xLandmark->Attribute("a"),"-1"); + std::string b = xmltoa(xLandmark->Attribute("b"),"-1"); + + AILandmark landmark(Point(std::stod(pxreal),std::stod(pyreal))); + + if (roomId=="NaN") + { + Log->Write("ERROR:\t Subroom Id is NaN!"); + return; + } + landmark.SetId(std::stoi(id)); + landmark.SetCaption(caption); + landmark.SetType(type); + landmark.SetRealPos(Point(std::stod(pxreal),std::stod(pyreal))); + landmark.SetPosInMap(Point(std::stod(pxinmap),std::stod(pyinmap))); + landmark.SetA(std::stod(a)); + landmark.SetB(std::stod(b)); + //landmark->SetRoom(_building->GetSubRoomByUID(std::stoi(roomId))); + + //processing the rooms node + TiXmlNode* xAssociationsNode = xLandmark->FirstChild("associations"); + if (!xAssociationsNode) { + Log->Write("Landmark with no association!"); + continue; + } + + for(TiXmlElement* xAsso = xAssociationsNode->FirstChildElement("association"); xAsso; + xAsso = xAsso->NextSiblingElement("association")) + { + std::string asso_id = xmltoa(xAsso->Attribute("id"), "-1"); + std::string asso_caption = xmltoa(xAsso->Attribute("caption"), "0"); + //std::string asso_type = xmltoa(xAsso->Attribute("type"),"-1"); + std::string asso_x = xmltoa(xAsso->Attribute("px"),"-1"); + std::string asso_y = xmltoa(xAsso->Attribute("py"),"-1"); + std::string asso_a = xmltoa(xAsso->Attribute("a"),"-1"); + std::string asso_b = xmltoa(xAsso->Attribute("b"),"-1"); + int connection = std::stoi(xmltoa(xAsso->Attribute("connectedwith"),"-1")); + //std::string priority = xmltoa(xAsso->Attribute("priority"),"-1"); + + AILandmark assolandmark(Point(std::stod(asso_x),std::stod(asso_y)), + std::stod(asso_a),std::stod(asso_b)); + assolandmark.SetId(std::stod(asso_id)); + //std::cout << assolandmark.GetId() << std::endl; + assolandmark.SetCaption(asso_caption); + //assolandmark->AddConnection(std::stoi(connection)); + //assolandmark->SetPriority(std::stod(priority)); + bool connected=false; + if (connection==landmark.GetId()) + connected=true; + landmark.AddAssociation(AIAssociation(&landmark,&assolandmark,connected)); + + region.AddLandmarkSubCs(assolandmark); + + + } + + region.AddLandmark(landmark); + Log->Write("INFO:\tLandmark added!"); + + } + + //processing the connections node + TiXmlNode* xConnectionsNode = xRegion->FirstChild("connections"); + if (!xConnectionsNode) { + //Log->Write("ERROR: \tGeometry file without landmark definition!"); + Log->Write("No connections specified"); + //return; + } + + else + { + for(TiXmlElement* xConnection = xConnectionsNode->FirstChildElement("connection"); xConnection; + xConnection = xConnection->NextSiblingElement("connection")) + { + std::string idC = xmltoa(xConnection->Attribute("id"), "-1"); + std::string captionC = xmltoa(xConnection->Attribute("caption")); + std::string typeC = xmltoa(xConnection->Attribute("type"),"-1"); + std::string landmark1 = xmltoa(xConnection->Attribute("landmark1_id"),"-1"); + std::string landmark2 = xmltoa(xConnection->Attribute("landmark2_id"),"-1"); + + + AIConnection connection = AIConnection(std::stoi(idC),captionC,typeC,std::stoi(landmark1),std::stoi(landmark2)); + + region.AddConnection(connection); + Log->Write("INFO:\tConnection added!"); + } + } + + _regions.push_back(region); + Log->Write("INFO:\tRegion added!"); + } +} + +void AIBrainStorage::CreateCortex(BStorageKeyType ped) +{ + + //todo: the possibility to have more then one creator. + + _corteces.insert(std::make_pair(ped, std::unique_ptr<Cortex>(new Cortex(_building, + ped, + &_visibleEnv, + &_roominternalNetworks))));// creator->CreateCognitiveMap(ped))); + + + + + // load cogmap and do first cognitive step + ParseCogMap(ped); + _corteces[ped]->GetCognitiveMap().AddRegions(_regions); + _corteces[ped]->GetCognitiveMap().InitLandmarkNetworksInRegions(); + _corteces[ped]->GetCognitiveMap().FindMainDestination(); + //debug + //cognitive_maps[ped]->GetNavigationGraph()->WriteToDotFile(building->GetProjectRootDir()); +} + +void AIBrainStorage::InitInternalNetwork(const SubRoom* sub_room) +{ + + _roominternalNetworks.emplace(sub_room,ptrIntNetwork(new InternNavigationNetwork(sub_room))); + + for (Crossing* crossing:sub_room->GetAllCrossings()) + { + _roominternalNetworks[sub_room]->AddVertex(crossing); + } + + for (Transition* transition:sub_room->GetAllTransitions()) + { + _roominternalNetworks[sub_room]->AddVertex(transition); + } + + for (Hline* hline:sub_room->GetAllHlines()) + { + _roominternalNetworks[sub_room]->AddVertex(hline); + } + + _roominternalNetworks[sub_room]->EstablishConnections(); + +} + +void AIBrainStorage::ParseSigns() { + + if (_signFiles=="") + { + Log->Write("INFO: \tNo signs specified \n"); + return; + } + std::string signFilenameWithPath = _building->GetProjectRootDir() + _signFiles; + + Log->Write("INFO: \tRead Signs from " + signFilenameWithPath); + TiXmlDocument docSigns(signFilenameWithPath); + if (!docSigns.LoadFile()) { + Log->Write("WARNING: \t%s", docSigns.ErrorDesc()); + Log->Write("\t could not parse the sign file"); + Log->Write("No signs specified"); + return; + } + + TiXmlElement *xRootNode = docSigns.RootElement(); + if (!xRootNode) { + Log->Write("WARNING:\tRoot element does not exist"); + Log->Write("No signs specified"); + return; + } + + if (xRootNode->ValueStr() != "signage") { + Log->Write("WARNING:\tRoot element value is not 'signage'."); + Log->Write("No signs specified"); + return; + } + if (xRootNode->Attribute("unit")) + if (std::string(xRootNode->Attribute("unit")) != "m") { + Log->Write("WARNING:\tOnly the unit m (meters) is supported. \n\tYou supplied [%s]", + xRootNode->Attribute("unit")); + Log->Write("No signs specified"); + return; + } + + double version = xmltof(xRootNode->Attribute("version"), -1); + + if (version < std::stod(JPS_OLD_VERSION)) { + Log->Write("WARNING: \tWrong file version!"); + Log->Write(" \tOnly version >= %s supported", JPS_VERSION); + Log->Write(" \tPlease update the version of your file to %s", JPS_VERSION); + Log->Write("No signs specified"); + return; + } + + //processing the regions node + TiXmlNode *xSignsNode = xRootNode->FirstChild("signs"); + if (!xSignsNode) { + Log->Write("WARNING: \tSignage file without signs!"); + Log->Write("No signs specified"); + return; + } + + + for (TiXmlElement *xSign = xSignsNode->FirstChildElement("sign"); xSign; + xSign = xSign->NextSiblingElement("sign")) { + + std::string id = xmltoa(xSign->Attribute("id"), "-1"); + std::string room_id = xmltoa(xSign->Attribute("room_id"), "-1"); + std::string px = xmltoa(xSign->Attribute("px"), "-1"); + std::string py = xmltoa(xSign->Attribute("py"), "-1"); + std::string alpha = xmltoa(xSign->Attribute("alpha"), "-1"); + std::string alphaPointing = xmltoa(xSign->Attribute("alphaPointing"), "-1"); + + Log->Write("Sign read!"); + + _visibleEnv.AddSign(_building->GetAllRooms().at(std::stod(room_id)).get(),Sign(std::stod(id), std::stod(room_id), Point(std::stod(px), std::stod(py)), + std::stod(alpha), std::stod(alphaPointing))); + + } + +} + +void AIBrainStorage::DeleteCortex(BStorageKeyType ped) +{ + _corteces.erase(ped); + +} diff --git a/routing/ai_router_trips/BrainStorage.h b/routing/ai_router_trips/BrainStorage.h new file mode 100644 index 0000000000000000000000000000000000000000..a01170eaf1ab31e38a716027319fc998d7682331 --- /dev/null +++ b/routing/ai_router_trips/BrainStorage.h @@ -0,0 +1,92 @@ +/** + * \file CognitiveMapStorage.h + * \date Feb 1, 2014 + * \version v0.7 + * \copyright <2009-2015> Forschungszentrum Jülich GmbH. All rights reserved. + * + * \section License + * This file is part of JuPedSim. + * + * JuPedSim is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * JuPedSim 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 Lesser General Public License + * along with JuPedSim. If not, see <http://www.gnu.org/licenses/>. + * + * \section Description + * Cognitive Map Storage + * + * + **/ + + +#ifndef BRAINSTORAGE_H_ +#define BRAINSTORAGE_H_ + +#include <unordered_map> +#include <vector> +#include "./cognitiveMap/cognitivemap.h" +#include "./perception/visibleenvironment.h" +#include "Cortex.h" + +class Building; +class Pedestrian; +class InternNavigationNetwork; + + +typedef const Pedestrian * BStorageKeyType; +typedef std::unique_ptr<Cortex> BStorageValueType; +typedef std::unordered_map<BStorageKeyType, BStorageValueType> BStorageType; + + + +/** + * @brief Brain Storage + * + * Cares about corteces, creation and delivery + * + */ +class AIBrainStorage { +public: + AIBrainStorage(const Building * const b, const std::string& cogMapFiles="", const std::string& signFiles=""); + + + Cortex* operator[] (BStorageKeyType key); + + void DeleteCortex(BStorageKeyType ped); + + +private: + const Building * const _building; + BStorageType _corteces; + + + //perception + //Complete environment + VisibleEnvironment _visibleEnv; + + //cognitive map + std::vector<AIRegion> _regions; + std::string _cogMapFiles; + std::string _signFiles; + + //Cortex + void CreateCortex(BStorageKeyType ped); + void ParseCogMap(BStorageKeyType ped); + void ParseSigns(); + + + // internal graph network in every room (for locomotion purposes) + void InitInternalNetwork(const SubRoom *sub_room); + std::unordered_map<const SubRoom*,ptrIntNetwork> _roominternalNetworks; + +}; + +#endif /* BRAINSTORAGE_H_ */ diff --git a/routing/ai_router_trips/Cortex.cpp b/routing/ai_router_trips/Cortex.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1f1fe31ece9a0a0f833cf36b75bdbb79e016532c --- /dev/null +++ b/routing/ai_router_trips/Cortex.cpp @@ -0,0 +1,554 @@ +#include <cstdlib> +#include <cmath> +#include "Cortex.h" +#include "../../pedestrian/Pedestrian.h" + +Cortex::Cortex() +{ + +} + +Cortex::Cortex(const Building *b, const Pedestrian *ped, const VisibleEnvironment *env, std::unordered_map<const SubRoom *, ptrIntNetwork> *roominternalNetworks) +{ + _b=b; + _ped=ped; + _wholeEnvironment=env; + _intNetworks=roominternalNetworks; + + _perceptionAbilities=VisualSystem(_b,_ped,_wholeEnvironment); + _cMap=AICognitiveMap(_ped,&_perceptionAbilities); + _lastBestVisNavLine=NavLine(Line(Point(0,0),Point(0,0))); + + if (_b->GetConfig()->GetRandomNumberGenerator()->GetRandomRealBetween0and1()<=0.11) + _statPreferToGoDown=true; + else + _statPreferToGoDown=false; + + +} + +AICognitiveMap &Cortex::GetCognitiveMap() +{ + return _cMap; +} + +VisualSystem &Cortex::GetPerceptionAbilities() +{ + return _perceptionAbilities; +} + +const NavLine* Cortex::GetNextNavLine(const NavLine* nextTarget) +{ + const SubRoom* currentSubRoom = _b->GetSubRoomByUID(_ped->GetSubRoomUID()); + + _currentIntNetwork=*(_intNetworks->operator [](currentSubRoom)); + + return _currentIntNetwork.GetNextNavLineOnShortestPathToTarget(_ped->GetPos(),nextTarget); +} + +bool Cortex::HlineReached() const +{ + const SubRoom* currentSubRoom = _b->GetSubRoomByUID(_ped->GetSubRoomUID()); + + for (Hline* hline:currentSubRoom->GetAllHlines()) + { + // if distance hline to ped lower than 1 m + if (hline->DistTo(_ped->GetPos())<1) + { + return true; + } + } + + return false; +} + + +std::vector<const NavLine*> Cortex::SortConGeneralKnow(const std::vector<const NavLine*> &navLines) +{ + + //std::cout << dis(_randomEngine) << std::endl; + + std::vector<const NavLine*> sortedNavLines; + std::vector<const NavLine*> sortedNavLinesOutside; + std::vector<const NavLine*> sortedNavLinesLobbies; + std::vector<const NavLine*> sortedNavLinesCirculs; + std::vector<const NavLine*> sortedNavLinesCommons; + std::vector<const NavLine*> sortedNavLinesStairsUp; + std::vector<const NavLine*> sortedNavLinesStairs; + std::vector<const NavLine*> sortedNavLinesStairsDown; + std::vector<const NavLine*> sortedNavLinesTrains; + + for (const NavLine* navLine:navLines) + { + + // navLine a transition? + const Transition* transition = _perceptionAbilities.IsTransition(navLine); + + if (!transition) + { + + if (_ped->GetRoomID()==11) + { + if (IsInsideRectangle(navLine->GetCentre(),Point(7.52,52.19),Point(11.31,46.0)) + || IsInsideRectangle(navLine->GetCentre(),Point(20.25,68.38),Point(27.2,64.91)) + || IsInsideRectangle(navLine->GetCentre(),Point(4.48,79.5),Point(13.78,73.14)) + || IsInsideRectangle(navLine->GetCentre(),Point(-7.06,54.69),Point(-1.48,50.48))) + { + sortedNavLinesStairsDown.push_back(navLine); + continue; + } + } + + + // is line on same floor? If yes: subRoom!=nullptr + const SubRoom* subRoom=_perceptionAbilities.IsInSubRoom(navLine->GetCentre()); + if (!subRoom) + sortedNavLinesStairsDown.push_back(navLine); + else if (subRoom->GetType()=="Lobby") + sortedNavLinesLobbies.push_back(navLine); + else if (subRoom->GetType()=="Corridor") + sortedNavLinesCirculs.push_back(navLine); + else if (subRoom->GetType()=="train") + sortedNavLinesTrains.push_back(navLine); + else + sortedNavLinesCommons.push_back(navLine); + } + else if (transition->IsExit()) + { + sortedNavLinesOutside.push_back(transition); + } + + else + { + if (transition->GetOtherRoom(_ped->GetRoomID())->GetZPos()>_b->GetRoom(_ped->GetRoomID())->GetZPos()) + { + sortedNavLinesStairsUp.push_back(transition); + } + + else + { + sortedNavLinesStairsDown.push_back(transition); + + } + + } + } + + + + //settings for underground station! + sortedNavLines.insert(std::end(sortedNavLines), std::begin(sortedNavLinesOutside), std::end(sortedNavLinesOutside)); + sortedNavLines.insert(std::end(sortedNavLines), std::begin(sortedNavLinesLobbies), std::end(sortedNavLinesLobbies)); + sortedNavLines.insert(std::end(sortedNavLines), std::begin(sortedNavLinesStairsUp), std::end(sortedNavLinesStairsUp)); + sortedNavLines.insert(std::end(sortedNavLines), std::begin(sortedNavLinesStairs), std::end(sortedNavLinesStairs)); + sortedNavLines.insert(std::end(sortedNavLines), std::begin(sortedNavLinesCirculs), std::end(sortedNavLinesCirculs)); + sortedNavLines.insert(std::end(sortedNavLines), std::begin(sortedNavLinesCommons), std::end(sortedNavLinesCommons)); + sortedNavLines.insert(std::end(sortedNavLines), std::begin(sortedNavLinesStairsDown), std::end(sortedNavLinesStairsDown)); + sortedNavLines.insert(std::end(sortedNavLines), std::begin(sortedNavLinesTrains), std::end(sortedNavLinesTrains)); + + + return sortedNavLines; +} + + + +const NavLine Cortex::FindApprCrossing(const std::vector<NavLine>& navLines) +{ + + // suffle navLines randomly -> if no factor influences ranking of navLines a specific navLine is chosen randomly. + std::vector<NavLine> shuffledNavLines=navLines; + + std::shuffle(shuffledNavLines.begin(),shuffledNavLines.end(),_b->GetConfig()->GetRandomNumberGenerator()->GetRandomEngine()); + + // knowledge generalized <- signage <- Cognitive Map <- Random shuffle + const NavLine* nextNvLine=SortConGeneralKnow(SortConSignage(_cMap.SortConLeastAngle(_cMap.GetNextTarget(),shuffledNavLines,_lastBestVisNavLine))).front(); + + _cMap.SetBestChoice(*nextNvLine); + if (!nextNvLine) + throw(std::invalid_argument("No NavLine found. Ped is trapped")); + else + return *nextNvLine; + +} + +const NavLine Cortex::FindApprVisibleCrossing(const NavLine& navLine, const std::vector<NavLine> &navLines) +{ + // if transition or outside is in sight + Point normal = Point(-(navLine.GetPoint2()-navLine.GetPoint1())._y,(navLine.GetPoint2()-navLine.GetPoint1())._x); + normal=normal/normal.Norm(); + Point dirVec1 = navLine.GetCentre()+normal*0.5-_ped->GetPos(); + Point dirVec2 = navLine.GetCentre()-normal*0.5-_ped->GetPos(); + + std::vector<int> transIDs= _b->GetRoom(_ped->GetRoomID())->GetAllTransitionsIDs(); + + for (int transID: transIDs) + { + if (*(_b->GetTransitionByUID(transID))==navLine) + { + Line targetToHead(Point(0.0,0.0),Point(0.0,0.0)); + if (dirVec1.Norm() > dirVec2.Norm()) + targetToHead=Line(navLine.GetPoint1()+normal*0.5,navLine.GetPoint2()+normal*0.5); + else + targetToHead=Line(navLine.GetPoint1()-normal*0.5,navLine.GetPoint2()-normal*0.5); + + const NavLine visibleNavLine(targetToHead); + return visibleNavLine; + } + } + + // not a transition + if (_lastBestVisNavLine.GetPoint1()==Point(0,0) && _lastBestVisNavLine.GetPoint2()==Point(0,0)) + { + double minDistance=FLT_MAX; + for (const NavLine& candidate:navLines) + { + if (candidate.DistTo(navLine.GetCentre())<=minDistance) + { + minDistance=candidate.DistTo(navLine.GetCentre()); + _lastBestVisNavLine=candidate; + } + } + return TargetToHead(&_lastBestVisNavLine); + } + + bool statFoundBestChoice=false; + + for (const NavLine& candidate:navLines) + { + Point vec1=candidate.GetPoint1()-_lastBestVisNavLine.GetPoint1(); + Point vec2=candidate.GetPoint2()-_lastBestVisNavLine.GetPoint2(); + Point vec3=candidate.GetPoint2()-_lastBestVisNavLine.GetPoint1(); + Point vec4=candidate.GetPoint1()-_lastBestVisNavLine.GetPoint2(); + if (vec1.Norm()<0.1 || vec2.Norm()<0.1 || vec3.Norm()<0.1 || vec4.Norm()<0.1) + { + _lastBestVisNavLine=candidate; + statFoundBestChoice=true; + } + } + + double pedAlpha = std::atan2(_ped->GetV()._x,_ped->GetV()._y); + bool statFound=false; + if (statFoundBestChoice) + { + + + double minDistance=_lastBestVisNavLine.DistTo(navLine.GetCentre()); + for (const NavLine& candidate:navLines) + { + Point vecPedNavLine = candidate.GetCentre()-_ped->GetPos(); + double navAlpha=std::atan2(vecPedNavLine._x,vecPedNavLine._y); + double diffAlpha=std::fabs(navAlpha-pedAlpha); + if (diffAlpha>M_PI) + diffAlpha=2*M_PI-diffAlpha; + if (diffAlpha<M_PI/3.0 && candidate.DistTo(navLine.GetCentre())<=minDistance*0.8) + { + minDistance=candidate.DistTo(navLine.GetCentre()); + _lastBestVisNavLine=candidate; + statFound=true; + } + } + + + if (!statFound) + { + for (const NavLine& candidate:navLines) + { + if (candidate.DistTo(navLine.GetCentre())<=minDistance*0.2) + { + minDistance=candidate.DistTo(navLine.GetCentre()); + _lastBestVisNavLine=candidate; + } + } + } + } + else + { + double minDistance=FLT_MAX; + for (const NavLine& candidate:navLines) + { + Point vecPedNavLine = candidate.GetCentre()-_ped->GetPos(); + double navAlpha=std::atan2(vecPedNavLine._x,vecPedNavLine._y); + //Log->Write(std::to_string(std::fabs(navAlpha-pedAlpha)*180/M_PI)); + double diffAlpha=std::fabs(navAlpha-pedAlpha); + if (diffAlpha>M_PI) + diffAlpha=2*M_PI-diffAlpha; + if (diffAlpha<M_PI/3.0 && candidate.DistTo(navLine.GetCentre())<=minDistance) + { + minDistance=candidate.DistTo(navLine.GetCentre()); + _lastBestVisNavLine=candidate; + statFound=true; + } + } + + if (!statFound) + { + for (const NavLine& candidate:navLines) + { + if (candidate.DistTo(navLine.GetCentre())<=minDistance) + { + minDistance=candidate.DistTo(navLine.GetCentre()); + _lastBestVisNavLine=candidate; + } + } + } + } + + + return TargetToHead(&_lastBestVisNavLine); +} + +const NavLine Cortex::TargetToHead(const NavLine* visibleCrossing) const +{ + Point P1 = visibleCrossing->GetPoint1(); + Point P2 = visibleCrossing->GetPoint2(); + Point dirVector=P2-P1; + dirVector=dirVector/dirVector.Norm(); + Point normalVector = Point(-dirVector._y,dirVector._x); + + Point distVec1=P1-_ped->GetPos(); + Point distVec2=P2-_ped->GetPos(); + + Point NavLineP1; + Point NavLineP2; + if (distVec1.Norm()<distVec2.Norm()) + { + NavLineP1=P1; + NavLineP2=P1-normalVector*1.5; + } + else + { + NavLineP1=P2; + NavLineP2=P2-normalVector*1.5; + } + + const NavLine navLine(Line(NavLineP1,NavLineP2)); + + return navLine; +} + + +std::vector<const NavLine *> Cortex::SortConSignage(const std::vector<const NavLine *> &navLines) +{ + + std::vector<const Sign*> perceivedSigns = _perceptionAbilities.TryToDetectSigns(); + std::vector<const Sign*> cSigns; + + for (const Sign* sign:perceivedSigns) + { + if (std::find(_notUsedSigns.begin(),_notUsedSigns.end(),sign)==_notUsedSigns.end()) + cSigns.push_back(sign); + } + + if (cSigns.empty()) + return navLines; + + + std::vector<const NavLine*> sortedNavLines; + //determine decisive sign (sign which has the recent instruction) + + const Sign* decisiveSign=nullptr; + if (cSigns.size()>1) + decisiveSign=DetermineDecisiveSign(cSigns); + else + decisiveSign=cSigns.front(); + + if (!decisiveSign) + return navLines; + + Point vecToNavLine; + double angleVecToNavLine; + double anglediff; + + // find navLine which is most appropriately to follow sign instruction + + for (const NavLine* navLine:navLines) + { + + vecToNavLine = navLine->GetCentre()-decisiveSign->GetPos(); + angleVecToNavLine = std::atan2(vecToNavLine._y,vecToNavLine._x)*180.0/M_PI; + + anglediff=std::fabs(angleVecToNavLine-decisiveSign->GetAlphaPointing()); + if (anglediff>180.0) + anglediff=360.0-anglediff; + + + if (anglediff<90.0) + { + sortedNavLines.push_back(navLine); + } + } + + if (sortedNavLines.empty()) + return navLines; + + const NavLine* bestChoice=nullptr; + + if (sortedNavLines.size()>1) + { + std::vector<double> orthogonalDistances; + for (const NavLine* navLine:sortedNavLines) + { + orthogonalDistances.push_back(OrthogonalDistanceFromTo(navLine,*decisiveSign)); + } + size_t index=std::min_element(orthogonalDistances.begin(),orthogonalDistances.end())-orthogonalDistances.begin(); + + bestChoice = *(sortedNavLines.begin()+index); + sortedNavLines.clear(); + sortedNavLines.push_back(bestChoice); + } + else + bestChoice=sortedNavLines.front(); + + + + + for (const NavLine* navLine:navLines) + { + if (navLine!=bestChoice) + { + sortedNavLines.push_back(navLine); + } + } + + return sortedNavLines; +} + +const Sign* Cortex::DetermineDecisiveSign(const std::vector<const Sign*> &signs) +{ + + std::vector<const Sign*> cSigns=signs; + + if (cSigns.size()==1) + return cSigns.front(); + + + std::vector<double> angles; + std::vector<const Sign*> signsNotPointing; + + for (size_t i=0; i<cSigns.size(); ++i) + { + std::vector<double> cAngles; + + for (size_t j=0; j<cSigns.size(); ++j) + { + if (i!=j) + { + Point vec_ij=cSigns[j]->GetPos()-cSigns[i]->GetPos(); + cAngles.push_back(std::fabs(cSigns[i]->GetAlphaPointing()-std::atan2(vec_ij._y,vec_ij._x)*180.0/M_PI)); + } + } + size_t index=std::min_element(cAngles.begin(),cAngles.end())-cAngles.begin(); + + if (cAngles.at(index)>45.0) + signsNotPointing.push_back(cSigns[i]); + angles.push_back(cAngles.at(index)); + } + + //how many angles are greater than 45 degrees or rather how many signs do not point to another? + if (signsNotPointing.size()>1) + { + std::shuffle(signsNotPointing.begin(),signsNotPointing.end(),_b->GetConfig()->GetRandomNumberGenerator()->GetRandomEngine()); + for (auto it=signsNotPointing.begin();it!=signsNotPointing.end();++it) + { + if (it==signsNotPointing.begin()) + continue; + _notUsedSigns.push_back(*it); + } + return signsNotPointing.front(); + } + else + { + + size_t index=std::max_element(angles.begin(),angles.end())-angles.begin(); + return cSigns.at(index); + } + +} + +double Cortex::OrthogonalDistanceFromTo(const Sign &sign1, const Sign &sign2) const +{ + // line created with dir Vector of sign2 + + double a; + double b; + + if (sign2.GetAlpha()==90.0 || sign2.GetAlpha()==270.0) + { + a=0.0; + b=1.0; + } + else + { + a=1.0; + b = std::tan(sign2.GetAlpha()*M_PI/180.); + } + + Point pa = sign2.GetPos(); + Point dirVec = Point(a,b); + + Line signLine = Line(pa-dirVec*1000,pa+dirVec*1000); + + return signLine.DistTo(sign1.GetPos()); +} + +double Cortex::OrthogonalDistanceFromTo(const NavLine* navLine, const Sign& sign) const +{ + // line created with dir Vector of sign + + double a; + double b; + //sign1->GetAlpha(); + if (sign.GetAlphaPointing()==90.0 || sign.GetAlphaPointing()==270.0) + { + a=0.0; + b=1.0; + } + else + { + a=1.0; + b = std::tan(sign.GetAlphaPointing()*M_PI/180.); + } + + Point pa = sign.GetPos(); + Point dirVec = Point(a,b); + + Line signLine = Line(pa-dirVec*1000,pa+dirVec*1000); + + return signLine.DistTo(navLine->GetCentre()); +} + +double Cortex::OrthogonalDistanceFromTo(const NavLine* navLine, const Point &pos, const double &angle) const +{ + // line created with dir Vector of sign + + double a; + double b; + + if (angle==90.0 || angle==270.0) + { + a=0.0; + b=1.0; + } + else + { + a=1.0; + b = std::tan(angle*M_PI/180.); + } + + Point dirVec = Point(a,b); + + Line line = Line(pos-dirVec*1000,pos+dirVec*1000); + + return line.DistTo(navLine->GetCentre()); +} + + + +bool IsInsideRectangle(const Point &point, const Point &leftUp, const Point &rightDown) +{ + if (point._x> leftUp._x && point._x< rightDown._x + && point._y>rightDown._y && point._y<leftUp._y) + return true; + else + return false; +} diff --git a/routing/ai_router_trips/Cortex.h b/routing/ai_router_trips/Cortex.h new file mode 100644 index 0000000000000000000000000000000000000000..ef814829ec6366673fd771129fdbaf7bdab64ea3 --- /dev/null +++ b/routing/ai_router_trips/Cortex.h @@ -0,0 +1,65 @@ +#ifndef CORTEX_H +#define CORTEX_H + +#include "cognitiveMap/cognitivemap.h" +#include "./perception/visibleenvironment.h" +#include "./cognitiveMap/internnavigationnetwork.h" +#include "./perception/visualsystem.h" + + +using ptrIntNetwork = std::unique_ptr<InternNavigationNetwork>; + + + +class Cortex +{ + +public: + Cortex(); + Cortex(const Building* b,const Pedestrian* ped, const VisibleEnvironment* env, std::unordered_map<const SubRoom*, ptrIntNetwork>* roominternalNetworks); + + AICognitiveMap& GetCognitiveMap(); + VisualSystem& GetPerceptionAbilities(); + + //to enable hline handling + const NavLine* GetNextNavLine(const NavLine *nextTarget); + bool HlineReached() const; + + std::vector<const NavLine*> SortConGeneralKnow(const std::vector<const NavLine *> &navLines); + + //select appropriate crossings + const NavLine FindApprCrossing(const std::vector<NavLine> &navLines); + const NavLine FindApprVisibleCrossing(const NavLine& navLine, const std::vector<NavLine> &navLines); + const NavLine TargetToHead(const NavLine *visibleCrossing) const; + + //implement sign instruction + std::vector<const NavLine *> SortConSignage(const std::vector<const NavLine *>& navLines); + const Sign* DetermineDecisiveSign(const std::vector<const Sign *> &signs); + double OrthogonalDistanceFromTo(const Sign& sign1, const Sign& sign2) const; + double OrthogonalDistanceFromTo(const NavLine* navLine, const Sign &sign) const; + double OrthogonalDistanceFromTo(const NavLine* navLine, const Point& pos, const double& angle) const; + + + +private: + const Building* _b; + const Pedestrian* _ped; + AICognitiveMap _cMap; + //whole environment + const VisibleEnvironment* _wholeEnvironment; + // reference of roominternalNetwork + std::unordered_map<const SubRoom*, ptrIntNetwork>* _intNetworks; + InternNavigationNetwork _currentIntNetwork; + //perception abilities + VisualSystem _perceptionAbilities; + NavLine _lastBestVisNavLine; + bool _statPreferToGoDown; + std::vector<const Sign*> _notUsedSigns; + + + +}; + +bool IsInsideRectangle(const Point& point, const Point& leftUp, const Point& rightDown); + +#endif // CORTEX_H diff --git a/routing/ai_router_trips/cognitiveMap/associations.cpp b/routing/ai_router_trips/cognitiveMap/associations.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d6926f8c4a42044462ecec30f93526f0b98a1267 --- /dev/null +++ b/routing/ai_router_trips/cognitiveMap/associations.cpp @@ -0,0 +1,61 @@ +#include "associations.h" +#include "connection.h" + + + +AIAssociation::AIAssociation() +{ + _landmark=nullptr; + _associatedLandmark=nullptr; + +} + +AIAssociation::AIAssociation(const AILandmark *landmark, const AILandmark *associated_landmark, bool connected) +{ + _landmark=landmark; + _associatedLandmark=associated_landmark; + +// if (connected) +// _connection = std::make_shared<Connection>(_landmark, _associatedLandmark); + +// else + _connection=nullptr; + + +} + +AIAssociation::AIAssociation(const AIConnection *connection) +{ + _connection=connection; + _landmark=nullptr; + _associatedLandmark=nullptr; +} + +AIAssociation::~AIAssociation() +{ + +} + +const AILandmark *AIAssociation::GetLandmarkAssociation(const AILandmark *landmark) const +{ + if (landmark==nullptr) + return nullptr; + if (_landmark==landmark) + { + return _associatedLandmark; + } + else + return nullptr; + +} + +const AIConnection *AIAssociation::GetConnectionAssoziation() const +{ + return _connection; +} + +bool AIAssociation::operator==(const AIAssociation &asso2) const +{ + return this==&asso2; +} + diff --git a/routing/ai_router_trips/cognitiveMap/associations.h b/routing/ai_router_trips/cognitiveMap/associations.h new file mode 100644 index 0000000000000000000000000000000000000000..deb958ebdd3d8343d53421abbd4a079e0c8156d6 --- /dev/null +++ b/routing/ai_router_trips/cognitiveMap/associations.h @@ -0,0 +1,26 @@ +#ifndef ASSOCIATIONS_H +#define ASSOCIATIONS_H + +#include<memory> + +class AILandmark; +class AIConnection; + + +class AIAssociation +{ +public: + AIAssociation(); + AIAssociation(const AILandmark* landmark, const AILandmark* associated_landmark, bool connected=false); + AIAssociation(const AIConnection* connection); + ~AIAssociation(); + const AILandmark* GetLandmarkAssociation(const AILandmark* landmark) const; + const AIConnection* GetConnectionAssoziation() const; + bool operator==(const AIAssociation& asso2) const; +private: + const AILandmark* _landmark; + const AILandmark* _associatedLandmark; + const AIConnection* _connection; +}; + +#endif // ASSOCIATIONS_H diff --git a/routing/ai_router_trips/cognitiveMap/cognitivemap.cpp b/routing/ai_router_trips/cognitiveMap/cognitivemap.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4981c3dbcbf7d622b89cba16e629795ce9fa2604 --- /dev/null +++ b/routing/ai_router_trips/cognitiveMap/cognitivemap.cpp @@ -0,0 +1,679 @@ +#include "cognitivemap.h" +#include <chrono> +#include <random> +#include <algorithm> +#include <cmath> +#include <fstream> +#include "../../../pedestrian/Pedestrian.h" +#include "../perception/visualsystem.h" + + + +AICognitiveMap::AICognitiveMap() +{ + +} + + +AICognitiveMap::AICognitiveMap(const Pedestrian *ped, const VisualSystem* perceptionalAbilities) +{ + _ped=ped; + + _perceptAbis=perceptionalAbilities; + //Destination and regions + _currentRegion=nullptr; + //Find maindestination in cogmapstorage + + _nextTarget=nullptr; + _lastBestChoice=NavLine(Line(Point(0,0),Point(0,0))); + _lastMinAngleDiff=FLT_MAX; + + +// Region newRegion(Point(0.0)); +// newRegion.SetA(20.0); +// newRegion.SetB(20.0); +// newRegion.SetId(1); + +// Landmark finalDest(Point(5.0,5.0),0.1,0.1,1); +// finalDest.SetPosInMap(Point(15.0,8.0)); +// finalDest.SetType("main"); + +// newRegion.AddLandmark(finalDest); +// AddRegion(newRegion); + +// InitLandmarkNetworksInRegions(); +// FindMainDestination(); + +} + + +void AICognitiveMap::UpdateMap() +{ + FindCurrentRegion(); + + CheckIfLandmarksReached(); + +} + + + +void AICognitiveMap::AddRegions(const AIRegions ®ions) +{ + for (const AIRegion& region:regions) + { + _regions.push_back(region); + } + +} + +void AICognitiveMap::AddRegion(const AIRegion& region) +{ + _regions.push_back(region); +} + +const AIRegion *AICognitiveMap::GetRegionByID(int regionID) const +{ + for (const AIRegion& region:_regions) + { + if (region.GetId()==regionID) + { + return ®ion; + } + } + return nullptr; +} + + +std::vector<const AILandmark *> AICognitiveMap::TriggerAssociations(const std::vector<const AILandmark *> &landmarks) +{ + std::vector<const AILandmark *> associatedlandmarks; + for (const AILandmark* landmark:landmarks) + { + AIAssociations associations = landmark->GetAssociations(); + for (const AIAssociation& association:associations) + { + if (association.GetLandmarkAssociation(landmark)!=nullptr) + { + associatedlandmarks.push_back(association.GetLandmarkAssociation(landmark)); + + } + + } + } + return associatedlandmarks; +} + + +std::vector<const NavLine*> AICognitiveMap::SortConLeastAngle(const AILandmark* landmark, const std::vector<NavLine> &navLines, const NavLine &focus) +{ + std::vector<const NavLine*> sortedNavLines; + std::vector<const NavLine*> cNavLines; + NavLine focusTo = focus; + for (const NavLine& navline:navLines) + { + cNavLines.push_back(&navline); + } + + + + //check if lastChoice still exists + bool statusLastChoice=false; + if (_lastBestChoice.GetPoint1()==Point(0,0) && _lastBestChoice.GetPoint2()==Point(0,0)) + { + _lastBestChoice=*(cNavLines.front()); + statusLastChoice=true; + focusTo=_lastBestChoice; + //return cNavLines; + } + else + { + + for (const NavLine* navline:cNavLines) + { + + Point pCloser = GetCloserPoint(_ped->GetPos(),navline->GetPoint1(),navline->GetPoint2()); + Point pCloserBestChoice = GetCloserPoint(_ped->GetPos(),_lastBestChoice.GetPoint1(),_lastBestChoice.GetPoint2()); + + if ((pCloserBestChoice-pCloser).Norm()<0.125) + { + statusLastChoice=true; + _lastBestChoice=*navline; + break; + } + } + } + + // check if ped already in landmark (ellipse); if so, random choice ******************* + if (landmark) + { + Point vecPedLCenter = _ped->GetPos()-landmark->GetPosInMap(); + double angleVecPedLCenter = std::atan2(vecPedLCenter._y,vecPedLCenter._x); + + Point onEllipse = Point(landmark->GetPosInMap()._x+landmark->GetA()*std::cos(angleVecPedLCenter),landmark->GetPosInMap()._y+landmark->GetB()*std::sin(angleVecPedLCenter)); + + if ((onEllipse-landmark->GetPosInMap()).Norm()>vecPedLCenter.Norm()) + { + landmark=nullptr; + //std::cout << "Hier" << std::endl; + } + } + //**************************************** + + if (!landmark) + { + if (statusLastChoice) + { + sortedNavLines.push_back(&_lastBestChoice); + for (const NavLine* navLine:cNavLines) + { + if (navLine!=&_lastBestChoice) + sortedNavLines.push_back(navLine); + } + return sortedNavLines; + } + else + { + + + //prefer navLines that are in the agent's current heading direction + double cMinAngle=M_PI/4.0; + std::vector<const NavLine*> navLinesBehindMe; + //const NavLine* navLineInHeadDir=nullptr; + Point vec1 = focusTo.GetCentre()-_ped->GetPos(); + double heading = std::atan2(vec1._y,vec1._x); + for (auto it=cNavLines.begin(); it!=cNavLines.end(); ++it) + { + Point vec2 = (*it)->GetCentre()-_ped->GetPos(); + double angle = std::fabs(std::atan2(vec2._y,vec2._x)-heading); + if (angle>M_PI) + angle=2*M_PI-angle; + // if crossing is behind agent put it to the bottom of the list + if (angle > cMinAngle) + { + //cNavLines.erase(std::find(cNavLines.begin(), cNavLines.end(), navLine)); + cNavLines.erase(it); + navLinesBehindMe.push_back(*it); + --it; + } + } + + cNavLines.insert(cNavLines.end(),navLinesBehindMe.begin(),navLinesBehindMe.end()); + _lastBestChoice=*(cNavLines.front()); + + return cNavLines; + + } + } + + + + + + Point beelineToLandmark = ShortestBeeLine(_ped->GetPos(),landmark); + double alpha1=std::atan2(beelineToLandmark._y,beelineToLandmark._x); + + double anglediff; + std::vector<double> vecMinAngleDiff; + double currentMinAngleDiff; + const NavLine* bestChoice=nullptr; + + while (cNavLines.size()>0) + { + bestChoice=nullptr; + currentMinAngleDiff=FLT_MAX; + for (auto it=cNavLines.begin(); it!=cNavLines.end(); ++it) + { + + double alpha2=std::atan2(((*it)->GetCentre()-_ped->GetPos())._y,((*it)->GetCentre()-_ped->GetPos())._x); + + anglediff=std::fabs(alpha1-alpha2); + + + if (anglediff>M_PI) + anglediff=2*M_PI-anglediff; + + + if (anglediff<currentMinAngleDiff) + { + currentMinAngleDiff=anglediff; + bestChoice=*it; + + } + + } + + if (*bestChoice==_lastBestChoice) + { + _lastMinAngleDiff=currentMinAngleDiff; + } + + vecMinAngleDiff.push_back(currentMinAngleDiff); + sortedNavLines.push_back(bestChoice); + cNavLines.erase(std::find(cNavLines.begin(), cNavLines.end(), bestChoice)); + } + + +// //prefer navLines that are in the agent's current heading direction****************** + +// double cMinAngle=M_PI/3.0; +// std::vector<const NavLine*> navLinesBehindMe; +// //const NavLine* navLineInHeadDir=nullptr; +// Point vec1 = focusTo.GetCentre()-_ped->GetPos(); +// double heading = std::atan2(vec1._y,vec1._x); +// for (auto it=sortedNavLines.begin(); it!=sortedNavLines.end(); ++it) +// { +// Point vec2 = (*it)->GetCentre()-_ped->GetPos(); +// double angle = std::fabs(std::atan2(vec2._y,vec2._x)-heading); +// if (angle>M_PI) +// angle=2*M_PI-angle; +// // if crossing is behind agent put it to the bottom of the list +// if (angle > cMinAngle) +// { +// //cNavLines.erase(std::find(cNavLines.begin(), cNavLines.end(), navLine)); +// sortedNavLines.erase(it); +// navLinesBehindMe.push_back(*it); +// --it; +// } +// } + +// sortedNavLines.insert(sortedNavLines.end(),navLinesBehindMe.begin(),navLinesBehindMe.end()); +// _lastBestChoice=*(cNavLines.front()); + + //********************************* + + if (_lastMinAngleDiff<vecMinAngleDiff.front()+M_PI/3.0 && statusLastChoice) + { + //erase the best choice navline from the vector and put it on the front + for (auto it=sortedNavLines.begin(); it!=sortedNavLines.end(); ++it) + { + if (*(*it)==_lastBestChoice) + { + sortedNavLines.erase(it); + break; + } + } + sortedNavLines.insert(sortedNavLines.begin(),&_lastBestChoice); + } + else + { + + _lastBestChoice=*(sortedNavLines.front()); + _lastMinAngleDiff=vecMinAngleDiff.front(); + + } + + + return sortedNavLines; + +} + + + +Point AICognitiveMap::ShortestBeeLine(const Point &pos, const AILandmark *landmark) +{ + Point pointOnShortestRoute = landmark->PointOnShortestRoute(pos); + Point beeLineVec = pointOnShortestRoute-pos; + return beeLineVec; +} + + +double AICognitiveMap::MakeItFuzzy(double mean, double std) +{ + using myClock = std::chrono::high_resolution_clock; + myClock::duration d = myClock::now().time_since_epoch(); + + auto seed = d.count(); + + std::default_random_engine generator(seed); + std::normal_distribution<double> distribution(mean,std); + + double number = distribution(generator); + + return number; +} + + + +std::vector<const AILandmark*> AICognitiveMap::GetLandmarksConnectedWith(const AILandmark* landmark) const +{ + const AIRegion* cRegion = GetRegionContaining(landmark); + + if (cRegion!=nullptr) + { + return cRegion->ConnectedWith(landmark); + } + else + { + return std::vector<const AILandmark*>(); + } +} + +const AIRegion* AICognitiveMap::GetRegionContaining(const AILandmark *landmark) const +{ + for (const AIRegion& region:_regions) + { + if (region.ContainsLandmark(landmark)) + return ®ion; + } + return nullptr; +} + +void AICognitiveMap::FindCurrentRegion() +{ + + //for test purposes. has to be changed + if (_regions.empty()) + return; + + //needs to be fixed + _currentRegion=&(_regions.back()); + return; + + for (const AIRegion& region:_regions) + { + if (region.Contains(_ped->GetPos())) + { + _currentRegion=®ion; + return; + } + } + //path searching to region + _currentRegion=nullptr; +} + +void AICognitiveMap::CheckIfLandmarksReached() +{ + + BoostPolygon boostPolygon=_perceptAbis->GetCurrentEnvironment(); + + if (_currentRegion!=nullptr) + { + for (const AILandmark& landmark:_currentRegion->GetLandmarks()) + { + if (boost::geometry::intersects(landmark.GetRealPos(),boostPolygon)) + { + if (std::find(_landmarksRecentlyVisited.begin(), _landmarksRecentlyVisited.end(), &landmark) == _landmarksRecentlyVisited.end() ) + { + _landmarksRecentlyVisited.push_back(&landmark); + + _mainDestination=nullptr; + _nextTarget=nullptr; + } + } + + } + } + + +} + +const AILandmark *AICognitiveMap::FindConnectionPoint(const AIRegion *currentRegion, const AIRegion *targetRegion) const +{ + if (currentRegion!=nullptr && targetRegion!=nullptr) + { + for (const AILandmark& landmarka:currentRegion->GetLandmarks()) + { + for (const AILandmark& landmarkb:targetRegion->GetLandmarks()) + { + if (&landmarka==&landmarkb) + { + return &landmarka; + } + } + } + } + + return nullptr; + +} + +void AICognitiveMap::FindMainDestination() +{ + std::vector<const AILandmark*> possibleMainTargets; + for (const AIRegion& region:_regions) + { + for (const AILandmark& landmark:region.GetLandmarks()) + { + if (landmark.GetType()=="main" && std::find(_landmarksRecentlyVisited.begin(),_landmarksRecentlyVisited.end(),&landmark)==_landmarksRecentlyVisited.end()) + { + possibleMainTargets.push_back(&landmark); + _targetRegion=®ion; + + } + } + } + if (possibleMainTargets.empty()) + { + _mainDestination=nullptr; + _targetRegion=nullptr; + return; + } + else + _mainDestination=GetNearestMainTarget(possibleMainTargets); + +} + +void AICognitiveMap::FindNextTarget() +{ + + _nextTarget=nullptr; + + // if not already in the region of the maindestination + if (_currentRegion==nullptr || _targetRegion==nullptr) + { + return; + } + + if (_targetRegion!=_currentRegion) + { + _nextTarget=FindConnectionPoint(_currentRegion,_targetRegion); + // if connection point does not exist: Path searching to region + if (_nextTarget==nullptr) + { + //Region is target + _nextTarget=_targetRegion->GetRegionAsLandmark(); + return; + } + + } + else //destination is in current region + { + _nextTarget=_mainDestination; + } + + // Function considers that nearLandmark can be the target itself if no nearer was found. + const AILandmark* nearLandmark = FindNearLandmarkConnectedToTarget(_nextTarget); + + _nextTarget=nearLandmark; + //Direct way to target much shorter than via near Landmark? + +} + +void AICognitiveMap::FindShortCut() +{ + +} + +const AILandmark* AICognitiveMap::FindNearLandmarkConnectedToTarget(const AILandmark* target) +{ + + std::vector<const AILandmark*> landmarksConnectedToTarget = FindLandmarksConnectedToTarget(target); + + //if target has no connections return nullptr + if (landmarksConnectedToTarget.empty()) + return target; + + //look for nearest located landmark + + //look for landmarks within a circle with the radius searchlimit + // if no landmarks were found radius will be enlarged + // if radius = distance(Pos->target) return target + + Point vector=target->GetPosInMap()-_ped->GetPos();//_YAHPointer.GetPos(); + + + double distanceToTarget=vector.Norm(); + int divisor = 24; + double searchlimit=distanceToTarget/divisor; + std::vector<const AILandmark*> nearLandmarks; + + while (searchlimit<distanceToTarget && nearLandmarks.empty()) + { + for (const AILandmark* landmark:landmarksConnectedToTarget) + { + + vector=landmark->GetPosInMap()-_ped->GetPos(); + + double distance = vector.Norm(); + + + if (distance<=searchlimit) + { + nearLandmarks.push_back(landmark); + } + } + searchlimit+=searchlimit; + + } + + if (nearLandmarks.empty()) + return target; + + // select best route to target from one of the nearLandmarks + + return FindBestRouteFromOneOf(nearLandmarks); + + +} + +std::vector<const AILandmark*> AICognitiveMap::FindLandmarksConnectedToTarget(const AILandmark* target) +{ + + std::vector<const AILandmark*> connectedLandmarks; + + // landmarks directly connected to target + std::vector<const AILandmark*> firstCandidates = GetLandmarksConnectedWith(target); + + for (const AILandmark* candidate:firstCandidates) + { + if(std::find(_landmarksRecentlyVisited.begin(), _landmarksRecentlyVisited.end(), candidate) == _landmarksRecentlyVisited.end()) + { + connectedLandmarks.push_back(candidate); + } + } + + //Landmarks connected to landmarks connected to target + std::vector<const AILandmark*> furtherCandidates; + + for (size_t i=0; i<connectedLandmarks.size(); ++i) + { + furtherCandidates=GetLandmarksConnectedWith(connectedLandmarks[i]); + + for (const AILandmark* candidate : furtherCandidates) + { + // if candidate not already taken into account, not visited before or target itself + if(std::find(connectedLandmarks.begin(), connectedLandmarks.end(), candidate) == connectedLandmarks.end() + && candidate!=target) + { + connectedLandmarks.push_back(candidate); + + } + } + } + return connectedLandmarks; +} + +const AILandmark* AICognitiveMap::FindBestRouteFromOneOf(const std::vector<const AILandmark*> &nearLandmarks) +{ + const AILandmark* bestChoice = nullptr; + double minDistance = FLT_MAX; + double cDistance; + for (const AILandmark* landmark:nearLandmarks) + { + cDistance=(_ped->GetPos()-landmark->GetRandomPoint()).Norm()+_currentRegion->PathLengthFromLandmarkToTarget(landmark, _nextTarget).second; + + if (cDistance<minDistance) + { + minDistance=cDistance; + + bestChoice=landmark; + } + } + std::vector<const AILandmark*> landmarksOnShortestPath = _currentRegion->PathLengthFromLandmarkToTarget(bestChoice, _nextTarget).first; + + for (int i=landmarksOnShortestPath.size()-1; i>=0; --i) + { + if (std::find(_landmarksRecentlyVisited.begin(),_landmarksRecentlyVisited.end(),landmarksOnShortestPath[i])==_landmarksRecentlyVisited.end()) + { + return landmarksOnShortestPath[i]; + } + + } + return nullptr; +} + +const AILandmark* AICognitiveMap::GetNearestMainTarget(const std::vector<const AILandmark*> &mainTargets) +{ + const AILandmark* nearest = nullptr; + double dNearest = FLT_MAX; + + if (mainTargets.size()==1) + return mainTargets[0]; + + for (const AILandmark* mainDest:mainTargets) + { + _nextTarget=mainDest; + const AILandmark* cLandmark = FindNearLandmarkConnectedToTarget(mainDest); + double cDistance=(_ped->GetPos()-cLandmark->GetRandomPoint()).Norm()+_currentRegion->PathLengthFromLandmarkToTarget(cLandmark,mainDest).second; + + if (cDistance<dNearest) + { + dNearest=cDistance; + nearest=mainDest; + } + } + + return nearest; +} + +void AICognitiveMap::SetBestChoice(const NavLine &navLine) +{ + _lastBestChoice=navLine; +} + +const NavLine* AICognitiveMap::GetBestChoice() const +{ + if (_lastBestChoice.GetPoint1()==Point(0,0) && _lastBestChoice.GetPoint2()==Point(0,0)) + return nullptr; + else + return &_lastBestChoice; +} + +void AICognitiveMap::InitLandmarkNetworksInRegions() +{ + for (AIRegion& region:_regions) + { + region.InitLandmarkNetwork(); + } +} + +const AILandmark* AICognitiveMap::GetNextTarget() const +{ + return _nextTarget; +} + + +Point GetCloserPoint(const Point &origin, const Point &target1, const Point& target2) +{ + + Point vec1 = target1-origin; + Point vec2 = target2-origin; + + if (vec1.Norm()<vec2.Norm()) + return target1; + else + return target2; +} + + + diff --git a/routing/ai_router_trips/cognitiveMap/cognitivemap.h b/routing/ai_router_trips/cognitiveMap/cognitivemap.h new file mode 100644 index 0000000000000000000000000000000000000000..15e5b5b56aa0640fe60ace6f1d7cd9cfa3f538d0 --- /dev/null +++ b/routing/ai_router_trips/cognitiveMap/cognitivemap.h @@ -0,0 +1,87 @@ +#ifndef COGNITIVEMAP_H +#define COGNITIVEMAP_H + +#include "associations.h" +#include "region.h" +#include <memory> +#include <vector> +#include "../../../geometry/NavLine.h" + + +class Pedestrian; +class VisualSystem; +using AIRegions = std::vector<AIRegion>; + + +class AICognitiveMap +{ +public: + AICognitiveMap(); + AICognitiveMap(const Pedestrian* ped, const VisualSystem* perceptionalAbilities); + //Map Updates + void UpdateMap(); + //Regions + void AddRegions(const AIRegions ®ions); + void AddRegion(const AIRegion ®ion); + const AIRegion* GetRegionByID(int regionID) const; + // Landmarks + void AddLandmarkInRegion(const AILandmark& landmark, AIRegion& region); + + // Associations + std::vector<const AILandmark *> TriggerAssociations(const std::vector<const AILandmark*> &landmarks); + + std::vector<const NavLine *> SortConLeastAngle(const AILandmark* landmark, const std::vector<NavLine> &navLines, const NavLine& focus); + + Point ShortestBeeLine(const Point& pos, const AILandmark* landmark); + + //Tools + double MakeItFuzzy(double mean, double std); + + //Find region/landmarks/connections + std::vector<const AILandmark *> GetLandmarksConnectedWith(const AILandmark *landmark) const; + const AIRegion* GetRegionContaining(const AILandmark* landmark) const; + + //Locater + void FindCurrentRegion(); + void CheckIfLandmarksReached(); + + //Find targets + const AILandmark* FindConnectionPoint(const AIRegion* currentRegion, const AIRegion* targetRegion) const; + void FindMainDestination(); + void FindNextTarget(); + const AILandmark *GetNextTarget() const; + void FindShortCut(); + const AILandmark *FindNearLandmarkConnectedToTarget(const AILandmark *target); + std::vector<const AILandmark *> FindLandmarksConnectedToTarget(const AILandmark *target); + const AILandmark *FindBestRouteFromOneOf(const std::vector<const AILandmark *> &nearLandmarks); + const AILandmark *GetNearestMainTarget(const std::vector<const AILandmark *> &mainTargets); + + void SetBestChoice(const NavLine &navLine); + const NavLine *GetBestChoice() const; + + //Init LandmarkNetworks + void InitLandmarkNetworksInRegions(); + + +private: + const Pedestrian* _ped; + const VisualSystem* _perceptAbis; + + std::vector<const AILandmark*> _landmarksRecentlyVisited; + + const AILandmark* _mainDestination; + const AILandmark* _nextTarget; + AIRegions _regions; + const AIRegion* _currentRegion; + const AIRegion* _targetRegion; + + NavLine _lastBestChoice; + double _lastMinAngleDiff; + + + +}; + +Point GetCloserPoint(const Point &origin, const Point &target1, const Point& target2); + +#endif // COGNITIVEMAP_H diff --git a/routing/ai_router_trips/cognitiveMap/connection.cpp b/routing/ai_router_trips/cognitiveMap/connection.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8941441f898741309e42cf9be107d248e8b46c06 --- /dev/null +++ b/routing/ai_router_trips/cognitiveMap/connection.cpp @@ -0,0 +1,53 @@ +#include "connection.h" +#include <utility> + +AIConnection::AIConnection(int id, const std::string &caption, const std::string &type, int landmarkId1, int landmarkId2) +{ + _landmarkId1=landmarkId1; + _landmarkId2=landmarkId2; + _id=id; + _caption=caption; + _type=type; +} + +AIConnection::~AIConnection() +{ + +} + +std::pair<int, int> AIConnection::GetLandmarkIds() const +{ + return std::make_pair(_landmarkId1, _landmarkId2); + +} + +int AIConnection::GetId() const +{ + return _id; +} + +const std::string &AIConnection::GetCaption() const +{ + return _caption; +} + +const std::string &AIConnection::GetType() const +{ + return _type; +} + +void AIConnection::SetId(int id) +{ + _id=id; +} + +void AIConnection::SetCaption(const std::string &caption) +{ + _caption=caption; +} + +void AIConnection::SetType(const std::string &type) +{ + _type=type; +} + diff --git a/routing/ai_router_trips/cognitiveMap/connection.h b/routing/ai_router_trips/cognitiveMap/connection.h new file mode 100644 index 0000000000000000000000000000000000000000..afc3457291fb12e7c9ccdd9c3ecaf59f4c6c61a9 --- /dev/null +++ b/routing/ai_router_trips/cognitiveMap/connection.h @@ -0,0 +1,35 @@ +#ifndef CONNECTION_H +#define CONNECTION_H + +#include "landmark.h" + + +using AILandmarks = std::vector<AILandmark>; + +class AIConnection +{ +public: + AIConnection(int id, const std::string& caption, const std::string& type, int landmarkId1, int landmarkId2); + ~AIConnection(); + + //Getter + int GetId() const; + const std::string& GetCaption() const; + const std::string& GetType() const; + + std::pair<int,int> GetLandmarkIds() const; + + //Setter + void SetId(int id); + void SetCaption(const std::string& caption); + void SetType(const std::string& type); + +private: + int _id; + std::string _caption; + std::string _type; + int _landmarkId1; + int _landmarkId2; +}; + +#endif // CONNECTION_H diff --git a/routing/ai_router_trips/cognitiveMap/internnavigationnetwork.cpp b/routing/ai_router_trips/cognitiveMap/internnavigationnetwork.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a7ad20ff60730d372c74b8e80d6a95fdda8b2d46 --- /dev/null +++ b/routing/ai_router_trips/cognitiveMap/internnavigationnetwork.cpp @@ -0,0 +1,184 @@ +#include "internnavigationnetwork.h" +#include "../../../geometry/Obstacle.h" + +#include <boost/graph/graph_traits.hpp> +#include <boost/graph/adjacency_list.hpp> +#include <boost/graph/dijkstra_shortest_paths.hpp> +#include <boost/geometry.hpp> + +InternNavigationNetwork::InternNavigationNetwork() +{ + _graph=Graph(); +} + +InternNavigationNetwork::InternNavigationNetwork(const SubRoom *subRoom) +{ + _graph=Graph(); + + _subRoom=subRoom; + + for (Wall wall:_subRoom->GetAllWalls()) + { + Linestring lineS; + boost::geometry::append(lineS,wall.GetPoint1()); + boost::geometry::append(lineS,wall.GetPoint2()); + _currentRoom.push_back(lineS); + + } + + const std::vector<Obstacle* > obstacles = _subRoom->GetAllObstacles(); + + for (Obstacle* obstacle:obstacles) + { + for (Wall wall: obstacle->GetAllWalls()) + { + Linestring lineS; + boost::geometry::append(lineS,wall.GetPoint1()); + boost::geometry::append(lineS,wall.GetPoint2()); + _currentRoom.push_back(lineS); + } + } +} + +void InternNavigationNetwork::AddVertex(const NavLine *navLine) +{ + Vertex v = boost::add_vertex(_graph); + _navLines.push_back(std::pair<const NavLine*,Vertex> (navLine,v)); +} + + +void InternNavigationNetwork::AddEdge(const NavLine *navLine1, const NavLine *navLine2) +{ + //find indeces of vertices(landmarks) in graph + Vertex A; + Vertex B; + + for (auto it=_navLines.begin(); it!=_navLines.end(); ++it) + { + int counter=0; + if (it->first==navLine1) + { + A = it->second; + counter++; + if (counter==2) + break; + } + else if (it->first==navLine2) + { + B = it->second; + counter++; + if (counter==2) + break; + } + } + + //vector between navLines + Point vector = navLine1->GetCentre()-navLine2->GetCentre(); + //distance between navLines + double distance = vector.Norm(); + _connections.push_back(std::pair<Edge,Weight>(Edge(A,B),distance)); + + boost::add_edge(A,B,distance,_graph); + boost::add_edge(B,A,distance,_graph); +} + +const NavLine *InternNavigationNetwork::GetNextNavLineOnShortestPathToTarget(const Point &pos, const NavLine *target) +{ + // new vertex for the actual position of the pedestrian + Vertex v = boost::add_vertex(_graph); + + // check whether actual position has intervisual contact with doors or hlines + + //building lines from center points of the navlines and pos and check if those lines intersect the polygon representing the current room + for (auto &it:_navLines) + { + if (!LineIntersectsWalls(std::make_pair<const Point&,const Point&>(it.first->GetCentre(),pos),_currentRoom)) + { + //vector between navLines + Point vector = pos-target->GetCentre(); + //distance between navLines + double distance = vector.Norm(); + + boost::add_edge(v,it.second,distance,_graph); + boost::add_edge(it.second,v,distance,_graph); + } + } + + //determine Vertex belonging to ptrNavline target + int targetVertex=-1; + + for (auto it=_navLines.begin(); it!=_navLines.end(); ++it) + { + if (it->first==target) + { + targetVertex=it->second; + break; + } + } + + if (targetVertex==-1) + return nullptr; + + // determine shortest path to target + std::vector<Vertex> pMap(boost::num_vertices(_graph)); + boost::dijkstra_shortest_paths(_graph, targetVertex, boost::predecessor_map(&pMap[0])); + // next vertex on shortest path: + Vertex predecessor = pMap[v]; + + + //remove pos from graph network + boost::clear_vertex(v,_graph); + boost::remove_vertex(v,_graph); + + //return navline belonging to predecessor vertex on shortest path + for (auto it=_navLines.begin(); it!=_navLines.end(); ++it) + { + if (it->second==predecessor) + { + return it->first; + } + } + + return nullptr; + +} + +void InternNavigationNetwork::EstablishConnections() +{ + + // Check which navlines have (vice-versa) visible connectivity + + //building lines from center points of the navlines and check if those lines intersect the polygon representing the current room + for (auto it=_navLines.begin(); it!=_navLines.end();++it) + { + auto it2=it; + it2++; + for (; it2!=_navLines.end(); ++it2) + { + if (it->first!=it2->first) + { + if (!LineIntersectsWalls(std::make_pair<const Point&,const Point&>(it->first->GetCentre(),it2->first->GetCentre()),_currentRoom)) + { + AddEdge(it->first,it2->first); + } + } + } + } +} + +bool InternNavigationNetwork::LineIntersectsWalls(const std::pair<const Point&, const Point&> &line, const std::vector<Linestring> &walls) +{ + + Linestring lineS; + boost::geometry::append(lineS,line.first); + boost::geometry::append(lineS,line.second); + + for (Linestring wall:walls) + { + if (boost::geometry::intersects(wall,lineS)) + return true; + } + + return false; + +} diff --git a/routing/ai_router_trips/cognitiveMap/internnavigationnetwork.h b/routing/ai_router_trips/cognitiveMap/internnavigationnetwork.h new file mode 100644 index 0000000000000000000000000000000000000000..daef48896abf177e84b83a95b5220ca50fda7286 --- /dev/null +++ b/routing/ai_router_trips/cognitiveMap/internnavigationnetwork.h @@ -0,0 +1,62 @@ +#ifndef INTERNNAVIGATIONNETWORK_H +#define INTERNNAVIGATIONNETWORK_H + + +//based on boost + +#include <boost/graph/graph_traits.hpp> +#include <boost/graph/adjacency_list.hpp> +#include <boost/graph/dijkstra_shortest_paths.hpp> +#include "../../../geometry/SubRoom.h" +#include "../../../geometry/NavLine.h" + +typedef boost::adjacency_list<boost::listS, boost::vecS, boost::directedS, + boost::no_property, boost::property<boost::edge_weight_t, double> > Graph; +typedef boost::graph_traits<Graph>::vertex_descriptor Vertex; +typedef boost::geometry::model::polygon<Point> BoostPolygon; +typedef boost::geometry::model::linestring<Point> Linestring; + + +typedef std::pair<Vertex, Vertex> Edge; +typedef double Weight; +typedef std::vector<Edge> Edges; + + + +class InternNavigationNetwork +{ +public: + InternNavigationNetwork(); + InternNavigationNetwork(const SubRoom* subRoom); + + void AddVertex(const NavLine* navLine); // add navline (crossing / transition or HLine) + //void RemoveVertex(ptrNavLine navLine); + void AddEdge(const NavLine* navLine1, const NavLine* navLine2); // add connection (edge) between two navlines. Function should only be + // used if each of the navline is visible from the position of the other. + //void RemoveEdge(ptrNavLine navLine1, ptrNavLine navLine2); + + const NavLine* GetNextNavLineOnShortestPathToTarget(const Point& pos, const NavLine* target); + + //Create edges (connection (edge) will be established if two vertices are visible from each other + void EstablishConnections(); + + + +private: + Graph _graph; + std::list<std::pair<const NavLine*,Vertex>> _navLines; //vertices + std::list<std::pair<Edge,Weight>> _connections; //edges; Weight equals length + const SubRoom* _subRoom; + // current Subroom as boost polygon + std::vector<Linestring> _currentRoom; + + bool LineIntersectsWalls(const std::pair<const Point&, const Point&> &line, const std::vector<Linestring> &walls); + + + + + + +}; + +#endif // INTERNNAVIGATIONNETWORK_H diff --git a/routing/ai_router_trips/cognitiveMap/landmark.cpp b/routing/ai_router_trips/cognitiveMap/landmark.cpp new file mode 100644 index 0000000000000000000000000000000000000000..16d7a1eca50425aad5b16c3366847f9aebeffe53 --- /dev/null +++ b/routing/ai_router_trips/cognitiveMap/landmark.cpp @@ -0,0 +1,198 @@ +#include "landmark.h" +#include "associations.h" +#include <math.h> +#include "../../../general/Macros.h" + +AILandmark::AILandmark(const Point &pos) +{ + _realPos=pos; + _visited=false; + _posInMap=Point(0.0,0.0); +} + +AILandmark::AILandmark(const Point& pos, double a, double b, int id) +{ + _realPos=pos; + _a=a; + _b=b; + _visited=false; + _id=id; + _posInMap=Point(0.0,0.0); +} + +AILandmark::~AILandmark() +{ + +} + +void AILandmark::SetId(int id) +{ + _id=id; +} + +void AILandmark::SetA(double a) +{ + _a=a; +} + +void AILandmark::SetB(double b) +{ + _b=b; +} + +void AILandmark::SetRealPos(const Point &point) +{ + _realPos=point; +} + +void AILandmark::SetPosInMap(const Point &point) +{ + _posInMap=point; +} + + + +void AILandmark::SetCaption(const std::string &string) +{ + _caption=string; +} + +void AILandmark::SetType(const std::string& type) +{ + _type=type; +} + +int AILandmark::GetId() const +{ + return _id; +} + +const Point &AILandmark::GetRealPos() const +{ + return _realPos; +} + +const Point &AILandmark::GetPosInMap() const +{ + return _posInMap; +} + +double AILandmark::GetA() const +{ + return _a; +} + +double AILandmark::GetB() const +{ + return _b; +} + +const std::string& AILandmark::GetType() const +{ + return _type; +} + + +const std::string &AILandmark::GetCaption() const +{ + return _caption; +} + + +Point AILandmark::GetRandomPoint() const +{ + const double pi = M_PI;//std::acos(-1); + int alpha1 = std::rand() % 360; + double factor_a = (std::rand() % 100)/100.0; + double x = _posInMap._x+std::cos(alpha1*pi/180.0)*factor_a*_a; + int alpha2 = std::rand() % 360; + double factor_b = (std::rand() % 100)/100.0; + double y = _posInMap._y+std::sin(alpha2*pi/180.0)*factor_b*_b; + + return Point(x,y); +} + +Point AILandmark::PointOnShortestRoute(const Point& point) const +{ + const double pi = std::acos(-1); + double distance; + int alpha_min=1; + double t_min=0; + double min=std::sqrt(std::pow((_posInMap._x+t_min*_a*std::cos(pi/180.0))-point._x,2)+std::pow((_posInMap._x+t_min*_b*std::sin(pi/180.0))-point._y,2)); + + for (double t=0.2; t<=1; t+=0.2) + { + for (int alpha=11; alpha<=360; alpha+=10) + { + distance=std::sqrt(std::pow((_posInMap._x+t*_a*std::cos(alpha*pi/180.0))-point._x,2)+std::pow((_posInMap._y+t*_b*std::sin(alpha*pi/180.0))-point._y,2)); + if (distance<min) + { + min=distance; + alpha_min=alpha; + t_min=t; + } + } + } + //Log->Write(std::to_string(min)); + return Point(_posInMap._x+_a*std::cos(alpha_min*pi/180.0),_posInMap._y+_b*std::sin(alpha_min*pi/180.0)); +} + +//bool Landmark::LandmarkReached(const Point& currentYAH) +//{ +// if (std::abs(_realPos._x-currentYAH._x)<0.75*_a && std::abs(_realPos._y-currentYAH._y)<0.75*_b) +// { +// Log->Write("INFO:\t Landmark reached"); +// Log->Write(std::to_string(currentYAH._x)+" "+std::to_string(currentYAH._y)); +// _visited=true; +// return true; +// } +// return false; +//} + +bool AILandmark::Visited() const +{ + return _visited; +} + +void AILandmark::SetVisited(bool stat) +{ + _visited=stat; +} + +bool AILandmark::Contains(const Point &point) const +{ + double x = _a-(std::fabs(this->GetPosInMap()._x-point._x)); + double y = _b-(std::fabs(this->GetPosInMap()._y-point._y)); + + if ((std::fabs(this->GetPosInMap()._x-point._x)<=_a && std::pow((1-std::pow(x,2)/std::pow(_a,2)),0.5)*_b<=_b) + || (std::fabs(this->GetPosInMap()._y-point._y)<=_b && std::pow((1-std::pow(y,2)/std::pow(_b,2)),0.5)*_a<=_a )) + return true; + + return false; +} + +const AIAssociations &AILandmark::GetAssociations() const +{ + return _assoContainer; +} + +void AILandmark::AddAssociation(const AIAssociation &asso) +{ + if (std::find(_assoContainer.begin(), _assoContainer.end(), asso)!=_assoContainer.end()) + return; + else + _assoContainer.push_back(asso); +} + +const AIRegion *AILandmark::IsInRegion() const +{ + return _region; +} + +void AILandmark::SetRegion(const AIRegion *region) +{ + _region=region; +} + + + diff --git a/routing/ai_router_trips/cognitiveMap/landmark.h b/routing/ai_router_trips/cognitiveMap/landmark.h new file mode 100644 index 0000000000000000000000000000000000000000..a6baccbcb62ae38626f613b32acd038d6f2adb2f --- /dev/null +++ b/routing/ai_router_trips/cognitiveMap/landmark.h @@ -0,0 +1,82 @@ +#ifndef LANDMARK_H +#define LANDMARK_H + +#include <vector> +#include <list> +#include "../../../geometry/Point.h" +#include "associations.h" + + + +using AIAssociations = std::vector<AIAssociation>; + +class AIRegion; + + +class AILandmark +{ + +public: + AILandmark(const Point& pos); + AILandmark(const Point &pos, double a, double b, int id=-1); + ~AILandmark(); + + + + //Setter + void SetId(int id); + void SetA(double a); + void SetB(double b); + void SetRealPos(const Point& point); + void SetPosInMap(const Point& point); + void SetCaption(const std::string& string); +// void SetPriority(double priority); + void SetType(const std::string &type); + //Getter + int GetId() const; + const Point& GetRealPos() const; + const Point& GetPosInMap() const; + double GetA() const; + double GetB() const; + const std::string& GetType() const; + const std::string& GetCaption() const; + //double GetPriority() const; + //Random point somewhere within the waypoint + Point GetRandomPoint() const; + // Shortest Distance from waypoint egde (ellipse) to specific point + Point PointOnShortestRoute(const Point &point) const; + // Check if Waypoint reached (if YAH-Pointer is in Waypoint) + //bool LandmarkReached(const Point& currentYAH); + + bool Visited() const; + void SetVisited(bool stat); + + //check if landmark ellipse in cogmap contains certain point + bool Contains(const Point &point) const; + + // Associations + const AIAssociations& GetAssociations() const; + void AddAssociation(const AIAssociation& asso); + + //Region + const AIRegion* IsInRegion() const; + void SetRegion(const AIRegion* region); + + + +private: + int _id; + std::string _caption; + Point _realPos; + Point _posInMap; + double _a; + double _b; + //double _priority; + bool _visited; + std::string _type; + std::list<int> _connectedWith; + AIAssociations _assoContainer; + const AIRegion* _region; +}; + +#endif // LANDMARK_H diff --git a/routing/ai_router_trips/cognitiveMap/landmarknetwork.cpp b/routing/ai_router_trips/cognitiveMap/landmarknetwork.cpp new file mode 100644 index 0000000000000000000000000000000000000000..32bc0b460325b4a4a6c1d6172832b33c3c8985a3 --- /dev/null +++ b/routing/ai_router_trips/cognitiveMap/landmarknetwork.cpp @@ -0,0 +1,258 @@ +#include "landmarknetwork.h" +#include "region.h" + +// In the following example, we construct a graph and apply dijkstra_shortest_paths(). +// The complete source code for the example is in examples/dijkstra-example.cpp. +// Dijkstra's algorithm computes the shortest distance from the starting vertex to every other vertex in the graph. + +// Dijkstra's algorithm requires that a weight property is associated with each edge and a distance property with each vertex. +// Here we use an internal property for the weight and an external property for the distance. +// For the weight property we use the property class and specify int as the type used to represent +// weight values and edge_weight_t for the property tag (which is one of the BGL predefined property tags). +// The weight property is then used as a template argument for adjacency_list. + +// The listS and vecS types are selectors that determine the data structure used inside the adjacency_list +// (see Section Choosing the Edgelist and VertexList). The directedS type specifies that the graph +// should be directed (versus undirected). The following code shows the specification of the graph +// type and then the initialization of the graph. The edges and weights are passed to the graph +// constructor in the form of iterators (a pointer qualifies as a RandomAccessIterator). + + +// For the external distance property we will use a std::vector for storage. +// BGL algorithms treat random access iterators as property maps, +// so we can just pass the beginning iterator of the distance +// vector to Dijkstra's algorithm. Continuing the above example, +// the following code shows the creation of the distance vector, +// the call to Dijkstra's algorithm (implicitly using the +// internal edge weight property), and then the output of the results. + +// typedef adjacency_list<listS, vecS, directedS, +// no_property, property<edge_weight_t, int> > Graph; +// typedef graph_traits<Graph>::vertex_descriptor Vertex; +// typedef std::pair<int,int> E; + +// const int num_nodes = 5; +// E edges[] = { E(0,2), +// E(1,1), E(1,3), E(1,4), +// E(2,1), E(2,3), +// E(3,4), +// E(4,0), E(4,1) }; +// int weights[] = { 1, 2, 1, 2, 7, 3, 1, 1, 1}; + +// Graph G(edges, edges + sizeof(edges) / sizeof(E), weights, num_nodes); + + +// // vector for storing distance property +// std::vector<int> d(num_vertices(G)); + +// // get the first vertex +// Vertex s = *(vertices(G).first); +// // invoke variant 2 of Dijkstra's algorithm +// dijkstra_shortest_paths(G, s, distance_map(&d[0])); + +// std::cout << "distances from start vertex:" << std::endl; +// graph_traits<Graph>::vertex_iterator vi; +// for(vi = vertices(G).first; vi != vertices(G).second; ++vi) +// std::cout << "distance(" << index(*vi) << ") = " +// << d[*vi] << std::endl; +// std::cout << std::endl; + + +AILandmarkNetwork::AILandmarkNetwork() +{ + _graph=Graph(); +} + +AILandmarkNetwork::AILandmarkNetwork(const AIRegion* region, const AILandmarks &landmarks, const std::vector<AIConnection> &connections) +{ + _region=region; + _graph=Graph(); + + for (const AILandmark& landmark:landmarks) + { + AddLandmark(&landmark); + } + + + for (const AIConnection& connection:connections) + { + AddConnection(&connection); + + } +} + + +AILandmarkNetwork::~AILandmarkNetwork() +{ + +} + +void AILandmarkNetwork::AddLandmark(const AILandmark *landmark) +{ + + Vertex v = boost::add_vertex(_graph); + //std::make_pair<int,int>(1,1); + _landmarks.emplace(landmark,v); + +} + +void AILandmarkNetwork::RemoveLandmark(const AILandmark *landmark) +{ + + + for (auto it=_landmarks.begin(); it!=_landmarks.end(); ++it) + { + if (it->first==landmark) + { + boost::clear_vertex(it->second,_graph); + RemoveAdjacentEdges(it->second); + boost::remove_vertex(it->second,_graph); + _landmarks.erase(it); + break; + } + } + + + +} + +void AILandmarkNetwork::AddConnection(const AIConnection *connection) +{ + //find indeces of vertices(landmarks) in graph + const AILandmark* landmarkA = _region->GetLandmarkByID(connection->GetLandmarkIds().first); + const AILandmark* landmarkB = _region->GetLandmarkByID(connection->GetLandmarkIds().second); + Vertex A; + Vertex B; + +// for (auto it=_landmarks.begin(); it!=_landmarks.end(); ++it) +// { +// int counter=0; +// if (it->first==landmarkA) +// { +// A = it->second; +// counter++; +// if (counter==2) +// break; +// } +// else if (it->first==landmarkB) +// { +// B = it->second; +// counter++; +// if (counter==2) +// break; +// } +// } + + A=_landmarks[landmarkA]; + B=_landmarks[landmarkB]; + + Point vector = landmarkA->GetRandomPoint()-landmarkB->GetRandomPoint();//->GetRandomPoint()-landmarkB->GetRandomPoint(); + double distance = vector.Norm(); + _connections.push_back(std::pair<Edge,Weight>(Edge(A,B),distance)); + + boost::add_edge(A,B,distance,_graph); + boost::add_edge(B,A,distance,_graph); +} + +std::pair<std::vector<const AILandmark*>,double> AILandmarkNetwork::LengthofShortestPathToTarget(const AILandmark *landmark, const AILandmark *target) const +{ + + if (landmark==target) + return std::make_pair<std::vector<const AILandmark*>,double>(std::vector<const AILandmark*>{landmark},0.0); + + int startVertex=-1; + int targetVertex=-1; + + // get the start vertex + + +// for (auto it=_landmarks.begin(); it!=_landmarks.end(); ++it) +// { + +// int counter=0; +// if (it->first==landmark) +// { +// startVertex=it->second; +// counter++; +// if (counter==2) +// break; +// } +// else if (it->first==target) +// { +// targetVertex=it->second; +// counter++; +// if (counter==2) +// break; +// } + +// } + + startVertex=_landmarks.at(landmark); + targetVertex=_landmarks.at(target); + + + if (targetVertex==-1 || startVertex==-1) + throw std::invalid_argument("Wrong target or landmark!"); + + //std::vector<double> edgeWeights; + + // vector for storing distance property + std::vector<Vertex> p(boost::num_vertices(_graph)); + std::vector<double> d(boost::num_vertices(_graph)); + +// for (auto it =_connections.begin(); it!=_connections.end(); ++it) +// { +// edgeWeights.push_back(it->second); +// } + + // invoke variant 2 of Dijkstra's algorithm + //boost::dijkstra_shortest_paths(_graph, startVertex, boost::distance_map(&d[0])); + boost::dijkstra_shortest_paths(_graph, startVertex, + boost::predecessor_map(boost::make_iterator_property_map(p.begin(), get(boost::vertex_index, _graph))). + distance_map(boost::make_iterator_property_map(d.begin(), get(boost::vertex_index, _graph)))); + + //std::cout << "distances and parents:" << std::endl; + Vertex vi=targetVertex; + + + std::vector<const AILandmark*> landmarksOnShortestPath; + + while (vi!=startVertex) + { + for (auto it=_landmarks.begin(); it!=_landmarks.end(); ++it) + { + if (it->second==vi) + { + landmarksOnShortestPath.push_back(it->first); + + break; + } + //Log->Write(std::to_string(vi)); + + } + vi=p[vi]; + + //std::cout << d[targetVertex] << std::endl; + //std::cout << "parent(" << name[*vi] << ") = " << name[p[*vi]] << std::endl; + } + landmarksOnShortestPath.push_back(landmark); + //std::cout << "distance from start vertex to target:" << std::endl; +// boost::graph_traits<Graph>::vertex_iterator vi; +// for(vi = boost::vertices(_graph).first; vi != boost::vertices(_graph).second; ++vi) +// std::cout << "distance = " << d[*vi] << std::endl; + + //Log->Write(std::to_string(landmarksOnShortestPath.size())); + return std::make_pair(landmarksOnShortestPath,d[targetVertex]); +} + +void AILandmarkNetwork::RemoveAdjacentEdges(const Vertex &vertex) +{ + for (auto it=_connections.begin(); it!=_connections.end(); ++it) + { + if (it->first.first == vertex || it->first.second == vertex) + { + _connections.remove(*it); + } + } +} + diff --git a/routing/ai_router_trips/cognitiveMap/landmarknetwork.h b/routing/ai_router_trips/cognitiveMap/landmarknetwork.h new file mode 100644 index 0000000000000000000000000000000000000000..1a8e0dc0a40e49c82f6616a18bc0aad82ea61859 --- /dev/null +++ b/routing/ai_router_trips/cognitiveMap/landmarknetwork.h @@ -0,0 +1,52 @@ +#ifndef LANDMARKNETWORK_H +#define LANDMARKNETWORK_H + + + +//based on boost + +#include <boost/graph/graph_traits.hpp> +#include <boost/graph/adjacency_list.hpp> +#include <boost/graph/dijkstra_shortest_paths.hpp> +#include "connection.h" +#include "landmark.h" +#include <unordered_map> + + + +typedef boost::adjacency_list<boost::listS, boost::vecS, boost::directedS, + boost::no_property, boost::property<boost::edge_weight_t, double> > Graph; +typedef boost::graph_traits<Graph>::vertex_descriptor Vertex; +typedef std::pair<Vertex, Vertex> Edge; +typedef double Weight; +typedef std::vector<Edge> Edges; + + +class AILandmarkNetwork +{ +public: + AILandmarkNetwork(); + AILandmarkNetwork(const AIRegion *region, const AILandmarks& landmarks, const std::vector<AIConnection> &connections); + ~AILandmarkNetwork(); + void AddLandmark(const AILandmark* landmark); + void RemoveLandmark(const AILandmark* landmark); + void AddConnection(const AIConnection* connection); + + //Calculations + std::pair<std::vector<const AILandmark *>, double> LengthofShortestPathToTarget(const AILandmark *landmark, const AILandmark *target) const; + + +private: + Graph _graph; + std::unordered_map<const AILandmark*,Vertex> _landmarks; //vertices + std::list<std::pair<Edge,Weight>> _connections; //edges; Weight equals length + //between two random point in the connected landmarks + const AIRegion* _region; + + void RemoveAdjacentEdges(const Vertex& vertex); + + + +}; + +#endif // LANDMARKNETWORK_H diff --git a/routing/ai_router_trips/cognitiveMap/region.cpp b/routing/ai_router_trips/cognitiveMap/region.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ddf0b49f8106649876c96513cdb953841846e123 --- /dev/null +++ b/routing/ai_router_trips/cognitiveMap/region.cpp @@ -0,0 +1,112 @@ +#include "region.h" + +AIRegion::AIRegion(const Point &pos) : AILandmark(pos) +{ + +} + +AIRegion::~AIRegion() +{ + +} + +void AIRegion::AddLandmark(const AILandmark &landmark) +{ + _landmarks.push_back(landmark); + +} + +void AIRegion::AddLandmarkSubCs(const AILandmark &landmark) +{ + _landmarksSubConcious.push_back(landmark); +} + +const AILandmark *AIRegion::GetRegionAsLandmark() const +{ + return static_cast<const AILandmark*>(this); +} + +const AILandmarks &AIRegion::GetLandmarks() const +{ + return _landmarks; +} + +const AILandmark *AIRegion::GetLandmarkByID(int ID) const +{ + for (const AILandmark& landmark:_landmarks) + { + if (landmark.GetId()==ID) + return &landmark; + } + return nullptr; +} + +bool AIRegion::ContainsLandmark(const AILandmark* landmark) const +{ + for (const AILandmark& clandmark:_landmarks) + { + if (&clandmark==landmark) + return true; + } + return false; +} + +void AIRegion::InitLandmarkNetwork() +{ + _landmarkNetwork = AILandmarkNetwork(this,GetLandmarks(),GetAllConnections()); +} + +std::pair<std::vector<const AILandmark*>,double> AIRegion::PathLengthFromLandmarkToTarget(const AILandmark* landmark, const AILandmark* target) const +{ + return _landmarkNetwork.LengthofShortestPathToTarget(landmark,target); +} + + +const std::vector<AIConnection>& AIRegion::GetAllConnections() const +{ + return _connections; +} + +void AIRegion::AddConnection(const AIConnection& connection) +{ + _connections.push_back(connection); +} + +void AIRegion::AddConnection(int id, const std::string& caption, const std::string& type, int landmarkId1, int landmarkId2) +{ + _connections.push_back(AIConnection(id,caption,type,landmarkId1,landmarkId2)); +} + +void AIRegion::RemoveConnections(const AILandmark* landmark) +{ + for (auto it=_connections.begin(); it!=_connections.end(); ++it) + { + if (it->GetLandmarkIds().first==landmark->GetId() || it->GetLandmarkIds().second==landmark->GetId()) + { + it=_connections.erase(it); + --it; + } + } +} + +std::vector<const AILandmark*> AIRegion::ConnectedWith(const AILandmark *landmark) const +{ + std::vector<const AILandmark*> cLandmarks; + for (const AIConnection& connection:_connections) + { + if (connection.GetLandmarkIds().first==landmark->GetId()) + { + cLandmarks.push_back(this->GetLandmarkByID(connection.GetLandmarkIds().second)); + } + else if (connection.GetLandmarkIds().second==landmark->GetId()) + { + cLandmarks.push_back(this->GetLandmarkByID(connection.GetLandmarkIds().first)); + } + } + + return cLandmarks; + +} + + + diff --git a/routing/ai_router_trips/cognitiveMap/region.h b/routing/ai_router_trips/cognitiveMap/region.h new file mode 100644 index 0000000000000000000000000000000000000000..cc89bac3c346af477d15c5e00211ae5d5b62c541 --- /dev/null +++ b/routing/ai_router_trips/cognitiveMap/region.h @@ -0,0 +1,46 @@ +#ifndef REGION_H +#define REGION_H + +#include "landmark.h" +#include "connection.h" +#include "landmarknetwork.h" + + +using AILandmarks = std::vector<AILandmark>; +using AIConnections = std::vector<AIConnection>; + +class AIRegion : public AILandmark +{ +public: + AIRegion(const Point& pos); + ~AIRegion(); + + void AddLandmark(const AILandmark& landmark); + void AddLandmarkSubCs(const AILandmark &landmark); + const AILandmark* GetRegionAsLandmark() const; + + //Getter + const AILandmarks& GetLandmarks() const; + const AILandmark* GetLandmarkByID(int ID) const; + bool ContainsLandmark(const AILandmark* landmark) const; + + //LandmarkNetwork + void InitLandmarkNetwork(); + std::pair<std::vector<const AILandmark*>, double> PathLengthFromLandmarkToTarget(const AILandmark* landmark, const AILandmark* target) const; + + //Connections + const std::vector<AIConnection> &GetAllConnections() const; + void AddConnection(const AIConnection &connection); + void AddConnection(int id, const std::string &caption, const std::string &type, int landmarkId1, int landmarkId2); + void RemoveConnections(const AILandmark *landmark); + std::vector<const AILandmark *> ConnectedWith(const AILandmark *landmark) const; + +private: + AILandmarks _landmarks; + AIConnections _connections; + AILandmarkNetwork _landmarkNetwork; + AIAssociations _assoContainer; + AILandmarks _landmarksSubConcious; +}; + +#endif // REGION_H diff --git a/routing/ai_router_trips/perception/cgalgeometry.cpp b/routing/ai_router_trips/perception/cgalgeometry.cpp new file mode 100644 index 0000000000000000000000000000000000000000..821b3b0840a36066b0bb66ae13d1451e62477aad --- /dev/null +++ b/routing/ai_router_trips/perception/cgalgeometry.cpp @@ -0,0 +1,268 @@ +#include "cgalgeometry.h" +#include "../../../geometry/SubRoom.h" +#include <iostream> + +CGALGeometry::CGALGeometry() +{ + +} + +CGALGeometry::CGALGeometry(const std::unordered_map<ptrFloor, std::vector<const Line*> > &allWalls) +{ + + // for every room (floor) separately: + + for (auto it=allWalls.begin(); it!=allWalls.end(); ++it) + { + //std::cout << it->first->GetCaption() << std::endl; + /// if geometry is not stored in advance (begin) + /// + const std::vector<const Line*>& walls=it->second; + + Polygon_with_holes_2 unionR; + + int n = 0; + + std::vector<Polygon_2> separatedPolygons; + + + Point dirVector = walls[0]->GetPoint2()- walls[0]->GetPoint1(); + dirVector = dirVector / dirVector.Norm(); + Point normalVector = Point(-dirVector._y, dirVector._x); + Point P1 = walls[0]->GetPoint1(); + Point P2 = walls[0]->GetPoint2(); + Point Ps1 = P1 - normalVector * 0.12 - dirVector * 0.12; + Point Ps2 = P2 - normalVector * 0.12 + dirVector * 0.12; + Point Ps3 = P2 + normalVector * 0.12 + dirVector * 0.12; + Point Ps4 = P1 + normalVector * 0.12 - dirVector * 0.12; + + unionR.outer_boundary().push_back(Point_2(Ps1._x, Ps1._y)); + unionR.outer_boundary().push_back(Point_2(Ps2._x, Ps2._y)); + unionR.outer_boundary().push_back(Point_2(Ps3._x, Ps3._y)); + unionR.outer_boundary().push_back(Point_2(Ps4._x, Ps4._y)); + + + Polygon_2 outer; + + + for (const Line* wall:walls) + { + if (n < 1) + { + n++; + continue; + } + dirVector = wall->GetPoint2()-wall->GetPoint1();//wall.at(1) - wall.at(0); + dirVector = dirVector / dirVector.Norm(); + normalVector = Point(-dirVector._y, dirVector._x); + //0.12 equals the half of a wall's width + //BoostPolygon boostHole; + Polygon_2 cgalPolygon; + + Point PP1 = wall->GetPoint1(); + Point PP2 = wall->GetPoint2(); + Point Pi1 = PP1 - normalVector * 0.12 - dirVector * 0.12; + Point Pi2 = PP2 - normalVector * 0.12 + dirVector * 0.12; + Point Pi3 = PP2 + normalVector * 0.12 + dirVector * 0.12; + Point Pi4 = PP1 + normalVector * 0.12 - dirVector * 0.12; + + cgalPolygon.push_back(Point_2(Pi1._x, Pi1._y)); + cgalPolygon.push_back(Point_2(Pi2._x, Pi2._y)); + cgalPolygon.push_back(Point_2(Pi3._x, Pi3._y)); + cgalPolygon.push_back(Point_2(Pi4._x, Pi4._y)); + + if (!CGAL::join(unionR, cgalPolygon, unionR)) + { + separatedPolygons.push_back(cgalPolygon); + Log->Write(std::to_string(n)+" of " + std::to_string(walls.size())); + } + + //outer = unionR.outer_boundary(); + + //try to join separated polygons + std::vector<Polygon_2>::iterator p = separatedPolygons.begin(); + while (p != separatedPolygons.end()) { + if (CGAL::join(unionR, *p, unionR)) { + p = separatedPolygons.erase(p); + } else + ++p; + } + ++n; + } + std::vector<Polygon_with_holes_2> listPolygons; + //listPolygons.push_back(unionR); + + size_t num_polygons; + while (!separatedPolygons.empty()) { + num_polygons = separatedPolygons.size(); + std::vector<Polygon_2>::iterator p = separatedPolygons.begin(); + while (p != separatedPolygons.end()) { + if (CGAL::join(unionR, *p, unionR)) { + p = separatedPolygons.erase(p); + } else + ++p; + } + if (num_polygons == separatedPolygons.size()) { + + listPolygons.push_back(unionR); + unionR=Polygon_with_holes_2(separatedPolygons.front()); + } + Log->Write(std::to_string(separatedPolygons.size())+" of " + std::to_string(walls.size())); + } + + listPolygons.push_back(unionR); + + std::vector<Segment_2> segments; + + Point_2 pb1=Point_2(it->first->GetBoundaryVertices()[0]._x-1,it->first->GetBoundaryVertices()[0]._y-1); + Point_2 pb2=Point_2(it->first->GetBoundaryVertices()[3]._x+1,it->first->GetBoundaryVertices()[3]._y-1); + Point_2 pb3=Point_2(it->first->GetBoundaryVertices()[2]._x+1,it->first->GetBoundaryVertices()[2]._y+1); + Point_2 pb4=Point_2(it->first->GetBoundaryVertices()[1]._x-1,it->first->GetBoundaryVertices()[1]._y+1); +// std::cout << pb1.x() << " " << pb1.y() << std::endl; +// std::cout << pb2.x() << " " << pb2.y() << std::endl; +// std::cout << pb3.x() << " " << pb3.y() << std::endl; +// std::cout << pb4.x() << " " << pb4.y() << std::endl; + segments.push_back(Segment_2(pb1,pb2)); + segments.push_back(Segment_2(pb2,pb3)); + segments.push_back(Segment_2(pb3,pb4)); + segments.push_back(Segment_2(pb4,pb1)); + + std::cout << "Room" << it->first->GetID() << std::endl; + for (Polygon_with_holes_2 poly:listPolygons) + { + outer = poly.outer_boundary(); + + +// Point veryFirstP=Point(roundfloat(CGAL::to_double(outer[0].x()),1),roundfloat(CGAL::to_double(outer[0].y()),1)); +// Point lastPoint = veryFirstP; + +// std::cout << "Polygon" << std::endl; +// std::cout << lastPoint._x << " " << lastPoint._y << std::endl; + + size_t i = 1; + for (; i < outer.size(); ++i) + { +// Point newPoint = Point(roundfloat(CGAL::to_double(outer[i].x()),1),roundfloat(CGAL::to_double(outer[i].y()),1)); +// if (newPoint!=lastPoint && newPoint!=veryFirstP) +// { +// segments.push_back(Segment_2(Point_2(lastPoint._x,lastPoint._y), Point_2(newPoint._x,newPoint._y)));//-1].y()),Point_2(outer[i].x(),outer[i].y()))); +// lastPoint=newPoint; +// std::cout << newPoint._x << " " << newPoint._y << std::endl;/ + segments.push_back(Segment_2(outer[i-1],outer[i])); +// } + } + segments.push_back(Segment_2(outer[i-1],outer[0])); +// Point_2 CGALlastP = Point_2(lastPoint._x,lastPoint._y); +// Point_2 CGALFirstP = Point_2(veryFirstP._x,veryFirstP._y); +// segments.push_back(Segment_2(CGALlastP,CGALFirstP)); + + + } + + + /// if geometry is not stored in advance (end) + /// + /// if geometry is stored in file in advance + +// std::vector<Segment_2> segments; + + +// Point_2 pb1=Point_2(it->first->GetBoundaryVertices()[0]._x-1,it->first->GetBoundaryVertices()[0]._y-1); +// Point_2 pb2=Point_2(it->first->GetBoundaryVertices()[3]._x+1,it->first->GetBoundaryVertices()[3]._y-1); +// Point_2 pb3=Point_2(it->first->GetBoundaryVertices()[2]._x+1,it->first->GetBoundaryVertices()[2]._y+1); +// Point_2 pb4=Point_2(it->first->GetBoundaryVertices()[1]._x-1,it->first->GetBoundaryVertices()[1]._y+1); +// segments.push_back(Segment_2(pb1,pb2)); +// segments.push_back(Segment_2(pb2,pb3)); +// segments.push_back(Segment_2(pb3,pb4)); +// segments.push_back(Segment_2(pb4,pb1)); + + +// std::string line; +// std::string filename = "D:/Dokumente/jpstests/osloerstr/polygons_id"+std::to_string(it->first->GetID())+".txt"; +// std::cout << filename << std::endl; +// std::ifstream myfile (filename); +// std::vector<std::vector<Point_2>> polyVertices; +// Point oldPoint=Point(FLT_MAX,FLT_MAX); +// if (myfile.is_open()) +// { +// while ( std::getline (myfile,line) ) +// { +// if (line=="Polygon") +// { +// polyVertices.push_back(std::vector<Point_2>{}); +// } +// else +// { +// std::stringstream linestream(line); +// //std::string data; +// //std::getline(linestream, data, ';'); +// double x; +// double y; +// linestream >> x >> y; +// Point point = Point(x,y); +// Point vec = oldPoint-point; +// if (vec.Norm()>=0.1) +// { +// polyVertices.back().push_back(Point_2(point._x,point._y)); +// //Log->Write(std::to_string(it->first->GetID())); +// //Log->Write(std::to_string(point._x)+" "+std::to_string(point._y)); +// oldPoint=point; +// } +// } +// } +// myfile.close(); +// } + +// else +// Log->Write("ERROR \t Unable to open file"); + + +// for (std::vector<Point_2> vector:polyVertices) +// { +// size_t i = 1; +// for (; i < vector.size(); ++i) +// { +// segments.push_back(Segment_2(vector[i - 1], vector[i])); +// } +// segments.push_back(Segment_2(vector.back(), vector.front())); +// } + /// end if + + + + // insert geometry into the arrangement + + Arrangement_2 env; + CGAL::insert_non_intersecting_curves(env, segments.begin(), segments.end()); + + //associate room with visible env (walls) of that room + _CGAL_env.emplace(it->first,env); + + //std::shared_ptr<TEV> tev=std::make_shared<TEV>(env); + + //associate room with TEV + _tev.emplace(it->first,std::unique_ptr<TEV>(new TEV(env))); + + + } + +} + + + + +const TEV *CGALGeometry::GetTEV(ptrFloor floor) const +{ + return _tev.at(floor).get(); +} + +const Arrangement_2 &CGALGeometry::GetVisEnvCGAL(ptrFloor floor) const +{ + return _CGAL_env.at(floor); +} + + +float roundfloat(float num, int precision) +{ + return floorf(num * pow(10.0f,precision) + .5f)/pow(10.0f,precision); +} diff --git a/routing/ai_router_trips/perception/cgalgeometry.h b/routing/ai_router_trips/perception/cgalgeometry.h new file mode 100644 index 0000000000000000000000000000000000000000..dd6dba67ca9c82bc784d4c66995073594fab45dc --- /dev/null +++ b/routing/ai_router_trips/perception/cgalgeometry.h @@ -0,0 +1,53 @@ +#ifndef CGALGEOMETRY_H +#define CGALGEOMETRY_H + +//#include <CGAL/Exact_predicates_inexact_constructions_kernel.h> +#include <CGAL/Simple_cartesian.h> +#include <CGAL/Boolean_set_operations_2.h> +#include <CGAL/Triangular_expansion_visibility_2.h> +#include <CGAL/Arr_segment_traits_2.h> +#include <CGAL/Arr_naive_point_location.h> +#include <CGAL/Arrangement_2.h> +//#include <CGAL/Lazy_exact_nt.h> + + +#include <unordered_map> + +#include "../../../geometry/Room.h" + +class Building; +class Line; + +//typedef CGAL::Lazy_exact_nt<CGAL::Quotient<CGAL::MP_Float> > NT; +//CGAL::Simple_cartesian::Point_2 CGAL::Lazy_exact_nt< +//typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; +typedef CGAL::Simple_cartesian<CGAL::Lazy_exact_nt<CGAL::Gmpq>> Kernel; +typedef Kernel::Point_2 Point_2; +//Exact_predicates_inexact_constructions_kernel::Point_3 +typedef Kernel::Segment_2 Segment_2; +typedef CGAL::Polygon_2<Kernel> Polygon_2; +typedef CGAL::Polygon_with_holes_2<Kernel> Polygon_with_holes_2; +typedef CGAL::Arr_segment_traits_2<Kernel> Traits_2; +typedef CGAL::Arrangement_2<Traits_2> Arrangement_2; +typedef CGAL::Triangular_expansion_visibility_2<Arrangement_2> TEV; + +using ptrFloor = const Room*; + +class CGALGeometry +{ +public: + explicit CGALGeometry(); + explicit CGALGeometry(const std::unordered_map<ptrFloor, std::vector<const Line *> > &allWalls); + + const Arrangement_2 &GetVisEnvCGAL(ptrFloor floor) const; + const TEV* GetTEV(ptrFloor floor) const; + +private: + + std::unordered_map<ptrFloor,const Arrangement_2> _CGAL_env; + std::unordered_map<ptrFloor,std::unique_ptr<TEV>> _tev; +}; + +float roundfloat(float num, int precision); + +#endif // CGALGEOMETRY_H diff --git a/routing/ai_router_trips/perception/sign.cpp b/routing/ai_router_trips/perception/sign.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e0071e3b23a282aa0b234a467c0a9472af67c9b6 --- /dev/null +++ b/routing/ai_router_trips/perception/sign.cpp @@ -0,0 +1,83 @@ +// +// Created by erik on 30.12.16. +// + +#include "sign.h" + +Sign::Sign(int id, int room_id, const Point &pos, const double &alpha, const double &alphaPointing, const double &width, const double &height) : _id(id), _roomId(room_id), _pos(pos), _alpha(alpha),_alphaPointing(alphaPointing),_width(width), _height(height) { + + +} + +Sign::~Sign() { + +} + +const Point &Sign::GetPos() const { + return _pos; +} + +double Sign::GetAlpha() const { + return _alpha; +} + +double Sign::GetAlphaPointing() const +{ + return _alphaPointing; +} + +void Sign::SetPos(const Point &pos) { + _pos = pos; +} + +void Sign::SetAlpha(const double& alpha) { + _alpha = alpha; +} + +void Sign::SetAlphaPointing(const double &alpha) +{ + _alphaPointing=alpha; +} + +int Sign::GetId() const { + return _id; +} + +int Sign::GetRoomId() const +{ + return _roomId; +} + +const double &Sign::GetWidth() const +{ + return _width; +} + +const double &Sign::GetHeight() const +{ + return _height; +} + +void Sign::SetId(int id) { + + _id=id; +} + +void Sign::SetRoomId(int room_id) +{ + _roomId=room_id; +} + +void Sign::SetWidth(const double &width) +{ + _width=width; +} + +void Sign::SetHeight(const double &height) +{ + _height=height; +} + + + + diff --git a/routing/ai_router_trips/perception/sign.h b/routing/ai_router_trips/perception/sign.h new file mode 100644 index 0000000000000000000000000000000000000000..97c0e000f730d2715788639e1e6d6157bbca9e99 --- /dev/null +++ b/routing/ai_router_trips/perception/sign.h @@ -0,0 +1,50 @@ +// +// Created by erik on 30.12.16. +// + +#ifndef JPSCORE_SIGN_H +#define JPSCORE_SIGN_H + + +#include "../../../geometry/Point.h" + +class Sign { + + +public: + + Sign(int id, int room_id, const Point &pos, const double& alpha, const double& alphaPointing, const double& width=0.3, const double &height=0.1); + + const Point &GetPos() const; + double GetAlpha() const; + double GetAlphaPointing() const; + int GetId() const; + int GetRoomId() const; + const double& GetWidth() const; + const double& GetHeight() const; + + void SetPos(const Point &pos); + void SetAlpha(const double& alpha); + void SetAlphaPointing(const double& alpha); + void SetId(int id); + void SetRoomId(int room_id); + void SetWidth(const double& width); + void SetHeight(const double& height); + + virtual ~Sign(); + +private: + + int _id; + int _roomId; + Point _pos; + double _alpha; // in deg + double _alphaPointing; + double _width; + double _height; + + +}; + + +#endif //JPSCORE_SIGN_H diff --git a/routing/ai_router_trips/perception/visibleenvironment.cpp b/routing/ai_router_trips/perception/visibleenvironment.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c88ba56eee943cbc7225a961884e952bbc8a3846 --- /dev/null +++ b/routing/ai_router_trips/perception/visibleenvironment.cpp @@ -0,0 +1,234 @@ +#include "visibleenvironment.h" +#include "../../../geometry/Building.h" +#include "../../../pedestrian/Pedestrian.h" +#include "../../../geometry/SubRoom.h" + +#include <boost/foreach.hpp> + + + +VisibleEnvironment::VisibleEnvironment():_b(nullptr),_cgalGeometry(CGALGeometry()) +{ + +} + +VisibleEnvironment::VisibleEnvironment(const Building *b):_b(b) +{ + + Log->Write("INFO: PERCEPTION: Preparing visible environment ..."); + + + //Creating complete spatial structure + + std::vector<Linestring> lines; + std::unordered_map<ptrFloor,std::vector<const Line*>> cWalls; + std::vector<const Line*> wallsOfOneRoom; + + //get walls of rooms and save them separately + for (auto it = _b->GetAllRooms().begin(); it != _b->GetAllRooms().end(); ++it) + { + lines.clear(); + wallsOfOneRoom.clear(); + for (auto it2 = it->second->GetAllSubRooms().begin(); it2 != it->second->GetAllSubRooms().end(); ++it2) + { + for (const Wall& wall:it2->second->GetAllWalls()) + { + + Linestring lineS; + boost::geometry::append(lineS, wall.GetPoint1()); + boost::geometry::append(lineS, wall.GetPoint2()); + if (std::find(lines.begin(), lines.end(), lineS) == lines.end()) + { + lines.push_back(lineS); + wallsOfOneRoom.push_back(&wall); + } + + + } + + for (const Obstacle *obstacle:it2->second->GetAllObstacles()) + { + for (const Wall& wall: obstacle->GetAllWalls()) + { + Linestring lineS; + boost::geometry::append(lineS, wall.GetPoint1()); + boost::geometry::append(lineS, wall.GetPoint2()); + if (std::find(lines.begin(), lines.end(), lineS) == lines.end()) + { + lines.push_back(lineS); + wallsOfOneRoom.push_back(&wall); + + } + + } + + } + + } + + if (it->second->GetID()==6 || it->second->GetID()==0 || it->second->GetID()==11) + { + + for (int transID:it->second->GetAllTransitionsIDs()) + { + //if ((it->second->GetID()==6 || it->second->GetID()==0))// && _b->GetTransitionByUID(transID)->GetOtherRoom(6)->GetID()==5) || (it->second->GetID()==11 && _b->GetTransitionByUID(transID)->GetOtherRoom(11)!=nullptr + //&& (_b->GetTransitionByUID(transID)->GetOtherRoom(11)->GetID()==9 || + // _b->GetTransitionByUID(transID)->GetOtherRoom(11)->GetID()==10))) + //{ + // shift transition a little bit to the outside + bool within=false; + Point normal = Point(-(_b->GetTransitionByUID(transID)->GetPoint2()-_b->GetTransitionByUID(transID)->GetPoint1())._y, + (_b->GetTransitionByUID(transID)->GetPoint2()-_b->GetTransitionByUID(transID)->GetPoint1())._x); + normal=normal/normal.Norm(); + Point P1 = _b->GetTransitionByUID(transID)->GetCentre()+normal*0.5; + + + for (auto it2 = it->second->GetAllSubRooms().begin(); it2 != it->second->GetAllSubRooms().end(); ++it2) + { + BoostPolygon boostP; + for (Point vertex:it2->second->GetPolygon()) + { + boost::geometry::append(boostP, vertex); + } + boost::geometry::correct(boostP); + + if (boost::geometry::within(P1, boostP)) + within=true; + + } + // MEMORY LEAK!!! FIX ME!!! + + if (within==true) + { + Linestring lineS; + boost::geometry::append(lineS,_b->GetTransitionByUID(transID)->GetPoint1()-normal*0.5); + boost::geometry::append(lineS,_b->GetTransitionByUID(transID)->GetPoint2()-normal*0.5); + lines.push_back(lineS); + Line* newLine = new Line(_b->GetTransitionByUID(transID)->GetPoint1()-normal*0.5,_b->GetTransitionByUID(transID)->GetPoint2()-normal*0.5); + wallsOfOneRoom.push_back(newLine); + + Linestring lineS1; + boost::geometry::append(lineS1,_b->GetTransitionByUID(transID)->GetPoint1()-normal*0.5); + boost::geometry::append(lineS1,_b->GetTransitionByUID(transID)->GetPoint1()); + lines.push_back(lineS1); + newLine = new Line(_b->GetTransitionByUID(transID)->GetPoint1()-normal*0.5,_b->GetTransitionByUID(transID)->GetPoint1()); + wallsOfOneRoom.push_back(newLine); + + Linestring lineS2; + boost::geometry::append(lineS2,_b->GetTransitionByUID(transID)->GetPoint2()-normal*0.5); + boost::geometry::append(lineS2,_b->GetTransitionByUID(transID)->GetPoint2()); + lines.push_back(lineS2); + newLine = new Line(_b->GetTransitionByUID(transID)->GetPoint2()-normal*0.5,_b->GetTransitionByUID(transID)->GetPoint2()); + wallsOfOneRoom.push_back(newLine); + } + else + { + Linestring lineS; + boost::geometry::append(lineS,_b->GetTransitionByUID(transID)->GetPoint1()+normal*0.5); + boost::geometry::append(lineS,_b->GetTransitionByUID(transID)->GetPoint2()+normal*0.5); + lines.push_back(lineS); + Line* newLine = new Line(_b->GetTransitionByUID(transID)->GetPoint1()+normal*0.5,_b->GetTransitionByUID(transID)->GetPoint2()+normal*0.5); + wallsOfOneRoom.push_back(newLine); + + Linestring lineS1; + boost::geometry::append(lineS1,_b->GetTransitionByUID(transID)->GetPoint1()+normal*0.5); + boost::geometry::append(lineS1,_b->GetTransitionByUID(transID)->GetPoint1()); + lines.push_back(lineS1); + newLine = new Line(_b->GetTransitionByUID(transID)->GetPoint1()+normal*0.5,_b->GetTransitionByUID(transID)->GetPoint1()); + wallsOfOneRoom.push_back(newLine); + + Linestring lineS2; + boost::geometry::append(lineS2,_b->GetTransitionByUID(transID)->GetPoint2()+normal*0.5); + boost::geometry::append(lineS2,_b->GetTransitionByUID(transID)->GetPoint2()); + lines.push_back(lineS2); + newLine = new Line(_b->GetTransitionByUID(transID)->GetPoint2()+normal*0.5,_b->GetTransitionByUID(transID)->GetPoint2()); + wallsOfOneRoom.push_back(newLine); + } + } + //lines.push_back(lineS); + + //} + + } + + + + _allWalls.emplace(it->second.get(),lines); + cWalls.emplace(it->second.get(),wallsOfOneRoom); + + } + + + _cgalGeometry=CGALGeometry(cWalls); + + + WriteOutWalls(); + + Log->Write("INFO: PERCEPTION: Visible environment prepared."); + +} + +//const VisiLibity::Environment* VisibleEnvironment::GetVisEnv() const +//{ +// return &_env; +//} + + + +const std::vector<Linestring> &VisibleEnvironment::GetAllWallsOfEnv(ptrFloor floor) const +{ + return _allWalls.at(floor); +} + +void VisibleEnvironment::WriteOutWalls() const +{ + + std::ofstream myfile; + std::string str = "./isovists/walls.txt"; + myfile.open (str); + + for (auto it=_allWalls.begin(); it!=_allWalls.end(); ++it) + { + const std::vector<Linestring>& walls=it->second; + for (Linestring wall:walls) + { + for (auto it2=boost::begin(wall);it2!=boost::end(wall);++it2) + myfile << std::to_string(it2->_x) << " " << std::to_string(it2->_y) << " "; + + myfile << std::endl; + + } + + } + + myfile.close(); +} + +void VisibleEnvironment::SetSigns(ptrFloor floor, const std::vector<Sign> &signs) { + + _signs.clear(); + _signs.emplace(std::make_pair(floor,signs)); +} + +const CGALGeometry &VisibleEnvironment::GetCGALGeometry() const +{ + return _cgalGeometry; +} + +void VisibleEnvironment::AddSign(ptrFloor floor, const Sign &sign) +{ + _signs[floor].push_back(sign); +} + +void VisibleEnvironment::AddSign(ptrFloor floor, Sign &&sign) +{ + _signs[floor].push_back(std::move(sign)); +} + +const std::vector<Sign> *VisibleEnvironment::GetSignsOfFloor(ptrFloor floor) const +{ + if (_signs.find(floor)==_signs.end()) + return nullptr; + else + return &_signs.at(floor); +} diff --git a/routing/ai_router_trips/perception/visibleenvironment.h b/routing/ai_router_trips/perception/visibleenvironment.h new file mode 100644 index 0000000000000000000000000000000000000000..b19aeb18f2a1997cd7adea7da3701c2d0eb25767 --- /dev/null +++ b/routing/ai_router_trips/perception/visibleenvironment.h @@ -0,0 +1,40 @@ +#ifndef VISIBLEENVIRONMENT_H +#define VISIBLEENVIRONMENT_H + +#include "sign.h" +#include "cgalgeometry.h" +#include <boost/geometry/geometry.hpp> + +// Define the used kernel and arrangement + +typedef boost::geometry::model::linestring<Point> Linestring; +typedef boost::geometry::model::polygon<Point> BoostPolygon; + +class VisibleEnvironment +{ +public: + explicit VisibleEnvironment(); + explicit VisibleEnvironment(const Building* b); + //const VisiLibity::Environment *GetVisEnv() const; + const std::vector<Linestring>& GetAllWallsOfEnv(ptrFloor floor) const; + void WriteOutWalls() const; + void SetSigns(ptrFloor floor, const std::vector<Sign> &signs); + const CGALGeometry& GetCGALGeometry() const; + void AddSign(ptrFloor floor, const Sign& sign); + void AddSign(ptrFloor floor, Sign&& sign); + const std::vector<Sign> *GetSignsOfFloor(ptrFloor floor) const; + + +private: + const Building* _b; + //VisiLibity::Environment _env; + // int: room_id: LinestringVector: Walls in room with id=room_id + std::unordered_map<ptrFloor,std::vector<Linestring>> _allWalls; + //std::vector<Sign> _signs; + std::unordered_map<ptrFloor, std::vector<Sign>> _signs; + CGALGeometry _cgalGeometry; + std::vector<Line> _additionalLines; + +}; + +#endif // VISIBLEENVIRONMENT_H diff --git a/routing/ai_router_trips/perception/visualsystem.cpp b/routing/ai_router_trips/perception/visualsystem.cpp new file mode 100644 index 0000000000000000000000000000000000000000..add09ca56b07587fe570e719309beab1390dbccb --- /dev/null +++ b/routing/ai_router_trips/perception/visualsystem.cpp @@ -0,0 +1,684 @@ +#include "visualsystem.h" +#include "visibleenvironment.h" +#include "../../../geometry/SubRoom.h" +#include "../../../pedestrian/Pedestrian.h" +#include <chrono> +#include <iostream> + + + +VisualSystem::VisualSystem() +{ + +} + +VisualSystem::VisualSystem(const Building *b, const Pedestrian *ped, const VisibleEnvironment *env) +{ + _building=b; + _ped=ped; + _env=env; + _signPerceived=0; + +} + +const std::unordered_map<ptrFloor, BoostPolygon> &VisualSystem::UpdateSeenEnv() +{ + + + //SubRoom * sub_room = _building->GetRoom(_ped->GetRoomID())->GetSubRoom(_ped->GetSubRoomID()); + + + //BoostPolygon roomP; + +// for (Point point:sub_room->GetPolygon()) +// boost::geometry::append(roomP,point); + +// std::vector<BoostPolygon> intersectPolygon; +// boost::geometry::intersection(roomP,newPolygon,intersectPolygon); + + + //BoostPolygon currentEnv = GetCurrentEnvironment(); + + + + //limiting seen Environment + BoostPolygon rectangle; + + boost::geometry::append(rectangle,Point(_ped->GetPos()._x-35,_ped->GetPos()._y-35)); + boost::geometry::append(rectangle,Point(_ped->GetPos()._x-35,_ped->GetPos()._y+35)); + boost::geometry::append(rectangle,Point(_ped->GetPos()._x+35,_ped->GetPos()._y+35)); + boost::geometry::append(rectangle,Point(_ped->GetPos()._x+35,_ped->GetPos()._y-35)); + + + try { + std::vector<BoostPolygon> output2; + boost::geometry::intersection(_seenEnvironment[_building->GetRoom(_ped->GetRoomID())],rectangle,output2); + if (!output2.empty()) + _seenEnvironment.at(_building->GetRoom(_ped->GetRoomID()))=output2.front(); + + + //union of current and seen Env + + std::vector<BoostPolygon> output; + boost::geometry::union_(_seenEnvironment[_building->GetRoom(_ped->GetRoomID())],_currentEnv,output); + if (!output.empty()) + _seenEnvironment.at(_building->GetRoom(_ped->GetRoomID()))=output.front(); + + boost::geometry::correct(_seenEnvironment.at(_building->GetRoom(_ped->GetRoomID()))); + + } + catch (...) + { + // Code that handles another exception type + // ... + //cerr << e.what(); + _seenEnvironment.at(_building->GetRoom(_ped->GetRoomID())) = _currentEnv; + } + +// std::ofstream myfile2; +// std::string str2 = "./isovists/seen_env_"+std::to_string(_ped->GetID())+"_"+std::to_string((int)(std::round(_ped->GetGlobalTime()*100)))+".txt"; +// myfile2.open (str2); +// for(auto it1 = boost::begin(boost::geometry::exterior_ring(_seenEnvironment.at(_building->GetRoom(_ped->GetRoomID())))); +// it1 != boost::end(boost::geometry::exterior_ring(_seenEnvironment.at(_building->GetRoom(_ped->GetRoomID())))); +// ++it1) +// { +// myfile2 << it1->_x << " " << it1->_y << std::endl; +// //seenEnvPolygon.push_back(*it1); + +// } +// myfile2.close(); + +// std::ofstream myfile3; +// std::string str3 = "./isovists/real_pos"+std::to_string(_ped->GetID())+"_"+std::to_string((int)(std::round(_ped->GetGlobalTime()*100)))+".txt"; +// myfile3.open (str3); +// myfile3 << std::to_string(_ped->GetPos()._x) << " " << std::to_string(_ped->GetPos()._y) << std::endl; +// myfile3.close(); + + + return _seenEnvironment; + +} + +const BoostPolygon &VisualSystem::UpdateCurrentEnvironment() +{ +// VisiLibity::Visibility_Polygon +// my_visibility_polygon(VisiLibity::Point(_ped->GetPos()._x, _ped->GetPos()._y), (*_env->GetVisEnv()),0.01); +// //std::cout << "The visibility polygon is \n" << my_visibility_polygon << std::endl; + + + + // find the face of the query point + // (usually you may know that by other means) + Point_2 q(_ped->GetPos()._x,_ped->GetPos()._y); + Arrangement_2::Face_const_handle *face; + //only visEnv of actual room + const Arrangement_2& arr = _env->GetCGALGeometry().GetVisEnvCGAL(_building->GetRoom(_ped->GetRoomID())); + CGAL::Arr_naive_point_location<Arrangement_2> pl(arr); + CGAL::Arr_point_location_result<Arrangement_2>::Type obj = pl.locate(q); + // The query point locates in the interior of a face + face = boost::get<Arrangement_2::Face_const_handle> (&obj); + + + //visibility query + Arrangement_2 output_arr; + Arrangement_2::Face_handle fh = _env->GetCGALGeometry().GetTEV(_building->GetRoom(_ped->GetRoomID()))->compute_visibility(q,*face,output_arr);//query_point, he, output_arr); + + //print out the visibility region. +// std::cout << "Regularized visibility region of q has " +// << output_arr.number_of_edges() +// << " edges." << std::endl; + + //std::cout << "Boundary edges of the visibility region:" << std::endl; + Arrangement_2::Ccb_halfedge_circulator curr = fh->outer_ccb(); + + BoostPolygon currentEnv; + + //std::cout << "[" << curr->source()->point() << " -> " << curr->target()->point() << "]" << std::endl; + boost::geometry::append(currentEnv,Point(CGAL::to_double(curr->source()->point().x()) ,CGAL::to_double(curr->source()->point().y()))); + while (++curr != fh->outer_ccb()) + { + boost::geometry::append(currentEnv,Point(CGAL::to_double(curr->source()->point().x()) ,CGAL::to_double(curr->source()->point().y()))); + //std::cout << "[" << curr->source()->point() << " -> " << curr->target()->point() << "]" << std::endl; + } + + //delete face; +// //std::vector<Point> visPolygon; + + +// for (size_t i=0; i<my_visibility_polygon.n(); ++i) +// { +// //check for small holes +// for (size_t j=i+1; j<my_visibility_polygon.n(); ++j) +// { +// Point vec = Point(my_visibility_polygon[i].x(),my_visibility_polygon[i].y())-Point(my_visibility_polygon[j].x(),my_visibility_polygon[j].y()); +// if (vec.Norm()<=0.1) +// { +// i=j+1; +// } +// } +// if (i==my_visibility_polygon.n()) +// break; + +// else +// { +// visPolygon.push_back(Point(my_visibility_polygon[i].x(),my_visibility_polygon[i].y())); +// boost::geometry::append(currentEnv,visPolygon[i]); +// } + +// } + +// std::ofstream myfile; +// std::string str = "./isovists/current_env_"+std::to_string(_ped->GetID())+"_"+std::to_string((int)(std::round(_ped->GetGlobalTime()*100)))+".txt"; +// myfile.open (str); +// for(auto it1 = boost::begin(boost::geometry::exterior_ring(currentEnv)); +// it1 != boost::end(boost::geometry::exterior_ring(currentEnv)); +// ++it1) +// { +// myfile << it1->_x << " " << it1->_y << std::endl; +// //seenEnvPolygon.push_back(*it1); + +// } +// myfile.close(); + + boost::geometry::correct(currentEnv); + + _currentEnv=currentEnv; + + //PerceiveSigns(); + + return _currentEnv; + +} + +std::vector<NavLine> VisualSystem::GetPossibleNavLines(const std::vector<Point>& envPolygon) const +{ + //check if transitions are in the visible surroundings + std::vector<NavLine> navLines=FindTransitions(); + //std::vector<ptrNavLine> navLines; +// if (!navLines.empty()) +// return navLines; + + + std::vector<Point> visPolygon = envPolygon;// UpdateSeenEnv(); + //visPolygon.push_back(visPolygon.front()); + + + for (size_t i=1; i<visPolygon.size(); ++i) + { + +// for(auto it1 = boost::begin(boost::geometry::exterior_ring(_seenEnvironment)++); +// it1 != boost::end(boost::geometry::exterior_ring(_seenEnvironment)); +// ++it1) + //{ + bool intersect=false; +// bool intersectsSeenEnv=false; + Point center = (visPolygon[i-1]+visPolygon[i])/2.0;// + +// std::cout << boost::geometry::distance(center,_seenEnvironment) << std::endl; + //if (boost::geometry::within(center,_seenEnvironment)) + //{ +// bool status=false; +// for (ptrNavLine navLine:_oldNavLines) +// { +// Linestring old_line; + +// boost::geometry::append(old_line, navLine->GetPoint1()); +// boost::geometry::append(old_line, navLine->GetPoint2()); +// if (boost::geometry::distance(center,navLine->GetCentre())<1.0) +// { +// status=true; +// } +// } +// if (!status) +// continue; + // intersectsSeenEnv=true; + //} + for (Linestring lineS: _env->GetAllWallsOfEnv(_building->GetRoom(_ped->GetRoomID()))) + { + //Point vecWall = lineS.at(1)-lineS.at(0); + //Point vecVis = visPolygon[i]+visPolygon[i-1]; + if (boost::geometry::distance(center,lineS)<=0.15)// && boost::geometry::distance(visPolygon[i],lineS)<=0.15) + { + //std::cout << "Distance" << boost::geometry::distance(visPolygon[i-1],lineS) << " " << boost::geometry::distance(visPolygon[i],lineS) << std::endl; + intersect=true; + break; + } + } + // if not wall and in current room (no transition) + if (intersect==false) //&& (IsInSubRoom(visPolygon[i-1])!=nullptr || IsInSubRoom(visPolygon[i])!=nullptr)) + { + if (Line(visPolygon[i-1],visPolygon[i]).GetLength()>0.4) + { + +// Point dirVector=visPolygon[i-1]-visPolygon[i]; +// dirVector=dirVector/dirVector.Norm(); +// Point normalVector = Point(-dirVector._y,dirVector._x); + + //if (!intersectsSeenEnv) + const NavLine navLine(Line(visPolygon[i-1],visPolygon[i])); + navLines.push_back(navLine);//Line(center+normalVector*3.0,center))); + //else + // navLinesSecondChoice.push_back(std::make_shared<const NavLine>(Line(center+normalVector*3.0,center))); +// Log->Write("Center:"); +// Log->Write(std::to_string((center-normalVector*3.0)._x)); +// Log->Write(std::to_string((center-normalVector*3.0)._y)); +// Log->Write(" "); + } + } + + } + +// std::cout << navLines.size() << std::endl; + if (navLines.empty()) + { + + navLines.push_back(NavLine(Line(_ped->GetPos(),_ped->GetPos()+_ped->GetV()))); + + +//// if (navLinesSecondChoice.empty()) +//// { +// Log->Write("ERROR:\t Pedestrian seems to be trapped. No way out^^\n"); +// Log->Write(std::to_string(_ped->GetID())); +// Log->Write("\n"); +// std::cout << _ped->GetPos()._x << " " << _ped->GetPos()._y << std::endl; +// for(auto it1 = boost::begin(boost::geometry::exterior_ring(_currentEnv)); +// it1 != boost::end(boost::geometry::exterior_ring(_currentEnv)); +// ++it1) +// { +// std::cout << it1->_x << " " << it1->_y << std::endl; +// //seenEnvPolygon.push_back(*it1); + +// } + //exit(EXIT_FAILURE); +// //return navLines; +//// } +//// return navLinesSecondChoice; + } + // std::cout << navLines.size() << std::endl; + + //_oldNavLines=navLines; + return navLines; + + +} + +const std::unordered_map<ptrFloor, BoostPolygon> &VisualSystem::GetSeenEnvironment() const +{ + return _seenEnvironment; +} + +const BoostPolygon &VisualSystem::GetCurrentEnvironment() const +{ + return _currentEnv; +} + +std::vector<Point> VisualSystem::GetSeenEnvironmentAsPointVec(const ptrFloor& floor) const +{ + std::vector<Point> seenEnvPolygon; + for(auto it = boost::begin(boost::geometry::exterior_ring(_seenEnvironment.at(floor))); + it != boost::end(boost::geometry::exterior_ring(_seenEnvironment.at(floor))); + ++it) + { + seenEnvPolygon.push_back(*it); + } + + + + for(size_t j=0; j<_seenEnvironment.at(floor).inners().size(); ++j) + { + auto innerPolygon = _seenEnvironment.at(floor).inners()[j]; + for (size_t i=0; i<innerPolygon.size(); ++i) + { + seenEnvPolygon.push_back(innerPolygon[i]); + } + } + + + return seenEnvPolygon; +} + +std::vector<Point> VisualSystem::GetCurrentEnvironmentAsPointVec() const +{ + std::vector<Point> currentEnvPolygon; + for(auto it = boost::begin(boost::geometry::exterior_ring(_currentEnv)); + it != boost::end(boost::geometry::exterior_ring(_currentEnv)); + ++it) + { + currentEnvPolygon.push_back(*it); + + } + + for(size_t j=0; j<_currentEnv.inners().size(); ++j) + { + auto innerPolygon = _currentEnv.inners()[j]; + for (size_t i=0; i<innerPolygon.size(); ++i) + { + currentEnvPolygon.push_back(innerPolygon[i]); + } + } + + return currentEnvPolygon; +} + +std::vector<NavLine> VisualSystem::FindTransitions() const +{ + + std::vector<Transition*> transitions; + for (int transID:_building->GetRoom(_ped->GetRoomID())->GetAllTransitionsIDs()) + { + Point normal = Point(-(_building->GetTransitionByUID(transID)->GetPoint2()-_building->GetTransitionByUID(transID)->GetPoint1())._y, + (_building->GetTransitionByUID(transID)->GetPoint2()-_building->GetTransitionByUID(transID)->GetPoint1())._x); + normal=normal/normal.Norm(); + + if (boost::geometry::within(_building->GetTransitionByUID(transID)->GetCentre()+normal*0.5,_currentEnv) || + boost::geometry::within(_building->GetTransitionByUID(transID)->GetCentre()-normal*0.5,_currentEnv)) + { + transitions.push_back(_building->GetTransitionByUID(transID)); + } + } + std::vector<NavLine> transNavLines; + for (const Transition* transition:transitions) + { + //Point dirVec = transition->GetCentre()-_ped->GetPos(); + //dirVec=dirVec/dirVec.Norm(); + NavLine navLine(Line(transition->GetPoint1(),transition->GetPoint2())); + + transNavLines.push_back(navLine); + + } + return transNavLines; + +} + +const SubRoom* VisualSystem::IsInSubRoom(const Point &point) const +{ + + for (auto it2 = _building->GetRoom(_ped->GetRoomID())->GetAllSubRooms().begin(); it2 != _building->GetRoom(_ped->GetRoomID())->GetAllSubRooms().end(); ++it2) + { + + BoostPolygon boostP; + for (Point vertex:it2->second->GetPolygon()) + boost::geometry::append(boostP, vertex); + + boost::geometry::correct(boostP); + + if (boost::geometry::within(point, boostP)) + return it2->second.get(); + + } + return nullptr; + + +} + +const Room* VisualSystem::IsInRoom(const Point &point) const +{ + + std::vector<int> transIDs= _building->GetRoom(_ped->GetRoomID())->GetAllTransitionsIDs(); + std::vector<const Room*> adjacentRooms; + + for (int transID:transIDs) + { + if (_building->GetTransitionByUID(transID)->GetOtherRoom(_ped->GetRoomID())) + adjacentRooms.push_back(_building->GetTransitionByUID(transID)->GetOtherRoom(_ped->GetRoomID())); + } + + for (const Room* room:adjacentRooms) + { + for (auto it2 = room->GetAllSubRooms().begin(); it2 != room->GetAllSubRooms().end(); ++it2) + { + + BoostPolygon boostP; + for (Point vertex:it2->second->GetPolygon()) + { + boost::geometry::append(boostP, vertex); + } + boost::geometry::correct(boostP); + + if (boost::geometry::within(point, boostP)) + return _building->GetRoom(room->GetID()); + + } + } + + + return nullptr; + +} + +const Transition* VisualSystem::IsTransition(const NavLine *navLine) const +{ + std::vector<int> transIDs= _building->GetRoom(_ped->GetRoomID())->GetAllTransitionsIDs(); + + for (int transID:transIDs) + { + if (navLine->GetCentre()==_building->GetTransitionByUID(transID)->GetCentre()) + return _building->GetTransitionByUID(transID); + } + + return nullptr; +} + +void VisualSystem::GetVisibleSigns() +{ + + _newVisibleSigns.clear(); + + const std::vector<Sign>* signsInCurrentFloor = _env->GetSignsOfFloor(_building->GetAllRooms().at(_ped->GetRoomID()).get()); + + if (!signsInCurrentFloor) + { + _visibleSigns.clear(); + _detectedSigns.clear(); + return; + } + + for (const Sign& sign:*signsInCurrentFloor) + { + // if sign is in visible range + if (SignLegible(sign)) + { + // sign in visibility range for the first time + if (std::find(_visibleSigns.begin(),_visibleSigns.end(),&sign)==_visibleSigns.end()) + { + _newVisibleSigns.push_back(&sign); + _visibleSigns.push_back(&sign); + } + } + // if sign not anymore in field of view + else if (std::find(_visibleSigns.begin(),_visibleSigns.end(),&sign)!=_visibleSigns.end()) + { + _visibleSigns.erase(std::find(_visibleSigns.begin(),_visibleSigns.end(),&sign)); + + if (std::find(_detectedSigns.begin(),_detectedSigns.end(),&sign)!=_detectedSigns.end()) + _detectedSigns.erase(std::find(_detectedSigns.begin(),_detectedSigns.end(),&sign)); + + } + } + +} + +bool VisualSystem::SignLegible(const Sign &sign) const +{ + //following Signage Legibility Distances as a Function of Observation Angle by Xie et al. 2007 + + + Point vec1 = _ped->GetPos()-sign.GetPos(); + Point vec2 = sign.GetAlpha()*M_PI/180.0; + + double phi1=std::atan2(vec1._y,vec1._x); + double phi2=std::atan2(vec2._y,vec2._x); + double phi = std::fabs(phi2-phi1); + + if(vec1.Norm()>LegibleDistance(phi)) + return false; + + //checking if sign is in not hided by walls or obstacles + if (boost::geometry::within(sign.GetPos(),_currentEnv)) + return true; + + + return false; + +} + +double VisualSystem::HeadDirCompToSign(const Sign &sign) const +{ + Point pedVec=_ped->GetV(); + double pedAlpha=std::atan2(pedVec._y,pedVec._x); + + Point pedToSignVec=sign.GetPos()-_ped->GetPos(); + double pedToSignAlpha=std::atan2(pedToSignVec._y,pedToSignVec._x); + + return std::fabs(pedAlpha-pedToSignAlpha); + +} + + +double VisualSystem::GetProbabilityToDetectSign(double headDir, bool hardlyVisSign) +{ + + double P=1.0; + double kappa_alpha=1.0; + + double zeta=0.0;//0.16; + double eta=0.0;//0.375; + + if (_signPerceived>=1) + P+=_signPerceived*zeta; + else if (_signPerceived<0) + P+=_signPerceived*eta; + + if (hardlyVisSign) + { + kappa_alpha=0.5; + P=(1-kappa_alpha)/(1-P*kappa_alpha)*P; + } + +// if (headDir>M_PI/4.0) +// { +// kappa_alpha=0.5; +// } +// else if (headDir>M_PI/2.0) +// { +// kappa_alpha=0.0; +// } +// else +// { +// kappa_alpha=1.0; +// } + + P*=kappa_alpha; + + + if (P<0) + P=0; + else if (P>1) + P=1; + + return P; + + + + + +} + +std::vector<const Sign*> VisualSystem::TryToDetectSigns() +{ + //Visible:= sign in visible range + //Detect:= Instruction of sign will be followed + GetVisibleSigns(); + + for (const Sign* sign:_newVisibleSigns) + { + double headDir=HeadDirCompToSign(*sign); + + TryToDetectSign(*sign, headDir,false); + //if hard to detect signs shall be considered: +// if (!TryToDetectSign(*sign, headDir,false))// && headDir>M_PI/4.0) +// { +// if(headDir>M_PI/4.0) +// { +// _hardlyVisibleSigns.push_back(sign); +// } +// } + } + +// for (const Sign* sign:_hardlyVisibleSigns) +// { +// double headDir=HeadDirCompToSign(*sign); + +// if (headDir<M_PI/4.0) +// { +// _hardlyVisibleSigns.erase(std::find(_hardlyVisibleSigns.begin(),_hardlyVisibleSigns.end(),sign)); +// TryToDetectSign(*sign,headDir,true); +// } +// } + + return _detectedSigns; + +} + +bool VisualSystem::TryToDetectSign(const Sign &sign, double headDir, bool hardlyVisSign) +{ + + double P=GetProbabilityToDetectSign(headDir,hardlyVisSign); + std::discrete_distribution<int> distribution {1.0-P,P}; + + int number = distribution(_building->GetConfig()->GetRandomNumberGenerator()->GetRandomEngine()); + + + if (number) + { + _detectedSigns.push_back(&sign); + if (_signPerceived<0) + _signPerceived=1; + else + ++_signPerceived; + + return true; + } + else + { + //if (headDir<M_PI/4.0) + //{ + if (_signPerceived<=0) + --_signPerceived; + //} + //Log->Write("ROUTEINFO: Sign not detected\n"); + return false; + } +} + +std::vector<std::pair<const Point&,double>> VisualSystem::PerceiveDirsOfOtherPedestrians() const +{ + std::vector<std::pair<const Point&,double>> dirVectors; + + if (_ped->GetV()._x==0.0 && _ped->GetV()._y==0.0) + return dirVectors; + + + const std::vector<Pedestrian*> pedestrians= _building->GetAllPedestrians(); + + for (const Pedestrian* ped:pedestrians) + { + if (ped!=_ped) + { + if (boost::geometry::within(ped->GetPos(),_currentEnv)) + { + double angle = std::atan2(ped->GetV()._y,ped->GetV()._x)*180/M_PI; + dirVectors.push_back(std::make_pair<const Point&,double&>(ped->GetPos(),angle)); + } + } + } + + return dirVectors; +} + + +double LegibleDistance(double phi) // based on Xie et al. 2007 +{ + return -0.003335*phi*phi+0.07863*phi+19.79; + +} diff --git a/routing/ai_router_trips/perception/visualsystem.h b/routing/ai_router_trips/perception/visualsystem.h new file mode 100644 index 0000000000000000000000000000000000000000..f063f2f10c2301ed11f8ab990e56f58d279ad772 --- /dev/null +++ b/routing/ai_router_trips/perception/visualsystem.h @@ -0,0 +1,87 @@ +#ifndef VISUALSYSTEM_H +#define VISUALSYSTEM_H + +#include <boost/geometry/geometry.hpp> +#include <memory> +#include <vector> +#include <unordered_map> +#include "../../../geometry/Point.h" +#include "../../../general/Macros.h" + + +class NavLine; +class Pedestrian; +class Building; +class VisibleEnvironment; +class Transition; +class Room; +class SubRoom; +class Sign; + +using ptrNavLine = std::unique_ptr<const NavLine>; +using ptrFloor = const Room*; +typedef boost::geometry::model::linestring<Point> Linestring; +typedef boost::geometry::model::polygon<Point> BoostPolygon; + + +class VisualSystem +{ +public: + VisualSystem(); + VisualSystem(const Building* b, const Pedestrian* ped, const VisibleEnvironment* env); + + const std::unordered_map<ptrFloor,BoostPolygon>& UpdateSeenEnv(); + const BoostPolygon& UpdateCurrentEnvironment(); + std::vector<NavLine> GetPossibleNavLines(const std::vector<Point> &envPolygon) const; + + const std::unordered_map<ptrFloor,BoostPolygon>& GetSeenEnvironment() const; + const BoostPolygon& GetCurrentEnvironment() const; + + std::vector<Point> GetSeenEnvironmentAsPointVec(const ptrFloor& room) const; + std::vector<Point> GetCurrentEnvironmentAsPointVec() const; + + //Transitions in currentEnv? + std::vector<NavLine> FindTransitions() const; + const SubRoom* IsInSubRoom(const Point& point) const; + const Room *IsInRoom(const Point& point) const; + const Transition* IsTransition(const NavLine* navLine) const; + + //Signage + void GetVisibleSigns(); + // sign not to far away in consideration of observation angle (see Xie et. al 2007) + bool SignLegible(const Sign& sign) const; + //Heading direction of ped compared to direction of beeline to sign + double HeadDirCompToSign(const Sign& sign) const; + //double GetAngleFactor(const double& angle) const; + //calculate probability to detect sign in current time step + double GetProbabilityToDetectSign(double headDir, bool hardlyVisSign); + std::vector<const Sign*> TryToDetectSigns(); + bool TryToDetectSign(const Sign& sign, double headDir, bool hardlyVisSign); + + //Herding + // walking direction of other pedestrians are returned / only an idea! not working! + std::vector<std::pair<const Point &, double> > PerceiveDirsOfOtherPedestrians() const; + +private: + + const Building* _building; + const Pedestrian* _ped; + const VisibleEnvironment* _env; + std::unordered_map<ptrFloor,BoostPolygon> _seenEnvironment; + BoostPolygon _currentEnv; + // in vector: sign,vector:time in visibility to sign, spent probability to detect sign, + //last angle sector to sign, spent probability to detect sign in current angle sector + std::vector<const Sign*> _visibleSigns; + std::vector<const Sign*> _newVisibleSigns; + std::vector<const Sign*> _hardlyVisibleSigns; + std::vector<const Sign*> _detectedSigns; + int _signPerceived; + + + + +}; + +double LegibleDistance(double phi); + +#endif // VISUALSYSTEM_H diff --git a/routing/ff_router_trips/FFKit.cpp b/routing/ff_router_trips/FFKit.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2a22f377a21127acd4701792a02413d46375f52e --- /dev/null +++ b/routing/ff_router_trips/FFKit.cpp @@ -0,0 +1,43 @@ +// +// Created by arne on 3/29/17. +// +/** + * \file FFKit.h + * \date Mar 29, 2017 + * \version + * \copyright <2016-2022> Forschungszentrum Jülich GmbH. All rights reserved. + * + * \section License + * This file is part of JuPedSim. + * + * JuPedSim is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * JuPedSim 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 Lesser General Public License + * along with JuPedSim. If not, see <http://www.gnu.org/licenses/>. + * + * \section Description + * The Floorfield Kit is the rework of the prototype FloorfieldViaFM - class. + * It will restructur the former version to avoid several downsides (diamond + * inheritance, line/centerpoint version (DRY), overdoing the precalc, ...) + * We try to build it in a cleaner, easier version. + * + * A floorfield will be calculated and stored. The user can access distances(cost) + * values as well as the gradient of that. + * + * Cost Values are calculated via the fastmarching algorithm. Other algorithms might + * follow, if they provide better abilities to work in paralell. + * + * The new grid-code/subroom version should be reused for all floorfields, that work + * on the same part of the geometry (room/subroom e.g. floor/room) + * + **/ + +#include "FFKit.h" diff --git a/routing/ff_router_trips/FFKit.h b/routing/ff_router_trips/FFKit.h new file mode 100644 index 0000000000000000000000000000000000000000..557dcfe84e9ad3be5eca94545684ea45bc68f460 --- /dev/null +++ b/routing/ff_router_trips/FFKit.h @@ -0,0 +1,103 @@ +// +// Created by arne on 3/29/17. +// +/** + * \file ffRouter.h + * \date Feb 19, 2016 + * \version v0.8 + * \copyright <2016-2022> Forschungszentrum Jülich GmbH. All rights reserved. + * + * \section License + * This file is part of JuPedSim. + * + * JuPedSim is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * JuPedSim 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 Lesser General Public License + * along with JuPedSim. If not, see <http://www.gnu.org/licenses/>. + * + * \section Description + * + * The Floorfield Kit is the rework of the prototype FloorfieldViaFM - class. + * It will restructur the former version to avoid several downsides (diamond + * inheritance, line/centerpoint version (DRY), overdoing the precalc, ...) + * We try to build it in a cleaner, easier version. + * + * A floorfield will be calculated and stored. The user can access distances(cost) + * values as well as the gradient of that. + * + * Cost Values are calculated via the fastmarching algorithm. Other algorithms might + * follow, if they provide better abilities to work in paralell. + * + * The new grid-code/subroom version should be reused for all floorfields, that work + * on the same part of the geometry (room/subroom e.g. floor/room) + * + * old ffrouter description: + * This router is an update of the former Router.{cpp, h} - Global-, Quickest + * Router System. In the __former__ version, a graph was created with doors and + * hlines as nodes and the distances of (doors, hlines), connected with a line- + * of-sight, was used as edge-costs. If there was no line-of-sight, there was no + * connecting edge. On the resulting graph, the Floyd-Warshall algorithm was + * used to find any paths. In the "quickest-___" variants, the edge cost was not + * determined by the distance, but by the distance multiplied by a speed- + * estimate, to find the path with minimum travel times. This whole construct + * worked pretty well, but dependend on hlines to create paths with line-of- + * sights to the next target (hline/door). + * + * In the ffRouter, we want to overcome hlines by using floor fields to + * determine the distances. A line of sight is not required any more. We hope to + * reduce the graph complexity and the preparation-needs for new geometries. + * + * To find a counterpart for the "quickest-____" router, we can either use + * __special__ floor fields, that respect the travel time in the input-speed map, + * or take the distance-floor field and multiply it by a speed-estimate (analog + * to the former construct. + * + * We will derive from the <Router> class to fit the interface. + * + * Questions to solve: how to deal with goalID == doorID problem in matrix + * + * Restrictions/Requirements: Floorfields are not really 3D supporting: + * + * A room may not consist of subrooms which overlap in their projection (onto + * x-y-plane). So subrooms, that are positioned on top of others (in stairways + * for example), must be separated into different rooms. + * + * floorfields do not consider z-coordinates. Distances of two grid points are + * functions of (x, y) and not (x, y, z). Any slope will be neglected. + * + **/ + +#ifndef JPSCORE_FFKIT_H +#define JPSCORE_FFKIT_H + +#include <vector> +#include <unordered_set> +#include <cmath> +#include <functional> +#include "mesh/RectGrid.h" +#include "../../geometry/Wall.h" +#include "../../geometry/Point.h" +#include "../../geometry/Building.h" +#include "../../geometry/SubRoom.h" //check: should Room.h include SubRoom.h?? +#include "./mesh/Trial.h" +#include "../../pedestrian/Pedestrian.h" + +//class Building; +//class Pedestrian; +class OutputHandler; + +//log output +extern OutputHandler* Log; + + + + +#endif //JPSCORE_FFKIT_H diff --git a/routing/ff_router_trips/FloorfieldViaFM.cpp b/routing/ff_router_trips/FloorfieldViaFM.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b25c1a52e8cfdb4d99068995a347eda794e0b13e --- /dev/null +++ b/routing/ff_router_trips/FloorfieldViaFM.cpp @@ -0,0 +1,1785 @@ +/** + * \file FloorfieldViaFM.cpp + * \date Mar 05, 2015 + * \version N/A (v0.6) + * \copyright <2009-2014> Forschungszentrum Jülich GmbH. All rights reserved. + * + * \section License + * This file is part of JuPedSim. + * + * JuPedSim is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * JuPedSim 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 Lesser General Public License + * along with JuPedSim. If not, see <http://www.gnu.org/licenses/>. + * + * \section Description + * Implementation of classes for ... + * + * + **/ +#define TESTING +#define GEO_UP_SCALE 1 +#include "FloorfieldViaFM.h" +//#include <iostream> +//#include <string> +//#include <sstream> +//#include <fstream> +//#include <cfloat> +//#include <chrono> + +#ifdef _OPENMP +#else +#define omp_get_thread_num() 0 +#define omp_get_max_threads() 1 +#endif + +FloorfieldViaFM::FloorfieldViaFM() +{ + //ctor (very ugly) + //std::cerr << "The defaultconsturctor FloorfieldViaFM should not be called!" << std::endl; +} + +FloorfieldViaFM::~FloorfieldViaFM() +{ + //dtor + delete _grid; + if (_gcode) delete[] _gcode; + if (_subrooms) delete[] _subrooms; + if (_dist2Wall) delete[] _dist2Wall; + if (_speedInitial) delete[] _speedInitial; + if (_modifiedspeed) delete[] _modifiedspeed; + if (_densityspeed) delete[] _densityspeed; + //if (cost) delete[] cost; + //if (neggrad) delete[] neggrad; + if (_dirToWall) delete[] _dirToWall; + //if (trialfield) delete[] trialfield; + for ( const auto& goalid : _goalcostmap) { + if (goalid.second) delete[] goalid.second; + } + for ( const auto& id : _costmap) { + //if (id.first == -1) continue; + if (id.second) delete[] id.second; + if (_neggradmap.at(id.first)) delete[] _neggradmap.at(id.first); + //map will be deleted by itself + } + +} + +FloorfieldViaFM::FloorfieldViaFM(const Building* const buildingArg, const double hxArg, const double hyArg, + const double wallAvoidDistance, const bool useDistancefield, const bool onlyRoomsWithExits) { + //ctor + //_threshold = -1; //negative value means: ignore threshold + _threshold = wallAvoidDistance; + _building = buildingArg; + _useDistanceToWall = useDistancefield; + + if (hxArg != hyArg) { + //std::cerr << "ERROR: hx != hy <========="; + Log->Write("WARNING: \tFloor field: stepsize hx differs from hy! Taking hx = %d for both.", hxArg); + } + //parse building and create list of walls/obstacles (find xmin xmax, ymin, ymax, and add border?) + //Log->Write("INFO: \tStart Parsing: Building"); + if (onlyRoomsWithExits) { + parseBuildingForExits(buildingArg, hxArg, hyArg); + } else { + parseBuilding(buildingArg, hxArg, hyArg); + } + //Log->Write("INFO: \tFinished Parsing: Building"); + //testoutput("AALineScan.vtk", "AALineScan.txt", dist2Wall); + + prepareForDistanceFieldCalculation(onlyRoomsWithExits); + //Log->Write("INFO: \tGrid initialized: Walls"); + + calculateDistanceField(-1.); //negative threshold is ignored, so all distances get calculated. this is important since distances is used for slowdown/redirect + //Log->Write("INFO: \tGrid initialized: Walldistances"); + + setSpeed(useDistancefield); //use distance2Wall + //Log->Write("INFO: \tGrid initialized: Speed"); + + calculateFloorfield(_exitsFromScope, _cost, _neggrad); + //writing FF-file disabled, we will not revive it ( @todo: argraf ) +} + +FloorfieldViaFM::FloorfieldViaFM(const FloorfieldViaFM &other) : +_building(other._building) + +{ + _grid = other.getGrid(); + long int otherNumOfPoints = _grid->GetnPoints(); + + _wall.clear(); + _wall.reserve(other._wall.size()); + std::copy(other._wall.begin(), other._wall.end(), _wall.begin()); + + _exitsFromScope.clear(); + _exitsFromScope.reserve(other._exitsFromScope.size()); + std::copy(other._exitsFromScope.begin(), other._exitsFromScope.end(), _exitsFromScope.begin()); + + _numOfExits = other._numOfExits; + + _gcode = new int[otherNumOfPoints]; + std::copy(other._gcode, other._gcode + otherNumOfPoints, _gcode); + + _dist2Wall = new double[otherNumOfPoints]; + std::copy(other._dist2Wall, other._dist2Wall + otherNumOfPoints, _dist2Wall); + + _speedInitial = new double[otherNumOfPoints]; + std::copy(other._speedInitial, other._speedInitial + otherNumOfPoints, _speedInitial); + + _modifiedspeed = new double[otherNumOfPoints]; + std::copy(other._modifiedspeed, other._modifiedspeed + otherNumOfPoints, _modifiedspeed); + + _densityspeed = new double[otherNumOfPoints]; + std::copy(other._densityspeed, other._densityspeed + otherNumOfPoints, _densityspeed); + + _cost = new double[otherNumOfPoints]; + std::copy(other._cost, other._cost + otherNumOfPoints, _cost); + + _neggrad = new Point[otherNumOfPoints]; //gradients + std::copy(other._neggrad, other._neggrad + otherNumOfPoints, _neggrad); + + _dirToWall = new Point[otherNumOfPoints]; + std::copy(other._dirToWall, other._dirToWall + otherNumOfPoints, _dirToWall); + + _threshold = other._threshold; + _useDistanceToWall = other._useDistanceToWall; +} + +FloorfieldViaFM::FloorfieldViaFM(const std::string& filename) { + + + Log->Write("ERROR: \tReading FF from file not supported!!"); + Log->Write(filename); + +} + +//void FloorfieldViaFM::getDirectionAt(const Point& position, Point& direction){ +// long int key = grid->getKeyAtPoint(position); +// direction._x = (neggrad[key]._x); +// direction._y = (neggrad[key]._y); +//} + +void FloorfieldViaFM::getDirectionToDestination(Pedestrian* ped, Point& direction){ + const Point& position = ped->GetPos(); + int destID = ped->GetExitIndex(); + long int key = _grid->getKeyAtPoint(position); + getDirectionToUID(destID, key, direction); + if (direction._x == DBL_MAX && direction._y == DBL_MAX) { + // This can be interpreted differently by the different operational models. + // The inaccuracy introduced by this is negligible. --f.mack + direction._x = 0; + direction._y = 0; + } +} + +void FloorfieldViaFM::getDirectionToUID(int destID, const long int key, Point &direction) { + getDirectionToUID(destID, key, direction, global_shortest); +} + +void FloorfieldViaFM::getDirectionToUID(int destID, const long int key, Point& direction, int mode) { + //what if goal == -1, meaning closest exit... is GetExitIndex then -1? NO... ExitIndex is UID, given by router + //if (ped->GetFinalDestination() == -1) /*go to closest exit*/ destID != -1; + + if ((key < 0) || (key >= _grid->GetnPoints())) { // @todo: ar.graf: this check in a #ifdef-block? + Log->Write("ERROR: \t Floorfield tried to access a key out of grid!"); + direction._x = 0.; + direction._y = 0.; + return; + } + Point* localneggradptr = nullptr; + double* localcostptr = nullptr; + { + if (_neggradmap.count(destID) == 0) { + //Log->Write("FF for destID %d does not exist (key is %d)", destID, key); + //check, if distID is in this grid + Hline* destLine = _building->GetTransOrCrossByUID(destID); + Point A = destLine->GetPoint1(); + Point B = destLine->GetPoint2(); + if (!(_grid->includesPoint(A)) || !(_grid->includesPoint(B))) { + Log->Write("ERROR: \t Destination ID %d is not in grid!", destID); + direction._x = direction._y = 0.; + //return; + } + } + localneggradptr = (_neggradmap.count(destID) == 0) ? nullptr : _neggradmap.at(destID); + localcostptr = (_costmap.count(destID) == 0) ? nullptr : _costmap.at(destID); + if (localneggradptr == nullptr) { + bool isBeingCalculated; +#pragma omp critical(floorfieldsBeingCalculated) + { + if (!(isBeingCalculated = _floorfieldsBeingCalculated.count(destID) > 0)) { + _floorfieldsBeingCalculated.insert(destID); + } + } + if (isBeingCalculated) { + // we do not want to wait until the other calculation has finished, so we return immediately + // the values are corrected in getDirectionToDestination(), and getCostToDestination doesn't care about the direction + direction._x = DBL_MAX; + direction._y = DBL_MAX; + return; + } + + //create floorfield (remove mapentry with nullptr, allocate memory, add mapentry, create ff) + localcostptr = new double[_grid->GetnPoints()]; + localneggradptr = new Point[_grid->GetnPoints()]; +#pragma omp critical(neggradmap) + _neggradmap.erase(destID); +#pragma omp critical(neggradmap) + _neggradmap.emplace(destID, localneggradptr); +#pragma omp critical(costmap) + _costmap.erase(destID); +#pragma omp critical(costmap) + _costmap.emplace(destID, localcostptr); + + //create ff (prepare Trial-mechanic, then calc) +// for (long int i = 0; i < grid->GetnPoints(); ++i) { +// //set Trialptr to fieldelements +// trialfield[i].cost = localcostptr + i; +// trialfield[i].neggrad = localneggradptr + i; +// trialfield[i].father = nullptr; +// trialfield[i].child = nullptr; +// } +// clearAndPrepareForFloorfieldReCalc(localcostptr); + std::vector<Line> localline = {Line((Line) *(_building->GetTransOrCrossByUID(destID)))}; +// setNewGoalAfterTheClear(localcostptr, localline); + //Log->Write("Starting FF for UID %d (ID %d)", destID, dynamic_cast<Crossing*>(building->GetTransOrCrossByUID(destID))->GetID()); + //std::cerr << "\rW\tO\tR\tK\tI\tN\tG"; + if (mode == quickest) { + calculateFloorfield(localline, localcostptr, localneggradptr, _densityspeed); + } else { + calculateFloorfield(localline, localcostptr, localneggradptr, _modifiedspeed); + } +#pragma omp critical(floorfieldsBeingCalculated) + { + if (_floorfieldsBeingCalculated.count(destID) != 1) { + Log->Write("ERROR: FloorfieldViaFM::getDirectionToUID: key %d was calculating FF for destID %d, but it was removed from floorfieldsBeingCalculated meanwhile", key, destID); + } + _floorfieldsBeingCalculated.erase(destID); + } + //Log->Write("Ending FF for UID %d", destID); + //std::cerr << "\r W\t O\t R\t K\t I\t N\t G"; + } + } + direction._x = (localneggradptr[key]._x); + direction._y = (localneggradptr[key]._y); +} + +void FloorfieldViaFM::createMapEntryInLineToGoalID(const int goalID) +{ + Point* localneggradptr; + double* localcostptr; + if (goalID < 0) { + Log->Write("WARNING: \t goalID was negative in FloorfieldViaFM::createMapEntryInLineToGoalID"); + return; + } + if (!_building->GetFinalGoal(goalID)) { + Log->Write("WARNING: \t goalID was unknown in FloorfieldViaFM::createMapEntryInLineToGoalID"); + return; + } + + // The scope of this critical section can probably be reduced (maybe use a GoalsBeingCalculated similar to FloorfieldViaFM::getDirectionToUID) +#pragma omp critical(FloorfieldViaFM_maps) + { + if (_goalcostmap.count(goalID) == 0) { //no entry for goalcostmap, so we need to calc FF + _goalcostmap.emplace(goalID, nullptr); + _goalneggradmap.emplace(goalID, nullptr); + _goalToLineUIDmap.emplace(goalID, -1); + _goalToLineUIDmap2.emplace(goalID, -1); + _goalToLineUIDmap3.emplace(goalID, -1); + } + localneggradptr = _goalneggradmap.at(goalID); + localcostptr = _goalcostmap.at(goalID); + if (localneggradptr == nullptr) { + //create floorfield (remove mapentry with nullptr, allocate memory, add mapentry, create ff) + localcostptr = new double[_grid->GetnPoints()]; + localneggradptr = new Point[_grid->GetnPoints()]; + _goalneggradmap.erase(goalID); + //goalneggradmap.emplace(goalID, localneggradptr); + _goalcostmap.erase(goalID); + //goalcostmap.emplace(goalID, localcostptr); + //create ff (prepare Trial-mechanic, then calc) +// for (long int i = 0; i < grid->GetnPoints(); ++i) { +// //set Trialptr to fieldelements +// trialfield[i].cost = localcostptr + i; +// trialfield[i].neggrad = localneggradptr + i; +// trialfield[i].father = nullptr; +// trialfield[i].child = nullptr; +// } +// clearAndPrepareForFloorfieldReCalc(localcostptr); + + //get all lines/walls of goalID + vector<Line> localline; + const std::map<int, Goal*>& allgoals = _building->GetAllGoals(); + vector<Wall> localwalls = allgoals.at(goalID)->GetAllWalls(); + + double xMin = _grid->GetxMin(); + double xMax = _grid->GetxMax(); + + double yMin = _grid->GetyMin(); + double yMax = _grid->GetyMax(); + + for (const auto& iwall:localwalls) { + const Point& a = iwall.GetPoint1(); + const Point& b = iwall.GetPoint2(); + if ( + (a._x >= xMin) && (a._x <= xMax) + &&(a._y >= yMin) && (a._y <= yMax) + &&(b._x >= xMin) && (b._x <= xMax) + &&(b._y >= yMin) && (b._y <= yMax) + ) + { + localline.emplace_back( Line( (Line) iwall ) ); + } else { + std::cerr << "GOAL " << goalID << " includes point out of grid!" << std::endl; + std::cerr << "Point: " << a._x << ", " << a._y << std::endl; + std::cerr << "Point: " << b._x << ", " << b._y << std::endl; + } + } + +// setNewGoalAfterTheClear(localcostptr, localline); + + //performance-measurement: + //auto start = std::chrono::steady_clock::now(); + + calculateFloorfield(localline, localcostptr, localneggradptr); + + //performance-measurement: + //auto end = std::chrono::steady_clock::now(); + //auto diff = end - start; + //std::cerr << std::chrono::duration_cast<std::chrono::seconds>(end - start).count() << std::endl; + //std::cerr << "new GOALfield " << goalID << " : " << localline[0].GetPoint1().GetX() << " " << localline[0].GetPoint1().GetY() << " " << localline[0].GetPoint2().GetX() << " " << localline[0].GetPoint2().GetY() << std::endl; + //Log->Write("new GOALfield " + std::to_string(goalID) + " : " + std::to_string(localline[0].GetPoint1().GetX())); + //Log->Write("new GOALfield " + std::to_string(goalID) + " : " + std::to_string( std::chrono::duration_cast<std::chrono::seconds>(end - start).count() ) + " " + std::to_string(localline.size()) ); + //find closest door and add to cheatmap "goalToLineUID" map + + const std::map<int, Transition*>& transitions = _building->GetAllTransitions(); + int UID_of_MIN = -1; + int UID_of_MIN2 = -1; + int UID_of_MIN3 = -1; + double cost_of_MIN = DBL_MAX; + double cost_of_MIN2 = DBL_MAX; + double cost_of_MIN3 = DBL_MAX; + long int dummykey; + for (const auto& loctrans : transitions) { + if (!loctrans.second->IsExit() || !loctrans.second->IsOpen()) { + continue; + } + dummykey = _grid->getKeyAtPoint(loctrans.second->GetCentre()); + if ((cost_of_MIN > localcostptr[dummykey]) && (localcostptr[dummykey] >= 0.)) { + UID_of_MIN3 = UID_of_MIN2; + cost_of_MIN3 = cost_of_MIN2; + + UID_of_MIN2 = UID_of_MIN; + cost_of_MIN2 = cost_of_MIN; + + UID_of_MIN = loctrans.second->GetUniqueID(); + cost_of_MIN = localcostptr[dummykey]; + //std::cerr << std::endl << "Closer Line found: " << UID_of_MIN ; + continue; + } + if ((cost_of_MIN2 > localcostptr[dummykey]) && (localcostptr[dummykey] >= 0.)) { + UID_of_MIN3 = UID_of_MIN2; + cost_of_MIN3 = cost_of_MIN2; + + UID_of_MIN2 = loctrans.second->GetUniqueID(); + cost_of_MIN2 = localcostptr[dummykey]; + continue; + } + if ((cost_of_MIN3 > localcostptr[dummykey]) && (localcostptr[dummykey] >= 0.)) { + UID_of_MIN3 = loctrans.second->GetUniqueID(); + cost_of_MIN3 = localcostptr[dummykey]; + continue; + } + } + _goalToLineUIDmap.erase(goalID); + _goalToLineUIDmap.emplace(goalID, UID_of_MIN); + _goalToLineUIDmap2.erase(goalID); + _goalToLineUIDmap2.emplace(goalID, UID_of_MIN2); + _goalToLineUIDmap3.erase(goalID); + _goalToLineUIDmap3.emplace(goalID, UID_of_MIN3); + delete[] localcostptr; + delete[] localneggradptr; + } + } +} + +double FloorfieldViaFM::getCostToDestination(const int destID, const Point& position) { + return getCostToDestination(destID, position, global_shortest); +} + +double FloorfieldViaFM::getCostToDestination(const int destID, const Point& position, int mode) { + if ((_costmap.count(destID) == 0) || (_costmap.at(destID) == nullptr)) { + Point dummy; + getDirectionToUID(destID, 0, dummy, mode); //this call induces the floorfieldcalculation + } + if ((_costmap.count(destID) == 0) || (_costmap.at(destID) == nullptr)) { + Log->Write("ERROR: \tDestinationUID %d is invalid / out of grid.", destID); + return DBL_MAX; + } + if (_grid->getKeyAtPoint(position) == -1) { //position is out of grid + return -7; + } + return (_costmap.at(destID))[_grid->getKeyAtPoint(position)]; +} + +void FloorfieldViaFM::getDir2WallAt(const Point& position, Point& direction){ + long int key = _grid->getKeyAtPoint(position); + //debug assert + if (key < 0) { + Log->Write("ERROR: \tgetDir2WallAt error"); + } else { + direction._x = (_dirToWall[key]._x); + direction._y = (_dirToWall[key]._y); + } +} + +double FloorfieldViaFM::getDistance2WallAt(const Point& position) { + long int key = _grid->getKeyAtPoint(position); + //debug assert + if (key < 0) { + Log->Write("ERROR: \tgetDistance2WallAt error"); + return 1.; + } else { + return _dist2Wall[key]; + } +} + +//int FloorfieldViaFM::getSubroomUIDAt(const Point &position) { +// long int key = grid->getKeyAtPoint(position); +// return subroomUID[key]; +//} + +/*! + * \brief Parsing geo-info but conflicts in multi-floor-buildings OBSOLETE + * + * When parsing a building with multiple floors, the projection of all floors onto the x-/y- plane leads to wrong + * results. + * + * \param[in] buildingArg provides the handle to all information + * \param[out] stepSizeX/-Y discretization of the grid, which will be created here + * \return member attributes of FloorfieldViaFM class are allocated and initialized + * \sa Macros.h, RectGrid.h + * \note + * \warning + */ +void FloorfieldViaFM::parseBuilding(const Building* const buildingArg, const double stepSizeX, const double stepSizeY) { + _building = buildingArg; + //init min/max before parsing + double xMin = DBL_MAX; + double xMax = -DBL_MAX; + double yMin = xMin; + double yMax = xMax; + + if (stepSizeX != stepSizeY) Log->Write("ERROR: \tStepsizes in x- and y-direction must be identical!"); + + _costmap.clear(); + _neggradmap.clear(); + _wall.clear(); + _exitsFromScope.clear(); + + //create a list of walls + const std::map<int, Transition*>& allTransitions = buildingArg->GetAllTransitions(); + for (auto& trans : allTransitions) { + if ( + trans.second->IsExit() && trans.second->IsOpen() + ) + { + _exitsFromScope.emplace_back(Line ( (Line) *(trans.second))); + } + //populate both maps: costmap, neggradmap. These are the lookup maps for floorfields to specific transitions + _costmap.emplace(trans.second->GetUniqueID(), nullptr); + _neggradmap.emplace(trans.second->GetUniqueID(), nullptr); + } + _numOfExits = (unsigned int) _exitsFromScope.size(); + for (auto& trans : allTransitions) { + if (!trans.second->IsOpen()) { + _wall.emplace_back(Line ( (Line) *(trans.second))); + } + + } + for (const auto& itRoom : buildingArg->GetAllRooms()) { + for (const auto& itSubroom : itRoom.second->GetAllSubRooms()) { + std::vector<Obstacle*> allObstacles = itSubroom.second->GetAllObstacles(); + for (std::vector<Obstacle*>::iterator itObstacles = allObstacles.begin(); itObstacles != allObstacles.end(); ++itObstacles) { + + std::vector<Wall> allObsWalls = (*itObstacles)->GetAllWalls(); + for (std::vector<Wall>::iterator itObsWall = allObsWalls.begin(); itObsWall != allObsWalls.end(); ++itObsWall) { + _wall.emplace_back(Line( (Line) *itObsWall)); + // xMin xMax + if ((*itObsWall).GetPoint1()._x < xMin) xMin = (*itObsWall).GetPoint1()._x; + if ((*itObsWall).GetPoint2()._x < xMin) xMin = (*itObsWall).GetPoint2()._x; + if ((*itObsWall).GetPoint1()._x > xMax) xMax = (*itObsWall).GetPoint1()._x; + if ((*itObsWall).GetPoint2()._x > xMax) xMax = (*itObsWall).GetPoint2()._x; + + // yMin yMax + if ((*itObsWall).GetPoint1()._y < yMin) yMin = (*itObsWall).GetPoint1()._y; + if ((*itObsWall).GetPoint2()._y < yMin) yMin = (*itObsWall).GetPoint2()._y; + if ((*itObsWall).GetPoint1()._y > yMax) yMax = (*itObsWall).GetPoint1()._y; + if ((*itObsWall).GetPoint2()._y > yMax) yMax = (*itObsWall).GetPoint2()._y; + } + } + + std::vector<Wall> allWalls = itSubroom.second->GetAllWalls(); + for (std::vector<Wall>::iterator itWall = allWalls.begin(); itWall != allWalls.end(); ++itWall) { + _wall.emplace_back( Line( (Line) *itWall)); + + // xMin xMax + if ((*itWall).GetPoint1()._x < xMin) xMin = (*itWall).GetPoint1()._x; + if ((*itWall).GetPoint2()._x < xMin) xMin = (*itWall).GetPoint2()._x; + if ((*itWall).GetPoint1()._x > xMax) xMax = (*itWall).GetPoint1()._x; + if ((*itWall).GetPoint2()._x > xMax) xMax = (*itWall).GetPoint2()._x; + + // yMin yMax + if ((*itWall).GetPoint1()._y < yMin) yMin = (*itWall).GetPoint1()._y; + if ((*itWall).GetPoint2()._y < yMin) yMin = (*itWall).GetPoint2()._y; + if ((*itWall).GetPoint1()._y > yMax) yMax = (*itWall).GetPoint1()._y; + if ((*itWall).GetPoint2()._y > yMax) yMax = (*itWall).GetPoint2()._y; + } + + const vector<Crossing*>& allCrossings = itSubroom.second->GetAllCrossings(); + for (Crossing* crossPtr : allCrossings) { + if (!crossPtr->IsOpen()) { + _wall.emplace_back( Line( (Line) *crossPtr)); + + if (crossPtr->GetPoint1()._x < xMin) xMin = crossPtr->GetPoint1()._x; + if (crossPtr->GetPoint2()._x < xMin) xMin = crossPtr->GetPoint2()._x; + if (crossPtr->GetPoint1()._x > xMax) xMax = crossPtr->GetPoint1()._x; + if (crossPtr->GetPoint2()._x > xMax) xMax = crossPtr->GetPoint2()._x; + + if (crossPtr->GetPoint1()._y < yMin) yMin = crossPtr->GetPoint1()._y; + if (crossPtr->GetPoint2()._y < yMin) yMin = crossPtr->GetPoint2()._y; + if (crossPtr->GetPoint1()._y > yMax) yMax = crossPtr->GetPoint1()._y; + if (crossPtr->GetPoint2()._y > yMax) yMax = crossPtr->GetPoint2()._y; + } + } + } + } + + //all goals + const std::map<int, Goal*>& allgoals = buildingArg->GetAllGoals(); + for (auto eachgoal:allgoals) { + for (auto& eachwall:eachgoal.second->GetAllWalls() ) { + if (eachwall.GetPoint1()._x < xMin) xMin = eachwall.GetPoint1()._x; + if (eachwall.GetPoint2()._x < xMin) xMin = eachwall.GetPoint2()._x; + if (eachwall.GetPoint1()._x > xMax) xMax = eachwall.GetPoint1()._x; + if (eachwall.GetPoint2()._x > xMax) xMax = eachwall.GetPoint2()._x; + + if (eachwall.GetPoint1()._y < yMin) yMin = eachwall.GetPoint1()._y; + if (eachwall.GetPoint2()._y < yMin) yMin = eachwall.GetPoint2()._y; + if (eachwall.GetPoint1()._y > yMax) yMax = eachwall.GetPoint1()._y; + if (eachwall.GetPoint2()._y > yMax) yMax = eachwall.GetPoint2()._y; + } + _goalcostmap.emplace(eachgoal.second->GetId(), nullptr); + _goalneggradmap.emplace(eachgoal.second->GetId(), nullptr); + } + + //create Rect Grid + _grid = new RectGrid(); + _grid->setBoundaries(xMin, yMin, xMax, yMax); + _grid->setSpacing(stepSizeX, stepSizeX); + _grid->createGrid(); + + //create arrays + _subrooms = new SubRoom*[_grid->GetnPoints()](); + _gcode = new int[_grid->GetnPoints()]; //gcode: + // enum GridCode { //used in floor fields + // WALL = 0, + // INSIDE, + // OUTSIDE, + // OPEN_CROSSING, + // OPEN_TRANSITION, + // CLOSED_CROSSING, + // CLOSED_TRANSITION + // }; + _dist2Wall = new double[_grid->GetnPoints()]; + _speedInitial = new double[_grid->GetnPoints()]; + _modifiedspeed = new double[_grid->GetnPoints()]; + _densityspeed = new double[_grid->GetnPoints()]; + _cost = new double[_grid->GetnPoints()]; + _neggrad = new Point[_grid->GetnPoints()]; + _dirToWall = new Point[_grid->GetnPoints()]; + + _costmap.emplace(-1 , _cost); // enable default ff (closest exit) + _neggradmap.emplace(-1, _neggrad); + + //init grid with -3 as unknown distance to any wall + for(long int i = 0; i < _grid->GetnPoints(); ++i) { + _dist2Wall[i] = -3.; + _cost[i] = -2.; + _gcode[i] = OUTSIDE; + //flag[i] = FM_UNKNOWN; //unknown + } + drawLinesOnGrid<double>(_wall, _dist2Wall, 0.); + drawLinesOnGrid<double>(_wall, _cost, -7.); + drawLinesOnGrid<int>(_wall, _gcode, WALL); + drawLinesOnGrid<int>(_exitsFromScope, _gcode, OPEN_TRANSITION); +} + +/*! + * \brief Parsing geo-info ONLY considering rooms with EXITS to the outside to avoid conflicts in multi-floor-buildings + * + * When parsing a building with multiple floors, the projection of all floors onto the x-/y- plane leads to wrong + * results. We then decided, to consider each floor separately and each staircase separately. + * We still need to match the ouside goals to an exit (transition), so that the router can guide agents to that goals. + * Next problem was in buildings, where exits to the outside would now be on different rooms. This is why we create this + * function. We want to create one floorfield for all rooms, that lead outside. Reason: We want the router to lead agents + * to the maybe second or third best exit-door, which might be in a different room. + * + * \param[in] buildingArg provides the handle to all information + * \param[out] stepSizeX/-Y discretization of the grid, which will be created here + * \return member attributes of FloorfieldViaFM class are allocated and initialized + * \sa Macros.h, RectGrid.h + * \note + * \warning + */ +void FloorfieldViaFM::parseBuildingForExits(const Building* const buildingArg, const double stepSizeX, const double stepSizeY) { + _building = buildingArg; + //init min/max before parsing + double xMin = DBL_MAX; + double xMax = -DBL_MAX; + double yMin = xMin; + double yMax = xMax; + + if (stepSizeX != stepSizeY) Log->Write("ERROR: \tStepsizes in x- and y-direction must be identical!"); + + _costmap.clear(); + _neggradmap.clear(); + _wall.clear(); + _exitsFromScope.clear(); + + std::vector<int> exitRoomIDs; + exitRoomIDs.clear(); + + //create a list of walls + const std::map<int, Transition*>& allTransitions = buildingArg->GetAllTransitions(); + for (auto& trans : allTransitions) { + if ( + trans.second->IsExit() && trans.second->IsOpen() + ) + { + _exitsFromScope.emplace_back(Line ( (Line) *(trans.second))); + int roomID = -1; + if (trans.second->GetRoom1()) { + roomID = trans.second->GetRoom1()->GetID(); + } + if (trans.second->GetRoom2()) { + roomID = trans.second->GetRoom2()->GetID(); + } + if (std::find(exitRoomIDs.begin(), exitRoomIDs.end(), roomID) == exitRoomIDs.end()) { + exitRoomIDs.emplace_back(roomID); + } + } + //populate both maps: costmap, neggradmap. These are the lookup maps for floorfields to specific transitions + _costmap.emplace(trans.second->GetUniqueID(), nullptr); + _neggradmap.emplace(trans.second->GetUniqueID(), nullptr); + } + _numOfExits = (unsigned int) _exitsFromScope.size(); + for (auto& trans : allTransitions) { + if (!trans.second->IsOpen()) { + _wall.emplace_back(Line ( (Line) *(trans.second))); + } + + } + for (const auto& itRoom : buildingArg->GetAllRooms()) { + if (std::find(exitRoomIDs.begin(), exitRoomIDs.end(), itRoom.second->GetID()) == exitRoomIDs.end()) { //room with no exit + continue; + } + for (const auto& itSubroom : itRoom.second->GetAllSubRooms()) { + std::vector<Obstacle*> allObstacles = itSubroom.second->GetAllObstacles(); + for (std::vector<Obstacle*>::iterator itObstacles = allObstacles.begin(); itObstacles != allObstacles.end(); ++itObstacles) { + + std::vector<Wall> allObsWalls = (*itObstacles)->GetAllWalls(); + for (std::vector<Wall>::iterator itObsWall = allObsWalls.begin(); itObsWall != allObsWalls.end(); ++itObsWall) { + _wall.emplace_back(Line( (Line) *itObsWall)); + // xMin xMax + if ((*itObsWall).GetPoint1()._x < xMin) xMin = (*itObsWall).GetPoint1()._x; + if ((*itObsWall).GetPoint2()._x < xMin) xMin = (*itObsWall).GetPoint2()._x; + if ((*itObsWall).GetPoint1()._x > xMax) xMax = (*itObsWall).GetPoint1()._x; + if ((*itObsWall).GetPoint2()._x > xMax) xMax = (*itObsWall).GetPoint2()._x; + + // yMin yMax + if ((*itObsWall).GetPoint1()._y < yMin) yMin = (*itObsWall).GetPoint1()._y; + if ((*itObsWall).GetPoint2()._y < yMin) yMin = (*itObsWall).GetPoint2()._y; + if ((*itObsWall).GetPoint1()._y > yMax) yMax = (*itObsWall).GetPoint1()._y; + if ((*itObsWall).GetPoint2()._y > yMax) yMax = (*itObsWall).GetPoint2()._y; + } + } + + std::vector<Wall> allWalls = itSubroom.second->GetAllWalls(); + for (std::vector<Wall>::iterator itWall = allWalls.begin(); itWall != allWalls.end(); ++itWall) { + _wall.emplace_back( Line( (Line) *itWall)); + + // xMin xMax + if ((*itWall).GetPoint1()._x < xMin) xMin = (*itWall).GetPoint1()._x; + if ((*itWall).GetPoint2()._x < xMin) xMin = (*itWall).GetPoint2()._x; + if ((*itWall).GetPoint1()._x > xMax) xMax = (*itWall).GetPoint1()._x; + if ((*itWall).GetPoint2()._x > xMax) xMax = (*itWall).GetPoint2()._x; + + // yMin yMax + if ((*itWall).GetPoint1()._y < yMin) yMin = (*itWall).GetPoint1()._y; + if ((*itWall).GetPoint2()._y < yMin) yMin = (*itWall).GetPoint2()._y; + if ((*itWall).GetPoint1()._y > yMax) yMax = (*itWall).GetPoint1()._y; + if ((*itWall).GetPoint2()._y > yMax) yMax = (*itWall).GetPoint2()._y; + } + const vector<Crossing*>& allCrossings = itSubroom.second->GetAllCrossings(); + for (Crossing* crossPtr : allCrossings) { + if (!crossPtr->IsOpen()) { + _wall.emplace_back( Line( (Line) *crossPtr)); + + if (crossPtr->GetPoint1()._x < xMin) xMin = crossPtr->GetPoint1()._x; + if (crossPtr->GetPoint2()._x < xMin) xMin = crossPtr->GetPoint2()._x; + if (crossPtr->GetPoint1()._x > xMax) xMax = crossPtr->GetPoint1()._x; + if (crossPtr->GetPoint2()._x > xMax) xMax = crossPtr->GetPoint2()._x; + + if (crossPtr->GetPoint1()._y < yMin) yMin = crossPtr->GetPoint1()._y; + if (crossPtr->GetPoint2()._y < yMin) yMin = crossPtr->GetPoint2()._y; + if (crossPtr->GetPoint1()._y > yMax) yMax = crossPtr->GetPoint1()._y; + if (crossPtr->GetPoint2()._y > yMax) yMax = crossPtr->GetPoint2()._y; + } + } + } + } + + //all goals + const std::map<int, Goal*>& allgoals = buildingArg->GetAllGoals(); + for (auto eachgoal:allgoals) { + for (auto& eachwall:eachgoal.second->GetAllWalls() ) { + if (eachwall.GetPoint1()._x < xMin) xMin = eachwall.GetPoint1()._x; + if (eachwall.GetPoint2()._x < xMin) xMin = eachwall.GetPoint2()._x; + if (eachwall.GetPoint1()._x > xMax) xMax = eachwall.GetPoint1()._x; + if (eachwall.GetPoint2()._x > xMax) xMax = eachwall.GetPoint2()._x; + + if (eachwall.GetPoint1()._y < yMin) yMin = eachwall.GetPoint1()._y; + if (eachwall.GetPoint2()._y < yMin) yMin = eachwall.GetPoint2()._y; + if (eachwall.GetPoint1()._y > yMax) yMax = eachwall.GetPoint1()._y; + if (eachwall.GetPoint2()._y > yMax) yMax = eachwall.GetPoint2()._y; + } + //goalcostmap.emplace(eachgoal.second->GetId(), nullptr); + //goalneggradmap.emplace(eachgoal.second->GetId(), nullptr); + } + + //create Rect Grid + _grid = new RectGrid(); + _grid->setBoundaries(xMin, yMin, xMax, yMax); + _grid->setSpacing(stepSizeX, stepSizeX); + _grid->createGrid(); + + //create arrays + _gcode = new int[_grid->GetnPoints()]; //see Macros.h: enum GridCode {...} + _subrooms = new SubRoom*[_grid->GetnPoints()](); + _dist2Wall = new double[_grid->GetnPoints()]; + _speedInitial = new double[_grid->GetnPoints()]; + _modifiedspeed = new double[_grid->GetnPoints()]; + _cost = new double[_grid->GetnPoints()]; + _neggrad = new Point[_grid->GetnPoints()]; + _dirToWall = new Point[_grid->GetnPoints()]; + + _costmap.emplace(-1 , _cost); // enable default ff (closest exit) + _neggradmap.emplace(-1, _neggrad); + + //init grid with -3 as unknown distance to any wall + for(long int i = 0; i < _grid->GetnPoints(); ++i) { + _dist2Wall[i] = -3.; + _cost[i] = -2.; + _gcode[i] = OUTSIDE; //unknown + _subrooms[i] = nullptr; + } + drawLinesOnGrid<double>(_wall, _dist2Wall, 0.); + drawLinesOnGrid<double>(_wall, _cost, -7.); + drawLinesOnGrid<int>(_wall, _gcode, WALL); + drawLinesOnGrid<int>(_exitsFromScope, _gcode, OPEN_TRANSITION); +} + +//this function must only be used BEFORE calculateDistanceField(), because we set trialfield[].cost = dist2Wall AND we init dist2Wall with "-3" +void FloorfieldViaFM::prepareForDistanceFieldCalculation(const bool onlyRoomsWithExits) { //onlyRoomsWithExits means, outside points must be considered + for (long int i = 0; i < _grid->GetnPoints(); ++i) { + + switch (_gcode[i]) { + //wall, closed_cross/_trans are all coded with "WALL" after prasing building + case WALL: + _speedInitial[i] = .001; + _cost[i] = -7.; + _dist2Wall[i] = 0.; + break; + case CLOSED_CROSSING: + _speedInitial[i] = .001; + _cost[i] = -7.; + _dist2Wall[i] = 0.; + break; + case CLOSED_TRANSITION: + _speedInitial[i] = .001; + _cost[i] = -7.; + _dist2Wall[i] = 0.; + break; + //open transitions are marked + case OPEN_TRANSITION: + _speedInitial[i] = 1.; + _cost[i] = 0.; + _neggrad[i]._x = (0.); //must be changed to costarray/neggradarray? + _neggrad[i]._y = (0.); //we can leave it, if we agree on cost/neggrad being + _dirToWall[i]._x = (0.); //default floorfield using all exits and have the + _dirToWall[i]._y = (0.); //array mechanic on top + break; + //open crossings are not marked at all after parsing building + case OPEN_CROSSING: + _speedInitial[i] = 1.; + _cost[i] = -2.; + break; + //after parsing, none is INSIDE, but for style reasons, I want switch cases to show all values + case INSIDE: + _speedInitial[i] = 1.; + _cost[i] = -2.; + break; + //this is the main thing in this loop, we want to find and mark inside points (and it is costly!!) + case OUTSIDE: + { + SubRoom* subroom = isInside(i); + if (subroom || onlyRoomsWithExits) { + _speedInitial[i] = 1.; + _cost[i] = -2.; + _gcode[i] = INSIDE; + _subrooms[i] = subroom; + } + break; + } + } //switch + } //for loop (points) + // drawLinesOnGrid(exits, cost, 0.); //already mark targets/exits in cost array (for floorfieldcalc and crossout (LocalFF)) +} + +void FloorfieldViaFM::deleteAllFFs() { + for (size_t i = 0; i < _costmap.size(); ++i) { + auto costIter = _costmap.begin(); + auto negIter = _neggradmap.begin(); + std::advance(costIter, (_costmap.size() - (i+1))); + std::advance(negIter, (_neggradmap.size() - (i+1))); + + if (costIter->second) delete[] costIter->second; + if (negIter->second) delete[] negIter->second; + + costIter->second = nullptr; + negIter->second = nullptr; + } +} + +template <typename T> +void FloorfieldViaFM::drawLinesOnGrid(std::vector<Line>& wallArg, T* const target, const T value) { //no init, plz init elsewhere +// i~x; j~y; +//http://stackoverflow.com/questions/10060046/drawing-lines-with-bresenhams-line-algorithm +//src in answer of "Avi"; adapted to fit this application + +// //init with inside value: +// long int indexMax = grid->GetnPoints(); +// for (long int i = 0; i < indexMax; ++i) { +// target[i] = inside; +// } + + //grid handeling local vars: + long int iMax = _grid->GetiMax(); + + long int iStart, iEnd; + long int jStart, jEnd; + long int iDot, jDot; + long int key; + long int deltaX, deltaY, deltaX1, deltaY1, px, py, xe, ye, i; //Bresenham Algorithm + + for (auto& line : wallArg) { + key = _grid->getKeyAtPoint(line.GetPoint1()); + iStart = (long) _grid->get_i_fromKey(key); + jStart = (long) _grid->get_j_fromKey(key); + + key = _grid->getKeyAtPoint(line.GetPoint2()); + iEnd = (long) _grid->get_i_fromKey(key); + jEnd = (long) _grid->get_j_fromKey(key); + + deltaX = (int) (iEnd - iStart); + deltaY = (int) (jEnd - jStart); + deltaX1 = abs( (int) (iEnd - iStart)); + deltaY1 = abs( (int) (jEnd - jStart)); + + px = 2*deltaY1 - deltaX1; + py = 2*deltaX1 - deltaY1; + + if(deltaY1<=deltaX1) { + if(deltaX>=0) { + iDot = iStart; + jDot = jStart; + xe = iEnd; + } else { + iDot = iEnd; + jDot = jEnd; + xe = iStart; + } + if ((_gcode[jDot*iMax + iDot] != WALL) && (_gcode[jDot*iMax + iDot] != CLOSED_CROSSING) && (_gcode[jDot*iMax + iDot] != CLOSED_TRANSITION)) { + target[jDot * iMax + iDot] = value; + } + for (i=0; iDot < xe; ++i) { + ++iDot; + if(px<0) { + px+=2*deltaY1; + } else { + if((deltaX<0 && deltaY<0) || (deltaX>0 && deltaY>0)) { + ++jDot; + } else { + --jDot; + } + px+=2*(deltaY1-deltaX1); + } + if ((_gcode[jDot*iMax + iDot] != WALL) && (_gcode[jDot*iMax + iDot] != CLOSED_CROSSING) && (_gcode[jDot*iMax + iDot] != CLOSED_TRANSITION)) { + target[jDot * iMax + iDot] = value; + } + } + } else { + if(deltaY>=0) { + iDot = iStart; + jDot = jStart; + ye = jEnd; + } else { + iDot = iEnd; + jDot = jEnd; + ye = jStart; + } + if ((_gcode[jDot*iMax + iDot] != WALL) && (_gcode[jDot*iMax + iDot] != CLOSED_CROSSING) && (_gcode[jDot*iMax + iDot] != CLOSED_TRANSITION)) { + target[jDot * iMax + iDot] = value; + } + for(i=0; jDot<ye; ++i) { + ++jDot; + if (py<=0) { + py+=2*deltaX1; + } else { + if((deltaX<0 && deltaY<0) || (deltaX>0 && deltaY>0)) { + ++iDot; + } else { + --iDot; + } + py+=2*(deltaX1-deltaY1); + } + if ((_gcode[jDot*iMax + iDot] != WALL) && (_gcode[jDot*iMax + iDot] != CLOSED_CROSSING) && (_gcode[jDot*iMax + iDot] != CLOSED_TRANSITION)) { + target[jDot * iMax + iDot] = value; + } + } + } + } //loop over all walls + +} //drawLinesOnGrid + +void FloorfieldViaFM::setSpeed(bool useDistance2WallArg) { + if (useDistance2WallArg && (_threshold > 0)) { + double temp; //needed to only slowdown band of threshold. outside of band modifiedspeed should be 1 + for (long int i = 0; i < _grid->GetnPoints(); ++i) { + temp = (_dist2Wall[i] < _threshold) ? _dist2Wall[i] : _threshold; + _modifiedspeed[i] = 0.001 + 0.999 * (temp/_threshold); //linear ramp from wall (0.001) to thresholddistance (1.000) + } + } else { + if (useDistance2WallArg) { //favor middle of hallways/rooms + for (long int i = 0; i < _grid->GetnPoints(); ++i) { + if (_threshold == 0.) { //special case if user set (use_wall_avoidance = true, wall_avoid_distance = 0.0) and thus wants a plain floorfield + _modifiedspeed[i] = _speedInitial[i]; + } else { //this is the regular case for "favor middle of hallways/rooms + _modifiedspeed[i] = 0.001 + 0.999 * (_dist2Wall[i]/10); // @todo: ar.graf (10 ist ein hardgecodeter wert.. sollte ggf. angepasst werden) + } + } + } else { //do not use Distance2Wall + for (long int i = 0; i < _grid->GetnPoints(); ++i) { + _modifiedspeed[i] = _speedInitial[i]; + } + } + } + if (_densityspeed) { + std::copy(_modifiedspeed, _modifiedspeed + _grid->GetnPoints(), _densityspeed); + } +} + +void FloorfieldViaFM::setSpeedThruPeds(Pedestrian* const * pedsArg, int nsize, int modechoice, double radius) { + + long int delta = radius / _grid->Gethx(); + long int posIndex = 0; + long int pos_i = 0; + long int pos_j = 0; + long int i_start = 0; + long int j_start = 0; + long int i_end = 0; + long int j_end = 0; + double indexDistance = 0.0; + + if (nsize == 0) { + Log->Write("WARNING: \tSetSpeedThruPeds: nsize is ZERO"); + } else { + Log->Write("INFO: \t\tNumber of Peds used in setSpeedThruPeds: %d",nsize); + } + + if ((modechoice == quickest) && (!_densityspeed)) { + _densityspeed = new double[_grid->GetnPoints()]; + } + //std::copy(modifiedspeed, modifiedspeed+(grid->GetnPoints()), densityspeed); + for (long int i = 0; i < _grid->GetnPoints(); ++i) { + _densityspeed[i] = _speedInitial[i]; + } + + for (int i = 0; i < nsize; ++i) { + //the following check is not 3D proof, we require the caller of this function to provide a list with "valid" + //pedestrian-pointer + if (!_grid->includesPoint(pedsArg[i]->GetPos())) { + continue; + } + /*this value defines the jam-speed threshold*/ +// if (pedsArg[i]->GetEllipse().GetV().Norm() > 0.8*pedsArg[i]->GetEllipse().GetV0()) { +// continue; +// } + posIndex = _grid->getKeyAtPoint(pedsArg[i]->GetPos()); + pos_i = _grid->get_i_fromKey(posIndex); + pos_j = _grid->get_j_fromKey(posIndex); + + i_start = ((pos_i - delta) < 0) ? 0 : (pos_i - delta); + i_end = ((pos_i + delta) >= _grid->GetiMax()) ? _grid->GetiMax()-1 : (pos_i + delta); + + j_start = ((pos_j - delta) < 0) ? 0 : (pos_j - delta); + j_end = ((pos_j + delta) >= _grid->GetjMax()) ? _grid->GetjMax()-1 : (pos_j + delta); + + for (long int curr_i = i_start; curr_i < i_end; ++curr_i) { + for (long int curr_j = j_start; curr_j < j_end; ++curr_j) { + //indexDistance holds the square + indexDistance = ( (curr_i - pos_i)*(curr_i - pos_i) + (curr_j - pos_j)*(curr_j - pos_j) ); + //now using indexDistance to store the (speed) reduction value + //indexDistance = (delta*delta) - (indexDistance); + //if (indexDistance < 0) { indexDistance = 0.;} + //scale to [0 .. 1] + //indexDistance = indexDistance/(delta*delta); + + //densityspeed[curr_j*grid->GetiMax() + curr_i] = (indexDistance > (delta*delta)) ? densityspeed[curr_j*grid->GetiMax() + curr_i] : .001; + if (indexDistance < (delta*delta)) { + //std::cout << "c h a n g i n g "; + _densityspeed[curr_j*_grid->GetiMax() + curr_i] = 0.07; + } + } + } + } + +} + +void FloorfieldViaFM::calculateFloorfield(std::vector<Line>& targetlines, double* costarray, Point* neggradarray) { + calculateFloorfield(targetlines, costarray, neggradarray, _modifiedspeed); +} + +void FloorfieldViaFM::calculateFloorfield(std::vector<Line>& targetlines, double* costarray, Point* neggradarray, double* speedarray) { + + std::priority_queue<TrialP, std::vector<TrialP>, std::greater<TrialP>> trialqueue; + int* flag = new int[_grid->GetnPoints()]; + + //re-init memory + for (long int i = 0; i < _grid->GetnPoints(); ++i) { + if ((_gcode[i] == INSIDE) || (_gcode[i] == OPEN_TRANSITION) || (_gcode[i] == OPEN_CROSSING)) { + flag[i] = FM_UNKNOWN; //inside + } else { + flag[i] = FM_OUTSIDE; //wall, outside or closed + } + + costarray[i] = -7.; + } + drawLinesOnGrid<double>(targetlines, costarray, 0.); + drawLinesOnGrid<int>(targetlines, flag, FM_FINAL); + + //init narrowband + for (long int i = 0; i < _grid->GetnPoints(); ++i) { + if (flag[i] == FM_FINAL) { + TrialP newTrialP = TrialP(i, costarray, speedarray, flag, neggradarray); + checkNeighborsAndAddToNarrowband(trialqueue, newTrialP, + [&] (TrialP trialPArg) {this->calcFloorfield(trialPArg);}); + } + } + + // initial narrowband done, now loop (while not empty: remove smallest, add neighbors of removed) + while (!trialqueue.empty()) { + long int keyOfSmallest = trialqueue.top().key; + flag[keyOfSmallest] = FM_FINAL; + trialqueue.pop(); + TrialP newTrialP = TrialP(keyOfSmallest, costarray, speedarray, flag, neggradarray); + checkNeighborsAndAddToNarrowband(trialqueue, newTrialP, + [&] (TrialP trialPArg) { this->calcFloorfield(trialPArg);}); + } + delete[] flag; +} + +void FloorfieldViaFM::calculateDistanceField(const double thresholdArg) { //if threshold negative, then ignore it + + std::priority_queue<TrialP, std::vector<TrialP>, std::greater<TrialP>> trialqueue; + int* flag = new int[_grid->GetnPoints()]; +#ifdef TESTING + //sanity check (fields <> 0) + if (flag == nullptr) return; + if (_dist2Wall == 0) return; + if (_speedInitial == 0) return; + if (_cost == 0) return; + if (_neggrad == 0) return; +#endif //TESTING + + //re-init memory + for (long int i = 0; i < _grid->GetnPoints(); ++i) { + if ((_gcode[i] == INSIDE) || (_gcode[i] == OPEN_TRANSITION) || (_gcode[i] == OPEN_CROSSING)) { + flag[i] = FM_UNKNOWN; //inside + } else if (_gcode[i] == OUTSIDE) { + flag[i] = FM_OUTSIDE; //outside + } else { + flag[i] = FM_FINAL; //wall or closed + } + } + + for (long int i = 0; i < _grid->GetnPoints(); ++i) { + //if ((dist2Wall[i] == 0.) && (flag[i] == -7.)) { + if ((_gcode[i] == WALL) || (_gcode[i] == CLOSED_TRANSITION) || (_gcode[i] == CLOSED_CROSSING)) { + TrialP newP = TrialP(i, _dist2Wall, _speedInitial, flag, _dirToWall); + checkNeighborsAndAddToNarrowband(trialqueue, newP, + [&] (TrialP trialPArg) { this->calcDist2Wall(trialPArg);} ); + } + } + + while (!trialqueue.empty()) { + long int keyOfSmallest = trialqueue.top().key; + flag[keyOfSmallest] = FM_FINAL; + trialqueue.pop(); + if ((thresholdArg > 0) && (_dist2Wall[keyOfSmallest] > thresholdArg)) { //set rest of nearfield and rest of unknown to this max value: + + //rest of nearfield + while (!trialqueue.empty()) { + long int currKey = trialqueue.top().key; + _dist2Wall[currKey] = _dist2Wall[keyOfSmallest]; + flag[currKey] = FM_FINAL; + _dirToWall[currKey]._x = (0.); + _dirToWall[currKey]._y = (0.); + trialqueue.pop(); + } + + //rest of unknown + for (long int i = 0; i < _grid->GetnPoints(); ++i) { + if (flag[i] == FM_UNKNOWN) { + flag[i] = FM_FINAL; + _dist2Wall[i] = _dist2Wall[keyOfSmallest]; + _dirToWall[i]._x = (0.); + _dirToWall[i]._y = (0.); + } + } + } else { + //Log->Write(std::to_string(debugcounter++) + " " + std::to_string(grid->GetnPoints())); + TrialP smallestP = TrialP(keyOfSmallest, _dist2Wall, _speedInitial, flag, _dirToWall); + checkNeighborsAndAddToNarrowband(trialqueue, smallestP, + [&] (TrialP trialPArg) { this->calcDist2Wall(trialPArg);} ); + } + } + delete[] flag; +} //calculateDistancField + +void FloorfieldViaFM::checkNeighborsAndAddToNarrowband(std::priority_queue<TrialP, std::vector<TrialP>, std::greater<TrialP>>& trialqueue, TrialP keyP, + std::function<void (TrialP)> calc) { + int* flag = keyP.flag; + + directNeighbor dNeigh = _grid->getNeighbors(keyP.key); + + //check for valid neigh + long int aux = dNeigh.key[0]; + //hint: trialfield[i].cost = dist2Wall + i; <<< set in prepareForDistanceFieldCalc + if ((aux != -2) && (flag[aux] == FM_UNKNOWN)) { + flag[aux] = FM_ADDED; // added to trial but not calculated + TrialP currNeighP = TrialP(aux, keyP.cost, keyP.speed, flag, keyP.neggrad); + calc(currNeighP); + trialqueue.emplace(currNeighP); + } + aux = dNeigh.key[1]; + if ((aux != -2) && (flag[aux] == FM_UNKNOWN)) { + flag[aux] = FM_ADDED; // added to trial but not calculated + TrialP currNeighP = TrialP(aux, keyP.cost, keyP.speed, flag, keyP.neggrad); + calc(currNeighP); + trialqueue.emplace(currNeighP); + } + aux = dNeigh.key[2]; + if ((aux != -2) && (flag[aux] == FM_UNKNOWN)) { + flag[aux] = FM_ADDED; // added to trial but not calculated + TrialP currNeighP = TrialP(aux, keyP.cost, keyP.speed, flag, keyP.neggrad); + calc(currNeighP); + trialqueue.emplace(currNeighP); + } + aux = dNeigh.key[3]; + if ((aux != -2) && (flag[aux] == FM_UNKNOWN)) { + flag[aux] = FM_ADDED; // added to trial but not calculated + TrialP currNeighP = TrialP(aux, keyP.cost, keyP.speed, flag, keyP.neggrad); + calc(currNeighP); + trialqueue.emplace(currNeighP); + } +} + +void FloorfieldViaFM::calcDist2Wall(TrialP newPoint) { + double row; + double col; + long int aux; + bool pointsUp; + bool pointsRight; + + int* flag = newPoint.flag; + double* cost = newPoint.cost; + long int key = newPoint.key; + + row = 100000.; + col = 100000.; + //aux = -1; + + directNeighbor dNeigh = _grid->getNeighbors(key); + + aux = dNeigh.key[0]; + //hint: trialfield[i].cost = dist2Wall + i; <<< set in resetGoalAndCosts + if ((aux != -2) && //neighbor is a gridpoint + (flag[aux] != FM_UNKNOWN) && //gridpoint holds a calculated value + (_gcode[aux] != OUTSIDE)) //gridpoint holds a calculated value + { + row = cost[aux]; + pointsRight = true; + if (row < 0) { + std::cerr << "hier ist was schief " << row << " " << aux << " " << flag[aux] << std::endl; + row = 100000; + } + } + aux = dNeigh.key[2]; + if ((aux != -2) && //neighbor is a gridpoint + (flag[aux] != FM_UNKNOWN) && //gridpoint holds a calculated value + (_gcode[aux] != OUTSIDE) && + (cost[aux] < row)) //calculated value promises smaller cost + { + row = cost[aux]; + pointsRight = false; + } + + aux = dNeigh.key[1]; + //hint: trialfield[i].cost = dist2Wall + i; <<< set in parseBuilding after linescan call + if ((aux != -2) && //neighbor is a gridpoint + (flag[aux] != FM_UNKNOWN) && //gridpoint holds a calculated value + (_gcode[aux] != OUTSIDE)) + { + col = cost[aux]; + pointsUp = true; + if (col < 0) { + std::cerr << "hier ist was schief " << col << " " << aux << " " << flag[aux] << std::endl; + col = 100000; + } + } + aux = dNeigh.key[3]; + if ((aux != -2) && //neighbor is a gridpoint + (flag[aux] != FM_UNKNOWN) && //gridpoint holds a calculated value + (_gcode[aux] != OUTSIDE) && + (cost[aux] < col)) //calculated value promises smaller cost + { + col = cost[aux]; + pointsUp = false; + } + if (col == 100000.) { //one sided update with row + cost[key] = onesidedCalc(row, _grid->Gethx()); + flag[key] = FM_SINGLE; + if (pointsRight) { + _dirToWall[key]._x = (-(cost[key+1]-cost[key])/_grid->Gethx()); + _dirToWall[key]._y = (0.); + } else { + _dirToWall[key]._x = (-(cost[key]-cost[key-1])/_grid->Gethx()); + _dirToWall[key]._y = (0.); + } + _dirToWall[key] = _dirToWall[key].Normalized(); //@todo: ar.graf: what yields better performance? scale every point here or scale each read value? more points or more calls to any element of dir2Wall + return; + } + + if (row == 100000.) { //one sided update with col + cost[key] = onesidedCalc(col, _grid->Gethy()); + flag[key] = FM_SINGLE; + if (pointsUp) { + _dirToWall[key]._x = (0.); + _dirToWall[key]._y = (-(cost[key+(_grid->GetiMax())]-cost[key])/_grid->Gethy()); + } else { + _dirToWall[key]._x = (0.); + _dirToWall[key]._y = (-(cost[key]-cost[key-(_grid->GetiMax())])/_grid->Gethy()); + } + _dirToWall[key] = _dirToWall[key].Normalized(); + return; + } + + //two sided update + double precheck = twosidedCalc(row, col, _grid->Gethx()); + if (precheck >= 0) { + cost[key] = precheck; + flag[key] = FM_DOUBLE; + if (pointsUp && pointsRight) { + _dirToWall[key]._x = (-(cost[key+1]-cost[key])/_grid->Gethx()); + _dirToWall[key]._y = (-(cost[key+(_grid->GetiMax())]-cost[key])/_grid->Gethy()); + } + if (pointsUp && !pointsRight) { + _dirToWall[key]._x = (-(cost[key]-cost[key-1])/_grid->Gethx()); + _dirToWall[key]._y = (-(cost[key+(_grid->GetiMax())]-cost[key])/_grid->Gethy()); + } + if (!pointsUp && pointsRight) { + _dirToWall[key]._x = (-(cost[key+1]-cost[key])/_grid->Gethx()); + _dirToWall[key]._y = (-(cost[key]-cost[key-(_grid->GetiMax())])/_grid->Gethy()); + } + if (!pointsUp && !pointsRight) { + _dirToWall[key]._x = (-(cost[key]-cost[key-1])/_grid->Gethx()); + _dirToWall[key]._y = (-(cost[key]-cost[key-(_grid->GetiMax())])/_grid->Gethy()); + } + } else { + std::cerr << "else in twosided Dist " << std::endl; + } + _dirToWall[key] = _dirToWall[key].Normalized(); +} + +void FloorfieldViaFM::calcFloorfield(TrialP newPoint) { + double row; + double col; + long int aux; + bool pointsUp = false; + bool pointsRight = false; + + int* flag = newPoint.flag; + double* cost = newPoint.cost; + long int key = newPoint.key; + double* speed = newPoint.speed; + Point* neggrad = newPoint.neggrad; + + row = DBL_MAX; + col = DBL_MAX; + //aux = -1; + + directNeighbor dNeigh = _grid->getNeighbors(key); + + aux = dNeigh.key[0]; + //hint: trialfield[i].cost = costarray + i; <<< set in calculateFloorfield + if ((aux != -2) && //neighbor is a gridpoint + (flag[aux] != FM_UNKNOWN) && + //((gcode[aux] == INSIDE) || (gcode[aux] == OPEN_CROSSING) || (gcode[aux] == OPEN_TRANSITION)) ) //gridpoint holds a calculated value + (flag[aux] != FM_OUTSIDE)) + { + row = cost[aux]; + pointsRight = true; + } + aux = dNeigh.key[2]; + if ((aux != -2) && //neighbor is a gridpoint + (flag[aux] != FM_UNKNOWN) && + (flag[aux] != FM_OUTSIDE) && //gridpoint holds a calculated value + (cost[aux] < row)) //calculated value promises smaller cost + { + row = cost[aux]; + pointsRight = false; + } + + aux = dNeigh.key[1]; + //hint: trialfield[i].cost = cost + i; <<< set in calculateFloorfield + if ((aux != -2) && //neighbor is a gridpoint + (flag[aux] != FM_UNKNOWN) && + (flag[aux] != FM_OUTSIDE)) //gridpoint holds a calculated value + { + col = cost[aux]; + pointsUp = true; + } + aux = dNeigh.key[3]; + if ((aux != -2) && //neighbor is a gridpoint + (flag[aux] != FM_UNKNOWN) && + (flag[aux] != FM_OUTSIDE) && //gridpoint holds a calculated value + (cost[aux] < col)) //calculated value promises smaller cost + { + col = cost[aux]; + pointsUp = false; + } + if ((col == DBL_MAX) && (row == DBL_MAX)) { + std::cerr << "Issue 175 in FloorfieldViaFM: invalid combination of row,col (both on max)" <<std::endl; + return; + } + if (col == DBL_MAX) { //one sided update with row + cost[key] = onesidedCalc(row, _grid->Gethx()/speed[key]); + flag[key] = FM_SINGLE; + if (pointsRight && (dNeigh.key[0] != -2)) { + neggrad[key]._x = (-(cost[key+1]-cost[key])/_grid->Gethx()); + neggrad[key]._y = (0.); + } else if (dNeigh.key[2] != -2) { + neggrad[key]._x = (-(cost[key]-cost[key-1])/_grid->Gethx()); + neggrad[key]._y = (0.); + } + return; + } + + if (row == DBL_MAX) { //one sided update with col + cost[key] = onesidedCalc(col, _grid->Gethy()/speed[key]); + flag[key] = FM_SINGLE; + if ((pointsUp) && (dNeigh.key[1] != -2)) { + neggrad[key]._x = (0.); + neggrad[key]._y = (-(cost[key+(_grid->GetiMax())]-cost[key])/_grid->Gethy()); + } else if (dNeigh.key[3] != -2){ + neggrad[key]._x = (0.); + neggrad[key]._y = (-(cost[key]-cost[key-(_grid->GetiMax())])/_grid->Gethy()); + } + return; + } + + //two sided update + double precheck = twosidedCalc(row, col, _grid->Gethx()/speed[key]); +// if (precheck > 10000) { +// Log->Write("ERROR \t\t\t is in twosided"); +// } + if (precheck >= 0) { + cost[key] = precheck; + flag[key] = FM_DOUBLE; + if (pointsUp && pointsRight) { + neggrad[key]._x = (-(cost[key+1]-cost[key])/_grid->Gethx()); + neggrad[key]._y = (-(cost[key+(_grid->GetiMax())]-cost[key])/_grid->Gethy()); + } + if (pointsUp && !pointsRight) { + neggrad[key]._x = (-(cost[key]-cost[key-1])/_grid->Gethx()); + neggrad[key]._y = (-(cost[key+(_grid->GetiMax())]-cost[key])/_grid->Gethy()); + } + if (!pointsUp && pointsRight) { + neggrad[key]._x = (-(cost[key+1]-cost[key])/_grid->Gethx()); + neggrad[key]._y = (-(cost[key]-cost[key-(_grid->GetiMax())])/_grid->Gethy()); + } + if (!pointsUp && !pointsRight) { + neggrad[key]._x = (-(cost[key]-cost[key-1])/_grid->Gethx()); + neggrad[key]._y = (-(cost[key]-cost[key-(_grid->GetiMax())])/_grid->Gethy()); + } + } else { + std::cerr << "else in twosided Floor " << precheck << " " << row << " " << col << std::endl; + } +} + +inline double FloorfieldViaFM::onesidedCalc(double xy, double hDivF) { + //if ( (xy+hDivF) > 10000) std::cerr << "error in onesided " << xy << std::endl; + return xy + hDivF; +} + +inline double FloorfieldViaFM::twosidedCalc(double x, double y, double hDivF) { //on error return -2 + double determinante = (2*hDivF*hDivF - (x-y)*(x-y)); + if (determinante >= 0) { + return (x + y + sqrt(determinante))/2; + } else { + return (x < y) ? (x + hDivF) : (y + hDivF); + } + std::cerr << "error in two-sided 2!!!!!!!!!!!!!!!!!!!!!!! o_O??" << std::endl; + return -2.; //this line should never execute +} //twosidedCalc + +void FloorfieldViaFM::testoutput(const char* filename1, const char* filename2, const double* target) { +//security debug check + std::ofstream file; + std::ofstream file2; + int numX = (int) ((_grid->GetxMax()-_grid->GetxMin())/_grid->Gethx()); + int numY = (int) ((_grid->GetyMax()-_grid->GetyMin())/_grid->Gethy()); + int numTotal = numX * numY; + //std::cerr << numTotal << " numTotal" << std::endl; + //std::cerr << grid->GetnPoints() << " grid" << std::endl; + file.open(filename1); + file2.open(filename2); + file << "# vtk DataFile Version 3.0" << std::endl; + file << "Testdata: Fast Marching: Test: " << std::endl; + file << "ASCII" << std::endl; + file << "DATASET STRUCTURED_POINTS" << std::endl; + file << "DIMENSIONS " << + std::to_string(_grid->GetiMax()) << + " " << + std::to_string(_grid->GetjMax()) << + " 1" << std::endl; + file << "ORIGIN " << _grid->GetxMin() << " " << _grid->GetyMin() << " 0" << std::endl; + file << "SPACING " << std::to_string(_grid->Gethx()) << " " << std::to_string(_grid->Gethy()) << " 1" << std::endl; + file << "POINT_DATA " << std::to_string(numTotal) << std::endl; + file << "SCALARS Cost float 1" << std::endl; + file << "LOOKUP_TABLE default" << std::endl; + for (long int i = 0; i < _grid->GetnPoints(); ++i) { + file << target[i] << std::endl; + Point iPoint = _grid->getPointFromKey(i); + file2 << iPoint._x /*- grid->GetxMin()*/ << " " << iPoint._y /*- grid->GetyMin()*/ << " " << target[i] << std::endl; + } + + if (target == _cost) { + file << "VECTORS Gradient float" << std::endl; + for (int i = 0; i < _grid->GetnPoints(); ++i) { + file << _neggrad[i]._x << " " << _neggrad[i]._y << " 0.0" << std::endl; + } + } + + file.close(); + file2.close(); + + //std::cerr << "INFO: \tFile closed: " << filename1 << std::endl; +} + +void FloorfieldViaFM::writeFF(const std::string& filename, std::vector<int> targetID) { + Log->Write("INFO: \tWrite Floorfield to file"); + Log->Write(filename); + std::ofstream file; + + Log->Write("FloorfieldViaFM::writeFF(): writing to file %s: There are %d targets.", filename.c_str(), targetID.size()); + + int numX = (int) ((_grid->GetxMax()-_grid->GetxMin())/_grid->Gethx()); + int numY = (int) ((_grid->GetyMax()-_grid->GetyMin())/_grid->Gethy()); + int numTotal = numX * numY; + //std::cerr << numTotal << " numTotal" << std::endl; + //std::cerr << grid->GetnPoints() << " grid" << std::endl; + file.open(filename); + + file << "# vtk DataFile Version 3.0" << std::endl; + file << "Testdata: Fast Marching: Test: " << std::endl; + file << "ASCII" << std::endl; + file << "DATASET STRUCTURED_POINTS" << std::endl; + file << "DIMENSIONS " << + std::to_string(_grid->GetiMax()) << + " " << + std::to_string(_grid->GetjMax()) << + " 1" << std::endl; + file << "ORIGIN " << _grid->GetxMin() << " " << _grid->GetyMin() << " 0" << std::endl; + file << "SPACING " << std::to_string(_grid->Gethx()) << " " << std::to_string(_grid->Gethy()) << " 1" << std::endl; + file << "POINT_DATA " << std::to_string(numTotal) << std::endl; + file << "SCALARS Dist2Wall float 1" << std::endl; + file << "LOOKUP_TABLE default" << std::endl; + for (long int i = 0; i < _grid->GetnPoints(); ++i) { + file << _dist2Wall[i] << std::endl; //@todo: change target to all dist2wall + //Point iPoint = grid->getPointFromKey(i); + //file2 << iPoint._x /*- grid->GetxMin()*/ << " " << iPoint._y /*- grid->GetyMin()*/ << " " << target[i] << std::endl; + } + + file << "VECTORS Dir2Wall float" << std::endl; + for (long int i = 0; i < _grid->GetnPoints(); ++i) { + file << _dirToWall[i]._x << " " << _dirToWall[i]._y << " 0.0" << std::endl; + } + + file << "SCALARS SubroomPtr float 1" << std::endl; + file << "LOOKUP_TABLE default" << std::endl; + for (long int i = 0; i < _grid->GetnPoints(); ++i) { + if (_subrooms[i]) { + file << _subrooms[i]->GetUID() << std::endl; + } else { + file << 0.0 << std::endl; + } + } + + for (unsigned int iTarget = 0; iTarget < targetID.size(); ++iTarget) { + Log->Write("%s: target number %d: UID %d", filename.c_str(), iTarget, targetID[iTarget]); + if (_neggradmap.count(targetID[iTarget]) == 0) { + continue; + } + + Point *gradarray = _neggradmap[targetID[iTarget]]; + if (gradarray == nullptr) { + continue; + } + + std::string name = _building->GetTransOrCrossByUID(targetID[iTarget])->GetCaption() + "-" + std::to_string(targetID[iTarget]); + std::replace(name.begin(), name.end(), ' ', '_'); + file << "VECTORS GradientTarget" << name << " float" << std::endl; + for (int i = 0; i < _grid->GetnPoints(); ++i) { + file << gradarray[i]._x << " " << gradarray[i]._y << " 0.0" << std::endl; + } + + double *costarray = _costmap[targetID[iTarget]]; + file << "SCALARS CostTarget" << name << " float 1" << std::endl; + file << "LOOKUP_TABLE default" << std::endl; + for (long int i = 0; i < _grid->GetnPoints(); ++i) { + file << costarray[i] << std::endl; + } + } + file << "SCALARS GCode float 1" << std::endl; + file << "LOOKUP_TABLE default" << std::endl; + for (long int i = 0; i < _grid->GetnPoints(); ++i) { + file << _gcode[i] << std::endl; + } + file.close(); +} + +void FloorfieldViaFM::writeGoalFF(const std::string& filename, std::vector<int> targetID) { + Log->Write("INFO: \tWrite Floorfield to file"); + Log->Write(filename); + std::ofstream file; + + int numX = (int) ((_grid->GetxMax()-_grid->GetxMin())/_grid->Gethx()); + int numY = (int) ((_grid->GetyMax()-_grid->GetyMin())/_grid->Gethy()); + int numTotal = numX * numY; + //std::cerr << numTotal << " numTotal" << std::endl; + //std::cerr << grid->GetnPoints() << " grid" << std::endl; + file.open(filename); + + file << "# vtk DataFile Version 3.0" << std::endl; + file << "Testdata: Fast Marching: Test: " << std::endl; + file << "ASCII" << std::endl; + file << "DATASET STRUCTURED_POINTS" << std::endl; + file << "DIMENSIONS " << + std::to_string(_grid->GetiMax()) << + " " << + std::to_string(_grid->GetjMax()) << + " 1" << std::endl; + file << "ORIGIN " << _grid->GetxMin() << " " << _grid->GetyMin() << " 0" << std::endl; + file << "SPACING " << std::to_string(_grid->Gethx()) << " " << std::to_string(_grid->Gethy()) << " 1" << std::endl; + file << "POINT_DATA " << std::to_string(numTotal) << std::endl; + //file << "SCALARS Dist2Wall float 1" << std::endl; + //file << "LOOKUP_TABLE default" << std::endl; + //for (long int i = 0; i < grid->GetnPoints(); ++i) { + // file << dist2Wall[i] << std::endl; //@todo: change target to all dist2wall + //Point iPoint = grid->getPointFromKey(i); + //file2 << iPoint._x /*- grid->GetxMin()*/ << " " << iPoint._y /*- grid->GetyMin()*/ << " " << target[i] << std::endl; + //} + + //file << "VECTORS Dir2Wall float" << std::endl; + //for (long int i = 0; i < grid->GetnPoints(); ++i) { + // file << dirToWall[i]._x << " " << dirToWall[i]._y << " 0.0" << std::endl; + //} + + for (unsigned int iTarget = 0; iTarget < targetID.size(); ++iTarget) { + if (_goalneggradmap.count(targetID[iTarget]) == 0) { + continue; + } + + Point *gradarray = _goalneggradmap[targetID[iTarget]]; + if (gradarray == nullptr) { + continue; + } + + file << "VECTORS GradientTarget" << targetID[iTarget] << " float" << std::endl; + for (int i = 0; i < _grid->GetnPoints(); ++i) { + file << gradarray[i]._x << " " << gradarray[i]._y << " 0.0" << std::endl; + } + + double *costarray = _goalcostmap[targetID[iTarget]]; + file << "SCALARS CostTarget" << targetID[iTarget] << " float 1" << std::endl; + file << "LOOKUP_TABLE default" << std::endl; + for (long int i = 0; i < _grid->GetnPoints(); ++i) { + file << costarray[i] << std::endl; + } + } + file << "SCALARS GCode float 1" << std::endl; + file << "LOOKUP_TABLE default" << std::endl; + for (long int i = 0; i < _grid->GetnPoints(); ++i) { + file << _gcode[i] << std::endl; + } + file.close(); +} + +SubRoom* FloorfieldViaFM::isInside(const long int key) { +// Point probe = _grid->getPointFromKey(key); + +// const std::map<int, std::shared_ptr<Room>>& roomMap = _building->GetAllRooms(); +// +// for (auto& roomPair : roomMap) { +// +// Room* roomPtr = roomPair.second.get(); +// const std::map<int, std::shared_ptr<SubRoom>>& subRoomMap = roomPtr->GetAllSubRooms(); +// +// for (auto& subRoomPair : subRoomMap) { +// +// SubRoom* subRoomPtr = subRoomPair.second.get(); +// +// if (subRoomPtr->IsInSubRoom(probe)) { +// return subRoomPtr; +// } +// } + + //FloorfieldViaFM does not support isInside because of unresolved problem of ambiguous projection of xy plane in multi- + //storage buildings + + return nullptr; +} + +void CentrePointFFViaFM::getDirectionToUID(int destID, const long int key, Point& direction, int mode) { + //what if goal == -1, meaning closest exit... is GetExitIndex then -1? NO... ExitIndex is UID, given by router + //if (ped->GetFinalDestination() == -1) /*go to closest exit*/ destID != -1; + + if ((key < 0) || (key >= _grid->GetnPoints())) { // @todo: ar.graf: this check in a #ifdef-block? + Log->Write("ERROR: \t Floorfield tried to access a key out of grid!"); + direction._x = 0.; + direction._y = 0.; + return; + } + Point* localneggradptr = nullptr; + double* localcostptr = nullptr; + { + if (_neggradmap.count(destID) == 0) { + //Log->Write("FF for destID %d does not exist (key is %d)", destID, key); + //check, if distID is in this grid + Hline* destLine = _building->GetTransOrCrossByUID(destID); + Point A = destLine->GetCentre(); + //Point B = destLine->GetPoint2(); + if (!(_grid->includesPoint(A))) { + Log->Write("ERROR: \t Destination ID %d is not in grid!", destID); + direction._x = direction._y = 0.; + //return; + } + } + localneggradptr = (_neggradmap.count(destID) == 0) ? nullptr : _neggradmap.at(destID); + localcostptr = (_costmap.count(destID) == 0) ? nullptr : _costmap.at(destID); + if (localneggradptr == nullptr) { + bool isBeingCalculated; +#pragma omp critical(floorfieldsBeingCalculated) + { + if (!(isBeingCalculated = _floorfieldsBeingCalculated.count(destID) > 0)) { + _floorfieldsBeingCalculated.insert(destID); + } + } + if (isBeingCalculated) { + // we do not want to wait until the other calculation has finished, so we return immediately + // the values are corrected in getDirectionToDestination(), and getCostToDestination doesn't care about the direction + direction._x = DBL_MAX; + direction._y = DBL_MAX; + return; + } + + //create floorfield (remove mapentry with nullptr, allocate memory, add mapentry, create ff) + localcostptr = new double[_grid->GetnPoints()]; + localneggradptr = new Point[_grid->GetnPoints()]; +#pragma omp critical(neggradmap) + _neggradmap.erase(destID); +#pragma omp critical(neggradmap) + _neggradmap.emplace(destID, localneggradptr); +#pragma omp critical(costmap) + _costmap.erase(destID); +#pragma omp critical(costmap) + _costmap.emplace(destID, localcostptr); + + + //create ff (prepare Trial-mechanic, then calc) +// for (long int i = 0; i < grid->GetnPoints(); ++i) { +// //set Trialptr to fieldelements +// trialfield[i].cost = localcostptr + i; +// trialfield[i].neggrad = localneggradptr + i; +// trialfield[i].father = nullptr; +// trialfield[i].child = nullptr; +// } +// clearAndPrepareForFloorfieldReCalc(localcostptr); + //std::vector<Line> localline = {Line((Line) *(building->GetTransOrCrossByUID(destID)))}; + Point centre = _building->GetTransOrCrossByUID(destID)->GetCentre(); + std::vector<Line> localline = {Line(centre, centre, 0)}; // only one point +// setNewGoalAfterTheClear(localcostptr, localline); + //Log->Write("Starting FF for UID %d (ID %d)", destID, dynamic_cast<Crossing*>(building->GetTransOrCrossByUID(destID))->GetID()); + //std::cerr << "\rW\tO\tR\tK\tI\tN\tG"; + if (mode == quickest) { + calculateFloorfield(localline, localcostptr, localneggradptr, _densityspeed); + } else { + calculateFloorfield(localline, localcostptr, localneggradptr, _modifiedspeed); + } + //when using CentralPoint, the rest of destID Line would not be calculated. We set that line to zero in the lines below. + //this way, the ffrouter understands, if a pedestrian reached the line, but is next to central point. + Point a = _building->GetTransOrCrossByUID(destID)->GetPoint1(); + Point b = _building->GetTransOrCrossByUID(destID)->GetPoint2(); + localline.emplace_back(Line(a, b, 0)); + drawLinesOnGrid<double>(localline, localcostptr, 0.); +#pragma omp critical(floorfieldsBeingCalculated) + { + if (_floorfieldsBeingCalculated.count(destID) != 1) { + Log->Write("ERROR: FloorfieldViaFM::getDirectionToUID: key %d was calculating FF for destID %d, but it was removed from floorfieldsBeingCalculated meanwhile", key, destID); + } + _floorfieldsBeingCalculated.erase(destID); + } + //Log->Write("Ending FF for UID %d", destID); + //std::cerr << "\r W\t O\t R\t K\t I\t N\t G"; + } + } + direction._x = (localneggradptr[key]._x); + direction._y = (localneggradptr[key]._y); +} + +SubRoom* FloorfieldViaFM::GetSubroom(Pedestrian* ped) { + long int key = _grid->getKeyAtPoint(ped->GetPos()); + assert(key >= 0 && key <= _grid->GetnPoints()); + return _subrooms[key]; +} \ No newline at end of file diff --git a/routing/ff_router_trips/FloorfieldViaFM.h b/routing/ff_router_trips/FloorfieldViaFM.h new file mode 100644 index 0000000000000000000000000000000000000000..c61eea13696aad6e947d9075120224172fcf2516 --- /dev/null +++ b/routing/ff_router_trips/FloorfieldViaFM.h @@ -0,0 +1,226 @@ +/** + * \file FloorfieldViaFM.h + * \date Mar 05, 2015 + * \version N/A (v0.6) + * \copyright <2009-2014> Forschungszentrum Jülich GmbH. All rights reserved. + * + * \section License + * This file is part of JuPedSim. + * + * JuPedSim is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * JuPedSim 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 Lesser General Public License + * along with JuPedSim. If not, see <http://www.gnu.org/licenses/>. + * + * \section Description + * Implementation of classes for ... + * + * + **/ + + //remark: + //refac the code to use enums instead of integer values where integer values code sth + //was considered, but enum classes do not implicitly cast to int + //rather use makros/masks like in plain C? or just makros (defines)? + //this would make it easier to read + +#ifndef FLOORFIELDVIAFM_H +#define FLOORFIELDVIAFM_H + +#include <vector> +#include <unordered_set> +#include <cmath> +#include <functional> +#include "mesh/RectGrid.h" +#include "../../geometry/Wall.h" +#include "../../geometry/Point.h" +#include "../../geometry/Building.h" +#include "../../geometry/SubRoom.h" //check: should Room.h include SubRoom.h?? +#include "./mesh/Trial.h" +#include "../../pedestrian/Pedestrian.h" + +//maybe put following in macros.h +#define LOWSPEED 0.001 + +class TrialP +{ +public: + long int key; + int* flag; + double* cost; + double* speed; + Point* neggrad; + + TrialP() { + key = 0; + flag = nullptr; + cost = nullptr; + speed = nullptr; + neggrad = nullptr; + } + + TrialP(long int keyArg, double* t, double* f, int* flagArg, Point* neggradArg) { + key = keyArg; + cost = t; + speed = f; + flag = flagArg; + neggrad = neggradArg; + } + + ~TrialP(){} + + bool operator <(const TrialP& rhs) const + { + return this->cost[this->key] < rhs.cost[rhs.key]; + } + + bool operator >(const TrialP& rhs) const + { + return this->cost[this->key] > rhs.cost[rhs.key]; + } + + bool operator ==(const TrialP& rhs) const + { + return this->cost[this->key] == rhs.cost[rhs.key]; + } +}; + +class FloorfieldViaFM +{ +public: + FloorfieldViaFM(); + FloorfieldViaFM(const std::string&); + FloorfieldViaFM(const Building* const buildingArg, const double hxArg, const double hyArg, + const double wallAvoidDistance, const bool useDistancefield, const bool onlyRoomsWithExits); + //FloorfieldViaFM(const FloorfieldViaFM* const refFM); + virtual ~FloorfieldViaFM(); + FloorfieldViaFM(const FloorfieldViaFM& other); //will not make a copy; only takes geometry info + //FloorfieldViaFM& operator=(const FloorfieldViaFM& other); + + //void getDirectionAt(const Point& position, Point& direction); //obsolete + //void getDirectionToDestination (const int destID, const Point& position, Point& direction); //obsolete + void getDirectionToUID(int destID, const long int key, Point& direction); // shall not be used any more, therefore not virtual + virtual void getDirectionToUID(int destID, const long int key, Point& direction, int mode); + //void getDirectionToUIDParallel(int destID, const long int key, Point& direction); + virtual void getDirectionToDestination (Pedestrian* ped, Point& direction); + //void getDirectionToFinalDestination(Pedestrian* ped, Point& direction); //this is router buissness! problem in multi-storage buildings + + void createMapEntryInLineToGoalID(const int goalID); + + double getCostToDestination(const int destID, const Point& position); + double getCostToDestination(const int destID, const Point& position, int mode); + //double getCostToDestinationParallel(const int destID, const Point& position); + + void getDir2WallAt(const Point& position, Point& direction); + double getDistance2WallAt(const Point& position); + + int getSubroomUIDAt(const Point& position); + + void parseBuilding(const Building* const buildingArg, const double stepSizeX, const double stepSizeY); + void parseBuildingForExits(const Building* const buildingArg, const double stepSizeX, const double stepSizeY); + void prepareForDistanceFieldCalculation(const bool withExits); + template <typename T> + void drawLinesOnGrid(std::vector<Line>& wallArg, T* const target, const T value); + void setSpeed(bool useDistance2WallArg); + void setSpeedThruPeds(Pedestrian* const* pedsArg, int nPeds, int modechoice, double radius); + void deleteAllFFs(); + void clearAndPrepareForFloorfieldReCalc(double* costarray); + void setNewGoalAfterTheClear(double* costarray, std::vector<Line>& GoalWallArg); + void calculateFloorfield(std::vector<Line>& wallArg, double* costarray, Point* neggradarray); //make private + void calculateFloorfield(std::vector<Line>& wallArg, double* costarray, Point* neggradarray, double* speedarray); + void calculateDistanceField(const double thresholdArg); //make private + + void checkNeighborsAndAddToNarrowband(std::priority_queue<TrialP, std::vector<TrialP>, std::greater<TrialP> >& trialfield, TrialP key, + std::function<void (TrialP)> calc); + + void calcDist2Wall(TrialP); + void calcFloorfield(TrialP); + //void (*checkNeighborsAndCalc)(const long int key); + + inline double onesidedCalc(double xy, double hDivF); + inline double twosidedCalc(double x, double y, double hDivF); + + void testoutput(const char*, const char*, const double*); + void writeFF(const std::string&, std::vector<int> targetID); + void writeGoalFF(const std::string&, std::vector<int> targetID); + + virtual SubRoom* isInside(const long int key); + SubRoom* GetSubroom(Pedestrian* p); + + std::map<int, int> getGoalToLineUIDmap() const + { + return _goalToLineUIDmap; + } + + std::map<int, int> getGoalToLineUIDmap2() const + { + return _goalToLineUIDmap2; + } + + std::map<int, int> getGoalToLineUIDmap3() const + { + return _goalToLineUIDmap3; + } + + RectGrid* getGrid() const + { + return _grid; + } + +#ifdef TESTING + void setGrid(RectGrid* gridArg) {_grid = gridArg;} +#endif //TESTING + +protected: + RectGrid* _grid = nullptr; + std::vector<Line> _wall; + std::vector<Line> _exitsFromScope; + unsigned int _numOfExits; + + const Building* _building; + + //GridPoint Data in independant arrays (shared primary key) + // changed to threadsafe creation when needed: int* flag; + int* _gcode = nullptr; //gridcode (see Macros.h) + SubRoom* * _subrooms = nullptr; // this is an array (first asterisk) of pointers (second asterisk) + double* _dist2Wall = nullptr; + double* _speedInitial = nullptr; + double* _modifiedspeed = nullptr; + double* _densityspeed = nullptr; + double* _cost = nullptr; + //long int* secKey; //secondary key to address ... not used yet + Point* _neggrad = nullptr; //gradients + Point* _dirToWall = nullptr; + // changed to threadsafe creation when needed: Trial* trialfield; + + std::map<int, double*> _goalcostmap; + std::map<int, int> _goalToLineUIDmap; //key is the goalID and value is the UID of closest transition -> it maps goal to LineUID + std::map<int, int> _goalToLineUIDmap2; + std::map<int, int> _goalToLineUIDmap3; + std::map<int, Point*> _goalneggradmap; + std::map<int, double*> _costmap; + std::map<int, Point*> _neggradmap; + // use an unordered_set for faster access (it is accessed within a critical region) + std::unordered_set<int> _floorfieldsBeingCalculated; + bool maps_deleted = false; // @todo f.mack remove + + double _threshold; + bool _useDistanceToWall; +}; + +// very similar to FloorfieldViaFM, but the calculation of floorfields starts at the center of the door only, not on the whole line +// this happens in order to avoid "door hopping" (for a pedestrian, it is shorter to go to a nearby door and skip half the door width) +class CentrePointFFViaFM : public virtual FloorfieldViaFM { +public: + virtual void getDirectionToUID(int destID, const long int key, Point& direction, int mode); +}; + +#endif // FLOORFIELDVIAFM_H diff --git a/routing/ff_router_trips/LocalFloorfieldViaFM.cpp b/routing/ff_router_trips/LocalFloorfieldViaFM.cpp new file mode 100644 index 0000000000000000000000000000000000000000..145c764e5a5a618068d644d5c8bcf5c6c547a2b2 --- /dev/null +++ b/routing/ff_router_trips/LocalFloorfieldViaFM.cpp @@ -0,0 +1,519 @@ +// +// Created by ar.graf on 1/28/16. +// + +#include "LocalFloorfieldViaFM.h" + +LocalFloorfieldViaFM::LocalFloorfieldViaFM(){}; +LocalFloorfieldViaFM::LocalFloorfieldViaFM(const Room* const roomArg, + const Building* buildingArg, + const double hxArg, const double hyArg, + const double wallAvoidDistance, const bool useDistancefield) { + //ctor + //_threshold = -1; //negative value means: ignore threshold + _threshold = wallAvoidDistance; + _building = buildingArg; + _room = roomArg; + + if (hxArg != hyArg) std::cerr << "ERROR: hx != hy <========="; + //parse building and create list of walls/obstacles (find xmin xmax, ymin, ymax, and add border?) + //Log->Write("INFO: \tStart Parsing: Room %d", roomArg->GetID()); + parseRoom(roomArg, hxArg, hyArg); + //Log->Write("INFO: \tFinished Parsing: Room %d", roomArg->GetID()); + //testoutput("AALineScan.vtk", "AALineScan.txt", dist2Wall); + + prepareForDistanceFieldCalculation(false); + //here we need to draw blocker lines @todo: ar.graf + //drawBlockerLines(); + //Log->Write("INFO: \tGrid initialized: Walls in room %d", roomArg->GetID()); + + calculateDistanceField(-1.); //negative threshold is ignored, so all distances get calculated. this is important since distances is used for slowdown/redirect + //Log->Write("INFO: \tGrid initialized: Walldistances in room %d", roomArg->GetID()); + + setSpeed(useDistancefield); //use distance2Wall + //Log->Write("INFO: \tGrid initialized: Speed in room %d", roomArg->GetID()); + calculateFloorfield(_exitsFromScope, _cost, _neggrad); + //Log->Write("INFO: \tFloor field for \"goal -1\" done in room %d", roomArg->GetID()); +}; + +//void LocalFloorfieldViaFM::getDirectionToGoalID(const int goalID){ +// std::cerr << "invalid call to LocalFloorfieldViaFM::getDirectionToGoalID!" << std::endl; +//}; + +void LocalFloorfieldViaFM::parseRoom(const Room* const roomArg, + const double hxArg, const double hyArg) +{ + _room = roomArg; + //init min/max before parsing + double xMin = DBL_MAX; + double xMax = -DBL_MAX; + double yMin = xMin; + double yMax = xMax; + _costmap.clear(); + _neggradmap.clear(); + + //create a list of walls + //add all transition and put open doors at the beginning of "wall" + std::map<int, Transition*> allTransitions; + for (auto& itSubroom : _room->GetAllSubRooms()) { + for (auto itTrans : itSubroom.second->GetAllTransitions()) { + if (!allTransitions.count(itTrans->GetUniqueID())) { + allTransitions[itTrans->GetUniqueID()] = &(*itTrans); + if (itTrans->IsOpen()) { + _exitsFromScope.emplace_back(Line((Line) *itTrans)); + _costmap.emplace(itTrans->GetUniqueID(), nullptr); + _neggradmap.emplace(itTrans->GetUniqueID(), nullptr); + } + } + } + } + _numOfExits = (unsigned int) _exitsFromScope.size(); + //put closed doors next, they are considered as walls later (index >= numOfExits) + for (auto& trans : allTransitions) { + if (!trans.second->IsOpen()) { + _wall.emplace_back(Line ( (Line) *(trans.second))); + } + } + + for (const auto& itSubroom : _room->GetAllSubRooms()) { + std::vector<Obstacle*> allObstacles = itSubroom.second->GetAllObstacles(); + for (std::vector<Obstacle*>::iterator itObstacles = allObstacles.begin(); itObstacles != allObstacles.end(); ++itObstacles) { + + std::vector<Wall> allObsWalls = (*itObstacles)->GetAllWalls(); + for (std::vector<Wall>::iterator itObsWall = allObsWalls.begin(); itObsWall != allObsWalls.end(); ++itObsWall) { + _wall.emplace_back(Line( (Line) *itObsWall)); + // xMin xMax + if ((*itObsWall).GetPoint1()._x < xMin) xMin = (*itObsWall).GetPoint1()._x; + if ((*itObsWall).GetPoint2()._x < xMin) xMin = (*itObsWall).GetPoint2()._x; + if ((*itObsWall).GetPoint1()._x > xMax) xMax = (*itObsWall).GetPoint1()._x; + if ((*itObsWall).GetPoint2()._x > xMax) xMax = (*itObsWall).GetPoint2()._x; + // yMin yMax + if ((*itObsWall).GetPoint1()._y < yMin) yMin = (*itObsWall).GetPoint1()._y; + if ((*itObsWall).GetPoint2()._y < yMin) yMin = (*itObsWall).GetPoint2()._y; + if ((*itObsWall).GetPoint1()._y > yMax) yMax = (*itObsWall).GetPoint1()._y; + if ((*itObsWall).GetPoint2()._y > yMax) yMax = (*itObsWall).GetPoint2()._y; + } + } + + std::vector<Wall> allWalls = itSubroom.second->GetAllWalls(); + for (std::vector<Wall>::iterator itWall = allWalls.begin(); itWall != allWalls.end(); ++itWall) { + _wall.emplace_back( Line( (Line) *itWall)); + // xMin xMax + if ((*itWall).GetPoint1()._x < xMin) xMin = (*itWall).GetPoint1()._x; + if ((*itWall).GetPoint2()._x < xMin) xMin = (*itWall).GetPoint2()._x; + if ((*itWall).GetPoint1()._x > xMax) xMax = (*itWall).GetPoint1()._x; + if ((*itWall).GetPoint2()._x > xMax) xMax = (*itWall).GetPoint2()._x; + // yMin yMax + if ((*itWall).GetPoint1()._y < yMin) yMin = (*itWall).GetPoint1()._y; + if ((*itWall).GetPoint2()._y < yMin) yMin = (*itWall).GetPoint2()._y; + if ((*itWall).GetPoint1()._y > yMax) yMax = (*itWall).GetPoint1()._y; + if ((*itWall).GetPoint2()._y > yMax) yMax = (*itWall).GetPoint2()._y; + } + const vector<Crossing*>& allCrossings = itSubroom.second->GetAllCrossings(); + for (Crossing* crossPtr : allCrossings) { + if (!crossPtr->IsOpen()) { + _wall.emplace_back( Line( (Line) *crossPtr)); + + if (crossPtr->GetPoint1()._x < xMin) xMin = crossPtr->GetPoint1()._x; + if (crossPtr->GetPoint2()._x < xMin) xMin = crossPtr->GetPoint2()._x; + if (crossPtr->GetPoint1()._x > xMax) xMax = crossPtr->GetPoint1()._x; + if (crossPtr->GetPoint2()._x > xMax) xMax = crossPtr->GetPoint2()._x; + + if (crossPtr->GetPoint1()._y < yMin) yMin = crossPtr->GetPoint1()._y; + if (crossPtr->GetPoint2()._y < yMin) yMin = crossPtr->GetPoint2()._y; + if (crossPtr->GetPoint1()._y > yMax) yMax = crossPtr->GetPoint1()._y; + if (crossPtr->GetPoint2()._y > yMax) yMax = crossPtr->GetPoint2()._y; + } + } + } + + //create Rect Grid + _grid = new RectGrid(); + _grid->setBoundaries(xMin, yMin, xMax, yMax); + _grid->setSpacing(hxArg, hyArg); + _grid->createGrid(); + + //create arrays + //flag = new int[grid->GetnPoints()]; + _gcode = new int[_grid->GetnPoints()]; + _subrooms = new SubRoom*[_grid->GetnPoints()](); // initializes with nullptr + _dist2Wall = new double[_grid->GetnPoints()]; + _speedInitial = new double[_grid->GetnPoints()]; + _modifiedspeed = new double[_grid->GetnPoints()]; + _densityspeed = new double[_grid->GetnPoints()]; + _cost = new double[_grid->GetnPoints()]; + _neggrad = new Point[_grid->GetnPoints()]; + _dirToWall = new Point[_grid->GetnPoints()]; + //trialfield = new Trial[grid->GetnPoints()]; //created with other arrays, but not initialized yet + + _costmap.emplace(-1 , _cost); // enable default ff (closest exit) + _neggradmap.emplace(-1, _neggrad); + + //init grid with -3 as unknown distance to any wall + for(long int i = 0; i < _grid->GetnPoints(); ++i) { + _dist2Wall[i] = -3.; + _cost[i] = -2.; + _gcode[i] = OUTSIDE; //unknown + } + + drawLinesOnGrid<double>(_wall, _dist2Wall, 0.); + drawLinesOnGrid<double>(_wall, _cost, -7.); + drawLinesOnGrid<int>(_wall, _gcode, WALL); + drawLinesOnGrid<int>(_exitsFromScope, _gcode, OPEN_TRANSITION); +} + +void LocalFloorfieldViaFM::drawBlockerLines() { + //std::vector<Line> exits(wall.begin(), wall.begin()+numOfExits); + + //grid handeling local vars: + //long int iMax = grid->GetiMax(); + + long int iStart, iEnd; + long int jStart, jEnd; + long int iDot, jDot; + long int key; + long int deltaX, deltaY, deltaX1, deltaY1, px, py, xe, ye, i; //Bresenham Algorithm + + for(auto& line : _wall) { + key = _grid->getKeyAtPoint(line.GetPoint1()); + iStart = (long) _grid->get_i_fromKey(key); + jStart = (long) _grid->get_j_fromKey(key); + + key = _grid->getKeyAtPoint(line.GetPoint2()); + iEnd = (long) _grid->get_i_fromKey(key); + jEnd = (long) _grid->get_j_fromKey(key); + + deltaX = (int) (iEnd - iStart); + deltaY = (int) (jEnd - jStart); + deltaX1 = abs( (int) (iEnd - iStart)); + deltaY1 = abs( (int) (jEnd - jStart)); + + px = 2*deltaY1 - deltaX1; + py = 2*deltaX1 - deltaY1; + + if(deltaY1<=deltaX1) { + if(deltaX>=0) { + iDot = iStart; + jDot = jStart; + xe = iEnd; + } else { + iDot = iEnd; + jDot = jEnd; + xe = iStart; + } + //crossOutOutsideNeighbors(jDot*iMax + iDot); + for (i=0; iDot < xe; ++i) { + ++iDot; + if(px<0) { + px+=2*deltaY1; + } else { + if((deltaX<0 && deltaY<0) || (deltaX>0 && deltaY>0)) { + ++jDot; + } else { + --jDot; + } + px+=2*(deltaY1-deltaX1); + } + //crossOutOutsideNeighbors(jDot*iMax + iDot); + } + } else { + if(deltaY>=0) { + iDot = iStart; + jDot = jStart; + ye = jEnd; + } else { + iDot = iEnd; + jDot = jEnd; + ye = jStart; + } + //crossOutOutsideNeighbors(jDot*iMax + iDot); + for(i=0; jDot<ye; ++i) { + ++jDot; + if (py<=0) { + py+=2*deltaX1; + } else { + if((deltaX<0 && deltaY<0) || (deltaX>0 && deltaY>0)) { + ++iDot; + } else { + --iDot; + } + py+=2*(deltaX1-deltaY1); + } + // crossOutOutsideNeighbors(jDot*iMax + iDot); + } + } + } //loop over all walls + + for(auto& line : _exitsFromScope) { + key = _grid->getKeyAtPoint(line.GetPoint1()); + iStart = (long) _grid->get_i_fromKey(key); + jStart = (long) _grid->get_j_fromKey(key); + + key = _grid->getKeyAtPoint(line.GetPoint2()); + iEnd = (long) _grid->get_i_fromKey(key); + jEnd = (long) _grid->get_j_fromKey(key); + + deltaX = (int) (iEnd - iStart); + deltaY = (int) (jEnd - jStart); + deltaX1 = abs( (int) (iEnd - iStart)); + deltaY1 = abs( (int) (jEnd - jStart)); + + px = 2*deltaY1 - deltaX1; + py = 2*deltaX1 - deltaY1; + + if(deltaY1<=deltaX1) { + if(deltaX>=0) { + iDot = iStart; + jDot = jStart; + xe = iEnd; + } else { + iDot = iEnd; + jDot = jEnd; + xe = iStart; + } + //crossOutOutsideNeighbors(jDot*iMax + iDot); + for (i=0; iDot < xe; ++i) { + ++iDot; + if(px<0) { + px+=2*deltaY1; + } else { + if((deltaX<0 && deltaY<0) || (deltaX>0 && deltaY>0)) { + ++jDot; + } else { + --jDot; + } + px+=2*(deltaY1-deltaX1); + } + //crossOutOutsideNeighbors(jDot*iMax + iDot); + } + } else { + if(deltaY>=0) { + iDot = iStart; + jDot = jStart; + ye = jEnd; + } else { + iDot = iEnd; + jDot = jEnd; + ye = jStart; + } + //crossOutOutsideNeighbors(jDot*iMax + iDot); + for(i=0; jDot<ye; ++i) { + ++jDot; + if (py<=0) { + py+=2*deltaX1; + } else { + if((deltaX<0 && deltaY<0) || (deltaX>0 && deltaY>0)) { + ++iDot; + } else { + --iDot; + } + py+=2*(deltaX1-deltaY1); + } + // crossOutOutsideNeighbors(jDot*iMax + iDot); + } + } + } //loop over all exits + +} + +SubRoom* LocalFloorfieldViaFM::isInside(const long int key) { + Point probe = _grid->getPointFromKey(key); + auto neighbors = _grid->getNeighbors(key); + + for (auto& neighbor : neighbors.key) { + if (neighbor == -2) continue; // -2 is returned by getNeighbors() for invalid points + SubRoom* subroom = _subrooms[neighbor]; + if (subroom && subroom->IsInSubRoom(probe)) return subroom; + } + + // If we weren't successful so far, we have to search the whole list. + const std::map<int, std::shared_ptr<SubRoom>>& subRoomMap = _room->GetAllSubRooms(); + + for (auto& subRoomPair : subRoomMap) { + SubRoom* subRoomPtr = subRoomPair.second.get(); + if (subRoomPtr->IsInSubRoom(probe)) { + return subRoomPtr; + } + } + + return nullptr; +} + +SubLocalFloorfieldViaFM::SubLocalFloorfieldViaFM(){}; +SubLocalFloorfieldViaFM::SubLocalFloorfieldViaFM(SubRoom* const roomArg, + const Building* buildingArg, + const double hxArg, const double hyArg, + const double wallAvoidDistance, const bool useDistancefield) { + //ctor + //_threshold = -1; //negative value means: ignore threshold + _threshold = wallAvoidDistance; + _building = buildingArg; + _subroom = roomArg; + + if (hxArg != hyArg) std::cerr << "ERROR: hx != hy <========="; + //parse building and create list of walls/obstacles (find xmin xmax, ymin, ymax, and add border?) + //Log->Write("INFO: \tStart Parsing: Room %d" , roomArg->GetUID()); + parseRoom(roomArg, hxArg, hyArg); + //Log->Write("INFO: \tFinished Parsing: Room %d" , roomArg->GetUID()); + //testoutput("AALineScan.vtk", "AALineScan.txt", dist2Wall); + + prepareForDistanceFieldCalculation(false); + //here we need to draw blocker lines @todo: ar.graf + //Log->Write("INFO: \tGrid initialized: Walls"); + + calculateDistanceField(-1.); //negative threshold is ignored, so all distances get calculated. this is important since distances is used for slowdown/redirect + //Log->Write("INFO: \tGrid initialized: Walldistances"); + + setSpeed(useDistancefield); //use distance2Wall + //Log->Write("INFO: \tGrid initialized: Speed"); + calculateFloorfield(_exitsFromScope, _cost, _neggrad); +}; + +void SubLocalFloorfieldViaFM::getDirectionToDestination(Pedestrian* ped, + Point& direction) +{ + FloorfieldViaFM::getDirectionToDestination(ped, direction); + return; +} + +void SubLocalFloorfieldViaFM::getDirectionToGoalID(const int goalID){ + std::cerr << "invalid call to SubLocalFloorfieldViaFM::getDirectionToGoalID with goalID: " << goalID << std::endl; +}; + + +void SubLocalFloorfieldViaFM::parseRoom(SubRoom* const roomArg, + const double hxArg, const double hyArg) +{ + _subroom = roomArg; + //init min/max before parsing + double xMin = DBL_MAX; + double xMax = -DBL_MAX; + double yMin = xMin; + double yMax = xMax; + _costmap.clear(); + _neggradmap.clear(); + + //create a list of walls + //add all transition and put open doors at the beginning of "wall" + std::map<int, Transition*> allTransitions; + + for (auto itTrans : _subroom->GetAllTransitions()) { + if (!allTransitions.count(itTrans->GetUniqueID())) { + allTransitions[itTrans->GetUniqueID()] = &(*itTrans); + if (itTrans->IsOpen()) { + _exitsFromScope.emplace_back(Line((Line) *itTrans)); + _costmap.emplace(itTrans->GetUniqueID(), nullptr); + _neggradmap.emplace(itTrans->GetUniqueID(), nullptr); + } + } + } + + for (auto itCross : _subroom->GetAllCrossings()) { + if (itCross->IsOpen()) { + _exitsFromScope.emplace_back(Line((Line) *itCross)); + _costmap.emplace(itCross->GetUniqueID(), nullptr); + _neggradmap.emplace(itCross->GetUniqueID(), nullptr); + } else { + _wall.emplace_back(Line( (Line) *itCross)); + } + } + + _numOfExits = _exitsFromScope.size(); + //put closed doors next, they are considered as walls later (index >= numOfExits) + for (auto& trans : allTransitions) { + if (!trans.second->IsOpen()) { + _wall.emplace_back(Line ( (Line) *(trans.second))); + } + } + + + std::vector<Obstacle*> allObstacles = _subroom->GetAllObstacles(); + for (std::vector<Obstacle*>::iterator itObstacles = allObstacles.begin(); itObstacles != allObstacles.end(); ++itObstacles) { + + std::vector<Wall> allObsWalls = (*itObstacles)->GetAllWalls(); + for (std::vector<Wall>::iterator itObsWall = allObsWalls.begin(); itObsWall != allObsWalls.end(); ++itObsWall) { + _wall.emplace_back(Line( (Line) *itObsWall)); + // xMin xMax + if ((*itObsWall).GetPoint1()._x < xMin) xMin = (*itObsWall).GetPoint1()._x; + if ((*itObsWall).GetPoint2()._x < xMin) xMin = (*itObsWall).GetPoint2()._x; + if ((*itObsWall).GetPoint1()._x > xMax) xMax = (*itObsWall).GetPoint1()._x; + if ((*itObsWall).GetPoint2()._x > xMax) xMax = (*itObsWall).GetPoint2()._x; + // yMin yMax + if ((*itObsWall).GetPoint1()._y < yMin) yMin = (*itObsWall).GetPoint1()._y; + if ((*itObsWall).GetPoint2()._y < yMin) yMin = (*itObsWall).GetPoint2()._y; + if ((*itObsWall).GetPoint1()._y > yMax) yMax = (*itObsWall).GetPoint1()._y; + if ((*itObsWall).GetPoint2()._y > yMax) yMax = (*itObsWall).GetPoint2()._y; + } + } + + std::vector<Wall> allWalls = _subroom->GetAllWalls(); + for (std::vector<Wall>::iterator itWall = allWalls.begin(); itWall != allWalls.end(); ++itWall) { + _wall.emplace_back( Line( (Line) *itWall)); + // xMin xMax + if ((*itWall).GetPoint1()._x < xMin) xMin = (*itWall).GetPoint1()._x; + if ((*itWall).GetPoint2()._x < xMin) xMin = (*itWall).GetPoint2()._x; + if ((*itWall).GetPoint1()._x > xMax) xMax = (*itWall).GetPoint1()._x; + if ((*itWall).GetPoint2()._x > xMax) xMax = (*itWall).GetPoint2()._x; + // yMin yMax + if ((*itWall).GetPoint1()._y < yMin) yMin = (*itWall).GetPoint1()._y; + if ((*itWall).GetPoint2()._y < yMin) yMin = (*itWall).GetPoint2()._y; + if ((*itWall).GetPoint1()._y > yMax) yMax = (*itWall).GetPoint1()._y; + if ((*itWall).GetPoint2()._y > yMax) yMax = (*itWall).GetPoint2()._y; + } + + const vector<Crossing*>& allCrossings = _subroom->GetAllCrossings(); + for (Crossing* crossPtr : allCrossings) { + if (!crossPtr->IsOpen()) { + _wall.emplace_back( Line( (Line) *crossPtr)); + + if (crossPtr->GetPoint1()._x < xMin) xMin = crossPtr->GetPoint1()._x; + if (crossPtr->GetPoint2()._x < xMin) xMin = crossPtr->GetPoint2()._x; + if (crossPtr->GetPoint1()._x > xMax) xMax = crossPtr->GetPoint1()._x; + if (crossPtr->GetPoint2()._x > xMax) xMax = crossPtr->GetPoint2()._x; + + if (crossPtr->GetPoint1()._y < yMin) yMin = crossPtr->GetPoint1()._y; + if (crossPtr->GetPoint2()._y < yMin) yMin = crossPtr->GetPoint2()._y; + if (crossPtr->GetPoint1()._y > yMax) yMax = crossPtr->GetPoint1()._y; + if (crossPtr->GetPoint2()._y > yMax) yMax = crossPtr->GetPoint2()._y; + } + } + + + //create Rect Grid + _grid = new RectGrid(); + _grid->setBoundaries(xMin, yMin, xMax, yMax); + _grid->setSpacing(hxArg, hyArg); + _grid->createGrid(); + + //create arrays + _gcode = new int[_grid->GetnPoints()]; + _subrooms = new SubRoom*[_grid->GetnPoints()](); // initializes with nullptr + _dist2Wall = new double[_grid->GetnPoints()]; + _speedInitial = new double[_grid->GetnPoints()]; + _modifiedspeed = new double[_grid->GetnPoints()]; + _densityspeed = new double[_grid->GetnPoints()]; + _cost = new double[_grid->GetnPoints()]; + _neggrad = new Point[_grid->GetnPoints()]; + _dirToWall = new Point[_grid->GetnPoints()]; + //trialfield = new Trial[grid->GetnPoints()]; //created with other arrays, but not initialized yet + + _costmap.emplace(-1 , _cost); // enable default ff (closest exit) + _neggradmap.emplace(-1, _neggrad); + + //init grid with -3 as unknown distance to any wall + for(long int i = 0; i < _grid->GetnPoints(); ++i) { + _dist2Wall[i] = -3.; + _cost[i] = -2.; + _gcode[i] = OUTSIDE; + } + + drawLinesOnGrid<double>(_wall, _dist2Wall, 0.); + drawLinesOnGrid<double>(_wall, _cost, -7.); + drawLinesOnGrid<int>(_wall, _gcode, WALL); + drawLinesOnGrid<int>(_exitsFromScope, _gcode, OPEN_TRANSITION); +} + +SubRoom* SubLocalFloorfieldViaFM::isInside(const long int key) { + Point probe = _grid->getPointFromKey(key); + return _subroom->IsInSubRoom(probe) ? _subroom : nullptr; +} \ No newline at end of file diff --git a/routing/ff_router_trips/LocalFloorfieldViaFM.h b/routing/ff_router_trips/LocalFloorfieldViaFM.h new file mode 100644 index 0000000000000000000000000000000000000000..02a3d6ccd0691e2dbc55679f25d8729bfd8f2c90 --- /dev/null +++ b/routing/ff_router_trips/LocalFloorfieldViaFM.h @@ -0,0 +1,86 @@ +/** + * \file LocalFloorfieldViaFM.h + * \date Feb 1, 2016 + * \version v0.8 + + * \copyright <2009-2015> Forschungszentrum Jülich GmbH. All rights reserved. + * + * \section License + * This file is part of JuPedSim. + * + * JuPedSim is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * JuPedSim 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 Lesser General Public License + * along with JuPedSim. If not, see <http://www.gnu.org/licenses/>. + * + * \section Description + * The local FloorfieldViaFM is derived from the FloorfieldViaFM class. It will + * create Floorfields in a room (room consisting of one or more subrooms) for + * each exit. It is to be used in conjunction with a router, yielding intermediate + * goals which are a door in the same room. It uses all the calculations and the + * working on grid form the motherclass, yet it has a different constructor. + **/ + + +#ifndef JPSCORE_LOCALFLOORFIELDVIAFM_H +#define JPSCORE_LOCALFLOORFIELDVIAFM_H + +//#include <vector> +#include <string> +#include "FloorfieldViaFM.h" + +class LocalFloorfieldViaFM : public virtual FloorfieldViaFM { +public: + LocalFloorfieldViaFM(); + LocalFloorfieldViaFM(const Room* const room, const Building* buildingArg, + const double hxArg, const double hyArg, + const double wallAvoidDistance, + const bool useDistancefield); + + void parseRoom(const Room* const roomArg, const double hxArg, const double hyArg); +// void getDirectionToGoalID(const int goalID); + void drawBlockerLines(); + void crossOutOutsideNeighbors(const long int key); + // returns the UID of a subroom for a key of th grid, using the information from _subroomMap + virtual SubRoom* isInside(const long int key); +protected: + const Room* _room; +}; + +/* + NOTE: both parents are derived from FloorfieldViaFM with "public virtual" (it is called "Virtual Inheritance"). This + means that there is only one instance of FloorfieldViaFM in CentrePointLocalFFViaFM, so calls to FlorfieldViaFM's member + variables and functions are not ambiguous. See http://www.programering.com/a/MDOxITMwATk.html for some nice diagrams. + */ +class CentrePointLocalFFViaFM : public CentrePointFFViaFM, public LocalFloorfieldViaFM { +public: + CentrePointLocalFFViaFM(const Room* const room, const Building* buildingArg, + const double hxArg, const double hyArg, + const double wallAvoidDistance, + const bool useDistancefield) : LocalFloorfieldViaFM(room, buildingArg, hxArg, hyArg, wallAvoidDistance, useDistancefield) {}; +}; + +class SubLocalFloorfieldViaFM : public FloorfieldViaFM { +public: + SubLocalFloorfieldViaFM(); + SubLocalFloorfieldViaFM(SubRoom* const subroom, const Building* buildingArg, + const double hxArg, const double hyArg, + const double wallAvoidDistance, + const bool useDistancefield); + + void parseRoom(SubRoom* const subroomArg, const double hxArg, const double hyArg); + void getDirectionToDestination (Pedestrian* ped, Point& direction); + void getDirectionToGoalID(const int goalID); + virtual SubRoom* isInside(const long int key); +protected: + SubRoom* _subroom; +}; + +#endif //JPSCORE_LOCALFLOORFIELDVIAFM_H diff --git a/routing/ff_router_trips/UnivFFviaFM.cpp b/routing/ff_router_trips/UnivFFviaFM.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6a6e23b0fe32d9f71036754a6f1b33d98d530853 --- /dev/null +++ b/routing/ff_router_trips/UnivFFviaFM.cpp @@ -0,0 +1,1881 @@ +// +// Created by arne on 5/9/17. +// +#define NOMINMAX +#include <unordered_set> +#include "UnivFFviaFM.h" +#include "../../geometry/Line.h" +#include "../../geometry/Building.h" +#include "../../geometry/SubRoom.h" +#include "../../pedestrian/Pedestrian.h" +#include "mesh/RectGrid.h" + + +UnivFFviaFM::~UnivFFviaFM() { + if (_grid) delete _grid; + //size_t speedsize = _speedFieldSelector.size(); + for (auto speedPtr : _speedFieldSelector) { + if (speedPtr) delete[] speedPtr; + } + if (_gridCode) delete[] _gridCode; + if (_subrooms) delete[] _subrooms; + + std::map<int, double*>::reverse_iterator delIterCost; + + for (delIterCost = _costFieldWithKey.rbegin(); + delIterCost != _costFieldWithKey.rend(); + ++delIterCost) { + delete[] delIterCost->second; + } + std::map<int, Point*>::reverse_iterator delIterDir; + for (delIterDir = _directionFieldWithKey.rbegin(); + delIterDir != _directionFieldWithKey.rend(); + ++delIterDir) { + delete[] delIterDir->second; + } +} + +UnivFFviaFM::UnivFFviaFM(Room* r, Building* b, double hx, double wallAvoid, bool useWallDistances) + : UnivFFviaFM(r, b->GetConfig(), hx, wallAvoid, useWallDistances) { + _building = b; +} + +UnivFFviaFM::UnivFFviaFM(SubRoom* sr, Building* b, double hx, double wallAvoid, bool useWallDistances) + : UnivFFviaFM(sr, b->GetConfig(), hx, wallAvoid, useWallDistances) { + _building = b; +} + +UnivFFviaFM::UnivFFviaFM(Room* r, Configuration* const conf, double hx, double wallAvoid, bool useWallDistances) + : UnivFFviaFM(r, conf, hx, wallAvoid, useWallDistances, std::vector<int>()){ +} + +UnivFFviaFM::UnivFFviaFM(Room* roomArg, Configuration* const confArg, double hx, double wallAvoid, bool useWallDistances, std::vector<int> wantedDoors) { + //build the vector with walls(wall or obstacle), the map with <UID, Door(Cross or Trans)>, the vector with targets(UIDs) + //then call other constructor including the mode + + _configuration = confArg; + _scope = FF_ROOM_SCALE; + _room = roomArg->GetID(); + std::vector<Line> lines; + std::map<int, Line> tmpDoors; + Line anyDoor = Line{}; + for (auto& subroomMap : roomArg->GetAllSubRooms()) { + SubRoom* subRoomPtr = subroomMap.second.get(); + std::vector<Wall> walls = std::vector<Wall>(subRoomPtr->GetAllWalls()); + for (auto& wall : walls) { + lines.emplace_back((Line)wall); + } + + std::vector<Obstacle*> tmpObsPtrVec = subRoomPtr->GetAllObstacles(); + for (Obstacle* ptrObs : tmpObsPtrVec) { + const std::vector<Wall> obsWalls = ptrObs->GetAllWalls(); + for (auto& owall : obsWalls) { + lines.emplace_back((Line)owall); + } + } + + const std::vector<Crossing*> tmpCross = subRoomPtr->GetAllCrossings(); + const std::vector<Transition*> tmpTrans = subRoomPtr->GetAllTransitions(); + + int uidNotConst = 0; + bool isOpen = false; + for (auto& cross : tmpCross) { + uidNotConst = cross->GetUniqueID(); + isOpen = cross->IsOpen(); + if (!isOpen) { + //will be added twice! is it a problem? + lines.emplace_back((Line)*cross); + } else { + anyDoor = Line{*cross}; + if (tmpDoors.count(uidNotConst) == 0) { + tmpDoors.emplace(std::make_pair(uidNotConst, (Line) *cross)); + } + } + } + for (auto& trans : tmpTrans) { + uidNotConst = trans->GetUniqueID(); + isOpen = trans->IsOpen(); + if (!isOpen) { + //will be added twice! is it a problem? + lines.emplace_back((Line)*trans); + } else { + anyDoor = Line{*trans}; + if (tmpDoors.count(uidNotConst) == 0) { + tmpDoors.emplace(std::make_pair(uidNotConst, (Line) *trans)); + } + } + } + //find insidePoint and save it, together with UID + Point normalVec = anyDoor.NormalVec(); + double length = normalVec.Norm(); + Point midPoint = anyDoor.GetCentre(); + Point candidate01 = midPoint + (normalVec * 0.25); + Point candidate02 = midPoint - (normalVec * 0.25); + if (subRoomPtr->IsInSubRoom(candidate01)) { + _subRoomPtrTOinsidePoint.emplace(std::make_pair(subRoomPtr, candidate01)); + } else { + //candidate = candidate - (normalVec * 0.25); + if (subRoomPtr->IsInSubRoom(candidate02)) { + _subRoomPtrTOinsidePoint.emplace(std::make_pair(subRoomPtr, candidate02)); + } else { + Log->Write("ERROR:\t In UnivFF InsidePoint Analysis"); + bool a = subRoomPtr->IsInSubRoom(candidate01); + bool b = subRoomPtr->IsInSubRoom(candidate02); + a = b && a; //ignore this line. only to have a codeline after initialization of bools (to place a breakpoint) + } + } + //_subroomUIDtoSubRoomPtr.emplace(std::make_pair(subRoomPtr->GetUID(), subRoomPtr)); + } + //this will interpret "useWallDistances" as best as possible. Users should clearify with "setSpeedMode" before calling "AddTarget" + if (useWallDistances) { + create(lines, tmpDoors, wantedDoors, FF_WALL_AVOID, hx, wallAvoid, useWallDistances); + } else { + create(lines, tmpDoors, wantedDoors, FF_HOMO_SPEED, hx, wallAvoid, useWallDistances); + } +} + +UnivFFviaFM::UnivFFviaFM(SubRoom* sr, Configuration* const conf, double hx, double wallAvoid, bool useWallDistances) + : UnivFFviaFM(sr, conf, hx, wallAvoid, useWallDistances, std::vector<int>()){ +} + +UnivFFviaFM::UnivFFviaFM(SubRoom* subRoomArg, Configuration* const confArg, double hx, double wallAvoid, bool useWallDistances, std::vector<int> wantedDoors) { + //build the vector with walls(wall or obstacle), the map with <UID, Door(Cross or Trans)>, the vector with targets(UIDs) + //then call other constructor including the mode + _configuration = confArg; + _scope = FF_SUBROOM_SCALE; + _room = subRoomArg->GetRoomID(); + std::vector<Line> lines; + std::map<int, Line> tmpDoors; + + std::vector<Wall> walls = std::vector<Wall> (subRoomArg->GetAllWalls()); + for (auto& wall : walls) { + lines.emplace_back((Line)wall); + } + + std::vector<Obstacle*> tmpObsPtrVec = subRoomArg->GetAllObstacles(); + for (Obstacle* ptrObs : tmpObsPtrVec) { + const std::vector<Wall> obsWalls = ptrObs->GetAllWalls(); + for (auto& owall : obsWalls) { + lines.emplace_back((Line)owall); + } + } + + const std::vector<Crossing*> tmpCross = subRoomArg->GetAllCrossings(); + const std::vector<Transition*> tmpTrans = subRoomArg->GetAllTransitions(); + + int uidNotConst = 0; + bool isOpen = false; + for (auto& cross : tmpCross) { + uidNotConst = cross->GetUniqueID(); + isOpen = cross->IsOpen(); + if (!isOpen) { + lines.emplace_back((Line)*cross); + } else { + tmpDoors.emplace(std::make_pair(uidNotConst, (Line) *cross)); + } + } + for (auto& trans : tmpTrans) { + uidNotConst = trans->GetUniqueID(); + isOpen = trans->IsOpen(); + if (!isOpen) { + lines.emplace_back((Line)*trans); + } else { + tmpDoors.emplace(std::make_pair(uidNotConst, (Line) *trans)); + } + } + + //find insidePoint and save it, together with UID + Line anyDoor = Line{(--tmpDoors.end())->second}; + Point normalVec = anyDoor.NormalVec(); + double length = normalVec.Norm(); + Point midPoint = anyDoor.GetCentre(); + Point candidate01 = midPoint + (normalVec * 0.25); + Point candidate02 = midPoint - (normalVec * 0.25); + if (subRoomArg->IsInSubRoom(candidate01)) { + _subRoomPtrTOinsidePoint.emplace(std::make_pair(subRoomArg, candidate01)); + } else { + //candidate = candidate - (normalVec * 0.25); + if (subRoomArg->IsInSubRoom(candidate02)) { + _subRoomPtrTOinsidePoint.emplace(std::make_pair(subRoomArg, candidate02)); + } else { + Log->Write("ERROR:\t In UnivFF InsidePoint Analysis"); + bool a = subRoomArg->IsInSubRoom(candidate01); + bool b = subRoomArg->IsInSubRoom(candidate02); + a = b && a; + } + } + + //_subroomUIDtoSubRoomPtr.emplace(std::make_pair(subRoomArg->GetUID(), subRoomArg)); + + //this will interpret "useWallDistances" as best as possible. Users should clearify with "setSpeedMode" before calling "AddTarget" + if (useWallDistances) { + create(lines, tmpDoors, wantedDoors, FF_WALL_AVOID, hx, wallAvoid, useWallDistances); + } else { + create(lines, tmpDoors, wantedDoors, FF_HOMO_SPEED, hx, wallAvoid, useWallDistances); + } +} + +void UnivFFviaFM::create(std::vector<Line>& walls, std::map<int, Line>& doors, std::vector<int> targetUIDs, int mode, + double spacing, double wallAvoid, bool useWallDistances) { + + _wallAvoidDistance = wallAvoid; + _useWallDistances = useWallDistances; + _speedmode = mode; + + //find circumscribing rectangle (x_min/max, y_min/max) //create RectGrid + createRectGrid(walls, doors, spacing); + _nPoints = _grid->GetnPoints(); + + //allocate _gridCode and _speedFieldSelector and initialize them ("draw" walls and doors) + _gridCode = new int[_nPoints]; + processGeometry(walls, doors); + _speedFieldSelector.emplace(_speedFieldSelector.begin()+INITIAL_SPEED, new double[_nPoints]); + std::fill(_speedFieldSelector[INITIAL_SPEED], _speedFieldSelector[INITIAL_SPEED]+_nPoints, 1.0); + + _speedFieldSelector.emplace(_speedFieldSelector.begin()+REDU_WALL_SPEED, nullptr); + _speedFieldSelector.emplace(_speedFieldSelector.begin()+PED_SPEED, nullptr); + + //mark Inside areas (dont write outside areas) + for (auto subRoomPointPair : _subRoomPtrTOinsidePoint) { + markSubroom(subRoomPointPair.second, subRoomPointPair.first); + } + + //allocate _modifiedSpeed + if ((_speedmode == FF_WALL_AVOID) || (useWallDistances)) { + double* cost_alias_walldistance = new double[_nPoints]; + _costFieldWithKey[0] = cost_alias_walldistance; + Point* gradient_alias_walldirection = new Point[_nPoints]; + _directionFieldWithKey[0] = gradient_alias_walldirection; + + //create wall distance field + //init costarray + for (int i = 0; i < _nPoints; ++i) { + if (_gridCode[i] == WALL) { + cost_alias_walldistance[i] = magicnum(WALL_ON_COSTARRAY); + } else { + cost_alias_walldistance[i] = magicnum(UNKNOWN_COST); + } + } + drawLinesOnWall(walls, cost_alias_walldistance, magicnum(TARGET_REGION)); + calcDF(cost_alias_walldistance, gradient_alias_walldirection, _speedFieldSelector[INITIAL_SPEED]); + //_uids.emplace_back(0); + + double* temp_reduWallSpeed = new double[_nPoints]; + if (_speedFieldSelector[REDU_WALL_SPEED]) { //free memory before overwriting + delete[] _speedFieldSelector[REDU_WALL_SPEED]; + } + _speedFieldSelector[REDU_WALL_SPEED] = temp_reduWallSpeed; + //init _reducedWallSpeed by using distance field + createReduWallSpeed(temp_reduWallSpeed); + } + + // parallel call + if (!targetUIDs.empty()) { + addTargetsParallel(targetUIDs); + } +} + +void UnivFFviaFM::createRectGrid(std::vector<Line>& walls, std::map<int, Line>& doors, double spacing) { + double x_min = DBL_MAX; double x_max = DBL_MIN; + double y_min = DBL_MAX; double y_max = DBL_MIN; + + for(auto& wall : walls) { + if (wall.GetPoint1()._x < x_min) x_min = wall.GetPoint1()._x; + if (wall.GetPoint1()._y < y_min) y_min = wall.GetPoint1()._y; + if (wall.GetPoint2()._x < x_min) x_min = wall.GetPoint2()._x; + if (wall.GetPoint2()._y < y_min) y_min = wall.GetPoint2()._y; + + if (wall.GetPoint1()._x > x_max) x_max = wall.GetPoint1()._x; + if (wall.GetPoint1()._y > y_max) y_max = wall.GetPoint1()._y; + if (wall.GetPoint2()._x > x_max) x_max = wall.GetPoint2()._x; + if (wall.GetPoint2()._y > y_max) y_max = wall.GetPoint2()._y; + } + + for(auto& doorPair:doors) { + Line& door = doorPair.second; + if (door.GetPoint1()._x < x_min) x_min = door.GetPoint1()._x; + if (door.GetPoint1()._y < y_min) y_min = door.GetPoint1()._y; + if (door.GetPoint2()._x < x_min) x_min = door.GetPoint2()._x; + if (door.GetPoint2()._y < y_min) y_min = door.GetPoint2()._y; + + if (door.GetPoint1()._x > x_max) x_max = door.GetPoint1()._x; + if (door.GetPoint1()._y > y_max) y_max = door.GetPoint1()._y; + if (door.GetPoint2()._x > x_max) x_max = door.GetPoint2()._x; + if (door.GetPoint2()._y > y_max) y_max = door.GetPoint2()._y; + } + + //create Rect Grid + _grid = new RectGrid(); + _grid->setBoundaries(x_min, y_min, x_max, y_max); + _grid->setSpacing(spacing, spacing); + _grid->createGrid(); +} + +void UnivFFviaFM::processGeometry(std::vector<Line>&walls, std::map<int, Line>& doors) { + for (int i = 0; i < _nPoints; ++i) { + _gridCode[i] = OUTSIDE; + } + + for (auto mapentry : doors) { + _doors.insert(mapentry); + } + //_doors = doors; + + drawLinesOnGrid<int>(walls, _gridCode, WALL); + drawLinesOnGrid(doors, _gridCode); //UIDs of doors will be drawn on _gridCode +} + +void UnivFFviaFM::markSubroom(const Point& insidePoint, SubRoom* const value) { + //value must not be nullptr. it would lead to infinite loop + if (!value) return; + + if(!_grid->includesPoint(insidePoint)) return; + + //alloc mem if needed + if (!_subrooms) { + _subrooms = new SubRoom*[_nPoints]; + for (long i = 0; i < _nPoints; ++i) { + _subrooms[i] = nullptr; + } + } + + //init start + _subrooms[_grid->getKeyAtPoint(insidePoint)] = value; + _gridCode[_grid->getKeyAtPoint(insidePoint)] = INSIDE; + + std::unordered_set<long> wavefront; + wavefront.reserve(_nPoints); + + directNeighbor _neigh = _grid->getNeighbors(_grid->getKeyAtPoint(insidePoint)); + long aux = _neigh.key[0]; + if ((aux != -2) && (_gridCode[aux] == INSIDE || _gridCode[aux] == OUTSIDE)) { + wavefront.insert(aux); + _subrooms[aux] = value; + } + + aux = _neigh.key[1]; + if ((aux != -2) && (_gridCode[aux] == INSIDE || _gridCode[aux] == OUTSIDE)) { + wavefront.insert(aux); + _subrooms[aux] = value; + } + + aux = _neigh.key[2]; + if ((aux != -2) && (_gridCode[aux] == INSIDE || _gridCode[aux] == OUTSIDE)) { + wavefront.insert(aux); + _subrooms[aux] = value; + } + + aux = _neigh.key[3]; + if ((aux != -2) && (_gridCode[aux] == INSIDE || _gridCode[aux] == OUTSIDE)) { + wavefront.insert(aux); + _subrooms[aux] = value; + } + + while(!wavefront.empty()) { + long current = *(wavefront.begin()); + wavefront.erase(current); + _gridCode[current] = INSIDE; + + _neigh = _grid->getNeighbors(current); + aux = _neigh.key[0]; + if ((aux != -2) && (_gridCode[aux] == INSIDE || _gridCode[aux] == OUTSIDE) && _subrooms[aux] == nullptr) { + wavefront.insert(aux); + _subrooms[aux] = value; + } + + aux = _neigh.key[1]; + if ((aux != -2) && (_gridCode[aux] == INSIDE || _gridCode[aux] == OUTSIDE) && _subrooms[aux] == nullptr) { + wavefront.insert(aux); + _subrooms[aux] = value; + } + + aux = _neigh.key[2]; + if ((aux != -2) && (_gridCode[aux] == INSIDE || _gridCode[aux] == OUTSIDE) && _subrooms[aux] == nullptr) { + wavefront.insert(aux); + _subrooms[aux] = value; + } + + aux = _neigh.key[3]; + if ((aux != -2) && (_gridCode[aux] == INSIDE || _gridCode[aux] == OUTSIDE) && _subrooms[aux] == nullptr) { + wavefront.insert(aux); + _subrooms[aux] = value; + } + } +} + +void UnivFFviaFM::createReduWallSpeed(double* reduWallSpeed){ + double factor = 1/_wallAvoidDistance; + double* wallDstAlias = _costFieldWithKey[0]; + + for (long int i = 0; i < _nPoints; ++i) { + if (wallDstAlias[i] > 0.) { + reduWallSpeed[i] = (wallDstAlias[i] > _wallAvoidDistance) ? 1.0 : (factor * wallDstAlias[i]); + } + } +} + +void UnivFFviaFM::recreateAllForQuickest() { + //allocate if neccessary (should not be!) + for (int doorUID : _uids) { + if (!_costFieldWithKey[doorUID]) { + _costFieldWithKey[doorUID] = new double[_nPoints]; + } + if (_user == DISTANCE_MEASUREMENTS_ONLY) { + if (_directionFieldWithKey.count(doorUID) != 0 && _directionFieldWithKey[doorUID]){ + delete[] _directionFieldWithKey[doorUID]; + } + _directionFieldWithKey[doorUID] = nullptr; + } + if (_user == DISTANCE_AND_DIRECTIONS_USED) { + //check if not in map OR (in map but) nullptr) + if ((_directionFieldWithKey.count(doorUID) == 0) || (!_directionFieldWithKey[doorUID])) { + _directionFieldWithKey[doorUID] = new Point[_nPoints]; + } + } + } + + //parallel region +#pragma omp parallel + { +#pragma omp for + for (signed int i = 0; i < _doors.size(); ++i) { + auto doorPair = _doors.begin(); + std::advance(doorPair, i); + addTarget(doorPair->first, _costFieldWithKey[doorPair->first], _directionFieldWithKey[doorPair->first]); + } + }; +} + +void UnivFFviaFM::createPedSpeed(Pedestrian *const *pedsArg, int nsize, int modechoice, double radius) { + long int delta = radius / _grid->Gethx(); + long int posIndex = 0; + long int pos_i = 0; + long int pos_j = 0; + long int i_start = 0; + long int j_start = 0; + long int i_end = 0; + long int j_end = 0; + long int iStride = _grid->GetiMax(); + double indexDistance = 0.0; + double newWaveSpeed = 0.0; + +// if (nsize == 0) { +// Log->Write("WARNING: \tcreatePedSpeed: nsize is ZERO"); +// } else { +// Log->Write("INFO: \t\tNumber of Peds used in createPedSpeed: %d",nsize); +// } + + if ((modechoice == quickest) && (!_speedFieldSelector[PED_SPEED])) { + _speedFieldSelector[PED_SPEED] = new double[_grid->GetnPoints()]; + } + + //we assume, that this function is only used by router and is not using REDU_WALL_SPEED + for (long int i = 0; i < _grid->GetnPoints(); ++i) { + _speedFieldSelector[PED_SPEED][i] = _speedFieldSelector[INITIAL_SPEED][i]; + } + if (_speedFieldSelector[REDU_WALL_SPEED] && _mode == LINESEGMENT) { //indicates, that the direction strat is using it + for (long int i = 0; i < _grid->GetnPoints(); ++i) { + _speedFieldSelector[PED_SPEED][i] = _speedFieldSelector[REDU_WALL_SPEED][i]; + } + } + + for (int i = 0; i < nsize; ++i) { + //the following check is not 3D proof, we require the caller of this function to provide a list with "valid" + //pedestrian-pointer + if (!_grid->includesPoint(pedsArg[i]->GetPos())) { + continue; + } + + newWaveSpeed = pedsArg[i]->GetEllipse().GetV().Norm()/pedsArg[i]->GetEllipse().GetV0(); // (current speed)/(desired speed) + newWaveSpeed *= 0.8; + if (newWaveSpeed < 0.1) { + newWaveSpeed = 0.1; + } + posIndex = _grid->getKeyAtPoint(pedsArg[i]->GetPos()); + pos_i = _grid->get_i_fromKey(posIndex); + pos_j = _grid->get_j_fromKey(posIndex); + + i_start = ((pos_i - delta) < 0) ? 0 : (pos_i - delta); + i_end = ((pos_i + delta) >= iStride) ? iStride-1 : (pos_i + delta); + + j_start = ((pos_j - delta) < 0) ? 0 : (pos_j - delta); + j_end = ((pos_j + delta) >= _grid->GetjMax()) ? _grid->GetjMax()-1 : (pos_j + delta); + + for (long int curr_i = i_start; curr_i < i_end; ++curr_i) { + for (long int curr_j = j_start; curr_j < j_end; ++curr_j) { + //indexDistance holds the square + indexDistance = ( (curr_i - pos_i)*(curr_i - pos_i) + (curr_j - pos_j)*(curr_j - pos_j) ); + + if (indexDistance < (delta*delta)) { + //_speedFieldSelector[PED_SPEED][curr_j*iStride + curr_i] = 0.2; + _speedFieldSelector[PED_SPEED][curr_j*iStride + curr_i] = std::min(newWaveSpeed, _speedFieldSelector[PED_SPEED][curr_j*iStride + curr_i]); + } + } + } + } +} + +void UnivFFviaFM::finalizeTargetLine(const int uid, const Line& line, Point* target, Point& value) { + // i~x; j~y; +//http://stackoverflow.com/questions/10060046/drawing-lines-with-bresenhams-line-algorithm +//src in answer of "Avi"; adapted to fit this application + + //grid handeling local vars: + long int iMax = _grid->GetiMax(); + + long int iStart, iEnd; + long int jStart, jEnd; + long int iDot, jDot; + long int key; + long int deltaX, deltaY, deltaX1, deltaY1, px, py, xe, ye, i; //Bresenham Algorithm + + long int goodneighbor; + directNeighbor neigh; + + + key = _grid->getKeyAtPoint(line.GetPoint1()); + iStart = (long) _grid->get_i_fromKey(key); + jStart = (long) _grid->get_j_fromKey(key); + + key = _grid->getKeyAtPoint(line.GetPoint2()); + iEnd = (long) _grid->get_i_fromKey(key); + jEnd = (long) _grid->get_j_fromKey(key); + + deltaX = (int) (iEnd - iStart); + deltaY = (int) (jEnd - jStart); + deltaX1 = abs( (int) (iEnd - iStart)); + deltaY1 = abs( (int) (jEnd - jStart)); + + px = 2*deltaY1 - deltaX1; + py = 2*deltaX1 - deltaY1; + + if(deltaY1<=deltaX1) { + if(deltaX>=0) { + iDot = iStart; + jDot = jStart; + xe = iEnd; + } else { + iDot = iEnd; + jDot = jEnd; + xe = iStart; + } + if (_gridCode[jDot*iMax + iDot] == uid) { + /* //find a good neighborvalue + neigh = _grid->getNeighbors(jDot*iMax + iDot); + if ((neigh.key[0] >= 0) && (_gridCode[neigh.key[0]] == INSIDE)) { + goodneighbor = neigh.key[0]; + } else if ((neigh.key[1] >= 0) && (_gridCode[neigh.key[1]] == INSIDE)) { + goodneighbor = neigh.key[1]; + } else if ((neigh.key[2] >= 0) && (_gridCode[neigh.key[2]] == INSIDE)) { + goodneighbor = neigh.key[2]; + } else if ((neigh.key[3] >= 0) && (_gridCode[neigh.key[3]] == INSIDE)) { + goodneighbor = neigh.key[3]; + } else { + //ERROR - should have an inside neighbor + Log->Write("ERROR:\t In finalizeTargetLine"); + } + //write the value on targetline + if ((target[goodneighbor]._x == 0.) && (target[goodneighbor]._y == 0.)) { + //ERROR - should have a true vector + Log->Write("ERROR:\t (0;0) In finalizeTargetLine"); + }*/ + target[jDot * iMax + iDot] = value; + } else if (_gridCode[jDot*iMax + iDot] == WALL) { + //do nothing + } else { + target[jDot * iMax + iDot] = value; + //Log->Write("ERROR:\t in finalizingTargetLine"); + } + for (i=0; iDot < xe; ++i) { + ++iDot; + if (px < 0) { + px += 2 * deltaY1; + } else { + if ((deltaX < 0 && deltaY < 0) || (deltaX > 0 && deltaY > 0)) { + ++jDot; + } else { + --jDot; + } + px += 2 * (deltaY1 - deltaX1); + } + if (_gridCode[jDot * iMax + iDot] == uid) { + /*//find a good neighborvalue + neigh = _grid->getNeighbors(jDot*iMax + iDot); + if ((neigh.key[0] >= 0) && (_gridCode[neigh.key[0]] == INSIDE)) { + goodneighbor = neigh.key[0]; + } else if ((neigh.key[1] >= 0) && (_gridCode[neigh.key[1]] == INSIDE)) { + goodneighbor = neigh.key[1]; + } else if ((neigh.key[2] >= 0) && (_gridCode[neigh.key[2]] == INSIDE)) { + goodneighbor = neigh.key[2]; + } else if ((neigh.key[3] >= 0) && (_gridCode[neigh.key[3]] == INSIDE)) { + goodneighbor = neigh.key[3]; + } else { + //ERROR - should have an inside neighbor + Log->Write("ERROR:\t In finalizeTargetLine"); + } + //write the value on targetline + if ((target[goodneighbor]._x == 0.) && (target[goodneighbor]._y == 0.)) { + //ERROR - should have a true vector + Log->Write("ERROR:\t (0;0) In finalizeTargetLine"); + }*/ + target[jDot * iMax + iDot] = value; + } else if (_gridCode[jDot*iMax + iDot] == WALL) { + //do nothing + } else { + target[jDot * iMax + iDot] = value; + //Log->Write("ERROR:\t in finalizingTargetLine"); + } + } + } else { + if(deltaY>=0) { + iDot = iStart; + jDot = jStart; + ye = jEnd; + } else { + iDot = iEnd; + jDot = jEnd; + ye = jStart; + } + if (_gridCode[jDot*iMax + iDot] == uid) { + /*//find a good neighborvalue + neigh = _grid->getNeighbors(jDot*iMax + iDot); + if ((neigh.key[0] >= 0) && (_gridCode[neigh.key[0]] == INSIDE)) { + goodneighbor = neigh.key[0]; + } else if ((neigh.key[1] >= 0) && (_gridCode[neigh.key[1]] == INSIDE)) { + goodneighbor = neigh.key[1]; + } else if ((neigh.key[2] >= 0) && (_gridCode[neigh.key[2]] == INSIDE)) { + goodneighbor = neigh.key[2]; + } else if ((neigh.key[3] >= 0) && (_gridCode[neigh.key[3]] == INSIDE)) { + goodneighbor = neigh.key[3]; + } else { + //ERROR - should have an inside neighbor + Log->Write("ERROR:\t In finalizeTargetLine"); + } + //write the value on targetline + if ((target[goodneighbor]._x == 0.) && (target[goodneighbor]._y == 0.)) { + //ERROR - should have a true vector + Log->Write("ERROR:\t (0;0) In finalizeTargetLine"); + }*/ + target[jDot * iMax + iDot] = value; + } else if (_gridCode[jDot*iMax + iDot] == WALL) { + //do nothing + } else { + target[jDot * iMax + iDot] = value; + //Log->Write("ERROR:\t in finalizingTargetLine"); + } + for(i=0; jDot<ye; ++i) { + ++jDot; + if (py<=0) { + py+=2*deltaX1; + } else { + if((deltaX<0 && deltaY<0) || (deltaX>0 && deltaY>0)) { + ++iDot; + } else { + --iDot; + } + py+=2*(deltaX1-deltaY1); + } + if (_gridCode[jDot*iMax + iDot] == uid) { + /*//find a good neighborvalue + neigh = _grid->getNeighbors(jDot*iMax + iDot); + if ((neigh.key[0] >= 0) && (_gridCode[neigh.key[0]] == INSIDE)) { + goodneighbor = neigh.key[0]; + } else if ((neigh.key[1] >= 0) && (_gridCode[neigh.key[1]] == INSIDE)) { + goodneighbor = neigh.key[1]; + } else if ((neigh.key[2] >= 0) && (_gridCode[neigh.key[2]] == INSIDE)) { + goodneighbor = neigh.key[2]; + } else if ((neigh.key[3] >= 0) && (_gridCode[neigh.key[3]] == INSIDE)) { + goodneighbor = neigh.key[3]; + } else { + //ERROR - should have an inside neighbor + Log->Write("ERROR:\t In finalizeTargetLine"); + } + //write the value on targetline + if ((target[goodneighbor]._x == 0.) && (target[goodneighbor]._y == 0.)) { + //ERROR - should have a true vector + Log->Write("ERROR:\t (0;0) In finalizeTargetLine"); + }*/ + target[jDot * iMax + iDot] = value; + } else if (_gridCode[jDot*iMax + iDot] == WALL) { + //do nothing + } else { + target[jDot * iMax + iDot] = value; + //Log->Write("ERROR:\t in finalizingTargetLine"); + } + } + } +} + +void UnivFFviaFM::drawLinesOnGrid(std::map<int, Line>& doors, int *const grid) { + for (auto&& doorPair : doors) { + int tempUID = doorPair.first; + Line tempDoorLine = Line(doorPair.second); + drawLinesOnGrid(tempDoorLine, grid, tempUID); + } +} + +template <typename T> +void UnivFFviaFM::drawLinesOnGrid(std::vector<Line>& wallArg, T* const target, const T value) { //no init, plz init elsewhere + + for (auto& line : wallArg) { + drawLinesOnGrid(line, target, value); + } //loop over all walls + +} //drawLinesOnGrid + +template <typename T> +void UnivFFviaFM::drawLinesOnGrid(Line& line, T* const target, const T value) { //no init, plz init elsewhere +// i~x; j~y; +//http://stackoverflow.com/questions/10060046/drawing-lines-with-bresenhams-line-algorithm +//src in answer of "Avi"; adapted to fit this application + + //grid handeling local vars: + long int iMax = _grid->GetiMax(); + + long int iStart, iEnd; + long int jStart, jEnd; + long int iDot, jDot; + long int key; + long int deltaX, deltaY, deltaX1, deltaY1, px, py, xe, ye, i; //Bresenham Algorithm + + + key = _grid->getKeyAtPoint(line.GetPoint1()); + iStart = (long) _grid->get_i_fromKey(key); + jStart = (long) _grid->get_j_fromKey(key); + + key = _grid->getKeyAtPoint(line.GetPoint2()); + iEnd = (long) _grid->get_i_fromKey(key); + jEnd = (long) _grid->get_j_fromKey(key); + + deltaX = (int) (iEnd - iStart); + deltaY = (int) (jEnd - jStart); + deltaX1 = abs( (int) (iEnd - iStart)); + deltaY1 = abs( (int) (jEnd - jStart)); + + px = 2*deltaY1 - deltaX1; + py = 2*deltaX1 - deltaY1; + + if(deltaY1<=deltaX1) { + if(deltaX>=0) { + iDot = iStart; + jDot = jStart; + xe = iEnd; + } else { + iDot = iEnd; + jDot = jEnd; + xe = iStart; + } + if ((_gridCode[jDot*iMax + iDot] != WALL) && (_gridCode[jDot*iMax + iDot] != CLOSED_CROSSING) && (_gridCode[jDot*iMax + iDot] != CLOSED_TRANSITION)) { + target[jDot * iMax + iDot] = value; + } + for (i=0; iDot < xe; ++i) { + ++iDot; + if(px<0) { + px+=2*deltaY1; + } else { + if((deltaX<0 && deltaY<0) || (deltaX>0 && deltaY>0)) { + ++jDot; + } else { + --jDot; + } + px+=2*(deltaY1-deltaX1); + } + if ((_gridCode[jDot*iMax + iDot] != WALL) && (_gridCode[jDot*iMax + iDot] != CLOSED_CROSSING) && (_gridCode[jDot*iMax + iDot] != CLOSED_TRANSITION)) { + target[jDot * iMax + iDot] = value; + } + } + } else { + if(deltaY>=0) { + iDot = iStart; + jDot = jStart; + ye = jEnd; + } else { + iDot = iEnd; + jDot = jEnd; + ye = jStart; + } + if ((_gridCode[jDot*iMax + iDot] != WALL) && (_gridCode[jDot*iMax + iDot] != CLOSED_CROSSING) && (_gridCode[jDot*iMax + iDot] != CLOSED_TRANSITION)) { + target[jDot * iMax + iDot] = value; + } + for(i=0; jDot<ye; ++i) { + ++jDot; + if (py<=0) { + py+=2*deltaX1; + } else { + if((deltaX<0 && deltaY<0) || (deltaX>0 && deltaY>0)) { + ++iDot; + } else { + --iDot; + } + py+=2*(deltaX1-deltaY1); + } + if ((_gridCode[jDot*iMax + iDot] != WALL) && (_gridCode[jDot*iMax + iDot] != CLOSED_CROSSING) && (_gridCode[jDot*iMax + iDot] != CLOSED_TRANSITION)) { + target[jDot * iMax + iDot] = value; + } + } + } +} //drawLinesOnGrid + +template <typename T> +void UnivFFviaFM::drawLinesOnWall(std::vector<Line>& wallArg, T* const target, const T value) { //no init, plz init elsewhere + + for (auto& line : wallArg) { + drawLinesOnWall(line, target, value); + } //loop over all walls + +} //drawLinesOnWall + +template <typename T> +void UnivFFviaFM::drawLinesOnWall(Line& line, T* const target, const T value) { //no init, plz init elsewhere +// i~x; j~y; +//http://stackoverflow.com/questions/10060046/drawing-lines-with-bresenhams-line-algorithm +//src in answer of "Avi"; adapted to fit this application + + //grid handeling local vars: + long int iMax = _grid->GetiMax(); + + long int iStart, iEnd; + long int jStart, jEnd; + long int iDot, jDot; + long int key; + long int deltaX, deltaY, deltaX1, deltaY1, px, py, xe, ye, i; //Bresenham Algorithm + + + key = _grid->getKeyAtPoint(line.GetPoint1()); + iStart = (long) _grid->get_i_fromKey(key); + jStart = (long) _grid->get_j_fromKey(key); + + key = _grid->getKeyAtPoint(line.GetPoint2()); + iEnd = (long) _grid->get_i_fromKey(key); + jEnd = (long) _grid->get_j_fromKey(key); + + deltaX = (int) (iEnd - iStart); + deltaY = (int) (jEnd - jStart); + deltaX1 = abs( (int) (iEnd - iStart)); + deltaY1 = abs( (int) (jEnd - jStart)); + + px = 2*deltaY1 - deltaX1; + py = 2*deltaX1 - deltaY1; + + if(deltaY1<=deltaX1) { + if(deltaX>=0) { + iDot = iStart; + jDot = jStart; + xe = iEnd; + } else { + iDot = iEnd; + jDot = jEnd; + xe = iStart; + } + if ((_gridCode[jDot*iMax + iDot] != CLOSED_CROSSING) && (_gridCode[jDot*iMax + iDot] != CLOSED_TRANSITION)) { + target[jDot * iMax + iDot] = value; + } + for (i=0; iDot < xe; ++i) { + ++iDot; + if(px<0) { + px+=2*deltaY1; + } else { + if((deltaX<0 && deltaY<0) || (deltaX>0 && deltaY>0)) { + ++jDot; + } else { + --jDot; + } + px+=2*(deltaY1-deltaX1); + } + if ((_gridCode[jDot*iMax + iDot] != CLOSED_CROSSING) && (_gridCode[jDot*iMax + iDot] != CLOSED_TRANSITION)) { + target[jDot * iMax + iDot] = value; + } + } + } else { + if(deltaY>=0) { + iDot = iStart; + jDot = jStart; + ye = jEnd; + } else { + iDot = iEnd; + jDot = jEnd; + ye = jStart; + } + if ((_gridCode[jDot*iMax + iDot] != CLOSED_CROSSING) && (_gridCode[jDot*iMax + iDot] != CLOSED_TRANSITION)) { + target[jDot * iMax + iDot] = value; + } + for(i=0; jDot<ye; ++i) { + ++jDot; + if (py<=0) { + py+=2*deltaX1; + } else { + if((deltaX<0 && deltaY<0) || (deltaX>0 && deltaY>0)) { + ++iDot; + } else { + --iDot; + } + py+=2*(deltaX1-deltaY1); + } + if ((_gridCode[jDot*iMax + iDot] != CLOSED_CROSSING) && (_gridCode[jDot*iMax + iDot] != CLOSED_TRANSITION)) { + target[jDot * iMax + iDot] = value; + } + } + } +} //drawLinesOnWall + +void UnivFFviaFM::calcFF(double* costOutput, Point* directionOutput, const double *const speed) { + //CompareCost comp = CompareCost(costOutput); + std::priority_queue<long int, std::vector<long int>, CompareCost> trialfield(costOutput); //pass the argument for the constr of CompareCost + //std::priority_queue<long int, std::vector<long int>, CompareCost> trialfield2(comp); //pass the CompareCost object directly + + directNeighbor local_neighbor = _grid->getNeighbors(0); + long int aux = 0; + //init trial field + for (long int i = 0; i < _nPoints; ++i) { + if (costOutput[i] == 0.0) { + //check for negative neighbours, calc that ones and add to queue trialfield + local_neighbor = _grid->getNeighbors(i); + + //check for valid neigh + aux = local_neighbor.key[0]; + if ((aux != -2) && (_gridCode[aux] != WALL) && (_gridCode[aux] != OUTSIDE) && (costOutput[aux] < 0.0)) { + calcCost(aux, costOutput, directionOutput, speed); + trialfield.emplace(aux); + //trialfield2.emplace(aux); + } + aux = local_neighbor.key[1]; + if ((aux != -2) && (_gridCode[aux] != WALL) && (_gridCode[aux] != OUTSIDE) && (costOutput[aux] < 0.0)) { + calcCost(aux, costOutput, directionOutput, speed); + trialfield.emplace(aux); + //trialfield2.emplace(aux); + } + aux = local_neighbor.key[2]; + if ((aux != -2) && (_gridCode[aux] != WALL) && (_gridCode[aux] != OUTSIDE) && (costOutput[aux] < 0.0)) { + calcCost(aux, costOutput, directionOutput, speed); + trialfield.emplace(aux); + //trialfield2.emplace(aux); + } + aux = local_neighbor.key[3]; + if ((aux != -2) && (_gridCode[aux] != WALL) && (_gridCode[aux] != OUTSIDE) && (costOutput[aux] < 0.0)) { + calcCost(aux, costOutput, directionOutput, speed); + trialfield.emplace(aux); + //trialfield2.emplace(aux); + } + } + } + + while(!trialfield.empty()) { + local_neighbor = _grid->getNeighbors(trialfield.top()); + trialfield.pop(); + + //check for valid neigh + aux = local_neighbor.key[0]; + if ((aux != -2) && (_gridCode[aux] != WALL) && (_gridCode[aux] != OUTSIDE) && (costOutput[aux] < 0.0)) { + calcCost(aux, costOutput, directionOutput, speed); + trialfield.emplace(aux); + //trialfield2.emplace(aux); + } + aux = local_neighbor.key[1]; + if ((aux != -2) && (_gridCode[aux] != WALL) && (_gridCode[aux] != OUTSIDE) && (costOutput[aux] < 0.0)) { + calcCost(aux, costOutput, directionOutput, speed); + trialfield.emplace(aux); + //trialfield2.emplace(aux); + } + aux = local_neighbor.key[2]; + if ((aux != -2) && (_gridCode[aux] != WALL) && (_gridCode[aux] != OUTSIDE) && (costOutput[aux] < 0.0)) { + calcCost(aux, costOutput, directionOutput, speed); + trialfield.emplace(aux); + //trialfield2.emplace(aux); + } + aux = local_neighbor.key[3]; + if ((aux != -2) && (_gridCode[aux] != WALL) && (_gridCode[aux] != OUTSIDE) && (costOutput[aux] < 0.0)) { + calcCost(aux, costOutput, directionOutput, speed); + trialfield.emplace(aux); + //trialfield2.emplace(aux); + } + } +} + +void UnivFFviaFM::calcCost(const long int key, double* cost, Point* dir, const double* const speed) { + //adapt from calcFloorfield + double row = DBL_MAX; + double col = DBL_MAX; + long int aux = -1; //will be set below + bool pointsUp = false; + bool pointsRight = false; + + directNeighbor dNeigh = _grid->getNeighbors(key); + + aux = dNeigh.key[0]; + //hint: trialfield[i].cost = dist2Wall + i; <<< set in resetGoalAndCosts + if ((aux != -2) && //neighbor is a gridpoint + (cost[aux] != magicnum(UNKNOWN_COST)) && (cost[aux] != magicnum(UNKNOWN_DISTANCE)) && //gridpoint holds a calculated value + (_gridCode[aux] != WALL)) //gridpoint holds a calculated value + { + row = cost[aux]; + pointsRight = true; + if (row < 0) { + std::cerr << "hier ist was schief " << row << " " << aux << " " << std::endl; + row = DBL_MAX; + } + } + aux = dNeigh.key[2]; + if ((aux != -2) && //neighbor is a gridpoint + (cost[aux] != magicnum(UNKNOWN_COST)) && (cost[aux] != magicnum(UNKNOWN_DISTANCE)) && //gridpoint holds a calculated value + (_gridCode[aux] != WALL) && + (cost[aux] < row)) //calculated value promises smaller cost + { + row = cost[aux]; + pointsRight = false; + } + + aux = dNeigh.key[1]; + //hint: trialfield[i].cost = dist2Wall + i; <<< set in parseBuilding after linescan call + if ((aux != -2) && //neighbor is a gridpoint + (cost[aux] != magicnum(UNKNOWN_COST)) && (cost[aux] != magicnum(UNKNOWN_DISTANCE)) && //gridpoint holds a calculated value + (_gridCode[aux] != WALL)) + { + col = cost[aux]; + pointsUp = true; + if (col < 0) { + std::cerr << "hier ist was schief " << col << " " << aux << " " << std::endl; + col = DBL_MAX; + } + } + aux = dNeigh.key[3]; + if ((aux != -2) && //neighbor is a gridpoint + (cost[aux] != magicnum(UNKNOWN_COST)) && (cost[aux] != magicnum(UNKNOWN_DISTANCE)) && //gridpoint holds a calculated value + (_gridCode[aux] != WALL) && + (cost[aux] < col)) //calculated value promises smaller cost + { + col = cost[aux]; + pointsUp = false; + } + if (col == DBL_MAX) { //one sided update with row + cost[key] = onesidedCalc(row, _grid->Gethx()/speed[key]); + //flag[key] = FM_SINGLE; + if (!dir) { + return; + } + if (pointsRight) { + dir[key]._x = (-(cost[key+1]-cost[key])/_grid->Gethx()); + dir[key]._y = (0.); + } else { + dir[key]._x = (-(cost[key]-cost[key-1])/_grid->Gethx()); + dir[key]._y = (0.); + } + dir[key] = dir[key].Normalized(); + return; + } + + if (row == DBL_MAX) { //one sided update with col + cost[key] = onesidedCalc(col, _grid->Gethy()/speed[key]); + //flag[key] = FM_SINGLE; + if (!dir) { + return; + } + if (pointsUp) { + dir[key]._x = (0.); + dir[key]._y = (-(cost[key+(_grid->GetiMax())]-cost[key])/_grid->Gethy()); + } else { + dir[key]._x = (0.); + dir[key]._y = (-(cost[key]-cost[key-(_grid->GetiMax())])/_grid->Gethy()); + } + dir[key] = dir[key].Normalized(); + return; + } + + //two sided update + double precheck = twosidedCalc(row, col, _grid->Gethx()/speed[key]); + if (precheck >= 0) { + cost[key] = precheck; + //flag[key] = FM_DOUBLE; + if (!dir) { + return; + } + if (pointsUp && pointsRight) { + dir[key]._x = (-(cost[key+1]-cost[key])/_grid->Gethx()); + dir[key]._y = (-(cost[key+(_grid->GetiMax())]-cost[key])/_grid->Gethy()); + } + if (pointsUp && !pointsRight) { + dir[key]._x = (-(cost[key]-cost[key-1])/_grid->Gethx()); + dir[key]._y = (-(cost[key+(_grid->GetiMax())]-cost[key])/_grid->Gethy()); + } + if (!pointsUp && pointsRight) { + dir[key]._x = (-(cost[key+1]-cost[key])/_grid->Gethx()); + dir[key]._y = (-(cost[key]-cost[key-(_grid->GetiMax())])/_grid->Gethy()); + } + if (!pointsUp && !pointsRight) { + dir[key]._x = (-(cost[key]-cost[key-1])/_grid->Gethx()); + dir[key]._y = (-(cost[key]-cost[key-(_grid->GetiMax())])/_grid->Gethy()); + } + } else { + std::cerr << "else in twosided Dist " << std::endl; + } + dir[key] = dir[key].Normalized(); +} + +void UnivFFviaFM::calcDF(double* costOutput, Point* directionOutput, const double *const speed) { + //CompareCost comp = CompareCost(costOutput); + std::priority_queue<long int, std::vector<long int>, CompareCost> trialfield(costOutput); //pass the argument for the constr of CompareCost + //std::priority_queue<long int, std::vector<long int>, CompareCost> trialfield2(comp); //pass the CompareCost object directly + + directNeighbor local_neighbor = _grid->getNeighbors(0); + long int aux = 0; + //init trial field + for (long int i = 0; i < _nPoints; ++i) { + if (costOutput[i] == 0.0) { + //check for negative neighbours, calc that ones and add to queue trialfield + local_neighbor = _grid->getNeighbors(i); + + //check for valid neigh + aux = local_neighbor.key[0]; + if ((aux != -2) && (_gridCode[aux] != WALL) && (_gridCode[aux] != OUTSIDE) && (costOutput[aux] < 0.0)) { + calcDist(aux, costOutput, directionOutput, speed); + trialfield.emplace(aux); + //trialfield2.emplace(aux); + } + aux = local_neighbor.key[1]; + if ((aux != -2) && (_gridCode[aux] != WALL) && (_gridCode[aux] != OUTSIDE) && (costOutput[aux] < 0.0)) { + calcDist(aux, costOutput, directionOutput, speed); + trialfield.emplace(aux); + //trialfield2.emplace(aux); + } + aux = local_neighbor.key[2]; + if ((aux != -2) && (_gridCode[aux] != WALL) && (_gridCode[aux] != OUTSIDE) && (costOutput[aux] < 0.0)) { + calcDist(aux, costOutput, directionOutput, speed); + trialfield.emplace(aux); + //trialfield2.emplace(aux); + } + aux = local_neighbor.key[3]; + if ((aux != -2) && (_gridCode[aux] != WALL) && (_gridCode[aux] != OUTSIDE) && (costOutput[aux] < 0.0)) { + calcDist(aux, costOutput, directionOutput, speed); + trialfield.emplace(aux); + //trialfield2.emplace(aux); + } + } + } + + while(!trialfield.empty()) { + local_neighbor = _grid->getNeighbors(trialfield.top()); + trialfield.pop(); + + //check for valid neigh + aux = local_neighbor.key[0]; + if ((aux != -2) && (_gridCode[aux] != WALL) && (_gridCode[aux] != OUTSIDE) && (costOutput[aux] < 0.0)) { + calcDist(aux, costOutput, directionOutput, speed); + trialfield.emplace(aux); + //trialfield2.emplace(aux); + } + aux = local_neighbor.key[1]; + if ((aux != -2) && (_gridCode[aux] != WALL) && (_gridCode[aux] != OUTSIDE) && (costOutput[aux] < 0.0)) { + calcDist(aux, costOutput, directionOutput, speed); + trialfield.emplace(aux); + //trialfield2.emplace(aux); + } + aux = local_neighbor.key[2]; + if ((aux != -2) && (_gridCode[aux] != WALL) && (_gridCode[aux] != OUTSIDE) && (costOutput[aux] < 0.0)) { + calcDist(aux, costOutput, directionOutput, speed); + trialfield.emplace(aux); + //trialfield2.emplace(aux); + } + aux = local_neighbor.key[3]; + if ((aux != -2) && (_gridCode[aux] != WALL) && (_gridCode[aux] != OUTSIDE) && (costOutput[aux] < 0.0)) { + calcDist(aux, costOutput, directionOutput, speed); + trialfield.emplace(aux); + //trialfield2.emplace(aux); + } + } +} + +void UnivFFviaFM::calcDist(const long int key, double* cost, Point* dir, const double* const speed) { + //adapt from calcFloorfield + double row = DBL_MAX; + double col = DBL_MAX; + long int aux = -1; //will be set below + bool pointsUp = false; + bool pointsRight = false; + + directNeighbor dNeigh = _grid->getNeighbors(key); + + aux = dNeigh.key[0]; + //hint: trialfield[i].cost = dist2Wall + i; <<< set in resetGoalAndCosts + if ((aux != -2) && //neighbor is a gridpoint + (cost[aux] != magicnum(UNKNOWN_COST)) && (cost[aux] != magicnum(UNKNOWN_DISTANCE)) //gridpoint holds a calculated value + ) //gridpoint holds a calculated value + { + row = cost[aux]; + pointsRight = true; + if (row < 0) { + std::cerr << "hier ist was schief " << row << " " << aux << " " << std::endl; + row = DBL_MAX; + } + } + aux = dNeigh.key[2]; + if ((aux != -2) && //neighbor is a gridpoint + (cost[aux] != magicnum(UNKNOWN_COST)) && (cost[aux] != magicnum(UNKNOWN_DISTANCE)) //gridpoint holds a calculated value + && + (cost[aux] < row)) //calculated value promises smaller cost + { + row = cost[aux]; + pointsRight = false; + } + + aux = dNeigh.key[1]; + //hint: trialfield[i].cost = dist2Wall + i; <<< set in parseBuilding after linescan call + if ((aux != -2) && //neighbor is a gridpoint + (cost[aux] != magicnum(UNKNOWN_COST)) && (cost[aux] != magicnum(UNKNOWN_DISTANCE)) //gridpoint holds a calculated value + ) + { + col = cost[aux]; + pointsUp = true; + if (col < 0) { + std::cerr << "hier ist was schief " << col << " " << aux << " " << std::endl; + col = DBL_MAX; + } + } + aux = dNeigh.key[3]; + if ((aux != -2) && //neighbor is a gridpoint + (cost[aux] != magicnum(UNKNOWN_COST)) && (cost[aux] != magicnum(UNKNOWN_DISTANCE)) && //gridpoint holds a calculated value + + (cost[aux] < col)) //calculated value promises smaller cost + { + col = cost[aux]; + pointsUp = false; + } + if (col == DBL_MAX) { //one sided update with row + cost[key] = onesidedCalc(row, _grid->Gethx()/speed[key]); + //flag[key] = FM_SINGLE; + if (!dir) { + return; + } + if (pointsRight) { + dir[key]._x = (-(cost[key+1]-cost[key])/_grid->Gethx()); + dir[key]._y = (0.); + } else { + dir[key]._x = (-(cost[key]-cost[key-1])/_grid->Gethx()); + dir[key]._y = (0.); + } + dir[key] = dir[key].Normalized(); + return; + } + + if (row == DBL_MAX) { //one sided update with col + cost[key] = onesidedCalc(col, _grid->Gethy()/speed[key]); + //flag[key] = FM_SINGLE; + if (!dir) { + return; + } + if (pointsUp) { + dir[key]._x = (0.); + dir[key]._y = (-(cost[key+(_grid->GetiMax())]-cost[key])/_grid->Gethy()); + } else { + dir[key]._x = (0.); + dir[key]._y = (-(cost[key]-cost[key-(_grid->GetiMax())])/_grid->Gethy()); + } + dir[key] = dir[key].Normalized(); + return; + } + + //two sided update + double precheck = twosidedCalc(row, col, _grid->Gethx()/speed[key]); + if (precheck >= 0) { + cost[key] = precheck; + //flag[key] = FM_DOUBLE; + if (!dir) { + return; + } + if (pointsUp && pointsRight) { + dir[key]._x = (-(cost[key+1]-cost[key])/_grid->Gethx()); + dir[key]._y = (-(cost[key+(_grid->GetiMax())]-cost[key])/_grid->Gethy()); + } + if (pointsUp && !pointsRight) { + dir[key]._x = (-(cost[key]-cost[key-1])/_grid->Gethx()); + dir[key]._y = (-(cost[key+(_grid->GetiMax())]-cost[key])/_grid->Gethy()); + } + if (!pointsUp && pointsRight) { + dir[key]._x = (-(cost[key+1]-cost[key])/_grid->Gethx()); + dir[key]._y = (-(cost[key]-cost[key-(_grid->GetiMax())])/_grid->Gethy()); + } + if (!pointsUp && !pointsRight) { + dir[key]._x = (-(cost[key]-cost[key-1])/_grid->Gethx()); + dir[key]._y = (-(cost[key]-cost[key-(_grid->GetiMax())])/_grid->Gethy()); + } + } else { + std::cerr << "else in twosided Dist " << std::endl; + } + dir[key] = dir[key].Normalized(); +} + +inline double UnivFFviaFM::onesidedCalc(double xy, double hDivF) { + //if ( (xy+hDivF) > 10000) std::cerr << "error in onesided " << xy << std::endl; + return xy + hDivF; +} + +inline double UnivFFviaFM::twosidedCalc(double x, double y, double hDivF) { //on error return -2 + double determinante = (2*hDivF*hDivF - (x-y)*(x-y)); + if (determinante >= 0) { + return (x + y + sqrt(determinante))/2; + } else { + return (x < y) ? (x + hDivF) : (y + hDivF); + } + std::cerr << "error in two-sided 2!!!!!!!!!!!!!!!!!!!!!!! o_O??" << std::endl; + return -2.; //this line should never execute +} //twosidedCalc + +void UnivFFviaFM::addTarget(const int uid, double* costarrayDBL, Point* gradarrayPt) { + if (_doors.count(uid) == 0) { + Log->Write("ERROR: \tCould not find door with uid %d in Room %d", uid, _room); + return; + } + Line tempTargetLine = Line(_doors[uid]); + Point tempCenterPoint = Point(tempTargetLine.GetCentre()); + if (_mode == LINESEGMENT) { + if (tempTargetLine.GetLength() > 0.6) { //shorten line from both Points to avoid targeting edges of real door + const Point &p1 = tempTargetLine.GetPoint1(); + const Point &p2 = tempTargetLine.GetPoint2(); + double length = tempTargetLine.GetLength(); + double u = 0.2 / length; + tempTargetLine = Line(p1 + (p2 - p1) * u, p1 + (p2 - p1) * (1 - u), 0); + } else if (tempTargetLine.GetLength() > 0.2) { + const Point &p1 = tempTargetLine.GetPoint1(); + const Point &p2 = tempTargetLine.GetPoint2(); + double length = tempTargetLine.GetLength(); + double u = 0.05 / length; + tempTargetLine = Line(p1 + (p2 - p1) * u, p1 + (p2 - p1) * (1 - u), 0); + } + } + + //this allocation must be on shared heap! to be accessible by any thread later (should be shared in openmp) + double* newArrayDBL = (costarrayDBL)? costarrayDBL : new double[_nPoints]; + Point* newArrayPt = nullptr; + if (_user == DISTANCE_AND_DIRECTIONS_USED) { + newArrayPt = (gradarrayPt)? gradarrayPt : new Point[_nPoints]; + } + + if ((_costFieldWithKey[uid]) && (_costFieldWithKey[uid] != costarrayDBL)) + delete[] _costFieldWithKey[uid]; + _costFieldWithKey[uid] = newArrayDBL; + + //init costarray + for (int i = 0; i < _nPoints; ++i) { + if (_gridCode[i] == WALL) { + newArrayDBL[i] = magicnum(WALL_ON_COSTARRAY); + } else { + newArrayDBL[i] = magicnum(UNKNOWN_COST); + } + } + + if ((_directionFieldWithKey[uid]) && (_directionFieldWithKey[uid] != gradarrayPt)) + delete[] _directionFieldWithKey[uid]; + if (newArrayPt) + _directionFieldWithKey[uid] = newArrayPt; + + //initialize start area + if (_mode == LINESEGMENT) { + drawLinesOnGrid(tempTargetLine, newArrayDBL, magicnum(TARGET_REGION)); + } + if (_mode == CENTERPOINT) { + newArrayDBL[_grid->getKeyAtPoint(tempCenterPoint)] = magicnum(TARGET_REGION); + } + + if (_speedmode == FF_WALL_AVOID) { + calcFF(newArrayDBL, newArrayPt, _speedFieldSelector[REDU_WALL_SPEED]); + } else if (_speedmode == FF_HOMO_SPEED) { + calcFF(newArrayDBL, newArrayPt, _speedFieldSelector[INITIAL_SPEED]); + } else if (_speedmode == FF_PED_SPEED) { + calcFF(newArrayDBL, newArrayPt, _speedFieldSelector[PED_SPEED]); + } + + //the rest of the door must be initialized if centerpoint was used. else ff_router will have probs getting localDist + if (_mode == CENTERPOINT) { + drawLinesOnGrid(tempTargetLine, newArrayDBL, magicnum(TARGET_REGION)); + } + //the directional field is yet undefined on the target line itself. we will use neighboring vector to help agents + //to cross the line + if (newArrayPt) { + Point passvector = tempTargetLine.NormalVec(); + Point trial = tempTargetLine.GetCentre() - passvector * 0.25; + Point trial2 = tempTargetLine.GetCentre() + passvector * 0.25; + if ((_grid->includesPoint(trial)) && (_gridCode[_grid->getKeyAtPoint(trial)] == INSIDE)) { + finalizeTargetLine(uid, _doors[uid], newArrayPt, passvector); + finalizeTargetLine(uid, tempTargetLine, newArrayPt, passvector); + } else if ((_grid->includesPoint(trial2)) && (_gridCode[_grid->getKeyAtPoint(trial2)] == INSIDE)) { + passvector = passvector * -1.0; + finalizeTargetLine(uid, _doors[uid], newArrayPt, passvector); + finalizeTargetLine(uid, tempTargetLine, newArrayPt, passvector); + + } else { + Log->Write("ERROR:\t in addTarget: calling finalizeTargetLine"); + } +// for (long int i = 0; i < _grid->GetnPoints(); ++i) { +// if ((_gridCode[i] != OUTSIDE) && (_gridCode[i] != WALL) && (newArrayPt[i] == Point(0.0, 0.0) )) { +// Log->Write("Mist"); +// } +// } + } +#pragma omp critical(_uids) + _uids.emplace_back(uid); +} + +void UnivFFviaFM::addTarget(const int uid, Line* door, double* costarray, Point* gradarray){ + if (_doors.count(uid) == 0) { + _doors.emplace(std::make_pair(uid, *door)); + } + addTarget(uid, costarray, gradarray); +} + +void UnivFFviaFM::addAllTargets() { + for (auto uidmap : _doors) { + addTarget(uidmap.first); + } +} + +void UnivFFviaFM::addAllTargetsParallel() { + //Reason: freeing and reallocating takes time. We do not use already allocated memory, because we do not know if it + // is shared memory. Maybe this is not neccessary - maybe reconsider. This way, it is safe. If this function + // is called from a parallel region, we all go to hell. + //free old memory + for (auto memoryDBL : _costFieldWithKey) { + if (memoryDBL.first == 0) continue; //do not free distancemap + if (memoryDBL.second) delete[](memoryDBL.second); + } + for (auto memoryPt : _directionFieldWithKey) { + if (memoryPt.first == 0) continue; //do not free walldirectionmap + if (memoryPt.second) delete[](memoryPt.second); + } + //allocate new memory + for (auto uidmap : _doors) { + _costFieldWithKey[uidmap.first] = new double[_nPoints]; + if (_user == DISTANCE_MEASUREMENTS_ONLY) { + _directionFieldWithKey[uidmap.first] = nullptr; + } + if (_user == DISTANCE_AND_DIRECTIONS_USED) { + _directionFieldWithKey[uidmap.first] = new Point[_nPoints]; + } + } + + //parallel region +#pragma omp parallel + { +#pragma omp for + for (signed int i = 0; i < _doors.size(); ++i) { + auto doorPair = _doors.begin(); + std::advance(doorPair, i); + addTarget(doorPair->first, _costFieldWithKey[doorPair->first], _directionFieldWithKey[doorPair->first]); + } + }; +} + +void UnivFFviaFM::addTargetsParallel(std::vector<int> wantedDoors) { + //free old memory (but not the distancemap with key == 0) + for (int targetUID : wantedDoors) { + if ((targetUID != 0) && _costFieldWithKey.count(targetUID) && _costFieldWithKey[targetUID]) { + delete[] _costFieldWithKey[targetUID]; + } + if ((targetUID != 0) && _directionFieldWithKey.count(targetUID) && _directionFieldWithKey[targetUID]) { + delete[] _directionFieldWithKey[targetUID]; + } + } + //allocate new memory + for (int targetUID : wantedDoors) { + _costFieldWithKey[targetUID] = new double[_nPoints]; + if (_user == DISTANCE_MEASUREMENTS_ONLY) { + _directionFieldWithKey[targetUID] = nullptr; + } + if (_user == DISTANCE_AND_DIRECTIONS_USED) { + _directionFieldWithKey[targetUID] = new Point[_nPoints]; + } + } + + //parallel region +#pragma omp parallel + { +#pragma omp for + for (signed int i = 0; i < wantedDoors.size(); ++i) { + auto doorUID = wantedDoors.begin(); + std::advance(doorUID, i); + addTarget(*doorUID, _costFieldWithKey[*doorUID], _directionFieldWithKey[*doorUID]); + } + }; +} + +SubRoom** UnivFFviaFM::getSubRoomFF(){ + return _subrooms; +} + +SubRoom* UnivFFviaFM::getSubRoom(const Point& pos){ + if (!_subrooms) return nullptr; + if (!_grid->includesPoint(pos)) return nullptr; + + long key = _grid->getKeyAtPoint(pos); + return _subrooms[key]; +} + +std::vector<int> UnivFFviaFM::getKnownDoorUIDs(){ + return _uids; +} + +void UnivFFviaFM::setUser(int userArg) { + _user=userArg; +} + +void UnivFFviaFM::setMode(int modeArg) { + _mode=modeArg; +} + +void UnivFFviaFM::setSpeedMode(int speedModeArg) { + _speedmode = speedModeArg; + if (_speedmode == FF_PED_SPEED && !_speedFieldSelector[PED_SPEED]) { + _speedFieldSelector[PED_SPEED] = new double[_nPoints]; + } +} + + +void UnivFFviaFM::writeFF(const std::string& filename, std::vector<int> targetID) { + Log->Write("INFO: \tWrite Floorfield to file"); + Log->Write(filename); + std::ofstream file; + + Log->Write("FloorfieldViaFM::writeFF(): writing to file %s: There are %d targets.", filename.c_str(), targetID.size()); + + int numX = (int) ((_grid->GetxMax()-_grid->GetxMin())/_grid->Gethx()); + int numY = (int) ((_grid->GetyMax()-_grid->GetyMin())/_grid->Gethy()); + //int numTotal = numX * numY; + //std::cerr << numTotal << " numTotal" << std::endl; + //std::cerr << grid->GetnPoints() << " grid" << std::endl; + file.open(_configuration->GetProjectRootDir()+filename); + + file << "# vtk DataFile Version 3.0" << std::endl; + file << "Testdata: Fast Marching: Test: " << std::endl; + file << "ASCII" << std::endl; + file << "DATASET STRUCTURED_POINTS" << std::endl; + file << "DIMENSIONS " << + std::to_string(_grid->GetiMax()) << + " " << + std::to_string(_grid->GetjMax()) << + " 1" << std::endl; + file << "ORIGIN " << _grid->GetxMin() << " " << _grid->GetyMin() << " 0" << std::endl; + file << "SPACING " << std::to_string(_grid->Gethx()) << " " << std::to_string(_grid->Gethy()) << " 1" << std::endl; + file << "POINT_DATA " << std::to_string(_grid->GetnPoints()) << std::endl; + file << "SCALARS GCode float 1" << std::endl; + file << "LOOKUP_TABLE default" << std::endl; + if (!_gridCode) { + return; + } + for (long int i = 0; i < _grid->GetnPoints(); ++i) { + file << _gridCode[i] << std::endl; + } + + if (_directionFieldWithKey[0]) { + file << "VECTORS Dir2Wall float" << std::endl; + for (long int i = 0; i < _grid->GetnPoints(); ++i) { + file << _directionFieldWithKey[0][i]._x << " " << _directionFieldWithKey[0][i]._y << " 0.0" << std::endl; + } + + file << "SCALARS Dist2Wall float 1" << std::endl; + file << "LOOKUP_TABLE default" << std::endl; + for (long int i = 0; i < _grid->GetnPoints(); ++i) { + file << _costFieldWithKey[0][i] << std::endl; //@todo: change target to all dist2wall + } + } + + if (_subrooms) { + file << "SCALARS SubroomPtr float 1" << std::endl; + file << "LOOKUP_TABLE default" << std::endl; + for (long int i = 0; i < _grid->GetnPoints(); ++i) { + if (_subrooms[i]) { + file << _subrooms[i]->GetUID() << std::endl; + } else { + file << 0.0 << std::endl; + } + } + } + + if (!targetID.empty()) { + for (unsigned int iTarget = 0; iTarget < targetID.size(); ++iTarget) { + if (_costFieldWithKey.count(targetID[iTarget]) == 0) { + continue; + } + double *costarray = _costFieldWithKey[targetID[iTarget]]; + + Log->Write("%s: target number %d: UID %d", filename.c_str(), iTarget, targetID[iTarget]); + + std::string name = _building->GetTransOrCrossByUID(targetID[iTarget])->GetCaption() + "-" + + std::to_string(targetID[iTarget]); + std::replace(name.begin(), name.end(), ' ', '_'); + + if (!costarray) { + continue; + } + + file << "SCALARS CostTarget" << name << " float 1" << std::endl; + file << "LOOKUP_TABLE default" << std::endl; + for (long int i = 0; i < _grid->GetnPoints(); ++i) { + file << costarray[i] << std::endl; + } + + if (_directionFieldWithKey.count(targetID[iTarget]) == 0) { + continue; + } + + Point *gradarray = _directionFieldWithKey[targetID[iTarget]]; + if (gradarray == nullptr) { + continue; + } + + + file << "VECTORS GradientTarget" << name << " float" << std::endl; + for (int i = 0; i < _grid->GetnPoints(); ++i) { + file << gradarray[i]._x << " " << gradarray[i]._y << " 0.0" << std::endl; + } + + + } + } + file.close(); +} + +//mode is argument, which should not be needed, the info is stored in members like speedmode, ... +double UnivFFviaFM::getCostToDestination(const int destID, const Point& position, int mode) { + assert(_grid->includesPoint(position)); + long int key = _grid->getKeyAtPoint(position); + if ((_gridCode[key] == OUTSIDE) || (_gridCode[key] == WALL)) { + //bresenham line (treppenstruktur) at middle and calculated centre of line are on different gridpoints + //find a key that belongs domain (must be one left or right and second one below or above) + if ((key+1 <= _grid->GetnPoints()) && (_gridCode[key+1] != OUTSIDE) && (_gridCode[key+1] != WALL)) { + key = key+1; + } else if ((key-1 >= 0) && (_gridCode[key-1] != OUTSIDE) && (_gridCode[key-1] != WALL)) { + key = key - 1; + } else if ((key >= _grid->GetiMax()) && (_gridCode[key-_grid->GetiMax()] != OUTSIDE) && (_gridCode[key-_grid->GetiMax()] != WALL)) { + key = key - _grid->GetiMax(); + } else if ((key < _grid->GetnPoints()-_grid->GetiMax()) && (_gridCode[key+_grid->GetiMax()] != OUTSIDE) && (_gridCode[key+_grid->GetiMax()] != WALL)) { + key = key + _grid->GetiMax(); + } else { + Log->Write("ERROR:\t In getCostToDestination(3 args)"); + } + } + if (_costFieldWithKey.count(destID)==1 && _costFieldWithKey[destID]) { + return _costFieldWithKey[destID][key]; + } else if (_directCalculation && _doors.count(destID) > 0) { + _costFieldWithKey[destID] = new double[_nPoints]; + if (_user == DISTANCE_AND_DIRECTIONS_USED) { + _directionFieldWithKey[destID] = new Point[_nPoints]; + } else { + _directionFieldWithKey[destID] = nullptr; + } + + addTarget(destID, _costFieldWithKey[destID], _directionFieldWithKey[destID]); + return getCostToDestination(destID, position, mode); + } else if (!_directCalculation && _doors.count(destID) > 0) { + //omp critical +#pragma omp critical(UnivFFviaFM_toDo) + _toDo.emplace_back(destID); + } + return DBL_MAX; +} + +double UnivFFviaFM::getCostToDestination(const int destID, const Point& position) { + assert(_grid->includesPoint(position)); + long int key = _grid->getKeyAtPoint(position); + if ((_gridCode[key] == OUTSIDE) || (_gridCode[key] == WALL)) { + //bresenham line (treppenstruktur) getKeyAtPoint yields gridpoint next to edge, although position is on edge + //find a key that belongs domain (must be one left or right and second one below or above) + if ((key+1 <= _grid->GetnPoints()) && (_gridCode[key+1] != OUTSIDE) && (_gridCode[key+1] != WALL)) { + key = key+1; + } else if ((key-1 >= 0) && (_gridCode[key-1] != OUTSIDE) && (_gridCode[key-1] != WALL)) { + key = key - 1; + } else if ((key >= _grid->GetiMax()) && (_gridCode[key-_grid->GetiMax()] != OUTSIDE) && (_gridCode[key-_grid->GetiMax()] != WALL)) { + key = key - _grid->GetiMax(); + } else if ((key < _grid->GetnPoints()-_grid->GetiMax()) && (_gridCode[key+_grid->GetiMax()] != OUTSIDE) && (_gridCode[key+_grid->GetiMax()] != WALL)) { + key = key + _grid->GetiMax(); + } else { + Log->Write("ERROR:\t In getCostToDestination(2 args)"); + } + } + if (_costFieldWithKey.count(destID)==1 && _costFieldWithKey[destID]) { + return _costFieldWithKey[destID][key]; + } else if (_directCalculation && _doors.count(destID) > 0) { + _costFieldWithKey[destID] = new double[_nPoints]; + if (_user == DISTANCE_AND_DIRECTIONS_USED) { + _directionFieldWithKey[destID] = new Point[_nPoints]; + } else { + _directionFieldWithKey[destID] = nullptr; + } + + addTarget(destID, _costFieldWithKey[destID], _directionFieldWithKey[destID]); + return getCostToDestination(destID, position); + } else if (!_directCalculation && _doors.count(destID) > 0) { +//omp critical +#pragma omp critical(UnivFFviaFM_toDo) + _toDo.emplace_back(destID); + } + return DBL_MAX; +} + +double UnivFFviaFM::getDistanceBetweenDoors(const int door1_ID, const int door2_ID) { + assert(_doors.count(door1_ID) != 0); + assert(_doors.count(door2_ID) != 0); + + if (_costFieldWithKey.count(door1_ID)==1 && _costFieldWithKey[door1_ID]) { + long int key = _grid->getKeyAtPoint(_doors.at(door2_ID).GetCentre()); + if (_gridCode[key] != door2_ID) { + //bresenham line (treppenstruktur) getKeyAtPoint yields gridpoint next to edge, although position is on edge + //find a key that belongs to door (must be one left or right and second one below or above) + if (_gridCode[key+1] == door2_ID) { + key = key+1; + } else if (_gridCode[key-1] == door2_ID){ + key = key-1; + } else { + Log->Write("ERROR:\t In DistanceBetweenDoors"); + } + } + return _costFieldWithKey[door1_ID][key]; + } else if (_directCalculation && _doors.count(door1_ID) > 0) { + _costFieldWithKey[door1_ID] = new double[_nPoints]; + if (_user == DISTANCE_AND_DIRECTIONS_USED) { + _directionFieldWithKey[door1_ID] = new Point[_nPoints]; + } else { + _directionFieldWithKey[door1_ID] = nullptr; + } + + addTarget(door1_ID, _costFieldWithKey[door1_ID], _directionFieldWithKey[door1_ID]); + return getDistanceBetweenDoors(door1_ID, door2_ID); + } else if (!_directCalculation && _doors.count(door1_ID) > 0) { +//omp critical +#pragma omp critical(UnivFFviaFM_toDo) + _toDo.emplace_back(door1_ID); + } + return DBL_MAX; +} + +RectGrid* UnivFFviaFM::getGrid(){ + return _grid; +} + +void UnivFFviaFM::getDirectionToUID(int destID, long int key, Point& direction, int mode){ + assert(key > 0 && key < _nPoints); + if ((_gridCode[key] == OUTSIDE) || (_gridCode[key] == WALL)) { + //bresenham line (treppenstruktur) getKeyAtPoint yields gridpoint next to edge, although position is on edge + //find a key that belongs domain (must be one left or right and second one below or above) + if ((key+1 <= _grid->GetnPoints()) && (_gridCode[key+1] != OUTSIDE) && (_gridCode[key+1] != WALL)) { + key = key+1; + } else if ((key-1 >= 0) && (_gridCode[key-1] != OUTSIDE) && (_gridCode[key-1] != WALL)) { + key = key - 1; + } else if ((key >= _grid->GetiMax()) && (_gridCode[key-_grid->GetiMax()] != OUTSIDE) && (_gridCode[key-_grid->GetiMax()] != WALL)) { + key = key - _grid->GetiMax(); + } else if ((key < _grid->GetnPoints()-_grid->GetiMax()) && (_gridCode[key+_grid->GetiMax()] != OUTSIDE) && (_gridCode[key+_grid->GetiMax()] != WALL)) { + key = key + _grid->GetiMax(); + } else { + Log->Write("ERROR:\t In getDirectionToUID (4 args)"); + } + } + if (_directionFieldWithKey.count(destID)==1 && _directionFieldWithKey[destID]) { + direction = _directionFieldWithKey[destID][key]; + } else if (_directCalculation && _doors.count(destID) > 0) { + //free memory if needed + if (_costFieldWithKey.count(destID) == 1 && _costFieldWithKey[destID]) { + delete[] _costFieldWithKey[destID]; + } + //allocate memory + _costFieldWithKey[destID] = new double[_nPoints]; + if (_user == DISTANCE_AND_DIRECTIONS_USED) { + _directionFieldWithKey[destID] = new Point[_nPoints]; + } else { + _directionFieldWithKey[destID] = nullptr; + } + + //calculate destID's fields and call function + addTarget(destID, _costFieldWithKey[destID], _directionFieldWithKey[destID]); + getDirectionToUID(destID, key, direction, mode); + } else if (!_directCalculation && _doors.count(destID) > 0) { +//omp critical +#pragma omp critical(UnivFFviaFM_toDo) + _toDo.emplace_back(destID); + direction._x = 0.; + direction._y = 0.; + } + return; +} + +void UnivFFviaFM::getDirectionToUID(int destID, long int key, Point& direction){ + //assert(key > 0 && key < _nPoints); + if(key <=0 || key>=_nPoints) + { + direction._x = 0.; + direction._y = 0.; + return; + } + if ((_gridCode[key] == OUTSIDE) || (_gridCode[key] == WALL)) { + //bresenham line (treppenstruktur) getKeyAtPoint yields gridpoint next to edge, although position is on edge + //find a key that belongs domain (must be one left or right and second one below or above) + if ((key+1 <= _grid->GetnPoints()) && (_gridCode[key+1] != OUTSIDE) && (_gridCode[key+1] != WALL)) { + key = key+1; + } else if ((key-1 >= 0) && (_gridCode[key-1] != OUTSIDE) && (_gridCode[key-1] != WALL)) { + key = key - 1; + } else if ((key >= _grid->GetiMax()) && (_gridCode[key-_grid->GetiMax()] != OUTSIDE) && (_gridCode[key-_grid->GetiMax()] != WALL)) { + key = key - _grid->GetiMax(); + } else if ((key < _grid->GetnPoints()-_grid->GetiMax()) && (_gridCode[key+_grid->GetiMax()] != OUTSIDE) && (_gridCode[key+_grid->GetiMax()] != WALL)) { + key = key + _grid->GetiMax(); + } else { + Log->Write("ERROR:\t In getDirectionToUID (3 args)"); + } + } + if (_directionFieldWithKey.count(destID)==1 && _directionFieldWithKey[destID]) { + direction = _directionFieldWithKey[destID][key]; + } else if (_directCalculation && _doors.count(destID) > 0) { + //free memory if needed + if (_costFieldWithKey.count(destID) == 1 && _costFieldWithKey[destID]) { + delete[] _costFieldWithKey[destID]; + } + //allocate memory + _costFieldWithKey[destID] = new double[_nPoints]; + if (_user == DISTANCE_AND_DIRECTIONS_USED) { + _directionFieldWithKey[destID] = new Point[_nPoints]; + } else { + _directionFieldWithKey[destID] = nullptr; + } + + //calculate destID's fields and call function + addTarget(destID, _costFieldWithKey[destID], _directionFieldWithKey[destID]); + getDirectionToUID(destID, key, direction); + } else if (!_directCalculation && _doors.count(destID) > 0) { +//omp critical +#pragma omp critical(UnivFFviaFM_toDo) + _toDo.emplace_back(destID); + direction._x = 0.; + direction._y = 0.; + } + return; +} + +void UnivFFviaFM::getDirectionToUID(int destID, const Point& pos, Point& direction, int mode) { + getDirectionToUID(destID, _grid->getKeyAtPoint(pos), direction, mode); +} + +void UnivFFviaFM::getDirectionToUID(int destID, const Point& pos,Point& direction) { + getDirectionToUID(destID, _grid->getKeyAtPoint(pos), direction); +} + +double UnivFFviaFM::getDistance2WallAt(const Point &pos) { + if (_useWallDistances || (_speedmode == FF_WALL_AVOID)) { + if (_costFieldWithKey[0]) { + return _costFieldWithKey[0][_grid->getKeyAtPoint(pos)]; + } + } + return DBL_MAX; +} + +void UnivFFviaFM::getDir2WallAt(const Point &pos, Point &p) { + if (_useWallDistances || (_speedmode == FF_WALL_AVOID)) { + if (_directionFieldWithKey[0]) { + p = _directionFieldWithKey[0][_grid->getKeyAtPoint(pos)]; + } + } else { + p = Point(0.0, 0.0); + } +} + +/* Log: + * todo: + * - implement error treatment: extend fctns to throw errors and handle them + * - error treatment will be advantageous, if calculation of FFs can be postponed + * to be done in Simulation::RunBody, where + * all cores are available + * - (WIP) fill subroom* array with correct values + * */ diff --git a/routing/ff_router_trips/UnivFFviaFM.h b/routing/ff_router_trips/UnivFFviaFM.h new file mode 100644 index 0000000000000000000000000000000000000000..53080ec1993fba5e32e6ae937c39868505dd41a3 --- /dev/null +++ b/routing/ff_router_trips/UnivFFviaFM.h @@ -0,0 +1,179 @@ +// +// Created by arne on 5/9/17. +// +/** + * \file UnivFFviaFM.h + * \date May 09, 2017 + * \version N/A (v0.8.x) + * \copyright <2017-2020> Forschungszentrum Jülich GmbH. All rights reserved. + * + * \section License + * This file is part of JuPedSim. + * + * JuPedSim is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * JuPedSim 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 Lesser General Public License + * along with JuPedSim. If not, see <http://www.gnu.org/licenses/>. + * + * \section Description + * Implementation of classes for a reworked floorfield. A floorfield in general + * yields a cost field to a specific goal and a correlated vectorfield for the + * optimal path in terms of the cost value. + * + * Rework focused on a cleaner structure and less inheritance (no diamond) and + * less workarounds. + * + * + **/ +#ifndef JPSCORE_UNIVFFVIAFM_H +#define JPSCORE_UNIVFFVIAFM_H + +#include <string> +#include <vector> +#include <map> +#include <float.h> +#include "../../general/Macros.h" + +class Pedestrian; +class Room; +class SubRoom; +class Building; +class Configuration; +class Point; +class RectGrid; +class Line; + +enum SPEEDFIELD { //this enum is used as index in _speedFieldSelector + INITIAL_SPEED=0, //homogen speed in walkable area, nealy zero in walls + REDU_WALL_SPEED=1, //reduced wall speed + PED_SPEED=2 //standing agents reduce speed, so that jams will be considered in ff +}; + +enum TARGETMODE { + LINESEGMENT=0, + CENTERPOINT +}; + +enum USERMODE { + DISTANCE_MEASUREMENTS_ONLY, + DISTANCE_AND_DIRECTIONS_USED +}; + +class CompareCost { //this class is used in std::priority_queue in UnivFFviaFM::calcFF +public: + CompareCost(double* costarray) : _costarray(costarray) {} + bool operator() (const int a, const int b) const { + return _costarray[a] > _costarray[b]; + } + +private: + double* _costarray = nullptr; +}; + +class UnivFFviaFM { +public: + UnivFFviaFM(Room* a, Building* b, double c, double e, bool f); + UnivFFviaFM(SubRoom* a, Building* b, double c, double e, bool f); + UnivFFviaFM(Room* a, Configuration* const b, double hx, double wallAvoid, bool useWallDistances); + UnivFFviaFM(Room* a, Configuration* const b, double hx, double wallAvoid, bool useWallDistances, std::vector<int> wantedDoors); + UnivFFviaFM(SubRoom* sr, Configuration* const conf, double hx, double wallAvoid, bool useWallDistances); + UnivFFviaFM(SubRoom* subRoomArg, Configuration* const confArg, double hx, double wallAvoid, bool useWallDistances, std::vector<int> wantedDoors); + void create(std::vector<Line>& walls, std::map<int, Line>& doors, std::vector<int> targetUIDs, int mode, + double spacing, double wallAvoidDist, bool useWallDistances); + void recreateAllForQuickest(); + UnivFFviaFM() {}; + UnivFFviaFM(UnivFFviaFM&){}; + virtual ~UnivFFviaFM(); + + void addTarget(const int uid, Line* door, double* costarray = nullptr, Point* gradarray = nullptr); + void addTarget(const int uid, double* costarray = nullptr, Point* gradarray = nullptr); + void addAllTargets(); + void addAllTargetsParallel(); + void addTargetsParallel(std::vector<int> wantedDoors); + std::vector<int> getKnownDoorUIDs(); + void setUser(int userArg); + void setMode(int modeArg); + void setSpeedMode(int speedModeArg); + SubRoom** getSubRoomFF(); + SubRoom* getSubRoom(const Point& pos); + + double getCostToDestination(const int destID, const Point& position, int mode); + double getCostToDestination(const int destID, const Point& position); + double getDistanceBetweenDoors(const int door1_ID, const int door2_ID); + RectGrid* getGrid(); + virtual void getDirectionToUID(int destID, long int key, Point& direction, int mode); + void getDirectionToUID(int destID, long int key, Point& direction); + virtual void getDirectionToUID(int destID, const Point& pos, Point& direction, int mode); + void getDirectionToUID(int destID, const Point& pos, Point& direction); + double getDistance2WallAt(const Point& pos); + void getDir2WallAt(const Point& pos, Point& p); + + void writeFF(const std::string&, std::vector<int> targetID); + + void createRectGrid(std::vector<Line>& walls, std::map<int, Line>& doors, double spacing); + void processGeometry(std::vector<Line>&walls, std::map<int, Line>& doors); + void markSubroom(const Point& insidePoint, SubRoom* const value); + void createReduWallSpeed(double* reduWallSpeed); + void createPedSpeed(Pedestrian* const * pedsArg, int nsize, int modechoice, double radius); + void finalizeTargetLine(const int uid, const Line& tempTargetLine, Point* newArrayPt, Point& passvector); + + void drawLinesOnGrid(std::map<int, Line>& doors, int *const grid); + template <typename T> + void drawLinesOnGrid(std::vector<Line>& wallArg, T* const target, const T value); + template <typename T> + void drawLinesOnGrid(Line& line, T* const target, const T value); + + template <typename T> + void drawLinesOnWall(std::vector<Line>& wallArg, T* const target, const T value); + template <typename T> + void drawLinesOnWall(Line& line, T* const target, const T value); + + void calcFF(double*, Point*, const double* const); + void calcCost(const long int key, double* cost, Point* dir, const double* const speed); + void calcDF(double*, Point*, const double* const); + void calcDist(const long int key, double* cost, Point* dir, const double* const speed); + inline double onesidedCalc(double xy, double hDivF); + inline double twosidedCalc(double x, double y, double hDivF); + +private: + Building* _building = nullptr; + Configuration* _configuration = nullptr; + int _room = -1; //not set + int _mode = LINESEGMENT; //default + int _user = DISTANCE_AND_DIRECTIONS_USED; //default + int _speedmode = FF_HOMO_SPEED; //default + int _scope = 0; //not set / unknown + bool _directCalculation = true; + RectGrid* _grid = nullptr; + long int _nPoints = 0; + std::vector<double*> _speedFieldSelector; + int* _gridCode = nullptr; + SubRoom* * _subrooms = nullptr; // this is an array (first asterisk) of pointers (second asterisk) + + double _wallAvoidDistance = 0.; + bool _useWallDistances = false; //could be used in DirectionStrategy even if mode _speedmode is FF_HOMO_SPEED + + //the following maps are responsible for dealloc the arrays + std::map<int, double*> _costFieldWithKey; + std::map<int, Point*> _directionFieldWithKey; + + std::vector<int> _uids; + std::map<int, Line> _doors; + std::vector<int> _toDo; + + std::map<int, Point> _subroomUIDtoInsidePoint; + std::map<int, SubRoom*> _subroomUIDtoSubRoomPtr; + std::map<SubRoom*, Point> _subRoomPtrTOinsidePoint; + +}; + + +#endif //JPSCORE_UNIVFFVIAFM_H diff --git a/routing/ff_router_trips/ffRouterTrips.cpp b/routing/ff_router_trips/ffRouterTrips.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dcecf4067b1b9a0736fe015f742124463c6fa5ed --- /dev/null +++ b/routing/ff_router_trips/ffRouterTrips.cpp @@ -0,0 +1,664 @@ +/** + * \file ffRouter.h + * \date Feb 19, 2016 + * \version v0.8 + * \copyright <2016-2022> Forschungszentrum Jülich GmbH. All rights reserved. + * + * \section License + * This file is part of JuPedSim. + * + * JuPedSim is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * JuPedSim 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 Lesser General Public License + * along with JuPedSim. If not, see <http://www.gnu.org/licenses/>. + * + * \section Description + * This router is an update of the former Router.{cpp, h} - Global-, Quickest + * Router System. In the __former__ version, a graph was created with doors and + * hlines as nodes and the distances of (doors, hlines), connected with a line- + * of-sight, was used as edge-costs. If there was no line-of-sight, there was no + * connecting edge. On the resulting graph, the Floyd-Warshall algorithm was + * used to find any paths. In the "quickest-___" variants, the edge cost was not + * determined by the distance, but by the distance multiplied by a speed- + * estimate, to find the path with minimum travel times. This whole construct + * worked pretty well, but dependend on hlines to create paths with line-of- + * sights to the next target (hline/door). + * + * In the ffRouter, we want to overcome hlines by using floor fields to + * determine the distances. A line of sight is not required any more. We hope to + * reduce the graph complexity and the preparation-needs for new geometries. + * + * To find a counterpart for the "quickest-____" router, we can either use + * __special__ floor fields, that respect the travel time in the input-speed map, + * or take the distance-floor field and multiply it by a speed-estimate (analog + * to the former construct. + * + * We will derive from the <Router> class to fit the interface. + * + **/ + +#include <cfloat> +#include <algorithm> +#include "ffRouterTrips.h" +//#include "FloorfieldViaFM.h" +//#include "../../geometry/Building.h" + +int FFRouterTrips::_cnt = 0; + +FFRouterTrips::FFRouterTrips() +{ + +} + +FFRouterTrips::FFRouterTrips(int id, RoutingStrategy s, bool hasSpecificGoals, Configuration* config):Router(id,s) { + _config = config; + _building = nullptr; + _hasSpecificGoals = hasSpecificGoals; + _globalFF = nullptr; + _targetWithinSubroom = true; //depending on exit_strat 8 => false, depending on exit_strat 9 => true; + _targetWithinSubroom = (_config->get_exit_strat() == 9); + if (s == ROUTING_FF_QUICKEST) { + _mode = quickest; + _recalc_interval = _config->get_recalc_interval(); + } else if (s == ROUTING_FF_LOCAL_SHORTEST) { + _mode = local_shortest; + _localShortestSafedPeds.clear(); + _localShortestSafedPeds.reserve(500); + } else if (s == ROUTING_FF_GLOBAL_SHORTEST) { + _mode = global_shortest; + } +} + +//FFRouter::FFRouter(const Building* const building) + +FFRouterTrips::~FFRouterTrips() +{ + if (_globalFF) { + delete _globalFF; + } + //delete localffs + std::map<int, UnivFFviaFM*>::reverse_iterator delIter; + for (delIter = _locffviafm.rbegin(); + delIter != _locffviafm.rend(); + ++delIter) { + delete (*delIter).second; + } +} + +bool FFRouterTrips::Init(Building* building) +{ + std::cout << "bool FFRouterTrips::Init(Building* building)" << std::endl; + _building = building; + if (_hasSpecificGoals) { + std::vector<int> goalIDs; + goalIDs.clear(); + //get global field to manage goals (which are not in a subroom) + _globalFF = new FloorfieldViaFM(building, 0.25, 0.25, 0.0, false, true); + for (auto &itrGoal : building->GetAllGoals()) { + _globalFF->createMapEntryInLineToGoalID(itrGoal.first); + goalIDs.emplace_back(itrGoal.first); + } + _goalToLineUIDmap = _globalFF->getGoalToLineUIDmap(); + _goalToLineUIDmap2 = _globalFF->getGoalToLineUIDmap2(); + _goalToLineUIDmap3 = _globalFF->getGoalToLineUIDmap3(); + _globalFF->writeGoalFF("goal.vtk", goalIDs); + } + + //get all door UIDs + _allDoorUIDs.clear(); + _TransByUID.clear(); + _ExitsByUID.clear(); + _CroTrByUID.clear(); + auto& allTrans = building->GetAllTransitions(); + auto& allCross = building->GetAllCrossings(); + std::vector<std::pair<int, int>> roomAndCroTrVector; + roomAndCroTrVector.clear(); + for (auto& pair:allTrans) { + if (pair.second->IsOpen()) { + _allDoorUIDs.emplace_back(pair.second->GetUniqueID()); + _CroTrByUID.insert(std::make_pair(pair.second->GetUniqueID(), pair.second)); + if (pair.second->IsExit()) { + _ExitsByUID.insert(std::make_pair(pair.second->GetUniqueID(), pair.second)); + } + Room* room1 = pair.second->GetRoom1(); + if (room1) roomAndCroTrVector.emplace_back(std::make_pair(room1->GetID(), pair.second->GetUniqueID())); + Room* room2 = pair.second->GetRoom2(); + if (room2) roomAndCroTrVector.emplace_back(std::make_pair(room2->GetID(), pair.second->GetUniqueID())); + } + } + for (auto& pair:allCross) { + if (pair.second->IsOpen()) { + _allDoorUIDs.emplace_back(pair.second->GetUniqueID()); + _CroTrByUID.insert(std::make_pair(pair.second->GetUniqueID(), pair.second)); + Room* room1 = pair.second->GetRoom1(); + if (room1) roomAndCroTrVector.emplace_back(std::make_pair(room1->GetID(), pair.second->GetUniqueID())); + } + } + //make unique + std::sort(_allDoorUIDs.begin(), _allDoorUIDs.end()); + _allDoorUIDs.erase( std::unique(_allDoorUIDs.begin(),_allDoorUIDs.end()), _allDoorUIDs.end()); + + //cleanse maps + _distMatrix.clear(); + _pathsMatrix.clear(); + + //init, yet no distances, only create map entries + for(auto& id1 : _allDoorUIDs) { + for(auto& id2 : _allDoorUIDs){ + std::pair<int, int> key = std::make_pair(id1, id2); + double value = (id1 == id2)? 0.0 : DBL_MAX; + //distMatrix[i][j] = 0, if i==j + //distMatrix[i][j] = max, else + _distMatrix.insert(std::make_pair( key , value)); + //pathsMatrix[i][j] = i or j ? (follow wiki:path_reconstruction, it should be j) + _pathsMatrix.insert(std::make_pair( key , id2 )); + //_subroomMatrix.insert(std::make_pair(key, nullptr)); + } + } + + //prepare all room-floor-fields-objects (one room = one instance) + _locffviafm.clear(); + //type of allRooms: const std::map<int, std::unique_ptr<Room> >& + const std::map<int, std::shared_ptr<Room> >& allRooms = _building->GetAllRooms(); + + + for (unsigned int i = 0; i < allRooms.size(); ++i) { + +#ifdef DEBUG + std::cerr << "Creating Floorfield for Room: " << pair.first << std::endl; +#endif + + auto pairRoomIt = allRooms.begin(); + std::advance(pairRoomIt, i); + UnivFFviaFM *locffptr = nullptr; + locffptr = new UnivFFviaFM(pairRoomIt->second.get(), building, 0.125, 0.0, false); + + locffptr->setUser(DISTANCE_MEASUREMENTS_ONLY); + locffptr->setMode(CENTERPOINT); + locffptr->setSpeedMode(FF_HOMO_SPEED); + locffptr->addAllTargetsParallel(); + //locffptr->writeFF("UnivFF"+std::to_string(pairRoomIt->first)+".vtk", locffptr->getKnownDoorUIDs()); + Log->Write("INFO: \tAdding distances in Room %d to matrix", (*pairRoomIt).first); +//#pragma omp critical(_locffviafm) + _locffviafm.insert(std::make_pair((*pairRoomIt).first, locffptr)); + } + + + // nowait, because the parallel region ends directly afterwards +//#pragma omp for nowait + //@todo: @ar.graf: it would be easier to browse thru doors of each field directly after "addAllTargetsParallel" as + // we do only want doors of same subroom anyway. BUT the router would have to switch from room-scope + // to subroom-scope. Nevertheless, we could omit the room info (used to acces correct field), if we + // do it like in "ReInit()". + for (unsigned int i = 0; i < roomAndCroTrVector.size(); ++i) { + auto rctIt = roomAndCroTrVector.begin(); + std::advance(rctIt, i); + + ////loop over upper triangular matrice (i,j) and write to (j,i) as well + for (auto otherDoor : roomAndCroTrVector) { + if (otherDoor.first != rctIt->first) continue; // we only want doors with one room in common + if (otherDoor.second <= rctIt->second) continue; // calculate every path only once + // if we exclude otherDoor.second == rctIt->second, the program loops forever + + //if the door is closed, then don't calc distances + //if (!_CroTrByUID.at(*otherDoor)->IsOpen()) { + // continue; + //} + + // if the two doors are not within the same subroom, do not consider (ar.graf) + // should fix problems of oscillation caused by doorgaps in the distancegraph + int thisUID1 = (_CroTrByUID.at(rctIt->second)->GetSubRoom1()) ? _CroTrByUID.at(rctIt->second)->GetSubRoom1()->GetUID() : -10 ; + int thisUID2 = (_CroTrByUID.at(rctIt->second)->GetSubRoom2()) ? _CroTrByUID.at(rctIt->second)->GetSubRoom2()->GetUID() : -20 ; + int otherUID1 = (_CroTrByUID.at(otherDoor.second)->GetSubRoom1()) ? _CroTrByUID.at(otherDoor.second)->GetSubRoom1()->GetUID() : -30 ; + int otherUID2 = (_CroTrByUID.at(otherDoor.second)->GetSubRoom2()) ? _CroTrByUID.at(otherDoor.second)->GetSubRoom2()->GetUID() : -40 ; + + if ( + (thisUID1 != otherUID1) && + (thisUID1 != otherUID2) && + (thisUID2 != otherUID1) && + (thisUID2 != otherUID2) ) { + continue; + } + + UnivFFviaFM* locffptr = _locffviafm[rctIt->first]; + double tempDistance = locffptr->getDistanceBetweenDoors(rctIt->second, otherDoor.second); + + if (tempDistance < locffptr->getGrid()->Gethx()) { + Log->Write("WARNING:\tIgnoring distance of doors %d and %d because it is too small: %f",rctIt->second, otherDoor.second, tempDistance); + //Log->Write("^^^^^^^^\tIf there are scattered subrooms, which are not connected, this is ok."); + continue; + } +// + std::pair<int, int> key_ij = std::make_pair(otherDoor.second, rctIt->second); + std::pair<int, int> key_ji = std::make_pair(rctIt->second, otherDoor.second); + + +#pragma omp critical(_distMatrix) + if (_distMatrix.at(key_ij) > tempDistance) { + _distMatrix.erase(key_ij); + _distMatrix.erase(key_ji); + _distMatrix.insert(std::make_pair(key_ij, tempDistance)); + _distMatrix.insert(std::make_pair(key_ji, tempDistance)); + } + } // 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 +// _locffviafm[4]->writeFF("ffTreppe.vtk", _allDoorUIDs); + + //int roomTest = (*(_locffviafm.begin())).first; + //int transTest = (building->GetRoom(roomTest)->GetAllTransitionsIDs())[0]; + //auto test = _CroTrByUID.at(1253); + + if (_config->get_write_VTK_files()) { + for (unsigned int i = 0; i < _locffviafm.size(); ++i) { + auto iter = _locffviafm.begin(); + std::advance(iter, i); + int roomNr = iter->first; + iter->second->writeFF("ffrouterOfRoom" + std::to_string(roomNr) + ".vtk", _allDoorUIDs); + } + } + +// std::ofstream matrixfile; +// matrixfile.open("Matrix.txt"); +// +// for (auto mapItem : _distMatrix) { +// matrixfile << mapItem.first.first << " to " << mapItem.first.second << " : " << mapItem.second << "\t via \t" << _pathsMatrix[mapItem.first]; +// matrixfile << "\t" << _CroTrByUID.at(mapItem.first.first)->GetID() << " to " << _CroTrByUID.at(mapItem.first.second)->GetID() << "\t via \t"; +// matrixfile << _CroTrByUID.at(_pathsMatrix[mapItem.first])->GetID() << std::endl; +// } +// matrixfile.close(); + + for (auto dist : _distMatrix){ + std::cout << dist.first.first << "->" << dist.first.second << ": " << dist.second << std::endl; + } + + Log->Write("INFO: \tFF Router Init done."); + return true; +} + +bool FFRouterTrips::ReInit() +{ + std::cout << "bool FFRouterTrips::ReInit()" << std::endl; + + //cleanse maps + _distMatrix.clear(); + _pathsMatrix.clear(); + + //init, yet no distances, only create map entries + for(auto& id1 : _allDoorUIDs) { + for(auto& id2 : _allDoorUIDs){ + std::pair<int, int> key = std::make_pair(id1, id2); + double value = (id1 == id2)? 0.0 : DBL_MAX; + //distMatrix[i][j] = 0, if i==j + //distMatrix[i][j] = max, else + _distMatrix.insert(std::make_pair( key , value)); + //pathsMatrix[i][j] = i or j ? (follow wiki:path_reconstruction, it should be j) + _pathsMatrix.insert(std::make_pair( key , id2 )); + } + } + + for (auto floorfield : _locffviafm) { + floorfield.second->setSpeedMode(FF_PED_SPEED); + //@todo: ar.graf: create a list of local ped-ptr instead of giving all peds-ptr + floorfield.second->createPedSpeed(_building->GetAllPedestrians().data(), _building->GetAllPedestrians().size(), _mode, 1.); + floorfield.second->recreateAllForQuickest(); + std::vector<int> allDoors(floorfield.second->getKnownDoorUIDs()); + for (auto firstDoor : allDoors) { + for (auto secondDoor : allDoors) { + if (secondDoor <= firstDoor) continue; // calculate every path only once + // if the two doors are not within the same subroom, do not consider (ar.graf) + // should fix problems of oscillation caused by doorgaps in the distancegraph + int thisUID1 = (_CroTrByUID.at(firstDoor)->GetSubRoom1()) ? _CroTrByUID.at(firstDoor)->GetSubRoom1()->GetUID() : -10 ; + int thisUID2 = (_CroTrByUID.at(firstDoor)->GetSubRoom2()) ? _CroTrByUID.at(firstDoor)->GetSubRoom2()->GetUID() : -20 ; + int otherUID1 = (_CroTrByUID.at(secondDoor)->GetSubRoom1()) ? _CroTrByUID.at(secondDoor)->GetSubRoom1()->GetUID() : -30 ; + int otherUID2 = (_CroTrByUID.at(secondDoor)->GetSubRoom2()) ? _CroTrByUID.at(secondDoor)->GetSubRoom2()->GetUID() : -40 ; + + if ( + (thisUID1 != otherUID1) && + (thisUID1 != otherUID2) && + (thisUID2 != otherUID1) && + (thisUID2 != otherUID2) ) { + continue; + } + + //double tempDistance = floorfield.second->getCostToDestination(firstDoor, _CroTrByUID.at(secondDoor)->GetCentre()); + double tempDistance = floorfield.second->getDistanceBetweenDoors(firstDoor, secondDoor); + if (tempDistance < floorfield.second->getGrid()->Gethx()) { + Log->Write("WARNING:\tDistance of doors %d and %d is too small: %f",firstDoor, secondDoor, tempDistance); + //Log->Write("^^^^^^^^\tIf there are scattered subrooms, which are not connected, this is ok."); + continue; + } + std::pair<int, int> key_ij = std::make_pair(secondDoor, firstDoor); + std::pair<int, int> key_ji = std::make_pair(firstDoor, secondDoor); + if (_distMatrix.at(key_ij) > tempDistance) { + _distMatrix.erase(key_ij); + _distMatrix.erase(key_ji); + _distMatrix.insert(std::make_pair(key_ij, tempDistance)); + _distMatrix.insert(std::make_pair(key_ji, tempDistance)); + } + } //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; +} + + +int FFRouterTrips::FindExit(Pedestrian* ped) +{ + std::cout << "Ped[" << ped->GetID() << "] in (" << ped->GetRoomID() << ", " << ped->GetSubRoomID() + << "/" << ped->GetSubRoomUID() << "): " << std::endl; + std::cout << "FinalDestination: " << ped->GetFinalDestination() << std::endl; +// std::cout << "ExitLine: " << ped->GetExitLine() << std::endl; + std::cout << "ExitIndex: " << ped->GetExitIndex() << std::endl << std::endl; + + + // Check if current position is already waiting area + // yes: set next goal and return findExit(p) + int ret = FindExit1(ped); + + std::cout << "Ped[" << ped->GetID() << "] in (" << ped->GetRoomID() << ", " << ped->GetSubRoomID() + << "/" << ped->GetSubRoomUID() << "): " << std::endl; + std::cout << "FinalDestination: " << ped->GetFinalDestination() << std::endl; +// std::cout << "ExitLine: " << ped->GetExitLine() << std::endl; + std::cout << "ExitIndex: " << ped->GetExitIndex() << std::endl; + std::cout << "====================================================" << std::endl; + + return ret; +} + +int FFRouterTrips::FindExit1(Pedestrian* p) +{ + + std::cout << "------ FindExit1 ------" << std::endl; + if (_mode == quickest) { + if (p->GetGlobalTime() > _recalc_interval + && _building->GetRoom(p->GetRoomID())->GetSubRoom(p->GetSubRoomID())->IsInSubRoom(p) + && _locffviafm[p->GetRoomID()]->getCostToDestination(p->GetExitIndex(), p->GetPos()) > 3.0 + && p->GetExitIndex() != -1) { + //delay possible + if ((int) p->GetGlobalTime() % 10 != p->GetID() % 10) { + return p->GetExitIndex(); //stay with old target + } + } + //new version: recalc densityspeed every x seconds + if ((p->GetGlobalTime() > _timeToRecalc) && (p->GetGlobalTime() > Pedestrian::GetMinPremovementTime() + _recalc_interval)) { + _plzReInit = true; + } + } + + double minDist = DBL_MAX; + int bestDoor = -1; + + int goalID = p->GetFinalDestination(); + + std::vector<int> validFinalDoor; //UIDs of doors + validFinalDoor.clear(); + + if (goalID == -1) { + for (auto& pairDoor : _ExitsByUID) { + //we add the all exits, + validFinalDoor.emplace_back(pairDoor.first); //UID + } + } else { //only one specific goal, goalToLineUIDmap gets + //populated in Init() + if ((_goalToLineUIDmap.count(goalID) == 0) || (_goalToLineUIDmap[goalID] == -1)) { + Log->Write("ERROR: \t ffRouter: unknown/unreachable goalID: %d in FindExit(Ped)",goalID); + } else { + validFinalDoor.emplace_back(_goalToLineUIDmap.at(goalID)); + } + } + + std::vector<int> DoorUIDsOfRoom; + DoorUIDsOfRoom.clear(); + if (_building->GetRoom(p->GetRoomID())->GetSubRoom(p->GetSubRoomID())->IsInSubRoom(p->GetPos())) { + //ped is in the subroom, according to its member attribs + } else { + bool located = false; + SubRoom* oldSubRoom = _building->GetRoom(p->GetRoomID())->GetSubRoom(p->GetSubRoomID()); + for (auto& room : _building->GetAllRooms()) { + if (located) {break;} + for (auto& subroom : room.second->GetAllSubRooms()) { + if (subroom.second->IsInSubRoom(p->GetPos()) && subroom.second->IsDirectlyConnectedWith(oldSubRoom)) { + //maybe room on wrong floor + p->SetRoomID(room.second->GetID(), room.second->GetCaption()); + p->SetSubRoomID(subroom.second->GetSubRoomID()); + p->SetSubRoomUID(subroom.second->GetUID()); + located = true; + break; + } + } + } + if (!located) { //ped is outside + return -1; + } + } + DoorUIDsOfRoom.clear(); + if (!_targetWithinSubroom) { + //candidates of current room (ID) (provided by Room) + for (auto transUID : _building->GetRoom(p->GetRoomID())->GetAllTransitionsIDs()) { + if ((_CroTrByUID.count(transUID) != 0) && (_CroTrByUID[transUID]->IsOpen())) { + DoorUIDsOfRoom.emplace_back(transUID); + } + } + for (auto &subIPair : _building->GetRoom(p->GetRoomID())->GetAllSubRooms()) { + for (auto &crossI : subIPair.second->GetAllCrossings()) { + if (crossI->IsOpen()) { + DoorUIDsOfRoom.emplace_back(crossI->GetUniqueID()); + } + } + } + } + else + { + //candidates of current subroom only + for (auto &crossI : _building->GetRoom(p->GetRoomID())->GetSubRoom(p->GetSubRoomID())->GetAllCrossings()) { + if (crossI->IsOpen()) { + DoorUIDsOfRoom.emplace_back(crossI->GetUniqueID()); + } + } + + for (auto &transI : _building->GetRoom(p->GetRoomID())->GetSubRoom(p->GetSubRoomID())->GetAllTransitions()) { + if (transI->IsOpen()) { + DoorUIDsOfRoom.emplace_back(transI->GetUniqueID()); + } + } + } + + int bestFinalDoor = -1; // to silence the compiler + 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 = 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; + } + std::pair<int, int> key = std::make_pair(doorUID, finalDoor); + //auto subroomDoors = _building->GetSubRoomByUID(p->GetSubRoomUID())->GetAllGoalIDs(); + //only consider, if paths exists + if (_pathsMatrix.count(key)==0) { + Log->Write("no key for %d %d", key.first, key.second); + continue; + } + + if ((_distMatrix.count(key)!=0) && (_distMatrix.at(key) != DBL_MAX)) { + if ((_distMatrix.at(key) + locDistToDoor) < minDist) { + minDist = _distMatrix.at(key) + locDistToDoor; + bestDoor = key.first; //doorUID + //if (locDistToDoor == 0.) { + if (true) { + auto subroomDoors = _building->GetSubRoomByUID(p->GetSubRoomUID())->GetAllGoalIDs(); + if (std::find(subroomDoors.begin(), subroomDoors.end(), _pathsMatrix[key]) != subroomDoors.end()) { + bestDoor = _pathsMatrix[key]; //@todo: @ar.graf: check this hack + } + } + bestFinalDoor = key.second; + } + } + } + } + + //at this point, bestDoor is either a crossing or a transition + if ((!_targetWithinSubroom) && (_CroTrByUID.count(bestDoor) != 0)) { + while (!_CroTrByUID[bestDoor]->IsTransition()) { + std::pair<int, int> key = std::make_pair(bestDoor, bestFinalDoor); + bestDoor = _pathsMatrix[key]; + } + } + +//#pragma omp critical(finalDoors) +// _finalDoors.emplace(std::make_pair(p->GetID(), bestFinalDoor)); + + if (_CroTrByUID.count(bestDoor)) { + p->SetExitIndex(bestDoor); + p->SetExitLine(_CroTrByUID.at(bestDoor)); + } + + std::cout << "-----------------------" << std::endl << std::endl; + + return bestDoor; //-1 if no way was found, doorUID of best, if path found +} + +void FFRouterTrips::FloydWarshall() +{ + bool change = false; + double savedDistance = 0.; + int totalnum = _allDoorUIDs.size(); + for(int k = 0; k<totalnum; ++k) { + for(int i = 0; i<totalnum; ++i) { + for(int j= 0; j<totalnum; ++j) { + std::pair<int, int> key_ij = std::make_pair(_allDoorUIDs[i], _allDoorUIDs[j]); + std::pair<int, int> key_ik = std::make_pair(_allDoorUIDs[i], _allDoorUIDs[k]); + std::pair<int, int> key_kj = std::make_pair(_allDoorUIDs[k], _allDoorUIDs[j]); + if ((_distMatrix[key_ik] < DBL_MAX) && (_distMatrix[key_kj] < DBL_MAX) && + (_distMatrix[key_ik] + _distMatrix[key_kj] < _distMatrix[key_ij])) + { + savedDistance = _distMatrix[key_ij] - _distMatrix[key_ik] - _distMatrix[key_kj]; + _distMatrix.erase(key_ij); + _distMatrix.insert(std::make_pair(key_ij, _distMatrix[key_ik] + _distMatrix[key_kj])); + _pathsMatrix.erase(key_ij); + _pathsMatrix.insert(std::make_pair(key_ij, _pathsMatrix[key_ik])); + change = true; + } + } + } + } + if (change) { + //Log->Write("Floyd nochmal!!! %f", savedDistance); + FloydWarshall(); + } else { + Log->Write("INFO:\t FloydWarshall done!"); + } +} + +void FFRouterTrips::SetMode(std::string s) +{ + if (s == "global_shortest"){ + _mode = global_shortest; + return; + } + + if (s == "quickest") { + _mode = quickest; + return; + } + + _mode = global_shortest; + return; +} + +bool FFRouterTrips::MustReInit() { + return _plzReInit; +} + +void FFRouterTrips::SetRecalc(double t) { + _timeToRecalc = t + _recalc_interval; +} \ No newline at end of file diff --git a/routing/ff_router_trips/ffRouterTrips.h b/routing/ff_router_trips/ffRouterTrips.h new file mode 100644 index 0000000000000000000000000000000000000000..5b4396bb8d07b5507c953913d88f8dd6e54d40df --- /dev/null +++ b/routing/ff_router_trips/ffRouterTrips.h @@ -0,0 +1,232 @@ +/** + * \file ffRouter.h + * \date Feb 19, 2016 + * \version v0.8 + * \copyright <2016-2022> Forschungszentrum Jülich GmbH. All rights reserved. + * + * \section License + * This file is part of JuPedSim. + * + * JuPedSim is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * JuPedSim 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 Lesser General Public License + * along with JuPedSim. If not, see <http://www.gnu.org/licenses/>. + * + * \section Description + * This router is an update of the former Router.{cpp, h} - Global-, Quickest + * Router System. In the __former__ version, a graph was created with doors and + * hlines as nodes and the distances of (doors, hlines), connected with a line- + * of-sight, was used as edge-costs. If there was no line-of-sight, there was no + * connecting edge. On the resulting graph, the Floyd-Warshall algorithm was + * used to find any paths. In the "quickest-___" variants, the edge cost was not + * determined by the distance, but by the distance multiplied by a speed- + * estimate, to find the path with minimum travel times. This whole construct + * worked pretty well, but dependend on hlines to create paths with line-of- + * sights to the next target (hline/door). + * + * In the ffRouter, we want to overcome hlines by using floor fields to + * determine the distances. A line of sight is not required any more. We hope to + * reduce the graph complexity and the preparation-needs for new geometries. + * + * To find a counterpart for the "quickest-____" router, we can either use + * __special__ floor fields, that respect the travel time in the input-speed map, + * or take the distance-floor field and multiply it by a speed-estimate (analog + * to the former construct. + * + * We will derive from the <Router> class to fit the interface. + * + * Questions to solve: how to deal with goalID == doorID problem in matrix + * + * Restrictions/Requirements: Floorfields are not really 3D supporting: + * + * A room may not consist of subrooms which overlap in their projection (onto + * x-y-plane). So subrooms, that are positioned on top of others (in stairways + * for example), must be separated into different rooms. + * + * floorfields do not consider z-coordinates. Distances of two grid points are + * functions of (x, y) and not (x, y, z). Any slope will be neglected. + * + **/ + +#ifndef FFROUTERTRIPS_H_ +#define FFROUTERTRIPS_H_ + + +#include "../Router.h" +#include "../../general/Macros.h" +#include "../../geometry/Building.h" +#include "FloorfieldViaFM.h" +#include "UnivFFviaFM.h" + +class Building; +class Pedestrian; +class OutputHandler; + +//log output +extern OutputHandler* Log; + +/*! + * \class FFRouter + * + * \brief router using floor fields to measure distances of doors, no hlines + * + *\ingroup Router + * This router is an update of the former Router.{cpp, h} - Global-, Quickest + * Router System. In the __former__ version, a graph was created with doors and + * hlines as nodes and the distances of (doors, hlines), connected with a line- + * of-sight, was used as edge-costs. If there was no line-of-sight, there was no + * connecting edge. On the resulting graph, the Floyd-Warshall algorithm was + * used to find any paths. In the "quickest-___" variants, the edge cost was not + * determined by the distance, but by the distance multiplied by a speed- + * estimate, to find the path with minimum travel times. This whole construct + * worked pretty well, but dependend on hlines to create paths with line-of- + * sights to the next target (hline/door). + * + * In the ffRouter, we want to overcome hlines by using floor fields to + * determine the distances. A line of sight is not required any more. We hope to + * reduce the graph complexity and the preparation-needs for new geometries. + * + * To find a counterpart for the "quickest-____" router, we can either use + * __special__ floor fields, that respect the travel time in the input-speed map, + * or take the distance-floor field and multiply it by a speed-estimate (analog + * to the former construct. + * + * We will derive from the <Router> class to fit the interface. + * + * \author Arne Graf + * \date Feb, 2016 + */ + +class FFRouterTrips : public Router +{ +public: + /** + * A constructor. + * + */ + FFRouterTrips(); + FFRouterTrips(int id, RoutingStrategy s, bool hasSpecificGoals, Configuration* config); + //FFRouter(const Building* const); + + /** + * Destructor + */ + virtual ~FFRouterTrips(); + + /*! + * \brief Init the router (must be called before use) + * + * Init() will construct the graph (nodes = doors, edges = costs) and + * find shortest paths via Floyd-Warshall. It needs the floor fields + * + * + * \param[in] [name of input parameter] [its description] + * \param[out] [name of output parameter] [its description] + * \return [information about return value] + * \sa [see also section] + * \note [any note about the function you might have] + * \warning [any warning if necessary] + */ + virtual bool Init(Building* building); + + /*! + * \brief ReInit the router if quickest router is used. Current position of agents is considered. + * + * ReInit() will reconstruct the graph (nodes = doors, edges = costs) and + * find shortest paths via Floyd-Warshall. It will reconstruct the floorfield to + * evaluate the best doors to certain goals as they could change. + * + * + * \param[in] [name of input parameter] [its description] + * \param[out] [name of output parameter] [its description] + * \return [information about return value] + * \sa [see also section] + * \note [any note about the function you might have] + * \warning [any warning if necessary] + */ + virtual bool ReInit(); + + /*! + * \brief interface used by __Pedestrian__, sets (*p).exitline/.exitindex + * + * additional info: not available + * + */ + virtual int FindExit(Pedestrian* p); + + /*! + * \brief interface used by __Pedestrian__, sets (*p).exitline/.exitindex + * + * additional info: not available + * + */ + int FindExit1(Pedestrian* p); + + + /*! + * \brief Perform the FloydWarshall algorithm + */ + void FloydWarshall(); + +// /*! +// * \brief Sets the door that leaves the subroom in _pathsMatrix +// * +// * Due to the way we calculate door distances (entries in _pathsMatrix), pedestrians in a corridor +// * tend to jump from door to door, i.e. they walk to the next door in the correct direction, but they +// * do not traverse it. This algorithm searches for the door on the way that really leaves the subroom, +// * and sets this door in _pathsMatrix, which in turn is needed by GetPresumableExitRoute(). +// */ +// void AvoidDoorHopping(); + + /*! + * \brief set mode (shortest, quickest, ...) + */ + void SetMode(std::string s); + bool MustReInit(); + void SetRecalc(double t); + +private: + +protected: + Configuration* _config; + std::map< std::pair<int, int> , double > _distMatrix; + std::map< std::pair<int, int> , int > _pathsMatrix; + //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; + std::map<int, Transition*> _TransByUID; + std::map<int, Transition*> _ExitsByUID; + std::map<int, Crossing*> _CroTrByUID; + + std::map<int, int> _goalToLineUIDmap; //key is the goalID and value is the UID of closest transition -> it maps goal to LineUID + std::map<int, int> _goalToLineUIDmap2; + std::map<int, int> _goalToLineUIDmap3; + std::map<int, int> _finalDoors; // _finalDoors[i] the UID of the last door the pedestrian with ID i wants to walk through + + int _mode; + double _timeToRecalc = 0.; + double _recalc_interval; + bool _plzReInit = false; + bool _hasSpecificGoals; + bool _targetWithinSubroom; + // If we use CentrePointDistance (i.e. CentrePointLocalFFViaFM), some algorithms can maybe be simplified + // (AvoidDoorHopping and _subroomMatrix might be unnecessary, and some code in FindExit could go). --f.mack + bool _useCentrePointDistance = true; + //output filename counter: cnt + static int _cnt; +}; + +#endif /* FFROUTER_H_ */ diff --git a/routing/ff_router_trips/mesh/RectGrid.h b/routing/ff_router_trips/mesh/RectGrid.h new file mode 100644 index 0000000000000000000000000000000000000000..d12273a6ddcbde61f16b603e8e3e27eb2987004b --- /dev/null +++ b/routing/ff_router_trips/mesh/RectGrid.h @@ -0,0 +1,257 @@ +/** + * \file RectGrid.h + * \date Mar 05, 2014 + * \version v0.5 + * \copyright <2009-2014> Forschungszentrum Jülich GmbH. All rights reserved. + * + * \section License + * This file is part of JuPedSim. + * + * JuPedSim is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * JuPedSim 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 Lesser General Public License + * along with JuPedSim. If not, see <http://www.gnu.org/licenses/>. + * + * \section Description + * Header of class for rectangular grids. + * + * + **/ + +#ifndef RECTGRID_H +#define RECTGRID_H + +#include "../../../geometry/Point.h" + +// geometric interpretation of index in "directNeighbor" +// x (1) ^ y +// | | +// (2) x----0----x (0) o---> x +// | +// x (3) + +typedef struct directNeighbor_t { + long int key[4]; +} directNeighbor; + + +class RectGrid +{ + public: + RectGrid() { + this->isInitialized = false; + } + + virtual ~RectGrid() { + } + + RectGrid(const RectGrid& other) { + nPoints = other.nPoints; + xMin = other.xMin; + yMin = other.yMin; + xMax = other.xMax; + yMax = other.yMax; + hx = other.hx; + hy = other.hy; + iMax = other.iMax; + jMax = other.jMax; + isInitialized = other.isInitialized; + } + + RectGrid( long int nPointsArg, + double xMinArg, + double yMinArg, + double xMaxArg, + double yMaxArg, + double hxArg, + double hyArg, + long int iMaxArg, // indices must be smaller than iMax + long int jMaxArg, // indices must be smaller than jMax + bool isInitializedArg) { + nPoints = nPointsArg; // must not be corrupted.. + xMin = xMinArg; + yMin = yMinArg; + xMax = xMaxArg; + yMax = yMaxArg; + hx = hxArg; + hy = hyArg; + iMax = iMaxArg; + jMax = jMaxArg; + isInitialized = isInitializedArg; + } + + long int GetnPoints() const { return nPoints; } + //void SetnPoints(long int val) { nPoints = val; } + double GetxMin() const { return xMin; } + void SetxMin(double val) { if (!isInitialized) xMin = val; } + double GetyMin() const { return yMin; } + void SetyMin(double val) { if (!isInitialized) yMin = val; } + double GetxMax() const { return xMax; } + void SetxMax(double val) { if (!isInitialized) xMax = val; } + double GetyMax() const { return yMax; } + void SetyMax(double val) { if (!isInitialized) yMax = val; } + long int GetiMax() const { return iMax; } + //void SetiMax(long int val) { iMax = val; } + long int GetjMax() const { return jMax; } + //void SetjMax(long int val) { jMax = val; } + double Gethx() const { return hx; } + //void Sethx(double val) { hx = val; } + double Gethy() const { return hy; } + //void Sethy(double val) { hy = val; } + + double get_x_fromKey (long int key) const { return (key%iMax)*hx+xMin; } + double get_y_fromKey (long int key) const { return (key/iMax)*hy+yMin; } + double get_i_fromKey (long int key) const { return (key%iMax); } + double get_j_fromKey (long int key) const { return (key/iMax); } + + long int getKeyAtXY(const double x, const double y) const {//key = index in (extern managed) array + //Point nearest = getNearestGridPoint(Point(x,y)); + long int i = (long int)(((x-xMin)/hx)+.5); + long int j = (long int)(((y-yMin)/hy)+.5); + if ((i >= 0) && (i <= iMax) && (j >= 0) && (j <= jMax)) //@todo: ar.graf: check in #ifdef block + return (j*iMax+i); // 0-based; index of (closest gridpoint) + std::cerr << "ERROR 1 in RectGrid::getKeyAtPoint with:" << std::endl; + std::cerr << " Point: " << x << ", " << y << std::endl; + std::cerr << " xMin, yMin: " << xMin << ", " << yMin << std::endl; + std::cerr << " xMax, yMax: " << xMax << ", " << yMax << std::endl; + std::cerr << " Point is out of Grid-Scope, Tip: check if correct Floorfield is called" << std::endl; + return -1; // invalid indices + } + + long int getKeyAtPoint(const Point p) const { + long int i = (long int) (((p._x-xMin)/hx)+.5); + long int j = (long int) (((p._y-yMin)/hy)+.5); + //if ((i >= 0) && (i <= iMax) && (j >= 0) && (j <= jMax)) //@todo: ar.graf: check in #ifdef block + if (includesPoint(p)) //@todo: ar.graf: this if must be made work + return (j*iMax+i); // 0-based; index of (closest gridpoint) + else { + if (p._x < xMin) { std::cerr << "Out of left bound by: " << (xMin - p._x) << std::endl; i = 0; } + if (p._x > xMax) { std::cerr << "Out of right bound by: " << (p._x - xMax) << std::endl; i = iMax; } + if (p._y < yMin) { std::cerr << "Out of lower bound by: " << (yMin - p._y) << std::endl; j = 0; } + if (p._y > yMax) { std::cerr << "Out of upper bound by: " << (p._y - yMax) << std::endl; j = jMax; } + return (j * iMax + i); + } + std::cerr << "ERROR 2 in RectGrid::getKeyAtPoint with:" << std::endl; + std::cerr << " Point: " << p._x << ", " << p._y << std::endl; + std::cerr << " xMin, yMin: " << xMin << ", " << yMin << std::endl; + std::cerr << " xMax, yMax: " << xMax << ", " << yMax << std::endl; + std::cerr << " Point is out of Grid-Scope, Tip: check if correct Floorfield is called" << std::endl; + return -1; // invalid indices + } + + void setBoundaries(const double xMinA, const double yMinA, + const double xMaxA, const double yMaxA) { + if (!isInitialized) { + xMin = xMinA; + xMax = xMaxA; + yMin = yMinA; + yMax = yMaxA; + } + } + + void setBoundaries(const Point xy_min, const Point xy_max) { + if (!isInitialized) { + xMin = xy_min._x; + xMax = xy_max._x; + yMin = xy_min._y; + yMax = xy_max._y; + } + } + + void setSpacing(const double h_x, const double h_y) { + if (!isInitialized) { + hy = h_y; + hx = h_x; + } + } + + void createGrid(){ //what if cast chops off float, if any changes: get_x_fromKey still correct? + if (!isInitialized) { + iMax = (long int)((xMax-xMin)/hx) + 2; //check plus 2 (one for ceil, one for starting point) + jMax = (long int)((yMax-yMin)/hy) + 2; + nPoints = iMax * jMax; + //@todo: see if necessary to align xMax/yMax + xMax = xMin + iMax*hx; + yMax = yMin + jMax*hy; + isInitialized = true; + } + } + + Point getNearestGridPoint(const Point& currPoint) const { + //if ((currPoint._x > xMax) || (currPoint._y > yMax) || + // (currPoint._x < xMin) || (currPoint._y < yMin)) { + if (!includesPoint(currPoint)) { + std::cerr << "ERROR 3 in RectGrid::getKeyAtPoint with:" + << std::endl; + std::cerr << " Point: " << currPoint._x << ", " << currPoint._y << std::endl; + std::cerr << " xMin, yMin: " << xMin << ", " << yMin + << std::endl; + std::cerr << " xMax, yMax: " << xMax << ", " << yMax + << std::endl; + std::cerr + << " Point is out of Grid-Scope, Tip: check if correct Floorfield is called" + << std::endl; + return Point(-7, -7); + } + long int i = (long int)(((currPoint._x-xMin)/hx)+.5); + long int j = (long int)(((currPoint._y-yMin)/hy)+.5); + return Point(i*hx+xMin, j*hy+yMin); + } + + Point getPointFromKey(const long int key) const { + long int i = key%iMax; + long int j = key/iMax; //integer division + + return Point(i*hx+xMin, j*hy+yMin); + } + + directNeighbor getNeighbors(const long int key) const { + directNeighbor neighbors = {{-1, -1, -1, -1}}; //curleybrackets for struct, then for int[4] + long int i = get_i_fromKey(key); + long int j = get_j_fromKey(key); + + //right //-2 marks invalid neighbor + neighbors.key[0] = (i == (iMax-1)) ? -2 : (j*iMax+i+1); + //upper + neighbors.key[1] = (j == (jMax-1)) ? -2 : ((j+1)*iMax+i); + //left + neighbors.key[2] = (i == 0) ? -2 : (j*iMax+i-1); + //lower + neighbors.key[3] = (j == 0) ? -2 : ((j-1)*iMax+i); + + return neighbors; + } + + bool includesPoint(const Point& point) const { + if ((point._x < (xMin-hx/2)) || + (point._x > (xMax+hx/2)) || + (point._y < (yMin-hy/2)) || + (point._y > (yMax+hy/2))) { + return false; + } + return true; + } + + protected: + private: + long int nPoints; + double xMin; + double yMin; + double xMax; + double yMax; + double hx; + double hy; + long int iMax; // indices must be smaller than iMax + long int jMax; // indices must be smaller than jMax + bool isInitialized; +}; + +#endif // RECTGRID_H diff --git a/routing/ff_router_trips/mesh/Trial.h b/routing/ff_router_trips/mesh/Trial.h new file mode 100644 index 0000000000000000000000000000000000000000..a934fe1b2941a7ae272a61bd39cdece0ebe64091 --- /dev/null +++ b/routing/ff_router_trips/mesh/Trial.h @@ -0,0 +1,172 @@ +/** + * \file + * \date Mar 26, 2015 + * \version N/A (v0.6) + * \copyright <2009-2014> Forschungszentrum Jülich GmbH. All rights reserved. + * + * \section License + * This file is part of JuPedSim. + * + * JuPedSim is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * JuPedSim 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 Lesser General Public License + * along with JuPedSim. If not, see <http://www.gnu.org/licenses/>. + * + * \section Description + * Implementation of classes for a double linked list with smallest at start + * and biggest element at the end (biggest) and father < child relation. + * As updates can break relations, the function lift assures smallest elem at + * top of linked list (smallest). A true sort could be extended - keep this for + * later. + * + * + **/ + +#ifndef TRIAL_H +#define TRIAL_H + +#include "../../../geometry/Point.h" + +class Trial // father := smaller; child := bigger (in terms of cost); cost/speed := ptr to its cost/speed +{ + public: + long int key; + int* flag; + Trial* child; + Trial* father; + double* cost; //pointer to its cost (cost = &costarray[key]) + double* speed;//pointer to its speed (speed = &speedarray[key]) + Point* neggrad; + + Trial() { + key = 0; + flag = nullptr; + child = nullptr; + father = nullptr; + cost = nullptr; + speed = nullptr; + neggrad = nullptr; + } + + Trial(long int keyArg, Trial* fatherArg, Trial* childArg, double* t, double* f, int* flagArg, Point* neggradArg) { + key = keyArg; + father = fatherArg; + child = childArg; + cost = t; + speed = f; + flag = flagArg; + neggrad = neggradArg; + } + + virtual ~Trial() {} + + // insertion (in order) + void insert(Trial* &smallest, Trial* &biggest, Trial* add) { + if(smallest != nullptr) { + //long int key_curr = smallest->key; + if (smallest->cost[0] > add->cost[0]) { + add->child = smallest; + add->father = smallest->father; + smallest->father = add; + smallest = add; + } else { + insert(smallest->child, biggest, add); + } + } else { //wenn add der groesste wert ist, wird er durchgereich und muss korr am ende eingebunden werden. + smallest = add; //wird anstelle des nullptr eingefuegt (smallest ist nicht immer der global smallest sondern auch das rekursionsargument) + if (biggest == nullptr) { //indicator, dass keine liste existierte, also setze smallest und biggest auf add + add->father = nullptr; + } else { //es gab eine liste und das vorher letzte (biggest) wird nun vorletzter + add->father = biggest; + } + + add->child = nullptr; //add ist letzter, dahinter nichts + biggest = add; //add wird neuer letzter (bzw. einziger im sonderfall "alles leer") + } + } + +// void sink(Trial* &smallest, Trial* &biggest) { //only asserts that biggest is at end +// if (smallest != nullptr) { +// Trial* aux; +// aux = smallest->child; +// if (aux != nullptr) { +// if (smallest->cost[0] > aux->cost[0]) { +// if (aux->child != nullptr) { +// (aux->child)->father = smallest; +// } else { +// biggest = smallest; +// } +// if (smallest->father != nullptr) { +// (smallest->father)->child = aux; +// } +// aux->father = smallest->father; +// smallest->child = aux->child; +// smallest->father = aux; +// aux->child = smallest; +// } +// sink(aux, biggest); +// } +// } +// } + + void lift(Trial* &smallest, Trial* &biggest) { //only asserts that smallest is at start + if (biggest != nullptr) { + Trial* aux; + aux = biggest->father; + if (aux != nullptr) { + if (biggest->cost[0] < aux->cost[0]) { + if (aux->father != nullptr) { + (aux->father)->child = biggest; + } else { + smallest = biggest; + } + if (biggest->child != nullptr) { + (biggest->child)->father = aux; + } + aux->child = biggest->child; + biggest->father = aux->father; + biggest->child = aux; + aux->father = biggest; + } + lift(smallest, aux); + } + } + } + + void removecurr(Trial* &smallest, Trial* &biggest, Trial* curr) { + if (smallest == curr && biggest == curr) { + smallest = nullptr; + biggest = nullptr; + } else { + if (smallest == curr) { + curr->child->father = nullptr; + smallest = curr->child; + } else { + if (biggest == curr) { + curr->father->child = nullptr; + biggest = curr->father; + } else { + curr->father->child = curr->child; + curr->child->father = curr->father; + } + } + } + curr->father = nullptr; + curr->child = nullptr; + } + + + + protected: + private: +}; + +#endif // TRIAL_H diff --git a/routing/global_shortest_trips/AccessPoint.cpp b/routing/global_shortest_trips/AccessPoint.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b7d0acd1173b5f7027ecb986b5e2dcd251a93218 --- /dev/null +++ b/routing/global_shortest_trips/AccessPoint.cpp @@ -0,0 +1,315 @@ +/** + * \file AccessPoint.cpp + * \date Aug 24, 2010 + * \version v0.7 + * \copyright <2009-2015> Forschungszentrum Jülich GmbH. All rights reserved. + * + * \section License + * This file is part of JuPedSim. + * + * JuPedSim is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * JuPedSim 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 Lesser General Public License + * along with JuPedSim. If not, see <http://www.gnu.org/licenses/>. + * + * \section Description + * + * + **/ + + +#include "AccessPoint.h" + +using namespace std; + + +AccessPoint::AccessPoint(int id, double center[2],double radius) +{ + _id=id; + _center[0]=center[0]; + _center[1]=center[1]; + _radius=radius; + _finaExitToOutside=false; + _finalGoalOutside=false; + _room1ID=-1; + _room2ID=-1; + _connectingAPs.clear(); + _mapDestToDist.clear(); + pCentre=Point(center[0],center[1]); + _transitPedestrians = vector<Pedestrian*>(); + _connectingAPs = vector<AccessPoint*>(); + _isClosed=0; + _navLine=nullptr; +} + +AccessPoint::~AccessPoint() +{ + if(_navLine) delete _navLine; +} + +int AccessPoint::GetID() +{ + return _id; +} + +int AccessPoint::IsClosed() +{ + return _isClosed; +} + +void AccessPoint::SetClosed(int isClosed) +{ + _isClosed=isClosed; +} +void AccessPoint::SetFinalExitToOutside(bool isFinal) +{ + _finaExitToOutside=isFinal; +} + +bool AccessPoint::GetFinalExitToOutside() +{ + return _finaExitToOutside; +} + +const Point& AccessPoint::GetCentre() const +{ + return pCentre; +} + +void AccessPoint::SetFinalGoalOutside(bool isFinal) +{ + _finalGoalOutside=isFinal; +} + +bool AccessPoint::GetFinalGoalOutside() +{ + return _finalGoalOutside; +} + +//TODO: possibly remove +void AccessPoint::AddIntermediateDest(int final, int inter) +{ + _mapDestToAp[final]=inter; +} + +void AccessPoint::AddFinalDestination(int UID, double distance) +{ + _mapDestToDist[UID]=distance; +} + +double AccessPoint::GetDistanceTo(int UID) +{ + //this is probably a final destination + if(_mapDestToDist.count(UID)==0) { + Log->Write("ERROR:\tNo route to destination [ %d ]",UID); + Log->Write("ERROR:\tCheck your configuration file"); + Dump(); + //return 0; + exit(EXIT_FAILURE); + } + return _mapDestToDist[UID]; +} + +double AccessPoint::GetDistanceTo(AccessPoint* ap) +{ + return (pCentre-ap->GetCentre()).Norm(); +} + +void AccessPoint::AddConnectingAP(AccessPoint* ap) +{ + //only add of not already inside + for(unsigned int p=0; p<_connectingAPs.size(); p++) { + if(_connectingAPs[p]->GetID()==ap->GetID()) return; + } + _connectingAPs.push_back(ap); +} + +//TODO: remove this one +int AccessPoint::GetNextApTo(int UID) +{ + //this is probably a final destination + if(_mapDestToAp.count(UID)==0) { + Log->Write("ERROR:\tNo route to destination [ %d ]",UID); + Log->Write("ERROR:\t Did you forget to define the goal in the configuration file?"); + Dump(); + exit(EXIT_FAILURE); + } + return _mapDestToAp[UID]; +} + +int AccessPoint::GetNearestTransitAPTO(int UID) +{ + const vector <AccessPoint*>& possibleDest=_navigationGraphTo[UID]; + + if(possibleDest.size()==0) { + return -1; + } else if (possibleDest.size()==1) { + return possibleDest[0]->GetID(); + } else { + AccessPoint* best_ap=possibleDest[0]; + double min_dist=GetDistanceTo(best_ap) + best_ap->GetDistanceTo(UID); // FIXME: add the shortest distance to outside + + for (unsigned int i=0; i<possibleDest.size(); i++) { + double tmp= GetDistanceTo(possibleDest[i]); + if(tmp<min_dist) { + min_dist=tmp; + best_ap=possibleDest[i]; + } + } + return best_ap->GetID(); + } +} + +void AccessPoint::setConnectingRooms(int r1, int r2) +{ + _room1ID=r1; + _room2ID=r2; +} + +double AccessPoint::DistanceTo(double x, double y) +{ + + return sqrt((x-_center[0])*(x-_center[0]) + (y-_center[1])*(y-_center[1])); + //return _navLine->DistTo(Point(x,y)); +} + + +bool AccessPoint::isInRange(int roomID) +{ + if((roomID!=_room1ID) && (roomID!=_room2ID)) { + return false; + } + return true; +} + +bool AccessPoint::IsInRange(double xPed, double yPed, int roomID) +{ + + if((roomID!=_room1ID)&& (roomID!=_room2ID)) { + return false; + } + if (((xPed - _center[0]) * (xPed - _center[0]) + (yPed - _center[1]) * (yPed + - _center[1])) <= _radius * _radius) + return true; + + return false; +} + +void AccessPoint::SetNavLine(NavLine* line) +{ + //todo: check this + //_navLine= line; + //_navLine->SetPoint1(line->GetPoint1()); + //_navLine->SetPoint2(line->GetPoint2()); + _navLine= new NavLine(*line); +} + +NavLine* AccessPoint::GetNavLine() const +{ + return _navLine; +} + +const vector <AccessPoint*>& AccessPoint::GetConnectingAPs() +{ + return _connectingAPs; +} + +void AccessPoint::RemoveConnectingAP(AccessPoint* ap) +{ + vector<AccessPoint*>::iterator it; + it = find (_connectingAPs.begin(), _connectingAPs.end(), ap); + if(it==_connectingAPs.end()) { + cout<<" there is no connection to AP: "<< ap->GetID()<<endl; + } else { + _connectingAPs.erase(it); + } +} + +const vector <AccessPoint*>& AccessPoint::GetTransitAPsTo(int UID) +{ + return _navigationGraphTo[UID]; +} + +void AccessPoint::AddTransitAPsTo(int UID,AccessPoint* ap) +{ + _navigationGraphTo[UID].push_back(ap); +} + +void AccessPoint::Reset(int UID) +{ + _navigationGraphTo[UID].clear(); +} + + +void AccessPoint::SetFriendlyName(const std::string& name) +{ + _friendlyName=name; +} + + +const std::string AccessPoint::GetFriendlyName() +{ + return _friendlyName; +} + + +void AccessPoint::Dump() +{ + + cout<<endl<<"--------> Dumping AP <-----------"<<endl<<endl; + //cout<<" ID: " <<_id<<" centre = [ "<< _center[0] <<", " <<_center[1] <<" ]"<<endl; + cout<<" Friendly ID: " <<_friendlyName<<" centre = [ "<< _center[0] <<", " <<_center[1] <<" ]"<<endl; + cout<<" Real ID: " <<_id<<endl; + cout<<" Length: "<<_navLine->LengthSquare()<<endl; + + cout <<" Is final exit to outside :"<<GetFinalExitToOutside()<<endl; + cout <<" Distance to final goals"<<endl; + + for(std::map<int, double>::iterator p = _mapDestToDist.begin(); p != _mapDestToDist.end(); ++p) { + cout<<"\t [ "<<p->first<<", " << p->second<<" m ]"; + } + cout<<endl<<endl; + + cout<<" transit to final goals:"<<endl; + for(std::map<int, std::vector<AccessPoint*> >::iterator p = _navigationGraphTo.begin(); p != _navigationGraphTo.end(); ++p) { + cout<<endl<<"\t to UID ---> [ "<<p->first <<" ]"; + + if(p->second.size()==0) { + cout<<"\t ---> [ Nothing ]"; + } else { + + for(unsigned int i=0; i<p->second.size(); i++) { + cout<<"\t distance ---> [ "<<GetDistanceTo(p->second[i])+p->second[i]->GetDistanceTo(p->first) <<" m via "<<p->second[i]->GetID() <<" ]"; + //cout<<"\t distance ---> [ "<<p->second[i]->GetID()<<" @ " << GetDistanceTo(p->first)<<" ]"; + } + } + } + + cout<<endl<<endl; + + cout<<" connected to aps : " ; + for(unsigned int p=0; p<_connectingAPs.size(); p++) { + //cout<<" [ "<<_connectingAPs[p]->GetID()<<" , "<<_connectingAPs[p]->GetDistanceTo(this)<<" m ]"; + cout<<endl<<"\t [ "<<_connectingAPs[p]->GetID()<<"_"<<_connectingAPs[p]->GetFriendlyName()<<" , "<<_connectingAPs[p]->GetDistanceTo(this)<<" m ]"; + } + + cout<<endl<<endl; + cout <<" queue [ "; + for(unsigned int p=0; p<_transitPedestrians.size(); p++) { + cout<<" "<<_transitPedestrians[p]->GetID(); + } + cout<<" ]"<<endl; + + //cout<<endl<<" connected to rooms: "<<_room1ID<<" and "<<_room2ID<<endl; + cout<<endl; + cout<<endl<<"------------------------------"<<endl<<endl; + +} diff --git a/routing/global_shortest_trips/AccessPoint.h b/routing/global_shortest_trips/AccessPoint.h new file mode 100644 index 0000000000000000000000000000000000000000..a214729ca44e30c909e1df46ec3db107eca948d6 --- /dev/null +++ b/routing/global_shortest_trips/AccessPoint.h @@ -0,0 +1,228 @@ +/** + * \file AccessPoint.h + * \date Aug 24, 2010 + * \version v0.7 + * \copyright <2009-2015> Forschungszentrum J�lich GmbH. All rights reserved. + * + * \section License + * This file is part of JuPedSim. + * + * JuPedSim is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * JuPedSim 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 Lesser General Public License + * along with JuPedSim. If not, see <http://www.gnu.org/licenses/>. + * + * \section Description + * + * + **/ + + +#ifndef ACCESSPOINT_H_ +#define ACCESSPOINT_H_ + +#include <map> +#include <vector> +#include <cmath> +#include <iostream> +#include <string> +#include <algorithm> +#include "../../pedestrian/Pedestrian.h" +#include "../../geometry/Point.h" +#include "../../geometry/Line.h" + +class AccessPoint { +public: + + /** + * create a new access point with the provided data + * + * @param id + * @param center + * @param radius + */ + + AccessPoint(int id, double center[2],double radius=0.30); + + /** + * + * destroy the access point + */ + virtual ~AccessPoint(); + + /** + * @return the Id of the access point + */ + int GetID(); + + /** + * determines if the given coordinates (x ,y) are in the range of that Aps. + * + * @param x + * @param y + * @return + */ + bool IsInRange(double xPed, double yPed, int roomID); + + /** + * given the actual room of the pedestrian + * determine if that AP is 'visible' + */ + + bool isInRange(int roomID); + + /** + * each AP is connected to at most 2 rooms + * @param r1 + * @param r1 + */ + void setConnectingRooms(int r1, int r2); + + int GetConnectingRoom1() + { + return _room1ID; + } + + int GetConnectingRoom2() + { + return _room2ID; + } + + /** + * return the distance to the point x ,y + * @param x + * @param y + * @return + */ + double DistanceTo(double x, double y); + + /** + * set/get a human readable name for this points. + * t23 means Transition with id 23 + * c23 means Crossing with id 23 + * h23 means Hlines with id 23 + */ + void SetFriendlyName(const std::string& name); + + /** + * set/get a human readable name for this points. + * t23 means Transition with id 23 + * c23 means Crossing with id 23 + * h23 means Hlines with id 23 + */ + const std::string GetFriendlyName(); + + /** + * dump the class + */ + void Dump(); + + /** + * Set/Get the centre of the navigation line defining this access point + */ + const Point& GetCentre() const; + + /** + * True if this is the last exit that leads to the outside + */ + void SetFinalExitToOutside(bool isFinal); + + /** + * True if this is the last exit that leads to the outside + */ + bool GetFinalExitToOutside(); + + /** + * @return true if the door is closed + */ + int IsClosed(); + + /** + * Close the door + * @param isClosed + */ + void SetClosed(int isClosed); + + /** + * Set/Get the navigation line. + * The direction taken by the pedestrian strongly depends on this line. + */ + void SetNavLine(NavLine* line); + + /** + * Set/Get the navigation line. + * The direction taken by the pedestrian strongly depends on this line. + */ + NavLine* GetNavLine() const; + + /** + * True if this is a goal outside the building + */ + void SetFinalGoalOutside(bool isFinal); + + /** + * True if this is a goal outside the building + */ + bool GetFinalGoalOutside(); + + void AddIntermediateDest(int final, int inter); + void AddFinalDestination(int UID, double distance); + double GetDistanceTo(int UID); + double GetDistanceTo(AccessPoint* ap); + void RemoveConnectingAP(AccessPoint* ap); + + // reset all setting relative to the destination + void Reset(int UID=FINAL_DEST_OUT); + + //FIXME: remove those functions + void AddConnectingAP(AccessPoint* ap); + int GetNextApTo(int UID=FINAL_DEST_OUT); //default is the shortest path to the outside ( -1 ) + const std::vector <AccessPoint*>& GetConnectingAPs(); + + + const std::vector <AccessPoint*>& GetTransitAPsTo(int UID=FINAL_DEST_OUT); + int GetNearestTransitAPTO(int UID=FINAL_DEST_OUT); + void AddTransitAPsTo(int UID,AccessPoint* ap); + +private: + int _id; + double _center[2]; + double _radius; + /// true if this exit leads to outside + bool _finaExitToOutside; + /// true if this a goal outside the building + bool _finalGoalOutside; + int _room1ID; + int _room2ID; + Point pCentre; + NavLine* _navLine; + std::vector<Pedestrian*> _transitPedestrians; + int _isClosed; + std::string _friendlyName; + + // stores the connecting APs + std::vector<AccessPoint*>_connectingAPs; + + // store part of a graph + // map a final destination to the next ap to reach it + // store the nearest AP to reach the destination + std::map<int, int> _mapDestToAp; + + // store part of the weight matrix + // store the total distance to the destination int + std::map <int,double> _mapDestToDist; + + //store the navigation graph + std::map<int,std::vector<AccessPoint*> > _navigationGraphTo; + +}; + +#endif /* ACCESSPOINT_H_ */ diff --git a/routing/global_shortest_trips/ConvexDecomp.h b/routing/global_shortest_trips/ConvexDecomp.h new file mode 100644 index 0000000000000000000000000000000000000000..a88841552231f80f6fcc241f7d6728d31e6fa09e --- /dev/null +++ b/routing/global_shortest_trips/ConvexDecomp.h @@ -0,0 +1,327 @@ +/** + * \file ConvexDecomp.h + * \date Jul 4, 2014 + * \version v0.7 + * \copyright <2009-2015> Forschungszentrum Jülich GmbH. All rights reserved. + * + * \section License + * This file is part of JuPedSim. + * + * JuPedSim is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * JuPedSim 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 Lesser General Public License + * along with JuPedSim. If not, see <http://www.gnu.org/licenses/>. + * + * \section Description + * + * + **/ + + +#ifndef _GEOMETRY_FUNCTIONS_ +#define _GEOMETRY_FUNCTIONS_ + +#ifdef _CGAL +#include <CGAL/basic.h> +#include <CGAL/Exact_predicates_inexact_constructions_kernel.h> +#include <CGAL/Partition_traits_2.h> +#include <CGAL/Partition_is_valid_traits_2.h> +#include <CGAL/polygon_function_objects.h> +#include <CGAL/partition_2.h> +#include <CGAL/point_generators_2.h> +#include <CGAL/random_polygon_2.h> +#include <CGAL/connect_holes.h> +#include <cassert> +#include <list> +// +//#include <CGAL/Partition_traits_2.h> +//#include <CGAL/Partition_is_valid_traits_2.h> +//#include <CGAL/polygon_function_objects.h> +//#include <CGAL/partition_2.h> +//#include <CGAL/point_generators_2.h> +//#include <CGAL/random_polygon_2.h> +// + +#include <CGAL/Constrained_Delaunay_triangulation_2.h> +#include <CGAL/Triangulation_face_base_with_info_2.h> +#include <CGAL/Polygon_2.h> + +#include <CGAL/IO/Geomview_stream.h> +#include <CGAL/Delaunay_triangulation_2.h> +#include <CGAL/Alpha_shape_euclidean_traits_2.h> +#include <CGAL/Alpha_shape_vertex_base_2.h> +#include <CGAL/Triangulation_face_base_2.h> +#include <CGAL/Alpha_shape_face_base_2.h> +#include <CGAL/Alpha_shape_2.h> + +#include <iostream> +#include <cstdio> +#include <cstdlib> + + +#include <CGAL/Cartesian.h> +#include <CGAL/centroid.h> + + +struct FaceInfo2 { + FaceInfo2() {} + int nesting_level; + + bool in_domain() { + return nesting_level%2 == 1; + } +}; + + + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef CGAL::Partition_traits_2<K> Traits; +typedef CGAL::Is_convex_2<Traits> Is_convex_2; +typedef Traits::Polygon_2 Polygon_2; +//typedef CGAL::Polygon_2<K> Polygon_2; +typedef Traits::Point_2 Point_2; +typedef Polygon_2::Vertex_const_iterator Vertex_iterator; +typedef std::list<Polygon_2> Polygon_list; +typedef Polygon_list::const_iterator Polygon_iterator; +typedef CGAL::Partition_is_valid_traits_2<Traits, Is_convex_2> Validity_traits; +typedef Polygon_2::Edge_const_iterator Edge_iterator; + + +typedef CGAL::Triangulation_vertex_base_2<K> Vb; +typedef CGAL::Triangulation_face_base_with_info_2<FaceInfo2,K> Fbb; +typedef CGAL::Constrained_triangulation_face_base_2<K,Fbb> Fb; +typedef CGAL::Triangulation_data_structure_2<Vb,Fb> TDS; +typedef CGAL::Exact_predicates_tag Itag; +typedef CGAL::Constrained_Delaunay_triangulation_2<K, TDS, Itag> CDT; + + + +typedef CGAL::Alpha_shape_vertex_base_2<K> Av; +typedef CGAL::Triangulation_face_base_2<K> Tf; +typedef CGAL::Alpha_shape_face_base_2<K,Tf> Af; +typedef CGAL::Triangulation_default_data_structure_2<K,Av,Af> Tds; +typedef CGAL::Delaunay_triangulation_2<K,Tds> DT; + +typedef CGAL::Polygon_with_holes_2<K> Polygon_with_holes_2; + +typedef CGAL::Segment_2<K> Segment_2; + +//fast delauney +//typedef CGAL::Alpha_shape_vertex_base_3<K> Vb; +//typedef CGAL::Alpha_shape_cell_base_3<K> Fb; +//typedef CGAL::Triangulation_data_structure_3<Vb,Fb> Tds; +//typedef CGAL::Delaunay_triangulation_3<K,Tds,CGAL::Fast_location> Delaunay; +//typedef CGAL::Alpha_shape_3<Delaunay> Alpha_shape_3; +//typedef Alpha_shape_3::Alpha_iterator Alpha_iterator; +//typedef Alpha_shape_3::NT NT; + + +typedef CGAL::Alpha_shape_2<DT> Alpha_shape_2; +typedef Alpha_shape_2::Alpha_iterator Alpha_iterator; +typedef Alpha_shape_2::Vertex_iterator Alpha_vertex_iterator; + + + + +inline +void +mark_domains(CDT& ct, + CDT::Face_handle start, + int index, + std::list<CDT::Edge>& border ) +{ + if(start->info().nesting_level != -1) { + return; + } + std::list<CDT::Face_handle> queue; + queue.push_back(start); + + while(! queue.empty()) { + CDT::Face_handle fh = queue.front(); + queue.pop_front(); + if(fh->info().nesting_level == -1) { + fh->info().nesting_level = index; + for(int i = 0; i < 3; i++) { + CDT::Edge e(fh,i); + CDT::Face_handle n = fh->neighbor(i); + if(n->info().nesting_level == -1) { + if(ct.is_constrained(e)) border.push_back(e); + else queue.push_back(n); + } + } + } + } +} + +//explore set of facets connected with non constrained edges, +//and attribute to each such set a nesting level. +//We start from facets incident to the infinite vertex, with a nesting +//level of 0. Then we recursively consider the non-explored facets incident +//to constrained edges bounding the former set and increase the nesting level by 1. +//Facets in the domain are those with an odd nesting level. +inline +void +mark_domains(CDT& cdt) +{ + for(CDT::All_faces_iterator it = cdt.all_faces_begin(); it != cdt.all_faces_end(); ++it) { + it->info().nesting_level = -1; + } + + int index = 0; + std::list<CDT::Edge> border; + mark_domains(cdt, cdt.infinite_face(), index++, border); + while(! border.empty()) { + CDT::Edge e = border.front(); + border.pop_front(); + CDT::Face_handle n = e.first->neighbor(e.second); + if(n->info().nesting_level == -1) { + mark_domains(cdt, n, e.first->info().nesting_level+1, border); + } + } +} + +inline +void insert_polygon(CDT& cdt,const Polygon_2& polygon) +{ + if ( polygon.is_empty() ) return; + CDT::Vertex_handle v_prev=cdt.insert(*CGAL::cpp0x::prev(polygon.vertices_end())); + for (Polygon_2::Vertex_iterator vit=polygon.vertices_begin(); + vit!=polygon.vertices_end(); ++vit) { + CDT::Vertex_handle vh=cdt.insert(*vit); + cdt.insert_constraint(vh,v_prev); + v_prev=vh; + } +} + +inline +int test_triangulation( ) +{ + //construct two non-intersecting nested polygons + Polygon_2 polygon1; + polygon1.push_back(Point_2(0,0)); + polygon1.push_back(Point_2(2,0)); + polygon1.push_back(Point_2(2,2)); + polygon1.push_back(Point_2(0,2)); + Polygon_2 polygon2; + polygon2.push_back(Point_2(0.5,0.5)); + polygon2.push_back(Point_2(1.5,0.5)); + polygon2.push_back(Point_2(1.5,1.5)); + polygon2.push_back(Point_2(0.5,1.5)); + + //Insert the polyons into a constrained triangulation + CDT cdt; + insert_polygon(cdt,polygon1); + insert_polygon(cdt,polygon2); + + //Mark facets that are inside the domain bounded by the polygon + mark_domains(cdt); + + int count=0; + for (CDT::Finite_faces_iterator fit=cdt.finite_faces_begin(); + fit!=cdt.finite_faces_end(); ++fit) { + if ( fit->info().in_domain() ) ++count; + } + + //cdt.draw_triangulation(std::cout); + std::cout << "There are " << count << " facets in the domain." << std::endl; + + + CGAL::Geomview_stream gv(CGAL::Bbox_3(-100, -100, -100, 100, 100, 100)); + gv.set_line_width(4); + gv.set_trace(true); + gv.set_bg_color(CGAL::Color(0, 200, 200)); + // gv.clear(); + + // use different colors, and put a few sleeps/clear. + //gv << CGAL::BLUE; + //gv.set_wired(true); + + + CDT::Finite_faces_iterator it; + for (it = cdt.finite_faces_begin(); it != cdt.finite_faces_end(); it++) { + std::cout << cdt.triangle(it) << std::endl; + gv << cdt.triangle(it) ; + } + + return 0; +} + +inline +int test_alpha_shape() +{ + std::list<Point_2> lp; + + + lp.push_back(Point_2(0.5,0.5)); + lp.push_back(Point_2(1.5,0.5)); + lp.push_back(Point_2(1.5,1.5)); + lp.push_back(Point_2(0.5,1.5)); + + // compute alpha shape + Alpha_shape_2 as(lp.begin(),lp.end()); + std::cout << "Alpha shape computed in REGULARIZED mode by default" + << std::endl; + + + std::list<Point_2> env=as.Output(); + std::cout <<"size: " <<env.size()<<std::endl; + getc(stdin); + + for (std::list<Point_2>::const_iterator iterator = env.begin(), end = env.end(); iterator != end; ++iterator) { + std::cout << *iterator; + } + + // for (Alpha_vertex_iterator vit = as.Alpha_shape_vertices_begin(); + // vit != as.alpha_shape_vertices_end(); ++vit) { + // + // } + + // find optimal alpha value + Alpha_iterator opt = as.find_optimal_alpha(1); + std::cout << "Optimal alpha value to get one connected component is " + << *opt << std::endl; + as.set_alpha(*opt); + assert(as.number_of_solid_components() == 1); + return 0; +} + +template<class Kernel, class Container> +void print_polygon (const CGAL::Polygon_2<Kernel, Container>& P) +{ + typename CGAL::Polygon_2<Kernel, Container>::Vertex_const_iterator vit; + + std::cout << "[ " << P.size() << " vertices:"; + for (vit = P.vertices_begin(); vit != P.vertices_end(); ++vit) + std::cout << " (" << *vit << ')'; + std::cout << " ]" << std::endl; +} + +template<class Kernel, class Container> +void print_polygon_with_holes(const CGAL::Polygon_with_holes_2<Kernel, Container> & pwh) +{ + if (! pwh.is_unbounded()) { + std::cout << "{ Outer boundary = "; + print_polygon (pwh.outer_boundary()); + } else + std::cout << "{ Unbounded polygon." << std::endl; + + typename CGAL::Polygon_with_holes_2<Kernel,Container>::Hole_const_iterator hit; + unsigned int k = 1; + + std::cout << " " << pwh.number_of_holes() << " holes:" << std::endl; + for (hit = pwh.holes_begin(); hit != pwh.holes_end(); ++hit, ++k) { + std::cout << " Hole #" << k << " = "; + print_polygon (*hit); + } + std::cout << " }" << std::endl; +} +#endif /* _CGAL */ +#endif /* _GEOMETRY_FUNCTIONS_ */ diff --git a/routing/global_shortest_trips/DTriangulation.cpp b/routing/global_shortest_trips/DTriangulation.cpp new file mode 100644 index 0000000000000000000000000000000000000000..546bafebc682cef894ef0d2267014e8252955bf1 --- /dev/null +++ b/routing/global_shortest_trips/DTriangulation.cpp @@ -0,0 +1,78 @@ +/** + * \file DTriangulation.cpp + * \date Nov 30, 2012 + * \version v0.7 + * \copyright <2009-2015> Forschungszentrum Jülich GmbH. All rights reserved. + * + * \section License + * This file is part of JuPedSim. + * + * JuPedSim is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * JuPedSim 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 Lesser General Public License + * along with JuPedSim. If not, see <http://www.gnu.org/licenses/>. + * + * \section Description + * + * + **/ + + +#include "DTriangulation.h" + +using namespace std; + +DTriangulation::DTriangulation() +{ + _cdt=nullptr; +} + +DTriangulation::~DTriangulation() +{ + + for(unsigned int i = 0; i < _holesPolylines.size(); i++) { + vector<p2t::Point*> poly = _holesPolylines[i]; + FreeClear(poly); + } + + FreeClear(_outerConstraintsPolyline); + delete _cdt; +} + +void DTriangulation::Triangulate() +{ + + _cdt= new p2t::CDT(_outerConstraintsPolyline); + + for(unsigned int h=0; h<_holesPolylines.size(); h++) { + _cdt->AddHole(_holesPolylines[h]); + } + _cdt->Triangulate(); +} + +void DTriangulation::SetOuterPolygone(const std::vector<Point>& outPoly) +{ + + for(unsigned int i=0; i<outPoly.size(); i++) { + _outerConstraintsPolyline.push_back(new p2t::Point(outPoly[i]._x,outPoly[i]._y)); + } +} + +void DTriangulation::AddHole(const std::vector<Point>& hole) +{ + + std::vector<p2t::Point*> newHole; + + for(unsigned int i=0; i<hole.size(); i++) { + newHole.push_back(new p2t::Point(hole[i]._x,hole[i]._y)); + } + _holesPolylines.push_back(newHole); +} diff --git a/routing/global_shortest_trips/DTriangulation.h b/routing/global_shortest_trips/DTriangulation.h new file mode 100644 index 0000000000000000000000000000000000000000..69cbd10e5496a5c3702734957906bc7c5b7eb51f --- /dev/null +++ b/routing/global_shortest_trips/DTriangulation.h @@ -0,0 +1,96 @@ +/** + * \file DTriangulation.h + * \date Nov 30, 2012 + * \version v0.7 + * \copyright <2009-2015> Forschungszentrum Jülich GmbH. All rights reserved. + * + * \section License + * This file is part of JuPedSim. + * + * JuPedSim is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * JuPedSim 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 Lesser General Public License + * along with JuPedSim. If not, see <http://www.gnu.org/licenses/>. + * + * \section Description + * Perform the Delauney triangulation of a polygon with holes. + * + * + **/ + + +#ifndef DTRIANGULATION_H_ +#define DTRIANGULATION_H_ + +#include <cstdlib> +#include <time.h> +#include <fstream> +#include <string> +#include <sstream> +#include <algorithm> +#include <iterator> +#include <iostream> +#include <vector> + +#include "../../geometry/Point.h" +#include "../../poly2tri/poly2tri.h" + + +class DTriangulation { +public: + DTriangulation(); + + virtual ~DTriangulation(); + + /** + * Triangulate the specified domain + * \see SetOuterPolygone + * \see AddHole + */ + void Triangulate(); + + /** + * @return the triangles resulting from the triangulation + */ + std::vector<p2t::Triangle*> GetTriangles() { + return _cdt->GetTriangles(); + } + + /** + * Set the boundaries of the domain + * @param outerConstraints + */ + void SetOuterPolygone(const std::vector<Point>& outerConstraints); + + /** + * Add a new hole + * A domain can contains holes. + * They should fully be inside the domain. + */ + void AddHole(const std::vector<Point>& hole); + + //templates for freeing and clearing a vector of pointers + template <class C> void FreeClear( C & cntr ) { + for ( typename C::iterator it = cntr.begin(); + it != cntr.end(); ++it ) { + delete * it; + } + cntr.clear(); + } + +private: + std::vector< std::vector<p2t::Point*> > _holesPolylines; + std::vector<p2t::Point*> _outerConstraintsPolyline; + p2t::CDT* _cdt; + +}; + +#endif /* DTRIANGULATION_H_ */ diff --git a/routing/global_shortest_trips/GlobalRouterTrips.cpp b/routing/global_shortest_trips/GlobalRouterTrips.cpp new file mode 100644 index 0000000000000000000000000000000000000000..aef2dc98bd650322589c2b3839ebcfd420c6b792 --- /dev/null +++ b/routing/global_shortest_trips/GlobalRouterTrips.cpp @@ -0,0 +1,1612 @@ +/** + * \file GlobalRouter.cpp + * \date Dec 15, 2010 + * \version v0.7 + * \copyright <2009-2015> Forschungszentrum J�lich GmbH. All rights reserved. + * + * \section License + * This file is part of JuPedSim. + * + * JuPedSim is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * JuPedSim 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 Lesser General Public License + * along with JuPedSim. If not, see <http://www.gnu.org/licenses/>. + * + * \section Description + * + * + **/ + + +#include "GlobalRouterTrips.h" + +#include "AccessPoint.h" +//#include "Router.h" +//#include "NavMesh.h" +#include "DTriangulation.h" + +//#include "../geometry/Building.h" +//#include "../pedestrian/Pedestrian.h" +#include "../../tinyxml/tinyxml.h" +#include "../../geometry/SubRoom.h" +//#include "../geometry/Wall.h" +//#include "../IO/OutputHandler.h" + +//#include <sstream> +//#include <cfloat> +//#include <fstream> +//#include <iomanip> + + +using namespace std; + +GlobalRouterTrips::GlobalRouterTrips() : + Router() +{ + _accessPoints = map<int, AccessPoint*>(); + _map_id_to_index = std::map<int, int>(); + _map_index_to_id = std::map<int, int>(); + _distMatrix = nullptr; + _pathsMatrix = nullptr; + _building = nullptr; + _edgeCost=100; + // _rdDistribution = uniform_real_distribution<double> (0,1); + // _rdGenerator = default_random_engine(56); + +} + +GlobalRouterTrips::GlobalRouterTrips(int id, RoutingStrategy s) : Router(id, s) +{ + _accessPoints = map<int, AccessPoint*>(); + _map_id_to_index = std::map<int, int>(); + _map_index_to_id = std::map<int, int>(); + _distMatrix = nullptr; + _pathsMatrix = nullptr; + _building = nullptr; + _edgeCost=100; + + // _rdDistribution = uniform_real_distribution<double> (0,1); + // _rdGenerator = default_random_engine(56); + +} + +GlobalRouterTrips::~GlobalRouterTrips() +{ + if (_distMatrix && _pathsMatrix) { + const int exitsCnt = (const int) (_building->GetNumberOfGoals() + _building->GetAllGoals().size()); + for (int p = 0; p < exitsCnt; ++p) { + delete[] _distMatrix[p]; + delete[] _pathsMatrix[p]; + } + + delete[] _distMatrix; + delete[] _pathsMatrix; + } + + map<int, AccessPoint*>::const_iterator itr; + for (itr = _accessPoints.begin(); itr != _accessPoints.end(); ++itr) { + delete itr->second; + } + _accessPoints.clear(); +} + +bool GlobalRouterTrips::Init(Building* building) +{ + //necessary if the init is called several times during the simulation + Reset(); + Log->Write("INFO:\tInit the Global Router Trips Engine"); + _building = building; + //only load the information if not previously loaded + //if(_building->GetNumberOfGoals()==0) + + //TODO: implement the ParseAdditionalParameter Interface + LoadRoutingInfos(GetRoutingInfoFile()); + + if(_generateNavigationMesh) + { + //GenerateNavigationMesh(); + TriangulateGeometry(); + //return true; + } + + // initialize the distances matrix for the floydwahrshall + const int exitsCnt = (const int) (_building->GetNumberOfGoals() + _building->GetAllGoals().size()); + + _distMatrix = new double*[exitsCnt]; + _pathsMatrix = new int*[exitsCnt]; + + for (int i = 0; i < exitsCnt; ++i) { + _distMatrix[i] = new double[exitsCnt]; + _pathsMatrix[i] = new int[exitsCnt]; + } + + // Initializing the values + // all nodes are disconnected + for (int p = 0; p < exitsCnt; ++p) { + for (int r = 0; r < exitsCnt; ++r) { + _distMatrix[p][r] = (r == p) ? 0.0 : FLT_MAX;/*0.0*/ + _pathsMatrix[p][r] = p;/*0.0*/ + } + } + + // init the access points + int index = 0; + for(const auto & itr:_building->GetAllHlines()) + { + //int door=itr->first; + int door = itr.second->GetUniqueID(); + Hline* cross = itr.second; + Point centre = cross->GetCentre(); + double center[2] = { centre._x, centre._y }; + + AccessPoint* ap = new AccessPoint(door, center); + ap->SetNavLine(cross); + char friendlyName[CLENGTH]; + sprintf(friendlyName, "hline_%d_room_%d_subroom_%d", cross->GetID(), + cross->GetRoom1()->GetID(), + cross->GetSubRoom1()->GetSubRoomID()); + ap->SetFriendlyName(friendlyName); + + // save the connecting sub/rooms IDs + int id1 = -1; + if (cross->GetSubRoom1()) { + id1 = cross->GetSubRoom1()->GetUID(); + } + + ap->setConnectingRooms(id1, id1); + _accessPoints[door] = ap; + + //very nasty + _map_id_to_index[door] = index; + _map_index_to_id[index] = door; + index++; + } + + for(const auto & itr:_building->GetAllCrossings()) + { + int door = itr.second->GetUniqueID(); + Crossing* cross = itr.second; + const Point& centre = cross->GetCentre(); + double center[2] = { centre._x, centre._y }; + + AccessPoint* ap = new AccessPoint(door, center); + ap->SetNavLine(cross); + char friendlyName[CLENGTH]; + sprintf(friendlyName, "cross_%d_room_%d_subroom_%d", cross->GetID(), + cross->GetRoom1()->GetID(), + cross->GetSubRoom1()->GetSubRoomID()); + ap->SetFriendlyName(friendlyName); + + ap->SetClosed(!cross->IsOpen()); + // save the connecting sub/rooms IDs + int id1 = -1; + if (cross->GetSubRoom1()) { + id1 = cross->GetSubRoom1()->GetUID(); + } + + int id2 = -1; + if (cross->GetSubRoom2()) { + id2 = cross->GetSubRoom2()->GetUID(); + } + + ap->setConnectingRooms(id1, id2); + _accessPoints[door] = ap; + + //very nasty + _map_id_to_index[door] = index; + _map_index_to_id[index] = door; + index++; + } + + + for(const auto & itr:_building->GetAllTransitions()) + { + int door = itr.second->GetUniqueID(); + Transition* cross = itr.second; + const Point& centre = cross->GetCentre(); + double center[2] = { centre._x, centre._y }; + + AccessPoint* ap = new AccessPoint(door, center); + ap->SetNavLine(cross); + char friendlyName[CLENGTH]; + sprintf(friendlyName, "trans_%d_room_%d_subroom_%d", cross->GetID(), + cross->GetRoom1()->GetID(), + cross->GetSubRoom1()->GetSubRoomID()); + ap->SetFriendlyName(friendlyName); + + ap->SetClosed(!cross->IsOpen()); + // save the connecting sub/rooms IDs + int id1 = -1; + if (cross->GetSubRoom1()) { + id1 = cross->GetSubRoom1()->GetUID(); + } + + int id2 = -1; + if (cross->GetSubRoom2()) { + id2 = cross->GetSubRoom2()->GetUID(); + } + + ap->setConnectingRooms(id1, id2); + _accessPoints[door] = ap; + + //set the final destination + if (cross->IsExit() && cross->IsOpen()) { + ap->SetFinalExitToOutside(true); + Log->Write("INFO: \tExit to outside found: %d [%s]",ap->GetID(),ap->GetFriendlyName().c_str()); + } else if ((id1 == -1) && (id2 == -1)) { + Log->Write("INFO:\t a final destination outside the geometry was found"); + ap->SetFinalExitToOutside(true); + } else if (cross->GetRoom1()->GetCaption() == "outside") { + ap->SetFinalExitToOutside(true); + } + + //very nasty + _map_id_to_index[door] = index; + _map_index_to_id[index] = door; + index++; + } + + // populate the subrooms at the elevation + for(auto && itroom:_building->GetAllRooms()) + { + auto&& room= (shared_ptr<Room>&&) itroom.second; + for(const auto & it_sub:room->GetAllSubRooms()) + { + auto&& sub= (shared_ptr<SubRoom>&&) it_sub.second; + //maybe truncate the elevation. + // because using a double as key to map is not exact + //double elevation = ceilf(sub->GetMaxElevation() * 100) / 100; + //_subroomsAtElevation[elevation].push_back(sub.get()); + _subroomsAtElevation[sub->GetElevation(sub->GetCentroid())].push_back(sub.get()); + } + } + + // loop over the rooms + // loop over the subrooms + // get the transitions in the subrooms + // and compute the distances + + for(auto && itroom:_building->GetAllRooms()) + { + auto&& room= (shared_ptr<Room>&&) itroom.second; + for(const auto & it_sub:room->GetAllSubRooms()) + { + // The penalty factor should discourage pedestrians to evacuation through rooms + auto&& sub= (shared_ptr<SubRoom>&&) it_sub.second; + double penalty=1.0; + if((sub->GetType()!="floor") && (sub->GetType()!="dA") ) { + penalty=_edgeCost; + } + + //collect all navigation objects + vector<Hline*> allGoals; + const auto & crossings = sub->GetAllCrossings(); + allGoals.insert(allGoals.end(), crossings.begin(), crossings.end()); + const auto & transitions = sub->GetAllTransitions(); + allGoals.insert(allGoals.end(), transitions.begin(), + transitions.end()); + const auto & hlines = sub->GetAllHlines(); + allGoals.insert(allGoals.end(), hlines.begin(), hlines.end()); + + + //process the hlines + //process the crossings + //process the transitions + for (unsigned int n1 = 0; n1 < allGoals.size(); n1++) + { + Hline* nav1 = allGoals[n1]; + AccessPoint* from_AP = _accessPoints[nav1->GetUniqueID()]; + int from_door = _map_id_to_index[nav1->GetUniqueID()]; + if(from_AP->IsClosed()) continue; + + for (unsigned int n2 = 0; n2 < allGoals.size(); n2++) { + Hline* nav2 = allGoals[n2]; + AccessPoint* to_AP = _accessPoints[nav2->GetUniqueID()]; + if(to_AP->IsClosed()) continue; + + if (n1 == n2) + continue; + if (nav1->operator ==(*nav2)) + continue; + + //vector<SubRoom*> emptyVector; + //emptyVector.push_back(sub.get()); + //add all subrooms at the same elevation + //double elevation = sub->GetMaxElevation(); + + double elevation = sub->GetElevation(sub->GetCentroid()); + // special case for stairs and for convex rooms + //if() + + if (building->IsVisible(nav1->GetCentre(), nav2->GetCentre(), _subroomsAtElevation[elevation],true)) + { + int to_door = _map_id_to_index[nav2->GetUniqueID()]; + _distMatrix[from_door][to_door] = penalty*(nav1->GetCentre() + - nav2->GetCentre()).Norm(); + from_AP->AddConnectingAP( + _accessPoints[nav2->GetUniqueID()]); + } + } + } + } + } + + //complete the matrix with the final distances between the exits to the outside and the + //final marked goals + + for (int final_dest:_finalDestinations) + { + Goal* goal =_building->GetFinalGoal(final_dest); + const Wall& line=_building->GetFinalGoal(final_dest)->GetAllWalls()[0]; + double center[2] = { goal->GetCentroid()._x, goal->GetCentroid()._y }; + + AccessPoint* to_AP = new AccessPoint(line.GetUniqueID(), center); + to_AP->SetFinalGoalOutside(true); + //NavLine* tmpline=new NavLine(line); + NavLine tmpline(line); + to_AP->SetNavLine(&tmpline); + //delete tmpline; + + char friendlyName[CLENGTH]; + sprintf(friendlyName, "finalGoal_%d_located_outside", goal->GetId()); + to_AP->SetFriendlyName(friendlyName); + to_AP->AddFinalDestination(FINAL_DEST_OUT,0.0); + to_AP->AddFinalDestination(goal->GetId(),0.0); + _accessPoints[to_AP->GetID()] = to_AP; + + //very nasty + _map_id_to_index[to_AP->GetID()] = index; + _map_index_to_id[index] = to_AP->GetID(); + index++; + + //only make a connection to final exit to outside + for(const auto & itr1: _accessPoints) + { + AccessPoint* from_AP = itr1.second; + if(!from_AP->GetFinalExitToOutside()) continue; + if(from_AP->GetID()==to_AP->GetID()) continue; + from_AP->AddConnectingAP(to_AP); + int from_door= _map_id_to_index[from_AP->GetID()]; + int to_door= _map_id_to_index[to_AP->GetID()]; + // I assume a direct line connection between every exit connected to the outside and + // any final goal also located outside + _distMatrix[from_door][to_door] = _edgeCost*from_AP->GetNavLine()->DistTo(goal->GetCentroid()); + + // add a penalty for goals outside due to the direct line assumption while computing the distances + if (_distMatrix[from_door][to_door] > 10.0) + _distMatrix[from_door][to_door]*=100; + } + } + + //run the floyd warshall algorithm + FloydWarshall(); + + // set the configuration for reaching the outside + // set the distances to all final APs + + for(const auto & itr: _accessPoints) + { + AccessPoint* from_AP = itr.second; + int from_door = _map_id_to_index[itr.first]; + if(from_AP->GetFinalGoalOutside()) continue; + + //maybe put the distance to FLT_MAX + if(from_AP->IsClosed()) continue; + + double tmpMinDist = FLT_MAX; + int tmpFinalGlobalNearestID = from_door; + + for(const auto & itr1: _accessPoints) + { + AccessPoint* to_AP = itr1.second; + if(from_AP->GetID()==to_AP->GetID()) continue; + if(from_AP->GetFinalExitToOutside()) continue; + //if(from_AP->GetFinalGoalOutside()) continue; + + if (to_AP->GetFinalExitToOutside()) + { + int to_door = _map_id_to_index[itr1.first]; + if (from_door == to_door) + continue; + + //cout <<" checking final destination: "<< pAccessPoints[j]->GetID()<<endl; + double dist = _distMatrix[from_door][to_door]; + if (dist < tmpMinDist) { + tmpFinalGlobalNearestID = to_door; + tmpMinDist = dist; + } + } + } + + // in the case it is the final APs + if (tmpFinalGlobalNearestID == from_door) + tmpMinDist = 0.0; + + if (tmpMinDist == FLT_MAX) { + Log->Write( + "ERROR: \tGlobalRouter: There is no visibility path from [%s] to the outside 1\n" + " You can solve this by enabling triangulation.", + from_AP->GetFriendlyName().c_str()); + from_AP->Dump(); + return false; + } + + // set the distance to the final destination ( OUT ) + from_AP->AddFinalDestination(FINAL_DEST_OUT, tmpMinDist); + + // set the intermediate path to global final destination + GetPath(from_door, tmpFinalGlobalNearestID); + + if (_tmpPedPath.size() >= 2) { + from_AP->AddTransitAPsTo(FINAL_DEST_OUT, + _accessPoints[_map_index_to_id[_tmpPedPath[1]]]); + } else { + if ((!from_AP->GetFinalExitToOutside()) + && (!from_AP->IsClosed())) + { + Log->Write( + "ERROR: \tGlobalRouter: There is no visibility path from [%s] to the outside 2\n" + " \tYou can solve this by enabling triangulation.", + from_AP->GetFriendlyName().c_str()); + from_AP->Dump(); + return false; + } + } + _tmpPedPath.clear(); + } + + // set the configuration to reach the goals specified in the ini file + // set the distances to alternative destinations + + for (unsigned int p = 0; p < _finalDestinations.size(); p++) { + int to_door_uid = + _building->GetFinalGoal(_finalDestinations[p])->GetAllWalls()[0].GetUniqueID(); + int to_door_matrix_index=_map_id_to_index[to_door_uid]; + + // thats probably a goal located outside the geometry or not an exit from the geometry + if(to_door_uid==-1) { + Log->Write( + "ERROR: \tGlobalRouter: there is something wrong with the final destination [ %d ]\n", + _finalDestinations[p]); + return false; + } + + for(const auto & itr:_accessPoints) + { + AccessPoint* from_AP = itr.second; + if(from_AP->GetFinalGoalOutside()) continue; + if(from_AP->IsClosed()) continue; + int from_door_matrix_index = _map_id_to_index[itr.first]; + + //comment this if you want infinite as distance to unreachable destinations + double dist = _distMatrix[from_door_matrix_index][to_door_matrix_index]; + from_AP->AddFinalDestination(_finalDestinations[p], dist); + + // set the intermediate path + // set the intermediate path to global final destination + GetPath(from_door_matrix_index, to_door_matrix_index); + if (_tmpPedPath.size() >= 2) { + from_AP->AddTransitAPsTo(_finalDestinations[p], + _accessPoints[_map_index_to_id[_tmpPedPath[1]]]); + } else { + if (((!from_AP->IsClosed()))) { + Log->Write( + "ERROR: \tGlobalRouter: There is no visibility path from [%s] to goal [%d]\n" + " You can solve this by enabling triangulation.", + from_AP->GetFriendlyName().c_str(), _finalDestinations[p]); + from_AP->Dump(); + return false; + } + } + _tmpPedPath.clear(); + } + } + + //dumping the complete system + //DumpAccessPoints(15); exit(0); + //DumpAccessPoints(1259); + //DumpAccessPoints(4912); //exit(0); + //DumpAccessPoints(-1); exit(0); + //vector<string> rooms; + //rooms.push_back("Verteilerebene"); + //WriteGraphGV("routing_graph.gv",FINAL_DEST_OUT,rooms); exit(0); + //WriteGraphGV("routing_graph.gv",4,rooms);exit(0); + Log->Write("INFO:\tDone with the Global Router Engine!"); + return true; +} + +void GlobalRouterTrips::Reset(){ + //clean all allocated spaces + if (_distMatrix && _pathsMatrix) { + const int exitsCnt = _building->GetNumberOfGoals(); + for (int p = 0; p < exitsCnt; ++p) { + delete[] _distMatrix[p]; + delete[] _pathsMatrix[p]; + } + + delete[] _distMatrix; + delete[] _pathsMatrix; + } + + for (auto itr = _accessPoints.begin(); itr != _accessPoints.end(); ++itr) { + delete itr->second; + } + + _accessPoints.clear(); + _tmpPedPath.clear(); + _map_id_to_index.clear(); + _map_index_to_id.clear(); + _mapIdToFinalDestination.clear(); +} + + +void GlobalRouterTrips::SetEdgeCost(double cost) +{ + _edgeCost=cost; +} + +double GlobalRouterTrips::GetEdgeCost() const +{ + return _edgeCost; +} + +void GlobalRouterTrips::GetPath(int i, int j) +{ + if (_distMatrix[i][j] == FLT_MAX) + return; + if (i != j) + GetPath(i, _pathsMatrix[i][j]); + _tmpPedPath.push_back(j); +} + +bool GlobalRouterTrips::GetPath(Pedestrian* ped, std::vector<NavLine*>& path) +{ + std::vector<AccessPoint*> aps_path; + + bool done=false; + int currentNavLine = ped->GetNextDestination(); + if (currentNavLine == -1) + { + currentNavLine= GetBestDefaultRandomExit(ped); + } + aps_path.push_back(_accessPoints[currentNavLine]); + + int loop_count=1; + do + { + const auto & ap=aps_path.back(); + int next_dest = ap->GetNearestTransitAPTO(ped->GetFinalDestination()); + + if(next_dest==-1) break; //we are done + + auto & next_ap= _accessPoints[next_dest]; + + if(next_ap->GetFinalExitToOutside()) + { + done =true; + } + + if (! IsElementInVector(aps_path,next_ap)) + { + aps_path.push_back(next_ap); + } + else + { + Log->Write("WARNING:\t the line [%d] is already included in the path."); + } + + //work arround to detect a potential infinte loop. + if(loop_count++>1000) + { + Log->Write("ERROR:\t A path could not be found for pedestrian [%d] going to destination [%d]",ped->GetID(),ped->GetFinalDestination()); + Log->Write(" \t Stuck in an infinite loop [%d].",loop_count); + return false; + } + + + } while (!done); + + + for(const auto & aps: aps_path) + path.push_back(aps->GetNavLine()); + + return true; + +} + +bool GlobalRouterTrips::GetPath(Pedestrian*ped, int goalID, std::vector<SubRoom*>& path) +{ + + //clear the global variable holding the paths + _tmpPedPath.clear(); + + int tmpFinalDest=ped->GetFinalDestination(); + ped->SetFinalDestination(goalID); + + //find the nearest APs and start from there + int next = GetBestDefaultRandomExit(ped); + if(next==-1) { + Log->Write("ERROR:\t there is an error in getting the path for ped %d to the goal %d", ped->GetID(),goalID); + return false; + } + + // get the transformed goal_id + int to_door_uid = + _building->GetFinalGoal(goalID)->GetAllWalls()[0].GetUniqueID(); + int to_door_matrix_index=_map_id_to_index[to_door_uid]; + int from_door_matrix_index=_map_id_to_index[next]; + + // thats probably a goal located outside the geometry or not an exit from the geometry + if(to_door_uid==-1) { + Log->Write("ERROR: \tGlobalRouter: there is something wrong with final destination [ %d ]\n",goalID); + return false; + } + + //populate the line unique id to cross + GetPath(from_door_matrix_index,to_door_matrix_index); + + for(unsigned int i=0; i<_tmpPedPath.size(); i++) { + int ap_id= _map_index_to_id[_tmpPedPath[i]]; + int subroom_uid=_accessPoints[ap_id]->GetConnectingRoom1(); + if(subroom_uid==-1) continue; + SubRoom* sub = _building->GetSubRoomByUID(subroom_uid); + if (sub && !IsElementInVector(path, sub)) path.push_back(sub); + } + + for(unsigned int i=0; i<_tmpPedPath.size(); i++) { + int ap_id= _map_index_to_id[_tmpPedPath[i]]; + int subroom_uid=_accessPoints[ap_id]->GetConnectingRoom2(); + if(subroom_uid==-1) continue; + SubRoom* sub = _building->GetSubRoomByUID(subroom_uid); + if (sub && !IsElementInVector(path, sub)) path.push_back(sub); + } + + //clear the global variable holding the paths + _tmpPedPath.clear(); + + ped->SetFinalDestination(tmpFinalDest); + return true; + //double distance = _accessPoints[next]->GetDistanceTo(0)+ped->GetDistanceToNextTarget(); + //cout<<"shortest distance to outside: " <<distance<<endl; +} + +/* + floyd_warshall() + after calling this function dist[i][j] will the the minimum distance + between i and j if it exists (i.e. if there's a path between i and j) + or 0, otherwise + */ +void GlobalRouterTrips::FloydWarshall() +{ + const int n = (const int) (_building->GetNumberOfGoals() + _building->GetAllGoals().size()); + for (int k = 0; k < n; k++) + for (int i = 0; i < n; i++) + for (int j = 0; j < n; j++) + if (_distMatrix[i][k] + _distMatrix[k][j] < _distMatrix[i][j]) { + _distMatrix[i][j] = _distMatrix[i][k] + _distMatrix[k][j]; + _pathsMatrix[i][j] = _pathsMatrix[k][j]; + } +} + +//void GlobalRouter::DumpAccessPoints(int p) +//{ +// if (p != -1) { +// _accessPoints.at(p)->Dump(); +// } else { +// for (const auto & itr: _accessPoints) +// { +// itr.second->Dump(); +// } +// } +//} + +int GlobalRouterTrips::FindExit(Pedestrian* ped) +{ + if(!_useMeshForLocalNavigation) + { + std::vector<NavLine*> path; + GetPath(ped,path); + SubRoom* sub = _building->GetRoom(ped->GetRoomID())->GetSubRoom( + ped->GetSubRoomID()); + + //return the next path which is an exit + for(const auto & navLine: path) + { + //TODO: only set if the pedestrian is already in the subroom. + // cuz all lines are returned + if(IsCrossing(*navLine,{sub}) || IsTransition(*navLine,{sub})) + { + int nav_id= navLine->GetUniqueID(); + ped->SetExitIndex(nav_id); + ped->SetExitLine(navLine); + return nav_id; + } + } + + //something bad happens + Log->Write( + "ERROR:\t Cannot find a valid destination for ped [%d] located in room [%d] subroom [%d] going to destination [%d]", + ped->GetID(), ped->GetRoomID(), ped->GetSubRoomID(), + ped->GetFinalDestination()); + return -1; + + } + // else proceed as usual and return the closest navigation line + int nextDestination = ped->GetNextDestination(); + + if (nextDestination == -1) + { + return GetBestDefaultRandomExit(ped); + + } else + { + SubRoom* sub = _building->GetRoom(ped->GetRoomID())->GetSubRoom( + ped->GetSubRoomID()); + + for(const auto & apID:sub->GetAllGoalIDs()) + { + AccessPoint* ap = _accessPoints[apID]; + const Point& pt3 = ped->GetPos(); + double distToExit = ap->GetNavLine()->DistTo(pt3); + + if (distToExit > J_EPS_DIST) + continue; + + //continue until my target is reached + if(apID!=ped->GetExitIndex()) + continue; + + //one AP is near actualize destination: + nextDestination = ap->GetNearestTransitAPTO( + ped->GetFinalDestination()); + + //if(ped->GetID()==6) {ap->Dump();getc(stdin);} + if (nextDestination == -1) + { + // we are almost at the exit + // so keep the last destination + return ped->GetNextDestination(); + } else { + //check that the next destination is in the actual room of the pedestrian + if (!_accessPoints[nextDestination]->isInRange( + sub->GetUID())) { + //return the last destination if defined + int previousDestination = ped->GetNextDestination(); + + //we are still somewhere in the initialization phase + if (previousDestination == -1) { + ped->SetExitIndex(apID); + ped->SetExitLine(_accessPoints[apID]->GetNavLine()); + return apID; + } else { // we are still having a valid destination, don't change + return previousDestination; + } + } else { // we have reached the new room + ped->SetExitIndex(nextDestination); + ped->SetExitLine( + _accessPoints[nextDestination]->GetNavLine()); + return nextDestination; + } + } + } + + // still have a valid destination, so return it + return nextDestination; + } +} + +int GlobalRouterTrips::GetBestDefaultRandomExit(Pedestrian* ped) +{ + // get the relevant opened exits + vector <AccessPoint*> relevantAPs; + GetRelevantRoutesTofinalDestination(ped,relevantAPs); + //in the case there is only one alternative + //save some computation + if(relevantAPs.size()==1) + { + auto&& ap= (AccessPoint*&&) relevantAPs[0]; + ped->SetExitIndex(ap->GetID()); + ped->SetExitLine(ap->GetNavLine()); + return ap->GetID(); + } + + int bestAPsID = -1; + double minDistGlobal = FLT_MAX; + //double minDistLocal = FLT_MAX; + + // get the opened exits + SubRoom* sub = _building->GetRoom(ped->GetRoomID())->GetSubRoom( + ped->GetSubRoomID()); + + for(unsigned int g=0; g<relevantAPs.size(); g++) + { + AccessPoint* ap=relevantAPs[g]; + + if (!ap->isInRange(sub->GetUID())) + continue; + //check if that exit is open. + if (ap->IsClosed()) + continue; + + //the line from the current position to the centre of the nav line. + // at least the line in that direction minus EPS + const Point& posA = ped->GetPos(); + const Point& posB = ap->GetNavLine()->GetCentre(); + const Point& posC = (posB - posA).Normalized() * ((posA - posB).Norm() - J_EPS) + posA; + + //check if visible + //only if the room is convex + //otherwise check all rooms at that level + if(!_building->IsVisible(posA, posC, _subroomsAtElevation[sub->GetElevation(sub->GetCentroid())],true)) + { + ped->RerouteIn(10); + continue; + } + double dist1 = ap->GetDistanceTo(ped->GetFinalDestination()); + double dist2 = ap->DistanceTo(posA._x, posA._y); + double dist=dist1+dist2; + + if (dist < minDistGlobal) + { + bestAPsID = ap->GetID(); + minDistGlobal = dist; + //minDistLocal=dist2; + } + } + + if (bestAPsID != -1) + { + ped->SetExitIndex(bestAPsID); + ped->SetExitLine(_accessPoints[bestAPsID]->GetNavLine()); + return bestAPsID; + } + else + { + if (_building->GetRoom(ped->GetRoomID())->GetCaption() != "outside") + { + //Log->Write( + // + // "ERROR:\t GetBestDefaultRandomExit() \nCannot find valid destination for ped [%d] " + // "located in room [%d] subroom [%d] going to destination [%d]", + // ped->GetID(), ped->GetRoomID(), ped->GetSubRoomID(), + // ped->GetFinalDestination()); + + + //FIXME: assign the nearest and not only a random one + //{ + + relevantAPs[0]->GetID(); + ped->SetExitIndex(relevantAPs[0]->GetID()); + ped->SetExitLine(relevantAPs[0]->GetNavLine()); + ped->RerouteIn(5); + return relevantAPs[0]->GetID(); + } + return -1; + } +} + + +void GlobalRouterTrips::GetRelevantRoutesTofinalDestination(Pedestrian *ped, vector<AccessPoint*>& relevantAPS) +{ + + Room* room=_building->GetRoom(ped->GetRoomID()); + SubRoom* sub=room->GetSubRoom(ped->GetSubRoomID()); + + // This is best implemented by closing one door and checking if there is still a path to outside + // and itereating over the others. + // It might be time consuming, you many pre compute and cache the results. + if(sub->GetAllHlines().size()==0) + { + const vector<int>& goals=sub->GetAllGoalIDs(); + //filter to keep only the emergencies exits. + + for(unsigned int g1=0; g1<goals.size(); g1++) { + AccessPoint* ap=_accessPoints[goals[g1]]; + bool relevant=true; + for(unsigned int g2=0; g2<goals.size(); g2++) { + if(goals[g2]==goals[g1]) continue; // always skip myself + if(ap->GetNearestTransitAPTO(ped->GetFinalDestination())==goals[g2]) { + // crossings only + relevant=false; + break; + } + } + if(relevant) { + //only if not closed + if(ap->IsClosed()==0) + relevantAPS.push_back(ap); + } + } + } + //quick fix for extra hlines + // it should be safe now to delete the first preceding if block + else + { + const vector<int>& goals=sub->GetAllGoalIDs(); + for(unsigned int g1=0; g1<goals.size(); g1++) + { + AccessPoint* ap=_accessPoints[goals[g1]]; + + //check for visibility + //the line from the current position to the centre of the nav line. + // at least the line in that direction minus EPS + const Point& posA = ped->GetPos(); + const Point& posB = ap->GetNavLine()->GetCentre(); + const Point& posC = (posB - posA).Normalized() * ((posA - posB).Norm() - J_EPS) + posA; + + //check if visible + if (!_building->IsVisible(posA, posC, _subroomsAtElevation[sub->GetElevation(sub->GetCentroid())],true)) + //if (sub->IsVisible(posA, posC, true) == false) + { + continue; + } + + bool relevant=true; + for(unsigned int g2=0; g2<goals.size(); g2++) + { + if(goals[g2]==goals[g1]) continue; // always skip myself + if(ap->GetNearestTransitAPTO(ped->GetFinalDestination())==goals[g2]) + { + + //pointing only to the one i dont see + //the line from the current position to the centre of the nav line. + // at least the line in that direction minus EPS + AccessPoint* ap2=_accessPoints[goals[g2]]; + const Point& posA_ = ped->GetPos(); + const Point& posB_ = ap2->GetNavLine()->GetCentre(); + const Point& posC_ = (posB_ - posA_).Normalized()* ((posA_ - posB_).Norm() - J_EPS) + posA_; + + //it points to a destination that I can see anyway + if (_building->IsVisible(posA_, posC_, _subroomsAtElevation[sub->GetElevation(sub->GetCentroid())],true)) + //if (sub->IsVisible(posA_, posC_, true) == true) + { + relevant=false; + } + + break; + } + } + if(relevant) + { + if(ap->IsClosed()==0) + relevantAPS.push_back(ap); + } + } + } + + //fallback + if(relevantAPS.size()==0) + { + //fixme: this should also never happened. But happen due to previous bugs.. + const vector<int>& goals=sub->GetAllGoalIDs(); + for(unsigned int g1=0; g1<goals.size(); g1++) + { + relevantAPS.push_back(_accessPoints[goals[g1]]); + } + + } + +} + +//void GlobalRouter::WriteGraphGV(string filename, int finalDestination, +// const vector<string> rooms_captions) +//{ +// ofstream graph_file(filename.c_str()); +// if (graph_file.is_open() == false) { +// Log->Write("Unable to open file" + filename); +// return; +// } +// +// //header +// graph_file << "## Produced by JuPedSim" << endl; +// //graph_file << "##comand: \" sfdp -Goverlap=prism -Gcharset=latin1"<<filename <<"| gvmap -e | neato -Ecolor=\"#55555522\" -n2 -Tpng > "<< filename<<".png \""<<endl; +// graph_file << "##Command to produce the output: \"neato -n -s -Tpng " +// << filename << " > " << filename << ".png\"" << endl; +// graph_file << "digraph JUPEDSIM_ROUTING {" << endl; +// graph_file << "overlap=scale;" << endl; +// graph_file << "splines=false;" << endl; +// graph_file << "fontsize=20;" << endl; +// graph_file +// << "label=\"Graph generated by the routing engine for destination: " +// << finalDestination << "\"" << endl; +// +// vector<int> rooms_ids = vector<int>(); +// +// if (rooms_captions.empty()) +// { +// // then all rooms should be printed +// for(auto && itroom:_building->GetAllRooms()) +// { +// for(const auto & it_sub:itroom.second->GetAllSubRooms()) +// { +// rooms_ids.push_back(it_sub.second->GetUID()); +// } +// } +// +// +// } else { +// for (auto && caption: rooms_captions) +// { +// for(const auto & it_sub:_building->GetRoom(caption)->GetAllSubRooms()) +// { +// rooms_ids.push_back(it_sub.second->GetUID()); +// } +// } +// } +// +// for (map<int, AccessPoint*>::const_iterator itr = _accessPoints.begin(); +// itr != _accessPoints.end(); ++itr) { +// +// AccessPoint* from_AP = itr->second; +// +// int from_door = from_AP->GetID(); +// +// // check for valid room +// int room_id = from_AP->GetConnectingRoom1(); +// int room_id1=from_AP->GetConnectingRoom2(); +// +// if ( (IsElementInVector(rooms_ids, room_id) == false) && (IsElementInVector(rooms_ids, room_id1) == false) ) +// continue; +// double px = from_AP->GetCentre()._x; +// double py = from_AP->GetCentre()._y; +// //graph_file << from_door <<" [shape=ellipse, pos=\""<<px<<", "<<py<<" \"] ;"<<endl; +// //graph_file << from_door <<" [shape=ellipse, pos=\""<<px<<","<<py<<"\" ];"<<endl; +// +// //const vector<AccessPoint*>& from_aps = from_AP->GetConnectingAPs(); +// const vector<AccessPoint*>& from_aps = from_AP->GetTransitAPsTo( +// finalDestination); +// +// if (from_aps.size() == 0) { +// +// if (from_AP->GetFinalExitToOutside()) { +// graph_file << from_door << " [pos=\"" << px << ", " << py +// << " \", style=filled, color=green,fontsize=5] ;" +// << endl; +// // graph_file << from_door <<" [width=\"0.41\", height=\"0.31\",fixedsize=false,pos=\""<<px<<", "<<py<<" \", style=filled, color=green,fontsize=4] ;"<<endl; +// } else { +// graph_file << from_door << " [pos=\"" << px << ", " << py +// << " \", style=filled, color=red,fontsize=5] ;" << endl; +// // graph_file << from_door <<" [width=\"0.41\", height=\"0.31\",fixedsize=false,pos=\""<<px<<", "<<py<<" \", style=filled, color=red,fontsize=4] ;"<<endl; +// } +// } else { +// // check that all connecting aps are contained in the room_ids list +// // if not marked as sink. +// bool isSink = true; +// for (unsigned int j = 0; j < from_aps.size(); j++) +// { +// int room_id_ = from_aps[j]->GetConnectingRoom1(); +// if (IsElementInVector(rooms_ids, room_id_)) +// { +// isSink = false; +// break; +// } +// } +// +// if (isSink) { +// //graph_file << from_door <<" [width=\"0.3\", height=\"0.21\",fixedsize=false,pos=\""<<px<<", "<<py<<" \" ,style=filled, color=green, fontsize=4] ;"<<endl; +// graph_file << from_door << " [pos=\"" << px << ", " << py +// << " \" ,style=filled, color=blue, fontsize=5] ;" +// << endl; +// } else { +// //graph_file << from_door <<" [width=\"0.3\", height=\"0.231\",fixedsize=false, pos=\""<<px<<", "<<py<<" \", fontsize=4] ;"<<endl; +// graph_file << from_door << " [pos=\"" << px << ", " << py +// << " \", style=filled, color=yellow, fontsize=5] ;" +// << endl; +// } +// } +// +// } +// +// //connections +// for (const auto & itr: _accessPoints) +// { +// AccessPoint* from_AP = itr.second; +// int from_door = from_AP->GetID(); +// +// int room_id = from_AP->GetConnectingRoom1(); +// +// if (IsElementInVector(rooms_ids, room_id) == false) +// continue; +// +// //const vector<AccessPoint*>& aps = from_AP->GetConnectingAPs(); +// const vector<AccessPoint*>& aps = from_AP->GetTransitAPsTo( +// finalDestination); +// +// for (const auto & to_AP:aps) +// { +// int to_door = to_AP->GetID(); +// +// int lroom_id = to_AP->GetConnectingRoom1(); +// +// if (IsElementInVector(rooms_ids, lroom_id) == false) +// continue; +// +// graph_file << from_door << " -> " << to_door << " [ label=" +// << from_AP->GetDistanceTo(to_AP) +// + to_AP->GetDistanceTo(finalDestination) +// << ", fontsize=10]; " << endl; +// } +// } +// +// //graph_file << "node [shape=box]; gy2; yr2; rg2; gy1; yr1; rg1;"<<endl; +// //graph_file << "node [shape=circle,fixedsize=true,width=0.9]; green2; yellow2; red2; safe2; safe1; green1; yellow1; red1;"<<endl; +// +// //graph_file << "0 -> 1 ;"<<endl; +// +// graph_file << "}" << endl; +// +// //done +// graph_file.close(); +//} + +void GlobalRouterTrips::TriangulateGeometry() +{ + Log->Write("INFO:\tUsing the triangulation in the global router"); + for(auto&& itr_room: _building->GetAllRooms()) + { + for(auto&& itr_subroom: itr_room.second->GetAllSubRooms()) + { + auto&& subroom= (shared_ptr<SubRoom>&&) itr_subroom.second; + auto&& room= (shared_ptr<Room>&&) itr_room.second; + auto&& obstacles= (const vector<Obstacle*>&&) subroom->GetAllObstacles(); + if(!subroom->IsAccessible()) continue; + + //Triangulate if obstacle or concave and no hlines ? + //if(subroom->GetAllHlines().size()==0) + if((obstacles.size()>0 ) || !subroom->IsConvex()) + { + + // DTriangulation* tri= new DTriangulation(); + // auto outerhull=subroom->GetPolygon(); + // if(subroom->IsClockwise()) + // std::reverse(outerhull.begin(), outerhull.end()); + // + // tri->SetOuterPolygone(outerhull); + // + // for (const auto & obst: obstacles) + // { + // auto outerhullObst=obst->GetPolygon(); + // if(obst->IsClockwise()) + // std::reverse(outerhullObst.begin(), outerhullObst.end()); + // tri->AddHole(outerhullObst); + // } + // tri->Triangulate(); + // vector<p2t::Triangle*> triangles=tri->GetTriangles(); + + vector<p2t::Triangle*> triangles=subroom->GetTriangles(); + + for (const auto & tr: triangles) + { + Point P0 = Point (tr->GetPoint(0)->x,tr->GetPoint(0)->y); + Point P1 = Point (tr->GetPoint(1)->x,tr->GetPoint(1)->y); + Point P2 = Point (tr->GetPoint(2)->x,tr->GetPoint(2)->y); + vector<Line> edges; + edges.push_back(Line(P0,P1)); + edges.push_back(Line(P1,P2)); + edges.push_back(Line(P2,P0)); + + for (const auto & line: edges) + { + //reduce edge that are too close 50 cm is assumed + if (MinDistanceToHlines(line.GetCentre(), + *subroom) + < _minDistanceBetweenTriangleEdges) + continue; + + if (MinAngle(P0, P1, P2) < _minAngleInTriangles) + continue; + + if((IsWall(line,{subroom.get()})==false) && (IsCrossing(line,{subroom.get()})==false) + && (IsTransition(line,{subroom.get()})==false) && (IsHline(line,{subroom.get()})==false)) + { + //add as a Hline + int id=_building->GetAllHlines().size(); + Hline* h = new Hline(); + h->SetID(id); + h->SetPoint1(line.GetPoint1()); + h->SetPoint2(line.GetPoint2()); + h->SetRoom1(room.get()); + h->SetSubRoom1(subroom.get()); + subroom->AddHline(h); + Log->Write(std::to_string(h->GetPoint1()._x)+""+std::to_string(h->GetPoint1()._y)); + Log->Write(std::to_string(h->GetPoint2()._x)+""+std::to_string(h->GetPoint2()._y)); + _building->AddHline(h); + } + } + } + } + } + } + Log->Write("INFO:\tDone..."); +} + +bool GlobalRouterTrips::GenerateNavigationMesh() +{ + // //Navigation mesh implementation + // NavMesh* nv= new NavMesh(_building); + // nv->BuildNavMesh(); + // _building->SaveGeometry("test_geometry.xml"); + // exit(0); + // //nv->WriteToFileTraVisTo() + // + // const std::vector<NavMesh::JEdge*>& edges = nv->GetEdges(); + // + // for(const auto & edge: edges) + // { + // //construct and add a new navigation line if non existing + // Line line(edge->pStart.pPos,edge->pEnd.pPos); + // bool isEdge=false; + // + // //check if it is already a crossing + // const map<int, Crossing*>& crossings = _building->GetAllCrossings(); + // for (const auto & crossing: crossings) + // { + // Crossing* cross=crossing.second; + // if(line.operator ==(*cross)) + // { + // isEdge=true; + // break; + // } + // } + // if(isEdge) continue; + // + // + // //check if it is already a transition + // const map<int, Transition*>& transitions = _building->GetAllTransitions(); + // for (const auto & transition: transitions) + // { + // Transition* trans=transition.second; + // if(line.operator ==(*trans)) + // { + // isEdge=true; + // break; + // } + // } + // if(isEdge) continue; + // + // //check if it is already a + // const map<int, Hline*>& hlines = _building->GetAllHlines(); + // for (const auto & hline: hlines) + // { + // Hline* navLine=hline.second; + // if(line.operator ==(*navLine)) + // { + // isEdge=true; + // break; + // } + // } + // if(isEdge) continue; + // + // + // Hline* h = new Hline(); + // h->SetID(hlines.size()); + // int assigned=0; + // + // //look for the room/subroom containing the new edge + // const vector<Room*>& rooms=_building->GetAllRooms(); + // for(const auto & room: rooms) + // { + // const vector<SubRoom*>& subrooms= room->GetAllSubRooms(); + // + // for(const auto & subroom: subrooms) + // { + // if(subroom->IsInSubRoom(line.GetCentre())) + // { + // h->SetRoom1(room); + // h->SetSubRoom1(subroom); + // assigned++; + // } + // } + // } + // + // if(assigned!=1) + // { + // Log->Write("WARNING:\t a navigation line from the mesh was not correctly assigned"); + // return false; + // } + // //add the new edge as navigation line + // + // h->SetPoint1(edge->pStart.pPos); + // h->SetPoint2(edge->pEnd.pPos); + // h->GetSubRoom1()->AddHline(h); //double linked ?? + // _building->AddHline(h); + // + // } + // + // //string geometry; + // //nv->WriteToString(geometry); + // //Write("<geometry>"); + // //Write(geometry); + // //Write("</geometry>"); + // //nv->WriteToFile(building->GetProjectFilename()+".full.nav"); + // + // //cout<<"bye"<<endl; + // delete nv; + return true; +} + +string GlobalRouterTrips::GetRoutingInfoFile() +{ + + TiXmlDocument doc(_building->GetProjectFilename()); + if (!doc.LoadFile()) { + Log->Write("ERROR: \t%s", doc.ErrorDesc()); + Log->Write("ERROR: \t GlobalRouter: could not parse the project file"); + return ""; + } + + // everything is fine. proceed with parsing + TiXmlElement* xMainNode = doc.RootElement(); + TiXmlNode* xRouters=xMainNode->FirstChild("route_choice_models"); + string nav_line_file=""; + + for(TiXmlElement* e = xRouters->FirstChildElement("router"); e; + e = e->NextSiblingElement("router")) + { + + string strategy=e->Attribute("description"); + vector<string> routers={"local_shortest", "global_shortest", "global_safest","dynamic","quickest"}; + + if(std::find(routers.begin(), routers.end(), strategy) != routers.end()) + { + + if(e->FirstChild("parameters")) + { + + if (e->FirstChild("parameters")->FirstChildElement("navigation_lines")) //fixme: + //this + //reads + //the + //wronf + //router section + nav_line_file=e->FirstChild("parameters")->FirstChildElement("navigation_lines")->Attribute("file"); + + TiXmlElement* para =e->FirstChild("parameters")->FirstChildElement("navigation_mesh"); + if (para) + { + //triangulate the geometry + if(!_building->Triangulate()) + { + Log->Write("ERROR:\t could not triangulate the geometry!"); + exit (EXIT_FAILURE); + } + + string local_planing=xmltoa(para->Attribute("use_for_local_planning"),"false"); + if(local_planing=="true") { + _useMeshForLocalNavigation = 1; + } + else { + _useMeshForLocalNavigation = 0; + } + + string method = xmltoa(para->Attribute("method"),""); + if(method=="triangulation") + { + _generateNavigationMesh=true; + } + else + { + Log->Write("WARNING:\t only triangulation is supported for the mesh. You supplied [%s]",method.c_str()); + } + _minDistanceBetweenTriangleEdges=xmltof(para->Attribute("minimum_distance_between_edges"),-FLT_MAX); + _minAngleInTriangles=xmltof(para->Attribute("minimum_angle_in_triangles"),-FLT_MAX); + } + } + } + } + if (nav_line_file == "") + return nav_line_file; + else + return _building->GetProjectRootDir()+nav_line_file; +} + + +bool GlobalRouterTrips::LoadRoutingInfos(const std::string &filename) +{ + if(filename=="") return true; + + Log->Write("INFO:\tLoading extra routing information for the global/quickest path router"); + Log->Write("INFO:\t from the file "+filename); + + TiXmlDocument docRouting(filename); + if (!docRouting.LoadFile()) { + Log->Write("ERROR: \t%s", docRouting.ErrorDesc()); + Log->Write("ERROR: \t could not parse the routing file [%s]",filename.c_str()); + return false; + } + + TiXmlElement* xRootNode = docRouting.RootElement(); + if( ! xRootNode ) { + Log->Write("ERROR:\tRoot element does not exist"); + return false; + } + + if( xRootNode->ValueStr () != "routing" ) { + Log->Write("ERROR:\tRoot element value is not 'routing'."); + return false; + } + + string version = xRootNode->Attribute("version"); + if (version < JPS_OLD_VERSION) { + Log->Write("ERROR: \tOnly version greater than %d supported",JPS_OLD_VERSION); + Log->Write("ERROR: \tparsing routing file failed!"); + return false; + } + int HlineCount = 0; + for(TiXmlElement* xHlinesNode = xRootNode->FirstChildElement("Hlines"); xHlinesNode; + xHlinesNode = xHlinesNode->NextSiblingElement("Hlines")) { + + + for(TiXmlElement* hline = xHlinesNode->FirstChildElement("Hline"); hline; + hline = hline->NextSiblingElement("Hline")) { + + double id = xmltof(hline->Attribute("id"), -1); + int room_id = xmltoi(hline->Attribute("room_id"), -1); + int subroom_id = xmltoi(hline->Attribute("subroom_id"), -1); + + double x1 = xmltof( hline->FirstChildElement("vertex")->Attribute("px")); + double y1 = xmltof( hline->FirstChildElement("vertex")->Attribute("py")); + double x2 = xmltof( hline->LastChild("vertex")->ToElement()->Attribute("px")); + double y2 = xmltof( hline->LastChild("vertex")->ToElement()->Attribute("py")); + + Room* room = _building->GetRoom(room_id); + SubRoom* subroom = room->GetSubRoom(subroom_id); + + //new implementation + Hline* h = new Hline(); + h->SetID(id); + h->SetPoint1(Point(x1, y1)); + h->SetPoint2(Point(x2, y2)); + h->SetRoom1(room); + h->SetSubRoom1(subroom); + + if(_building->AddHline(h)) + { + subroom->AddHline(h); + HlineCount++; + //h is freed in building + } + else + { + delete h; + } + } + } + Log->Write("INFO:\tDone with loading extra routing information. Loaded <%d> Hlines", HlineCount); + return true; +} + +bool GlobalRouterTrips::IsWall(const Line& line, const std::vector<SubRoom*>& subrooms) const +{ + + for(auto&& subroom: subrooms) + { + for (auto&& obst: subroom->GetAllObstacles()) + { + for (auto&& wall:obst->GetAllWalls()) + { + if(line.operator ==(wall)) + return true; + } + } + for (auto&& wall:subroom->GetAllWalls()) + { + if(line.operator ==(wall)) + return true; + } + } + + return false; +} + +bool GlobalRouterTrips::IsCrossing(const Line& line, const std::vector<SubRoom*>& subrooms) const +{ + for(auto&& subroom: subrooms) + { + for (const auto & crossing : subroom->GetAllCrossings()) + { + if (crossing->operator ==(line)) + return true; + } + } + return false; +} + +bool GlobalRouterTrips::IsTransition(const Line& line, const std::vector<SubRoom*>& subrooms) const +{ + for(auto&& subroom: subrooms) + { + for(const auto & transition: subroom->GetAllTransitions()) + { + if (transition->operator ==(line)) + return true; + } + } + return false; +} + +bool GlobalRouterTrips::IsHline(const Line& line, const std::vector<SubRoom*>& subrooms) const +{ + for(auto&& subroom: subrooms) + { + for(const auto & hline: subroom->GetAllHlines()) + { + if (hline->operator ==(line)) + return true; + } + } + return false; +} + +double GlobalRouterTrips::MinDistanceToHlines(const Point& point, const SubRoom& sub) +{ + double minDist=FLT_MAX; + for(const auto & hline: sub.GetAllHlines()) + { + double dist=hline->DistTo(point); + if (dist<minDist) + minDist=dist; + } + for(const auto & cross: sub.GetAllCrossings()) + { + double dist=cross->DistTo(point); + if (dist<minDist) + minDist=dist; + } + for(const auto & trans: sub.GetAllTransitions()) + { + double dist=trans->DistTo(point); + if (dist<minDist) + minDist=dist; + } + for(const auto & wall: sub.GetAllWalls()) + { + double dist=wall.DistTo(point); + if (dist<minDist) + minDist=dist; + } + //also to the obstacles + for (const auto& obst: sub.GetAllObstacles()) + { + for(const auto & wall: obst->GetAllWalls()) + { + double dist=wall.DistTo(point); + if (dist<minDist) + minDist=dist; + } + } + + return minDist; +} + +double GlobalRouterTrips::MinAngle(const Point& p1, const Point& p2, const Point& p3) +{ + double a = (p1 - p2).NormSquare(); + double b = (p1 - p3).NormSquare(); + double c = (p3 - p2).NormSquare(); + + double alpha=acos((a+b-c)/(2*sqrt(a)*sqrt(b))); + double beta=acos((a+c-b)/(2*sqrt(a)*sqrt(c))); + double gamma=acos((c+b-a)/(2*sqrt(c)*sqrt(b))); + + if(fabs(alpha+beta+gamma-M_PI)<J_EPS) + { + std::vector<double> vec = { alpha, beta, gamma }; + return *std::min_element(vec.begin(), vec.end()) * (180.0 / M_PI); + } + else + { + Log->Write("ERROR:\t Error in angle calculation"); + exit(EXIT_FAILURE); + } + + return 0; +} diff --git a/routing/global_shortest_trips/GlobalRouterTrips.h b/routing/global_shortest_trips/GlobalRouterTrips.h new file mode 100644 index 0000000000000000000000000000000000000000..4bc3abdfc5bf622353bb792664e3656904696201 --- /dev/null +++ b/routing/global_shortest_trips/GlobalRouterTrips.h @@ -0,0 +1,244 @@ +/** + * \file GlobalRouter.h + * \date Dec 15, 2010 + * \version v0.7 + * \copyright <2009-2015> Forschungszentrum Jülich GmbH. All rights reserved. + * + * \section License + * This file is part of JuPedSim. + * + * JuPedSim is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * JuPedSim 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 Lesser General Public License + * along with JuPedSim. If not, see <http://www.gnu.org/licenses/>. + * + * \section Description + * implement the global shortest path using the dijkstra algorithm + * + * + **/ + + +#ifndef GLOBALROUTERTRIPS_H_ +#define GLOBALROUTERTRIPS_H_ + +#include <string> +#include <sstream> +#include <cfloat> +#include <fstream> +#include <vector> +#include "../Router.h" +#include "../../geometry/Building.h" + +// forwarded classes +class Pedestrian; +class AccessPoint; +class Building; +class OutputHandler; + +//log output +extern OutputHandler* Log; + +#include <random> + +/*! + * \class GlobalRouter + * + * \brief global router + * + *\ingroup Router + * + * We will derive from the <Router> class to fit the interface. + * + * \author Ulrich Kemloh + */ +class GlobalRouterTrips: public Router +{ + +public: + /** + * Constructor + */ + GlobalRouterTrips(); + GlobalRouterTrips(int id, RoutingStrategy s); + + /** + * Destructor + */ + virtual ~GlobalRouterTrips(); + + virtual bool Init(Building* building); + + virtual int FindExit(Pedestrian* p); + + /** + * Performs a check of the geometry and fixes if possible. + * NOT IMPLEMENTED + */ + void CheckInconsistencies(); + + /** + * write the graph as GV format to be used with graphviz + * @param filename + */ + void WriteGraphGV(std::string filename, int finalDestination, + const std::vector<std::string> rooms= std::vector<std::string>()); + + /** + * Reset the routing engine and clear all pre-computed paths + */ + void Reset(); + + /** + * Set/Get the edge cost for certain paths. + * prefer the use of paths through floors instead of rooms + */ + void SetEdgeCost(double cost); + + /** + * Set/Get the edge cost for certain paths. + * prefer the use of paths through floors instead of rooms + */ + double GetEdgeCost() const; + + +protected: + + void DumpAccessPoints(int p=-1); + + /** + * @return true if the two points are in the visibility range of each other + * @note based on http://alienryderflex.com/intersect/ + */ + bool CanSeeEachother(const Point&pt1, const Point&pt2); + + /** + * @obsolete + * return a random exit + */ + virtual int GetBestDefaultRandomExit(Pedestrian* p); + + /** + * Generate a navigation mesh based on delauney triangulation + */ + bool GenerateNavigationMesh(); + + + /** + * Triangulate the geometry and generate the navigation lines + */ + void TriangulateGeometry(); + + + /** + * + * @param ped the pedestrian + * @param goalID, the goal ID. + * @param path vector to store the intermediate destination + */ + bool GetPath(Pedestrian* ped, int goalID, std::vector<SubRoom*>& path); + + /** + * Populates the navigations line to cross in the vector path + * @param ped the pedestrian + * @param path, the vector to store + */ + bool GetPath(Pedestrian* ped, std::vector<NavLine*>& path); + + /** + * return the relevant aps that lead to the pedestrian final destination + * @param ped + */ + void GetRelevantRoutesTofinalDestination(Pedestrian *ped, std::vector<AccessPoint*>& relevantAPS); + +private: + /** + * Compute the intermediate paths between the two given transitions IDs + */ + void GetPath(int transID1, int transID2); + + /** + * Perform the FloydWahrshal algorithm + */ + void FloydWarshall(); + + /** + * Load extra routing information e.g navigation lines + */ + bool LoadRoutingInfos(const std::string &filename); + + /** + * Each router is responsible of getting the correct filename + * and doing other initializations + */ + virtual std::string GetRoutingInfoFile(); + + /** + * @return true if the supplied line is a wall. + */ + bool IsWall(const Line& line, const std::vector<SubRoom*>& subrooms) const; + + /** + * @return true if the supplied line is a Crossing. + */ + bool IsCrossing(const Line& line, const std::vector<SubRoom*>& subrooms) const; + + /** + * @return true if the supplied line is a Transition. + */ + bool IsTransition(const Line& line, const std::vector<SubRoom*>& subrooms) const; + + /** + * @return true if the supplied line is a navigation line. + */ + bool IsHline(const Line& line, const std::vector<SubRoom*>& subrooms) const; + + /** + * @return the minimum distance between the point and any line in the subroom. + * This include walls,hline,crossings,transitions,obstacles + */ + double MinDistanceToHlines(const Point& point, const SubRoom& sub); + + /** + * @return the minimal angle in the the triangle formed by the three points + */ + double MinAngle(const Point& p1, const Point& p2, const Point& p3); + +private: + int **_pathsMatrix; + double **_distMatrix; + double _edgeCost; + //if false, the router will only return the exits and not the navigations line created through the mesh or inserted + //via the routing file. The mesh will only be used for computing the distance. + bool _useMeshForLocalNavigation=true; + bool _generateNavigationMesh=false; + //used to filter skinny edges in triangulation + double _minDistanceBetweenTriangleEdges=-FLT_MAX; + double _minAngleInTriangles=-FLT_MAX; + std::vector<int> _tmpPedPath; + std::map<int,int> _map_id_to_index; + std::map<int,int> _map_index_to_id; + ///map the internal crossings/transition id to + ///the global ID (description) for that final destination + std::map<int, int> _mapIdToFinalDestination; + // normalize the probs + std::default_random_engine _rdGenerator; + std::uniform_real_distribution<double> _rdDistribution; + +protected: + // store all subrooms at the same elevation + std::map<double, std::vector<SubRoom*> > _subroomsAtElevation; + std::map <int, AccessPoint*> _accessPoints; + Building *_building; + +}; + +#endif /* GLOBALROUTER_H_ */ diff --git a/routing/global_shortest_trips/Triangulation.h b/routing/global_shortest_trips/Triangulation.h new file mode 100644 index 0000000000000000000000000000000000000000..4e2f95126279891d52a377662df15c938d5fe2c0 --- /dev/null +++ b/routing/global_shortest_trips/Triangulation.h @@ -0,0 +1,130 @@ +/** + * \file Triangulation.h + * \date Nov 30, 2012 + * \version v0.7 + * \copyright <2009-2015> Forschungszentrum Jülich GmbH. All rights reserved. + * + * \section License + * This file is part of JuPedSim. + * + * JuPedSim is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * JuPedSim 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 Lesser General Public License + * along with JuPedSim. If not, see <http://www.gnu.org/licenses/>. + * + * \section Description + * + * + **/ + + +#ifndef TRIANGULATION_H_ +#define TRIANGULATION_H_ + +#include <cstdlib> +#include <time.h> +#include <fstream> +#include <string> +#include <sstream> +#include <algorithm> +#include <iterator> +#include <iostream> +#include <vector> + +#include "../poly2tri/poly2tri.h" +using std::vector; +using std::endl; +using std::cout; + + +template <class C> void FreeClear( C & cntr ) +{ + for ( typename C::iterator it = cntr.begin(); + it != cntr.end(); ++it ) { + delete * it; + } + cntr.clear(); +} + +inline +std::vector<p2t::Triangle*> triangles () +{ + int num_points = 0; + double max, min; + + + vector< vector<p2t::Point*> > polylines; + vector<p2t::Point*> polyline; + + // Create a simple bounding box + polyline.push_back(new p2t::Point(min,min)); + polyline.push_back(new p2t::Point(min,max)); + polyline.push_back(new p2t::Point(max,max)); + polyline.push_back(new p2t::Point(max,min)); + + + cout << "Number of constrained edges = " << polyline.size() << endl; + polylines.push_back(polyline); + + + /* + * Perform triangulation! + */ + + + /* + * STEP 1: Create CDT and add primary polyline + * NOTE: polyline must be a simple polygon. The polyline's points + * constitute constrained edges. No repeat points!!! + */ + p2t::CDT* cdt = new p2t::CDT(polyline); + + /* + * STEP 2: Add holes or Steiner points if necessary + */ + + vector<p2t::Point*> head_hole ; + cdt->AddHole(head_hole); + // Add chest hole + vector<p2t::Point*> chest_hole; + cdt->AddHole(chest_hole); + polylines.push_back(head_hole); + polylines.push_back(chest_hole); + + /* + * STEP 3: Triangulate! + */ + cdt->Triangulate(); + + /// Constrained triangles + std::vector<p2t::Triangle*> triangles; + /// Triangle map + std::list<p2t::Triangle*> map; + + triangles = cdt->GetTriangles(); + map = cdt->GetMap(); + + cout << "Number of points = " << num_points << endl; + cout << "Number of triangles = " << triangles.size() << endl; + + + + // Free points + for(unsigned int i = 0; i < polylines.size(); i++) { + vector<p2t::Point*> poly = polylines[i]; + FreeClear(poly); + } + + // delete cdt; + return triangles; +} + +#endif /* TRIANGULATION_H_ */ diff --git a/routing/quickest/QuickestPathRouter.h b/routing/quickest/QuickestPathRouter.h index af8b081012c32e378571ec3441eb38a580e984b8..97e28964f7ed4e8c3dc125dd465aba1d9e756508 100644 --- a/routing/quickest/QuickestPathRouter.h +++ b/routing/quickest/QuickestPathRouter.h @@ -32,6 +32,9 @@ #include "../global_shortest/GlobalRouter.h" #include "../global_shortest/AccessPoint.h" +//#include "../global_shortest_trips/GlobalRouterTrips.h" +//#include "../global_shortest_trips/AccessPoint.h" + #include "../Router.h" #include "../../geometry/Building.h" #include "../../pedestrian/Pedestrian.h" @@ -70,8 +73,8 @@ extern OutputHandler* Log; * * \author Ulrich Kemloh */ -class QuickestPathRouter: public GlobalRouter -{ +class QuickestPathRouter: public GlobalRouter{ + public: QuickestPathRouter(); diff --git a/routing/trips_router/TripsRouter.cpp b/routing/trips_router/TripsRouter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..776f3da0ba85880f1607f1249416186a51a2c8ec --- /dev/null +++ b/routing/trips_router/TripsRouter.cpp @@ -0,0 +1,43 @@ +// +// Created by Tobias Schrödter on 2018-12-04. +// + +#include "TripsRouter.h" +#include <cfloat> +#include <algorithm> + +TripsRouter::TripsRouter() +{ + Log->Write("TripsRouter!"); +} + +TripsRouter::TripsRouter(int id, RoutingStrategy s, Configuration* config) : Router(id, s) +{ + +} + +TripsRouter::~TripsRouter() +{ + +} + +bool TripsRouter::Init(Building* building) +{ + return true; +} + +bool TripsRouter::ReInit() +{ + return true; +} + +int TripsRouter::FindExit(Pedestrian* p) +{ + // Check if current position is already waiting area + // yes: set next goal and return findExit(p) + + p->SetExitIndex(17); + p->SetExitLine(0); + + return 17; +} diff --git a/routing/trips_router/TripsRouter.h b/routing/trips_router/TripsRouter.h new file mode 100644 index 0000000000000000000000000000000000000000..1b035052a93af5632232e29c08379660eebf02f3 --- /dev/null +++ b/routing/trips_router/TripsRouter.h @@ -0,0 +1,37 @@ +// +// Created by Tobias Schrödter on 2018-12-04. +// + +#ifndef JPSCORE_TRIPSROUTER_H +#define JPSCORE_TRIPSROUTER_H + +#include "../Router.h" +#include "../../general/Macros.h" +#include "../../geometry/Building.h" + +#include "../../pedestrian/Pedestrian.h" + +class Building; +class Pedestrian; +class OutputHandler; + +//log output +extern OutputHandler* Log; + +class TripsRouter : public Router { + +public: + TripsRouter(); + TripsRouter(int id, RoutingStrategy s, Configuration* config); + + virtual ~TripsRouter(); + + virtual bool Init(Building* building); + + virtual bool ReInit(); + + virtual int FindExit(Pedestrian* p); + +}; + +#endif //JPSCORE_TRIPSROUTER_H