Compare commits
182 Commits
workingTon
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e527797a3 | ||
|
|
6cda6beedf | ||
|
|
2f1698c4cc | ||
|
|
462ba1199c | ||
|
|
9a7ca32f37 | ||
|
|
d96af81109 | ||
|
|
2668cfc600 | ||
|
|
8761dce3ce | ||
|
|
09e57292f6 | ||
|
|
96c63ac3ec | ||
|
|
8d37e94647 | ||
|
|
cb61e0fe68 | ||
|
|
9de1e7142b | ||
|
|
7af6277da5 | ||
|
|
1dac907004 | ||
| 63bc8131ac | |||
|
|
ac91eeaa3d | ||
| 857d7a1553 | |||
| f990485d44 | |||
| d6ac8a72ca | |||
|
|
327b580b69 | ||
|
|
17f62e8788 | ||
|
|
9388e6e725 | ||
|
|
cbba812558 | ||
|
|
0bb22b54e9 | ||
|
|
d912a76246 | ||
|
|
4aec7bae26 | ||
|
|
32870a62c6 | ||
|
|
726d5f8b90 | ||
| 4d740d6236 | |||
|
|
e45cc1d3c6 | ||
|
|
baeb8bef11 | ||
|
|
0d22d91470 | ||
|
|
f7e4323d58 | ||
| 3d79fd5af0 | |||
| 8dd1ba0be6 | |||
|
|
a9368ee0f0 | ||
|
|
1408709efe | ||
| 083a1c2cf2 | |||
| 8ea7b7f3b6 | |||
|
|
f225788ac4 | ||
|
|
db7c0e310e | ||
|
|
da06bacb6b | ||
|
|
012abd5f80 | ||
| 18d237dcc8 | |||
| 2dee085131 | |||
|
|
74a424b555 | ||
|
|
d96f0588c2 | ||
|
|
94e53613ba | ||
|
|
6456e579cf | ||
|
|
dd9116086d | ||
|
|
19d9ce3961 | ||
| fb8061125f | |||
|
|
039f9c4cee | ||
|
|
ae3b95cb0e | ||
| 657e72b4c5 | |||
|
|
b50995ffe6 | ||
|
|
2036469491 | ||
|
|
38b633b9be | ||
| 9e6d9f4ce7 | |||
| a22290415e | |||
|
|
a8123d532d | ||
|
|
5bec3a5c0d | ||
|
|
d0b01b377d | ||
|
|
628aafaecd | ||
|
|
0fcc3fb1e9 | ||
|
|
1af670a0a9 | ||
|
|
24f05a2846 | ||
|
|
caca1bf219 | ||
|
|
a4ed06c4a4 | ||
|
|
2e6cf96f1d | ||
|
|
3b76c09986 | ||
|
|
1b9084a9c0 | ||
|
|
5d089f4b86 | ||
| bb31bc3b2d | |||
| 1c2081d406 | |||
|
|
e6444ceeb7 | ||
|
|
705763f03c | ||
|
|
f3b6155157 | ||
|
|
e0da02d29d | ||
| 8358f45674 | |||
|
|
1b22fdbc8e | ||
|
|
77ffb76252 | ||
| 42a3a47317 | |||
|
|
632b1ca7a7 | ||
|
|
0fef66ac62 | ||
| 2a923dfabc | |||
| 5e749d4da8 | |||
|
|
a35e043196 | ||
| 0bb1b707de | |||
|
|
4985d3bcdd | ||
| d8b91cf144 | |||
| e44d3421de | |||
|
|
4663c7d9e5 | ||
|
|
b207e5e0f2 | ||
|
|
1a1f249e9b | ||
| c9d02d440a | |||
|
|
3c72bc814c | ||
|
|
e81e8c112d | ||
|
|
e589a40da6 | ||
|
|
3591106b38 | ||
|
|
c02d07af51 | ||
| 55061ef0da | |||
|
|
fee6cd3496 | ||
| 3fc9688825 | |||
| ca6fed5371 | |||
|
|
c346b7f222 | ||
|
|
d48b0b8fd4 | ||
| 1114331fd2 | |||
|
|
74981c6a45 | ||
|
|
ade2425fbd | ||
|
|
63bc2f3046 | ||
|
|
55c061b344 | ||
| d03372ad3d | |||
|
|
95a5c8f34f | ||
|
|
c8063bc862 | ||
|
|
89c6b07e68 | ||
|
|
7af5131ccf | ||
|
|
72932ad90f | ||
|
|
ce558d0c01 | ||
|
|
08d8292976 | ||
|
|
3f8d21e146 | ||
| 284c6b11a6 | |||
|
|
d97b325650 | ||
|
|
ea351d6377 | ||
|
|
1c0e680b9d | ||
|
|
5d6d62c2d2 | ||
|
|
feaa2ea12c | ||
|
|
81b6b2c96c | ||
|
|
0151a4f6e4 | ||
|
|
3e31f6da53 | ||
|
|
72f083d32a | ||
| 3807c621c7 | |||
| 556bbe8829 | |||
| da477866c1 | |||
| 628be72e1f | |||
|
|
7eb3a16e48 | ||
| 686151b511 | |||
|
|
c19d18a3a6 | ||
| 845d89774d | |||
| e4cd9c6b8d | |||
|
|
c21925e86f | ||
| 3a92199777 | |||
| 8ed4305d2a | |||
| ba9559e5df | |||
| 9ea7da557b | |||
| 34271b5cb7 | |||
| fdc47215ac | |||
| 04d8ae8c74 | |||
| 0864f55a54 | |||
| 1ef3e33f2e | |||
| d40032ca74 | |||
|
|
8e295e25af | ||
|
|
99f282180e | ||
|
|
c42a592834 | ||
|
|
200aa94ca8 | ||
| f67f95d1ce | |||
|
|
a9cd8d5e68 | ||
| 8930be1e2c | |||
| 2fdaa795b2 | |||
| 4439123e5b | |||
|
|
d9788a71c0 | ||
|
|
a25ffb2ba3 | ||
|
|
749ee0b783 | ||
| e34e773e31 | |||
| c0cef979bb | |||
|
|
59502931e5 | ||
| b03804d378 | |||
|
|
b8b35d2a50 | ||
|
|
991f42060c | ||
| bb43e7f0fe | |||
| 18f48e23a8 | |||
|
|
3a83f5bdc9 | ||
| 4ac248d08e | |||
| 3868e42937 | |||
|
|
b6e396376e | ||
|
|
485be83b44 | ||
| 06e0e0a5aa | |||
|
|
d065015f7d | ||
| 7ec5fef697 | |||
|
|
e88f0b85a6 | ||
| b99bb2e226 |
13
Assertions.h
13
Assertions.h
@@ -22,7 +22,7 @@ namespace Assert {
|
||||
template <typename STR> static inline void doThrow(const STR err) {
|
||||
#ifdef WITH_ASSERTIONS
|
||||
std::string str = "in: ";
|
||||
str += __PRETTY_FUNCTION__;
|
||||
str += __FUNCTION__;
|
||||
str += " error: ";
|
||||
str += err;
|
||||
throw Exception(err);
|
||||
@@ -51,8 +51,8 @@ namespace Assert {
|
||||
if (v != nullptr) {doThrow(err);}
|
||||
}
|
||||
|
||||
template <typename T, typename STR> static inline void isNotNull(const T v, const STR err) {
|
||||
if (v == nullptr) {doThrow(err);}
|
||||
template <typename T, typename STR> static inline void isNotNull(const T& v, const STR err) {
|
||||
if (v == nullptr) {doThrow(err);}
|
||||
}
|
||||
|
||||
template <typename T, typename STR> static inline void isNotNaN(const T v, const STR err) {
|
||||
@@ -70,6 +70,13 @@ namespace Assert {
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename U, typename V, typename STR> static inline void isNear(const T v1, const U v2, const V delta, const STR err) {
|
||||
if (std::abs(v1-v2) > delta) {
|
||||
std::stringstream ss; ss << "\nexpected " << v1 << " +/- " << delta << " but is " << v2 << "\n";
|
||||
doThrow(err+ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename STR> static inline void isBetween(const T v, const T min, const T max, const STR err) {
|
||||
if (v < min || v > max) {
|
||||
std::stringstream ss; ss << "\n[" << min << ":" << max << "] but is " << v << "\n";
|
||||
|
||||
112
CMakeLists.txt
112
CMakeLists.txt
@@ -11,87 +11,115 @@ SET( CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" )
|
||||
PROJECT(Indoor)
|
||||
|
||||
IF(NOT CMAKE_BUILD_TYPE)
|
||||
MESSAGE(STATUS "No build type selected. Default to Debug")
|
||||
SET(CMAKE_BUILD_TYPE "Debug")
|
||||
MESSAGE(STATUS "No build type selected. Default to Debug")
|
||||
SET(CMAKE_BUILD_TYPE "Debug")
|
||||
ENDIF()
|
||||
|
||||
|
||||
|
||||
INCLUDE_DIRECTORIES(
|
||||
../
|
||||
/mnt/firma/kunden/HandyGames/
|
||||
../
|
||||
)
|
||||
|
||||
|
||||
FILE(GLOB HEADERS
|
||||
./*.h
|
||||
./*/*.h
|
||||
./*/*/*.h
|
||||
./*/*/*/*.h
|
||||
./*/*/*/*/*.h
|
||||
./*/*/*/*/*/*.h
|
||||
./tests/data/*
|
||||
./tests/data/*/*
|
||||
./tests/data/*/*/*
|
||||
./*.h
|
||||
./*/*.h
|
||||
./*/*/*.h
|
||||
./*/*/*/*.h
|
||||
./*/*/*/*/*.h
|
||||
./*/*/*/*/*/*.h
|
||||
./*/*/*/*/*/*/*.h
|
||||
./tests/data/*
|
||||
./tests/data/*/*
|
||||
./tests/data/*/*/*
|
||||
)
|
||||
|
||||
|
||||
FILE(GLOB SOURCES
|
||||
./*.cpp
|
||||
./*/*.cpp
|
||||
./*/*/*.cpp
|
||||
./*/*/*/*.cpp
|
||||
./*.cpp
|
||||
./*/*.cpp
|
||||
./*/*/*.cpp
|
||||
./*/*/*/*.cpp
|
||||
./*/*/*/*/*.cpp
|
||||
./*/*/*/*/*/*.cpp
|
||||
)
|
||||
|
||||
FIND_PACKAGE( OpenMP REQUIRED)
|
||||
if(OPENMP_FOUND)
|
||||
message("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}")
|
||||
endif()
|
||||
|
||||
if(${CMAKE_GENERATOR} MATCHES "Visual Studio")
|
||||
|
||||
SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_X86_ /D_USE_MATH_DEFINES")
|
||||
SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi /Oi /GL /Ot /Ox /D_X86_ /D_USE_MATH_DEFINES")
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG")
|
||||
SET(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG /INCREMENTAL:NO")
|
||||
add_definitions(
|
||||
-D_USE_MATH_DEFINES
|
||||
-DUNICODE
|
||||
-D_UNICODE
|
||||
-DNOGDI
|
||||
# -D_X86_
|
||||
)
|
||||
|
||||
set(CMAKE_CONFIGURATION_TYPES Release Debug)
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /permissive-")
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:__cplusplus")
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++17")
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:twoPhase-") # disable two-phase name lookup due to OpenMP
|
||||
|
||||
|
||||
#SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ")
|
||||
SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi /Oi /GL /Ot /Ox")
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG")
|
||||
SET(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG /INCREMENTAL:NO")
|
||||
|
||||
set(CMAKE_CONFIGURATION_TYPES Release Debug)
|
||||
|
||||
else()
|
||||
|
||||
# system specific compiler flags
|
||||
ADD_DEFINITIONS(
|
||||
|
||||
-std=gnu++11
|
||||
-std=gnu++17
|
||||
|
||||
-Wall
|
||||
-Werror=return-type
|
||||
-Wextra
|
||||
-Wpedantic
|
||||
-Wall
|
||||
-Werror=return-type
|
||||
-Wextra
|
||||
-Wpedantic
|
||||
|
||||
-fstack-protector-all
|
||||
|
||||
-g3
|
||||
#-O0
|
||||
-march=native
|
||||
|
||||
-DWITH_TESTS
|
||||
-DWITH_ASSERTIONS
|
||||
-DWITH_DEBUG_LOG
|
||||
-fstack-protector-all
|
||||
|
||||
-g3
|
||||
#-O0
|
||||
#-O2
|
||||
-march=native
|
||||
|
||||
-DWITH_TESTS
|
||||
-DWITH_ASSERTIONS
|
||||
-DWITH_DEBUG_LOG
|
||||
-D_GLIBCXX_DEBUG
|
||||
)
|
||||
|
||||
endif()
|
||||
|
||||
# build a binary file
|
||||
ADD_EXECUTABLE(
|
||||
${PROJECT_NAME}
|
||||
${HEADERS}
|
||||
${SOURCES}
|
||||
${PROJECT_NAME}
|
||||
${HEADERS}
|
||||
${SOURCES}
|
||||
)
|
||||
|
||||
#SET(EXTRA_LIBS ${EXTRA_LIBS} nl-genl-3 nl-3)
|
||||
#INCLUDE_DIRECTORIES(/usr/include/libnl3/)
|
||||
#SET(EXTRA_LIBS ${EXTRA_LIBS} iw)
|
||||
|
||||
# needed external libraries
|
||||
TARGET_LINK_LIBRARIES(
|
||||
${PROJECT_NAME}
|
||||
gtest
|
||||
pthread
|
||||
${PROJECT_NAME}
|
||||
gtest
|
||||
pthread
|
||||
${EXTRA_LIBS}
|
||||
)
|
||||
|
||||
SET(CMAKE_C_COMPILER ${CMAKE_CXX_COMPILER})
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
#include <string>
|
||||
|
||||
#ifdef ANDROID
|
||||
#include <QMessageBox>
|
||||
//include <QMessageBox>
|
||||
#include <QtDebug>
|
||||
#endif
|
||||
|
||||
class Exception : public std::exception {
|
||||
@@ -22,7 +23,10 @@ public:
|
||||
|
||||
// TODO better solution?
|
||||
#ifdef ANDROID
|
||||
QMessageBox::question(nullptr, "Exception", str.c_str(), QMessageBox::Ok);
|
||||
qDebug() << "-------- ERROR --------";
|
||||
qDebug() << str.c_str();
|
||||
qDebug() << "------------------------";
|
||||
//QMessageBox::question(nullptr, "Exception", str.c_str(), QMessageBox::Ok);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
125
data/File.h
Normal file
125
data/File.h
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef FS_FILE_H
|
||||
#define FS_FILE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
|
||||
|
||||
#if __cplusplus > 201103L
|
||||
|
||||
// use new cross-platform std::experimental stuff
|
||||
#include <experimental/filesystem>
|
||||
|
||||
namespace FS {
|
||||
|
||||
class File {
|
||||
|
||||
std::experimental::filesystem::path path;
|
||||
|
||||
public:
|
||||
|
||||
File(const std::string& fileOrFolder) : path(fileOrFolder) {
|
||||
|
||||
}
|
||||
|
||||
bool exists() const {
|
||||
return std::experimental::filesystem::exists(path);
|
||||
}
|
||||
|
||||
std::vector<File> listFiles() const {
|
||||
std::vector<File> res;
|
||||
for (std::experimental::filesystem::directory_entry entry : std::experimental::filesystem::directory_iterator(path)) {
|
||||
const std::string abs = entry.path().string();
|
||||
res.push_back(File(abs));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string getPath() const {
|
||||
return path.string();
|
||||
}
|
||||
|
||||
std::string getFilename() const {
|
||||
return path.filename().string();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// use linux-only fallback
|
||||
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace FS {
|
||||
|
||||
class File {
|
||||
|
||||
std::string path;
|
||||
|
||||
public:
|
||||
|
||||
File(const std::string& fileOrFolder) : path(fileOrFolder) {
|
||||
|
||||
}
|
||||
|
||||
bool exists() const {
|
||||
struct stat st;
|
||||
int res = stat(path.c_str(), &st );
|
||||
return res == 0;
|
||||
}
|
||||
|
||||
/** list of all files/folders within the current folder */
|
||||
std::vector<File> listFiles() const {
|
||||
|
||||
std::vector<File> res;
|
||||
|
||||
DIR* dir;
|
||||
|
||||
dir = opendir(path.c_str());
|
||||
if (!dir) {throw Exception("failed to open folder " + path);}
|
||||
|
||||
// fetch all entries
|
||||
while(true) {
|
||||
dirent* entry = readdir(dir);
|
||||
if (!entry) {break;}
|
||||
const std::string abs = path + "/" + entry->d_name;
|
||||
res.push_back(File(abs));
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
const std::string& getPath() const {
|
||||
return path;
|
||||
}
|
||||
|
||||
const std::string getFilename() const {
|
||||
const size_t lastSlash = path.find_last_of("/");
|
||||
if (std::string::npos == lastSlash) {return path;}
|
||||
std::string name = path.substr(lastSlash+1);
|
||||
return name;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // FS_FILE_H
|
||||
78
data/HistoryTS.h
Executable file
78
data/HistoryTS.h
Executable file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef HISTORYTS_H
|
||||
#define HISTORYTS_H
|
||||
|
||||
#include <vector>
|
||||
#include "Timestamp.h"
|
||||
#include <algorithm>
|
||||
|
||||
/**
|
||||
* keep the history of values for a given amount of time
|
||||
*/
|
||||
template <typename T> class HistoryTS {
|
||||
|
||||
private:
|
||||
|
||||
/** timestamp -> value combination */
|
||||
struct Entry {
|
||||
Timestamp ts;
|
||||
T value;
|
||||
Entry(const Timestamp ts, const T& value) : ts(ts), value(value) {;}
|
||||
};
|
||||
|
||||
/** the time-window to keep */
|
||||
Timestamp window;
|
||||
|
||||
/** the value history for the window-size */
|
||||
std::vector<Entry> history;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
/** ctor with the time-window to keep */
|
||||
HistoryTS(const Timestamp window) : window(window) {
|
||||
|
||||
}
|
||||
|
||||
/** add a new entry */
|
||||
void add(const Timestamp ts, const T& data) {
|
||||
|
||||
// append to history
|
||||
history.push_back(Entry(ts, data));
|
||||
|
||||
// remove too-old history entries
|
||||
const Timestamp oldest = ts - window;
|
||||
while(history.front().ts < oldest) {
|
||||
|
||||
// remove from history
|
||||
history.erase(history.begin());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** get the most recent entry */
|
||||
T getMostRecent() const {
|
||||
return history.back().value;
|
||||
}
|
||||
|
||||
/** get the oldest entry available */
|
||||
T getOldest() const {
|
||||
return history.front().value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // HISTORYTS_H
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef RINGBUFFER_H
|
||||
#define RINGBUFFER_H
|
||||
|
||||
|
||||
100
data/Timestamp.h
100
data/Timestamp.h
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef TIMESTAMP_H
|
||||
#define TIMESTAMP_H
|
||||
|
||||
@@ -11,28 +21,51 @@ struct Timestamp {
|
||||
private:
|
||||
|
||||
/** internal timestamp in milliseconds */
|
||||
int64_t _ms;
|
||||
//int64_t _ms;
|
||||
int64_t _us;
|
||||
|
||||
/** HIDDEN ctor. use factory methods */
|
||||
explicit Timestamp(const int64_t ms) : _ms(ms) {;}
|
||||
/** hidden ctor. for internal methods only */
|
||||
Timestamp(int64_t us) : _us(us) {;}
|
||||
|
||||
public:
|
||||
|
||||
/** empty ctor */
|
||||
explicit Timestamp() : _ms(0) {;}
|
||||
explicit Timestamp() : _us(0) {;}
|
||||
|
||||
|
||||
/** get timestamp from the given value which represents microseconds */
|
||||
static inline Timestamp fromUS(const int64_t us) {
|
||||
Timestamp ts;
|
||||
ts._us = us;
|
||||
return ts;
|
||||
}
|
||||
/** get timestamp from the given value which represents milliesconds */
|
||||
static inline Timestamp fromMS(const int64_t ms) {return Timestamp(ms);}
|
||||
static inline Timestamp fromMS(const int64_t ms) {
|
||||
Timestamp ts;
|
||||
ts._us = ms * 1000;
|
||||
return ts;
|
||||
}
|
||||
|
||||
/** get timestamp from the given value which represents seconds */
|
||||
static inline Timestamp fromSec(const float sec) {return Timestamp(sec*1000);}
|
||||
static inline Timestamp fromSec(const float sec) {
|
||||
Timestamp ts;
|
||||
ts._us = static_cast<int64_t>(sec * 1000 * 1000);
|
||||
return ts;
|
||||
}
|
||||
|
||||
/** get timestamp from the given value which represents a sample rate in hz */
|
||||
static inline Timestamp fromHz(const float hz) {
|
||||
const float sec = 1.0f / hz;
|
||||
return Timestamp::fromSec(sec);
|
||||
}
|
||||
|
||||
/** get timestamp for the current unix-time */
|
||||
static inline Timestamp fromUnixTime() {
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto duration = now.time_since_epoch();
|
||||
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
|
||||
return Timestamp(millis);
|
||||
//auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
|
||||
auto micros = std::chrono::duration_cast<std::chrono::microseconds>(duration).count();
|
||||
return Timestamp::fromUS(micros);
|
||||
}
|
||||
|
||||
/** get timestamp for the current system-time */
|
||||
@@ -45,41 +78,50 @@ public:
|
||||
|
||||
public:
|
||||
|
||||
/** get finest available value */
|
||||
inline int64_t finest() const {return _us;}
|
||||
|
||||
/** get timestamp in microseconds */
|
||||
inline int64_t us() const {return _us;}
|
||||
|
||||
/** get timestamp in milliseconds */
|
||||
inline int64_t ms() const {return _ms;}
|
||||
inline int64_t ms() const {return _us/1000;}
|
||||
|
||||
/** get timestamp in seconds */
|
||||
inline float sec() const {return _ms/1000.0f;}
|
||||
inline float sec() const {return _us/1000.0f/1000.0f;}
|
||||
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/** is this timestamp zero? */
|
||||
bool isZero() const {return _ms == 0;}
|
||||
bool isZero() const {return _us == 0;}
|
||||
|
||||
/** equal? */
|
||||
bool operator == (const Timestamp& o) const {return _ms == o._ms;}
|
||||
bool operator == (const Timestamp& o) const {return _us == o._us;}
|
||||
|
||||
/** not equal? */
|
||||
bool operator != (const Timestamp& o) const {return _ms != o._ms;}
|
||||
bool operator != (const Timestamp& o) const {return _us != o._us;}
|
||||
|
||||
/** smaller than the given one? */
|
||||
bool operator < (const Timestamp& o) const {return _ms < o._ms;}
|
||||
bool operator <= (const Timestamp& o) const {return _ms <= o._ms;}
|
||||
bool operator < (const Timestamp& o) const {return _us < o._us;}
|
||||
bool operator <= (const Timestamp& o) const {return _us <= o._us;}
|
||||
|
||||
/** greater than the given one? */
|
||||
bool operator > (const Timestamp& o) const {return _ms > o._ms;}
|
||||
bool operator >= (const Timestamp& o) const {return _ms >= o._ms;}
|
||||
bool operator > (const Timestamp& o) const {return _us > o._us;}
|
||||
bool operator >= (const Timestamp& o) const {return _us >= o._us;}
|
||||
|
||||
|
||||
|
||||
Timestamp operator - (const Timestamp& o) const {return Timestamp(_ms - o._ms);}
|
||||
Timestamp operator - (const Timestamp& o) const {return Timestamp(_us - o._us);}
|
||||
Timestamp& operator -= (const Timestamp& o) {_us += o._us; return *this;}
|
||||
|
||||
Timestamp operator + (const Timestamp& o) const {return Timestamp(_ms + o._ms);}
|
||||
Timestamp operator + (const Timestamp& o) const {return Timestamp(_us + o._us);}
|
||||
Timestamp& operator += (const Timestamp& o) {_us += o._us; return *this;}
|
||||
|
||||
Timestamp operator * (const float val) const {return Timestamp(_ms * val);}
|
||||
template <typename T> Timestamp operator * (const T val) const {return Timestamp(_us * val);}
|
||||
|
||||
template <typename T> Timestamp operator / (const T val) const {return Timestamp(_us / val);}
|
||||
|
||||
// /** cast to float */
|
||||
// operator float () const {return sec();}
|
||||
@@ -87,4 +129,22 @@ public:
|
||||
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template<> class numeric_limits<Timestamp> {
|
||||
public:
|
||||
static Timestamp min() {
|
||||
const int64_t minVal = std::numeric_limits<int64_t>::min();
|
||||
return Timestamp::fromMS(minVal);
|
||||
}
|
||||
static Timestamp lowest() {
|
||||
const int64_t minVal = std::numeric_limits<int64_t>::min();
|
||||
return Timestamp::fromMS(minVal);
|
||||
}
|
||||
static Timestamp max() {
|
||||
const int64_t maxVal = std::numeric_limits<int64_t>::max();
|
||||
return Timestamp::fromMS(maxVal);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif // TIMESTAMP_H
|
||||
|
||||
24
data/XMLload.h
Normal file
24
data/XMLload.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef XMLLOAD_H
|
||||
#define XMLLOAD_H
|
||||
|
||||
#include "xml.h"
|
||||
|
||||
class XMLload {
|
||||
|
||||
public:
|
||||
|
||||
virtual void readFromXML(XMLDoc* doc, XMLElem* src) = 0;
|
||||
|
||||
};
|
||||
|
||||
#endif // XMLLOAD_H
|
||||
24
data/XMLsave.h
Normal file
24
data/XMLsave.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef XMLSAVE_H
|
||||
#define XMLSAVE_H
|
||||
|
||||
#include "xml.h"
|
||||
|
||||
class XMLsave {
|
||||
|
||||
public:
|
||||
|
||||
virtual void writeToXML(XMLDoc* doc, XMLElem* dst) = 0;
|
||||
|
||||
};
|
||||
|
||||
#endif // XMLSAVE_H
|
||||
54
data/XMLserialize.h
Normal file
54
data/XMLserialize.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef XMLSERIALIZE_H
|
||||
#define XMLSERIALIZE_H
|
||||
|
||||
#include "XMLload.h"
|
||||
#include "XMLsave.h"
|
||||
#include "xml.h"
|
||||
|
||||
class XMLserialize : public XMLload, public XMLsave {
|
||||
|
||||
public:
|
||||
|
||||
void loadXML(const std::string& file) {
|
||||
|
||||
XMLDoc doc;
|
||||
assertOK(doc.LoadFile(file.c_str()), doc, "error while loading file");
|
||||
XMLElem* root = doc.FirstChildElement("root");
|
||||
readFromXML(&doc, root);
|
||||
|
||||
}
|
||||
|
||||
void saveXML(const std::string& file) {
|
||||
|
||||
XMLDoc doc;
|
||||
XMLElem* root = doc.NewElement("root");
|
||||
doc.InsertFirstChild(root);
|
||||
writeToXML(&doc, root);
|
||||
assertOK(doc.SaveFile(file.c_str()), doc, "error while saving file");
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
|
||||
static void assertOK(XMLErr res, XMLDoc& doc, const std::string& txt) {
|
||||
if (res != tinyxml2::XMLError::XML_SUCCESS) {
|
||||
const std::string err = doc.ErrorName();
|
||||
const std::string add = doc.GetErrorStr1();
|
||||
throw Exception(txt + ": " + err + " - " + add);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // XMLSERIALIZE_H
|
||||
105
data/csv.h
Normal file
105
data/csv.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef CSV_H
|
||||
#define CSV_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include "../misc/Debug.h"
|
||||
#include "../Exception.h"
|
||||
|
||||
class CSV {
|
||||
|
||||
static constexpr const char* NAME = "CSV";
|
||||
|
||||
public:
|
||||
|
||||
/** one column within the CSV */
|
||||
struct Col {
|
||||
std::string data;
|
||||
Col(const std::string& data) : data(data) {;}
|
||||
const std::string& asString() const {return data;}
|
||||
int asInt() const {return std::stoi(data);}
|
||||
long asLong() const {return std::stol(data);}
|
||||
double asDouble() const {return std::stod(data);}
|
||||
};
|
||||
|
||||
/** one row within the CSV */
|
||||
struct Row : std::vector<Col> {
|
||||
|
||||
};
|
||||
|
||||
/** one csv document */
|
||||
struct Doc : std::vector<Row> {
|
||||
|
||||
};
|
||||
|
||||
|
||||
class Reader {
|
||||
|
||||
private:
|
||||
|
||||
const char sep;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
Reader(const char sep) : sep(sep) {
|
||||
|
||||
}
|
||||
|
||||
/** read the given csv file */
|
||||
Doc read(const std::string& file) {
|
||||
|
||||
Log::add(NAME, "reading file: " + file);
|
||||
|
||||
Doc doc;
|
||||
|
||||
std::ifstream inp(file.c_str());
|
||||
|
||||
// sanity check
|
||||
if (!inp) {
|
||||
throw Exception("failed to open file: " + file);
|
||||
}
|
||||
|
||||
int rowCnt = 0;
|
||||
std::string line;
|
||||
|
||||
// read all lines within the CSV
|
||||
while(std::getline(inp, line)) {
|
||||
|
||||
++rowCnt;
|
||||
|
||||
// split
|
||||
Row row;
|
||||
std::stringstream ss(line);
|
||||
std::string tmp;
|
||||
while(getline(ss, tmp, sep)){
|
||||
row.push_back(Col(tmp));
|
||||
}
|
||||
|
||||
// apend row
|
||||
doc.push_back(row);
|
||||
|
||||
}
|
||||
|
||||
Log::add(NAME, "got " + std::to_string(rowCnt) + " rows");
|
||||
|
||||
return doc;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif // CSV_H
|
||||
43
data/xml.h
Normal file
43
data/xml.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef DATA_XML_H
|
||||
#define DATA_XML_H
|
||||
|
||||
#include "../lib/tinyxml/tinyxml2.h"
|
||||
|
||||
using XMLDoc = tinyxml2::XMLDocument;
|
||||
using XMLErr = tinyxml2::XMLError;
|
||||
using XMLNode = tinyxml2::XMLNode;
|
||||
using XMLElem = tinyxml2::XMLElement;
|
||||
using XMLAttr = tinyxml2::XMLAttribute;
|
||||
using XMLText = tinyxml2::XMLText;
|
||||
|
||||
#define XML_FOREACH_ATTR(attr, root) \
|
||||
for (const XMLAttr* attr = root->FirstAttribute(); attr != nullptr; attr = attr->Next())
|
||||
|
||||
#define XML_FOREACH_NODE(sub, root) \
|
||||
for (const XMLNode* sub = root->FirstChild(); sub != nullptr; sub = sub->NextSibling())
|
||||
|
||||
#define XML_FOREACH_ELEM(sub, root) \
|
||||
for (XMLElem* sub = (XMLElem*)root->FirstChild(); sub != nullptr; sub = (XMLElem*)sub->NextSibling())
|
||||
|
||||
#define XML_FOREACH_ELEM_NAMED(name, sub, root) \
|
||||
for (XMLElem* sub = root->FirstChildElement(name); sub != nullptr; sub = sub->NextSiblingElement(name))
|
||||
|
||||
#define XML_ID(node) node->Attribute("xml:id")
|
||||
#define XML_WITH_ID(node) node->Attribute("with_id")
|
||||
|
||||
#define XML_ASSERT_NODE_NAME(name, node) if (std::string(name) != std::string(node->Name())) {throw Exception("expected " + std::string(name) + " got " + node->Name());}
|
||||
|
||||
#define XML_MANDATORY_ATTR(node, attr) (node->Attribute(attr) ? node->Attribute(attr) : throw Exception(std::string("missing XML attribute: ") + attr));
|
||||
|
||||
|
||||
#endif // DATA_XML_H
|
||||
76
debug/PlotWifiMeasurements.h
Normal file
76
debug/PlotWifiMeasurements.h
Normal file
@@ -0,0 +1,76 @@
|
||||
#ifndef PLOTWIFIMEASUREMENTS_H
|
||||
#define PLOTWIFIMEASUREMENTS_H
|
||||
|
||||
#include <KLib/misc/gnuplot/Gnuplot.h>
|
||||
#include <KLib/misc/gnuplot/GnuplotPlot.h>
|
||||
#include <KLib/misc/gnuplot/GnuplotPlotElementLines.h>
|
||||
|
||||
#include "../sensors/radio/WiFiMeasurements.h"
|
||||
#include "../sensors/radio/WiFiQualityAnalyzer.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
/**
|
||||
* helper-class that plots incoming wifi measurements
|
||||
* one line per AP
|
||||
*/
|
||||
class PlotWifiMeasurements {
|
||||
|
||||
K::Gnuplot gp;
|
||||
K::GnuplotPlot gplot;
|
||||
K::GnuplotPlotElementLines lineQuality;
|
||||
std::unordered_map<MACAddress, K::GnuplotPlotElementLines*> lineForMac;
|
||||
WiFiQualityAnalyzer quality;
|
||||
|
||||
public:
|
||||
|
||||
PlotWifiMeasurements(const std::string& labelX = "time (sec)", const std::string& labelY = "dBm") {
|
||||
|
||||
gplot.getAxisX().setLabel(labelX);
|
||||
gplot.getAxisY().setLabel(labelY);
|
||||
|
||||
// quality indicator using the 2nd y axis
|
||||
gplot.add(&lineQuality); lineQuality.getStroke().setWidth(2); lineQuality.setUseAxis(1, 2);
|
||||
gplot.getAxisY2().setTicsVisible(true);
|
||||
gplot.getAxisY2().setRange(K::GnuplotAxis::Range(0, 1));
|
||||
|
||||
}
|
||||
|
||||
K::Gnuplot& getGP() {
|
||||
return gp;
|
||||
}
|
||||
|
||||
K::GnuplotPlot& getPlot() {
|
||||
return gplot;
|
||||
}
|
||||
|
||||
void add(const WiFiMeasurements& mes) {
|
||||
|
||||
const Timestamp ts = mes.entries.front().getTimestamp();
|
||||
|
||||
for (const WiFiMeasurement& m : mes.entries) {
|
||||
const auto& it = lineForMac.find(m.getAP().getMAC());
|
||||
if (it == lineForMac.end()) {
|
||||
K::GnuplotPlotElementLines* line = new K::GnuplotPlotElementLines();
|
||||
line->setTitle(m.getAP().getMAC().asString());
|
||||
line->getStroke().getColor().setAuto();
|
||||
lineForMac[m.getAP().getMAC()] = line;
|
||||
gplot.add(line);
|
||||
}
|
||||
const K::GnuplotPoint2 gp2(m.getTimestamp().sec(), m.getRSSI());
|
||||
lineForMac[m.getAP().getMAC()]->add(gp2);
|
||||
}
|
||||
|
||||
quality.add(mes);
|
||||
lineQuality.add(K::GnuplotPoint2(ts.sec(), quality.getQuality()));
|
||||
|
||||
}
|
||||
|
||||
void plot() {
|
||||
gp.draw(gplot);
|
||||
gp.flush();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // PLOTWIFIMEASUREMENTS_H
|
||||
198
floorplan/3D/Builder.h
Normal file
198
floorplan/3D/Builder.h
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef FLOORPLAN_3D_BUILDER_H
|
||||
#define FLOORPLAN_3D_BUILDER_H
|
||||
|
||||
#include "../v2/Floorplan.h"
|
||||
|
||||
#include "FloorplanMesh.h"
|
||||
#include "Obstacle3.h"
|
||||
|
||||
#include "Outline.h"
|
||||
#include "Stairs.h"
|
||||
#include "Handrails.h"
|
||||
#include "Objects.h"
|
||||
#include "Pillars.h"
|
||||
#include "Doors.h"
|
||||
|
||||
#include "Walls.h"
|
||||
#include "WallsViaCubes.h"
|
||||
#include "WallsViaCuttedQuads.h"
|
||||
|
||||
namespace Floorplan3D {
|
||||
|
||||
class Builder {
|
||||
|
||||
/** the to-be-exported map */
|
||||
const Floorplan::IndoorMap* map;
|
||||
|
||||
public:
|
||||
|
||||
bool exportCeilings = true;
|
||||
bool exportObstacles = true;
|
||||
bool exportStairs = true;
|
||||
bool fancyStairs = true;
|
||||
bool exportHandrails = true;
|
||||
bool exportDoors = true;
|
||||
// bool exportAboveDoors = true;
|
||||
bool doorsOpen = false;
|
||||
bool exportObjects = true;
|
||||
bool exportPillars = true;
|
||||
bool exportWallTops = false;
|
||||
bool center = false;
|
||||
|
||||
//Walls* walls = new WallsViaCubes();
|
||||
//Walls* walls = new WallsViaCuttedQuads();
|
||||
|
||||
public:
|
||||
|
||||
|
||||
/** ctor */
|
||||
Builder(const Floorplan::IndoorMap* map) : map(map) {
|
||||
|
||||
}
|
||||
|
||||
/** get the created mesh */
|
||||
FloorplanMesh getMesh() {
|
||||
|
||||
FloorplanMesh mesh;
|
||||
mesh.elements = triangulize();
|
||||
|
||||
if (center) {
|
||||
BBox3 bb = mesh.getBBox();
|
||||
mesh -= Point3(bb.getCenter().x, bb.getCenter().y, 0);
|
||||
}
|
||||
|
||||
return mesh;
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/** get all triangles grouped by obstacle */
|
||||
std::vector<Obstacle3D> triangulize() {
|
||||
|
||||
// TODO: filtering??
|
||||
std::vector<Floorplan::Floor*> floors = map->floors;
|
||||
|
||||
std::vector<Obstacle3D> res;
|
||||
|
||||
// get the to-be-exported floors (either "all" or "user defined")
|
||||
//const std::vector<Floorplan::Floor*>& floors = (exportFloors.empty()) ? (map->floors) : (exportFloors);
|
||||
|
||||
// process each floor
|
||||
for (const Floorplan::Floor* f : floors) {
|
||||
|
||||
if (!f->enabled) {continue;}
|
||||
|
||||
// triangulize the floor itself (floor/ceiling)
|
||||
if (exportCeilings) {
|
||||
Outline out;
|
||||
const std::vector<Obstacle3D> tmp = out.get(f);
|
||||
res.insert(res.end(), tmp.begin(), tmp.end());
|
||||
}
|
||||
|
||||
// process each obstacle within the floor
|
||||
if (f->obstacles.enabled) {
|
||||
|
||||
if (1 == 1) {
|
||||
const std::vector<Obstacle3D> tmp = getLines(f);
|
||||
res.insert(res.end(), tmp.begin(), tmp.end());
|
||||
}
|
||||
|
||||
if (1 == 1) {
|
||||
const std::vector<Obstacle3D> tmp = getWalls(f);
|
||||
res.insert(res.end(), tmp.begin(), tmp.end());
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (exportHandrails) {
|
||||
Handrails rails;
|
||||
const std::vector<Obstacle3D> tmp = rails.getHandrails(f);
|
||||
res.insert(res.end(), tmp.begin(), tmp.end());
|
||||
}
|
||||
|
||||
if (exportObjects) {
|
||||
Objects objs;
|
||||
const std::vector<Obstacle3D> tmp = objs.getObjects(f);
|
||||
res.insert(res.end(), tmp.begin(), tmp.end());
|
||||
}
|
||||
|
||||
if (exportPillars) {
|
||||
Pillars pillars;
|
||||
const std::vector<Obstacle3D> tmp = pillars.getPillars(f);
|
||||
res.insert(res.end(), tmp.begin(), tmp.end());
|
||||
}
|
||||
|
||||
if (exportDoors) {
|
||||
Doors doors;
|
||||
const std::vector<Obstacle3D> tmp = doors.getDoors(f);
|
||||
res.insert(res.end(), tmp.begin(), tmp.end());
|
||||
}
|
||||
|
||||
// for (const Floorplan::FloorObstacle* fo : f->obstacles) {
|
||||
// std::vector<Obstacle3D> tmp = getWalls(f);
|
||||
// res.insert(res.end(), tmp.begin(), tmp.end());
|
||||
// }
|
||||
}
|
||||
|
||||
// // stairs
|
||||
if (f->stairs.enabled && exportStairs) {
|
||||
Stairs stairs;
|
||||
const std::vector<Obstacle3D> tmp = stairs.getStairs(f);
|
||||
res.insert(res.end(), tmp.begin(), tmp.end());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** just get all walls */
|
||||
std::vector<Obstacle3D> getWalls(const Floorplan::Floor* f) {
|
||||
|
||||
WallsViaCuttedQuads walls;
|
||||
|
||||
for (const Floorplan::FloorObstacle* obs : f->obstacles) {
|
||||
const Floorplan::FloorObstacleWall* wall = dynamic_cast<const Floorplan::FloorObstacleWall*>(obs);
|
||||
if (wall) {walls.add(f, wall);}
|
||||
}
|
||||
|
||||
return walls.get();
|
||||
|
||||
}
|
||||
|
||||
/** get all old walls (lines) */
|
||||
std::vector<Obstacle3D> getLines(const Floorplan::Floor* f) {
|
||||
|
||||
WallsViaCubes walls;
|
||||
|
||||
for (const Floorplan::FloorObstacle* obs : f->obstacles) {
|
||||
const Floorplan::FloorObstacleLine* line = dynamic_cast<const Floorplan::FloorObstacleLine*>(obs);
|
||||
if (line) {
|
||||
if (line->type == Floorplan::ObstacleType::WALL) {
|
||||
walls.add(f, line, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return walls.get();
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FLOORPLAN_3D_BUILDER_H
|
||||
77
floorplan/3D/Doors.h
Normal file
77
floorplan/3D/Doors.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef FLOORPLAN_3D_DOORS_H
|
||||
#define FLOORPLAN_3D_DOORS_H
|
||||
|
||||
#include "Walls.h"
|
||||
#include "misc.h"
|
||||
#include "primitives/Cube.h"
|
||||
|
||||
namespace Floorplan3D {
|
||||
|
||||
class Doors {
|
||||
|
||||
Cube::Part cubeParts = (Cube::Part) 63; // leftright,topbottom,rearfront
|
||||
|
||||
public:
|
||||
|
||||
std::vector<Obstacle3D> getDoors(const Floorplan::Floor* floor) {
|
||||
std::vector<Obstacle3D> res;
|
||||
for (const Floorplan::FloorObstacle* o: floor->obstacles) {
|
||||
const Floorplan::FloorObstacleWall* wall = dynamic_cast<const Floorplan::FloorObstacleWall*>(o);
|
||||
if (wall) {
|
||||
for (const Floorplan::FloorObstacleWallDoor* door : wall->doors) {
|
||||
res.push_back(getDoor(floor, wall, door));
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Obstacle3D getDoor(const Floorplan::Floor* f, const Floorplan::FloorObstacleWall* wall, const Floorplan::FloorObstacleWallDoor* door) {
|
||||
|
||||
FloorPos fpos(f);
|
||||
|
||||
const float thickness_m = 0.1;
|
||||
const Point2 from = door->getStart(wall);
|
||||
const Point2 to = door->getEnd(wall);
|
||||
const Point2 cen2 = (from+to)/2;
|
||||
|
||||
const float rad = std::atan2(to.y - from.y, to.x - from.x);
|
||||
const float deg = rad * 180 / M_PI;
|
||||
|
||||
// cube's destination center
|
||||
const double height = door->height;
|
||||
const double cenZ = (fpos.z1 + height/2);
|
||||
const Point3 pos(cen2.x, cen2.y, cenZ);
|
||||
|
||||
// div by 2.01 to prevent overlapps and z-fighting
|
||||
const float sx = from.getDistance(to) / 2;
|
||||
const float sy = thickness_m / 2;
|
||||
const float sz = height / 2.01f; // prevent overlaps
|
||||
const Point3 size(sx, sy, sz);
|
||||
const Point3 rot(0,0,deg);
|
||||
|
||||
// build
|
||||
Cube cube(pos, size, rot, cubeParts);
|
||||
|
||||
// done
|
||||
Obstacle3D res(Obstacle3D::Type::DOOR, door->material);
|
||||
res.triangles = cube.getTriangles();
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FLOORPLAN_3D_DOORS_H
|
||||
356
floorplan/3D/FloorplanMesh.h
Normal file
356
floorplan/3D/FloorplanMesh.h
Normal file
@@ -0,0 +1,356 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef FLOORPLAN_3D_FLOORPLANMESH_H
|
||||
#define FLOORPLAN_3D_FLOORPLANMESH_H
|
||||
|
||||
#include "Obstacle3.h"
|
||||
#include "../../geo/BBox3.h"
|
||||
#include <fstream>
|
||||
|
||||
namespace Floorplan3D {
|
||||
|
||||
/**
|
||||
* meshed version of the floorplan
|
||||
*/
|
||||
struct FloorplanMesh {
|
||||
|
||||
std::vector<Obstacle3D> elements;
|
||||
|
||||
BBox3 getBBox() const {
|
||||
BBox3 bb;
|
||||
for (const Obstacle3D& o : elements) {
|
||||
for (const Triangle3& t : o.triangles) {
|
||||
bb.add(t.p1);
|
||||
bb.add(t.p2);
|
||||
bb.add(t.p3);
|
||||
}
|
||||
}
|
||||
return bb;
|
||||
}
|
||||
|
||||
void operator -= (const Point3 p) {
|
||||
for (Obstacle3D& o : elements) {
|
||||
for (Triangle3& t : o.triangles) {
|
||||
t -= p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** export as OBJ file */
|
||||
void exportOBJsimple(const std::string& file) {
|
||||
std::ofstream out(file.c_str());
|
||||
out << toOBJsimple();
|
||||
out.close();
|
||||
}
|
||||
|
||||
/** export as OBJ file */
|
||||
void exportOBJcomplex(const std::string& file, const std::string& nameOnly) {
|
||||
std::ofstream outOBJ((file+".obj").c_str());
|
||||
std::ofstream outMTL((file+".mtl").c_str());
|
||||
OBJData data = toOBJ(nameOnly);
|
||||
outOBJ << data.obj;
|
||||
outMTL << data.mtl;
|
||||
outOBJ.close();
|
||||
outMTL.close();
|
||||
}
|
||||
|
||||
/** export as PLY file */
|
||||
void exportPLY(const std::string& file) {
|
||||
std::ofstream out(file.c_str());
|
||||
out << toPLY();
|
||||
out.close();
|
||||
}
|
||||
|
||||
/** DEBUG: convert to .obj file code for exporting */
|
||||
std::string toOBJsimple() {
|
||||
|
||||
int nVerts = 1;
|
||||
std::string res;
|
||||
|
||||
// write each obstacle
|
||||
for (const Obstacle3D& o : elements) {
|
||||
|
||||
// write the vertices
|
||||
for (const Triangle3& t : o.triangles) {
|
||||
res += "v " + std::to_string(t.p1.x) + " " + std::to_string(t.p1.y) + " " + std::to_string(t.p1.z) + "\n";
|
||||
res += "v " + std::to_string(t.p2.x) + " " + std::to_string(t.p2.y) + " " + std::to_string(t.p2.z) + "\n";
|
||||
res += "v " + std::to_string(t.p3.x) + " " + std::to_string(t.p3.y) + " " + std::to_string(t.p3.z) + "\n";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// write each obstacle
|
||||
for (const Obstacle3D& o : elements) {
|
||||
|
||||
// write the faces
|
||||
for (size_t i = 0; i < o.triangles.size(); ++i) {
|
||||
res += "f " + std::to_string(nVerts+0) + " " + std::to_string(nVerts+1) + " " + std::to_string(nVerts+2) + "\n";
|
||||
nVerts += 3;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// done
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
struct OBJData {
|
||||
std::string obj;
|
||||
std::string mtl;
|
||||
};
|
||||
|
||||
/** DEBUG: convert to .obj file code for exporting */
|
||||
OBJData toOBJ(const std::string name) {
|
||||
|
||||
const BBox3 bb = getBBox();
|
||||
const float ox = bb.getCenter().x;
|
||||
const float oy = bb.getCenter().y;
|
||||
|
||||
bool swapYZ = true;
|
||||
int nVerts = 1;
|
||||
int nObjs = 0;
|
||||
OBJData res;
|
||||
|
||||
// write material file
|
||||
for (size_t idx = 0; idx < mats.size(); ++idx) {
|
||||
const Material& mat = mats[idx];
|
||||
res.mtl += "newmtl mat_" + mat.name + "\n";
|
||||
res.mtl += "Ka 0.000 0.000 0.000 \n"; // ambient
|
||||
res.mtl += "Kd " + std::to_string(mat.r/255.0f) + " " + std::to_string(mat.g/255.0f) + " " + std::to_string(mat.b/255.0f) + "\n";
|
||||
res.mtl += "Ks 0.000 0.000 0.000 \n";
|
||||
res.mtl += "d " + std::to_string(mat.a/255.0f) + "\n"; // alpha
|
||||
res.mtl += "Tr " + std::to_string(1.0f-mat.a/255.0f) + "\n"; // inv-alpha
|
||||
res.mtl += "illum 2 \n";
|
||||
res.mtl += "\n";
|
||||
}
|
||||
|
||||
|
||||
// use material file
|
||||
res.obj += "mtllib " + name + ".mtl" + "\n";
|
||||
|
||||
// write each obstacle
|
||||
for (const Obstacle3D& o : elements) {
|
||||
|
||||
// write the vertices
|
||||
for (const Triangle3& t : o.triangles) {
|
||||
if (!swapYZ) {
|
||||
res.obj += "v " + std::to_string(t.p1.x-ox) + " " + std::to_string(t.p1.y-oy) + " " + std::to_string(t.p1.z) + "\n";
|
||||
res.obj += "v " + std::to_string(t.p2.x-ox) + " " + std::to_string(t.p2.y-oy) + " " + std::to_string(t.p2.z) + "\n";
|
||||
res.obj += "v " + std::to_string(t.p3.x-ox) + " " + std::to_string(t.p3.y-oy) + " " + std::to_string(t.p3.z) + "\n";
|
||||
} else {
|
||||
res.obj += "v " + std::to_string(t.p1.x-ox) + " " + std::to_string(t.p1.z) + " " + std::to_string(t.p1.y-oy) + "\n";
|
||||
res.obj += "v " + std::to_string(t.p3.x-ox) + " " + std::to_string(t.p3.z) + " " + std::to_string(t.p3.y-oy) + "\n";
|
||||
res.obj += "v " + std::to_string(t.p2.x-ox) + " " + std::to_string(t.p2.z) + " " + std::to_string(t.p2.y-oy) + "\n";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// write each obstacle
|
||||
for (const Obstacle3D& o : elements) {
|
||||
|
||||
// create a new group
|
||||
//res.obj += "g elem_" + std::to_string(++nObjs) + "\n";
|
||||
|
||||
// create a new object
|
||||
res.obj += "o elem_" + std::to_string(++nObjs) + "\n";
|
||||
|
||||
// group's material
|
||||
res.obj += "usemtl mat_" + getMaterial(o).name + "\n";
|
||||
|
||||
// write the group's faces
|
||||
for (size_t i = 0; i < o.triangles.size(); ++i) {
|
||||
res.obj += "f " + std::to_string(nVerts+0) + " " + std::to_string(nVerts+1) + " " + std::to_string(nVerts+2) + "\n";
|
||||
nVerts += 3;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// done
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
/** convert to .ply file format */
|
||||
std::string toPLY() const {
|
||||
|
||||
std::stringstream res;
|
||||
res << "ply\n";
|
||||
res << "format ascii 1.0\n";
|
||||
|
||||
int faces = 0;
|
||||
int vertices = 0;
|
||||
for (const Obstacle3D& obs : elements) {
|
||||
vertices += obs.triangles.size() * 3;
|
||||
faces += obs.triangles.size();
|
||||
}
|
||||
|
||||
|
||||
res << "element material " << mats.size() << "\n";
|
||||
res << "property uchar red\n";
|
||||
res << "property uchar green\n";
|
||||
res << "property uchar blue\n";
|
||||
res << "property uchar alpha\n";
|
||||
|
||||
res << "element vertex " << vertices << "\n";
|
||||
res << "property float x\n";
|
||||
res << "property float y\n";
|
||||
res << "property float z\n";
|
||||
res << "property float nx\n";
|
||||
res << "property float ny\n";
|
||||
res << "property float nz\n";
|
||||
res << "property uchar red\n";
|
||||
res << "property uchar green\n";
|
||||
res << "property uchar blue\n";
|
||||
res << "property uchar alpha\n";
|
||||
res << "property int material_index\n";
|
||||
|
||||
res << "element face " << faces << "\n";
|
||||
res << "property list uchar int vertex_indices\n";
|
||||
|
||||
res << "end_header\n";
|
||||
|
||||
|
||||
|
||||
for (const Material& mat : mats) {
|
||||
res << mat.r << " " << mat.g << " " << mat.b << " " << mat.a << "\n";
|
||||
}
|
||||
|
||||
for (const Obstacle3D& obs : elements) {
|
||||
const int matIdx = getMaterialIdx(obs);
|
||||
const Material& mat = mats[matIdx];
|
||||
for (const Triangle3& tria : obs.triangles) {
|
||||
const Point3 n = cross(tria.p2-tria.p1, tria.p3-tria.p1).normalized();
|
||||
res << tria.p1.x << " " << tria.p1.y << " " << tria.p1.z << " " << n.x << " " << n.y << " " << n.z << " " << mat.r << " " << mat.g << " " << mat.b << " " << mat.a << " " << matIdx << "\n";
|
||||
res << tria.p2.x << " " << tria.p2.y << " " << tria.p2.z << " " << n.x << " " << n.y << " " << n.z << " " << mat.r << " " << mat.g << " " << mat.b << " " << mat.a << " " << matIdx << "\n";
|
||||
res << tria.p3.x << " " << tria.p3.y << " " << tria.p3.z << " " << n.x << " " << n.y << " " << n.z << " " << mat.r << " " << mat.g << " " << mat.b << " " << mat.a << " " << matIdx << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
int vidx = 0;
|
||||
for (const Obstacle3D& obs : elements) {
|
||||
for (const Triangle3& tria : obs.triangles) {
|
||||
(void) tria;
|
||||
res << "3 " << (vidx+0) << " " << (vidx+1) << " " << (vidx+2) << "\n";
|
||||
vidx += 3;
|
||||
}
|
||||
}
|
||||
|
||||
// done
|
||||
return res.str();
|
||||
|
||||
}
|
||||
|
||||
struct Material {
|
||||
int r, g, b, a;
|
||||
std::string name;
|
||||
Material(int r, int g, int b, int a) : r(r), g(g), b(b), a(a) {;}
|
||||
|
||||
Material(int r, int g, int b, int a, const std::string& name) : r(r), g(g), b(b), a(a), name(name) {;}
|
||||
};
|
||||
|
||||
|
||||
// // material
|
||||
// std::vector<Material> mats = {
|
||||
|
||||
// Material(0,128,0,255), // ground outdoor
|
||||
// Material(64,64,64,255), // ground outdoor
|
||||
// Material(255,96,96,255), // stair
|
||||
|
||||
// Material(128,128,128,255), // concrete
|
||||
// Material(64,128,255,64), // glass
|
||||
// Material(200,200,200,255), // default
|
||||
|
||||
// };
|
||||
|
||||
// int getMaterial(const Obstacle3D& o) const {
|
||||
// if (o.type == Obstacle3D::Type::GROUND_OUTDOOR) {return 0;}
|
||||
// if (o.type == Obstacle3D::Type::GROUND_INDOOR) {return 1;}
|
||||
// if (o.type == Obstacle3D::Type::STAIR) {return 2;}
|
||||
|
||||
// if (o.mat == Floorplan::Material::CONCRETE) {return 3;}
|
||||
// if (o.mat == Floorplan::Material::GLASS) {return 4;}
|
||||
// return 5;
|
||||
// }
|
||||
|
||||
std::vector<Material> mats = {
|
||||
|
||||
Material(255,0,0,255 , "error"), // error
|
||||
|
||||
Material(0,128,0,255 , "ground_outdoor"), // ground outdoor
|
||||
Material(64,64,64,255 , "ground_indoor"), // ground outdoor
|
||||
Material(105,105,105,255, "stair"), // stair
|
||||
Material(220,220,220,255, "handrail"), // handrail
|
||||
|
||||
Material(200,200,255,96 , "door_glass"), // door (glass)
|
||||
Material(140,140,140,255, "door_wood"), // door (wood)
|
||||
|
||||
Material(135,135,135,255, "concrete"), // concrete
|
||||
Material(240,240,255,96 , "glass"), // glass
|
||||
Material(170,170,255,96 , "glass_metallized"), // glass (metallized)
|
||||
Material(170,120,60,255 , "wood"), // wood
|
||||
Material(200,200,200,255, "drywall"), // drywall
|
||||
Material(255,255,255,255, "metal"), // metal
|
||||
|
||||
|
||||
Material(255,255,255,255, "object"), // object
|
||||
|
||||
Material(235,235,235,255, "dafult"), // default
|
||||
|
||||
};
|
||||
|
||||
int getMaterialIdx(const Obstacle3D& o) const {
|
||||
|
||||
if (o.type == Floorplan3D::Obstacle3D::Type::ERROR) {return 0;}
|
||||
|
||||
if (o.type == Floorplan3D::Obstacle3D::Type::GROUND_OUTDOOR) {return 1;}
|
||||
if (o.type == Floorplan3D::Obstacle3D::Type::GROUND_INDOOR) {return 2;}
|
||||
if (o.type == Floorplan3D::Obstacle3D::Type::STAIR) {return 3;}
|
||||
if (o.type == Floorplan3D::Obstacle3D::Type::HANDRAIL) {return 4;}
|
||||
if (o.type == Floorplan3D::Obstacle3D::Type::OBJECT) {return 12;}
|
||||
|
||||
if (o.type == Floorplan3D::Obstacle3D::Type::DOOR && o.mat == Floorplan::Material::GLASS) {return 5;}
|
||||
if (o.type == Floorplan3D::Obstacle3D::Type::DOOR) {return 6;}
|
||||
|
||||
if (o.mat == Floorplan::Material::CONCRETE) {return 7;}
|
||||
if (o.mat == Floorplan::Material::GLASS) {return 8;}
|
||||
if (o.mat == Floorplan::Material::METALLIZED_GLAS) {return 9;}
|
||||
|
||||
if (o.mat == Floorplan::Material::WOOD) {return 10;}
|
||||
if (o.mat == Floorplan::Material::DRYWALL) {return 11;}
|
||||
if (o.mat == Floorplan::Material::METAL) {return 12;}
|
||||
|
||||
|
||||
return 13;
|
||||
|
||||
}
|
||||
|
||||
const Material& getMaterial(const Obstacle3D& o) const {
|
||||
const int idx = getMaterialIdx(o);
|
||||
return mats[idx];
|
||||
}
|
||||
|
||||
|
||||
// Color getColor(const Obstacle3D& o) const {
|
||||
// if (o.type == Obstacle3D::Type::GROUND_OUTDOOR) {return Color(0,128,0,255);}
|
||||
// if (o.type == Obstacle3D::Type::GROUND_INDOOR) {return Color(64,64,64,255);}
|
||||
// if (o.mat == Floorplan::Material::CONCRETE) {return Color(128,128,128,255);}
|
||||
// if (o.mat == Floorplan::Material::GLASS) {return Color(128,128,255,64);}
|
||||
// return Color(200,200,200,255);
|
||||
// }
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FLOORPLAN_3D_FLOORPLANMESH_H
|
||||
93
floorplan/3D/Handrails.h
Normal file
93
floorplan/3D/Handrails.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef FLOORPLAN_3D_HANDRAILS_H
|
||||
#define FLOORPLAN_3D_HANDRAILS_H
|
||||
|
||||
#include "Obstacle3.h"
|
||||
#include "misc.h"
|
||||
|
||||
namespace Floorplan3D {
|
||||
|
||||
class Handrails {
|
||||
|
||||
public:
|
||||
|
||||
std::vector<Obstacle3D> getHandrails(const Floorplan::Floor* f) {
|
||||
std::vector<Obstacle3D> res;
|
||||
for (const Floorplan::FloorObstacle* o: f->obstacles) {
|
||||
const Floorplan::FloorObstacleLine* line = dynamic_cast<const Floorplan::FloorObstacleLine*>(o);
|
||||
if (line && line->type == Floorplan::ObstacleType::HANDRAIL) {
|
||||
res.push_back(getHandrail(f, line));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Obstacle3D getHandrail(const Floorplan::Floor* f, const Floorplan::FloorObstacleLine* fol) const {
|
||||
|
||||
FloorPos fpos(f);
|
||||
|
||||
// target
|
||||
Obstacle3D res(getType(fol), fol->material);
|
||||
|
||||
const float thickness_m = 0.05;
|
||||
const Point2 from = fol->from;
|
||||
const Point2 to = fol->to;
|
||||
const Point2 cen2 = (from+to)/2;
|
||||
|
||||
// edges
|
||||
const float z1 = fpos.z1;
|
||||
const float z2 = fpos.z1 + 1.0;
|
||||
Point3 p1 = Point3(from.x, from.y, z1);
|
||||
Point3 p2 = Point3(to.x, to.y, z1);
|
||||
Point3 p3 = Point3(from.x, from.y, z2);
|
||||
Point3 p4 = Point3(to.x, to.y, z2);
|
||||
|
||||
const float rad = std::atan2(to.y - from.y, to.x - from.x);
|
||||
const float deg = rad * 180 / M_PI;
|
||||
|
||||
// cube's destination center
|
||||
const Point3 pUp(cen2.x, cen2.y, z2);
|
||||
|
||||
const float sx = from.getDistance(to) / 2;
|
||||
const float sy = thickness_m / 2;
|
||||
const float sz = thickness_m / 2;
|
||||
const Point3 size(sx, sy, sz);
|
||||
const Point3 rot(0,0,deg);
|
||||
|
||||
// upper bar
|
||||
const Cube cubeUpper(pUp, size, rot);
|
||||
const std::vector<Triangle3> tmp = cubeUpper.getTriangles();
|
||||
res.triangles.insert(res.triangles.end(), tmp.begin(), tmp.end());
|
||||
|
||||
const Point3 d1 = p2-p1;
|
||||
const Point3 d2 = p4-p3;
|
||||
const int numBars = d2.length() / 0.75f;
|
||||
for (int i = 1; i < numBars; ++i) {
|
||||
const Point3 s = p1 + d1 * i / numBars;
|
||||
const Point3 e = p3 + d2 * i / numBars;
|
||||
const Point3 c = (s+e)/2;
|
||||
const Point3 size(thickness_m/2, thickness_m/2, s.getDistance(e)/2 - thickness_m);
|
||||
const Cube cube(c, size, rot);
|
||||
const std::vector<Triangle3> tmp = cube.getTriangles();
|
||||
res.triangles.insert(res.triangles.end(), tmp.begin(), tmp.end());
|
||||
}
|
||||
|
||||
// done
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FLOORPLAN_3D_HANDRAILS_H
|
||||
79
floorplan/3D/Lines.h
Normal file
79
floorplan/3D/Lines.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef FLOORPLAN_3D_LINES_H
|
||||
#define FLOORPLAN_3D_LINES_H
|
||||
|
||||
#include "Walls.h"
|
||||
#include "misc.h"
|
||||
#include "primitives/Cube.h"
|
||||
|
||||
namespace Floorplan3D {
|
||||
|
||||
/**
|
||||
* simply use one 3D cube per wall
|
||||
* if walls intersect in the 2D view, cubes will also intersect
|
||||
*/
|
||||
class LinesViaCubes : public Walls {
|
||||
|
||||
Cube::Part cubeParts = (Cube::Part) 63; // leftright,topbottom,rearfront
|
||||
|
||||
std::vector<Obstacle3D> vec;
|
||||
|
||||
public:
|
||||
|
||||
void clear() override {
|
||||
vec.clear();
|
||||
}
|
||||
|
||||
void add(const Floorplan::Floor* f, const Floorplan::FloorObstacleLine* fol, const Floorplan::FloorObstacleDoor* aboveDoor) override {
|
||||
|
||||
FloorPos fpos(f);
|
||||
|
||||
const float thickness_m = fol->thickness_m;
|
||||
const Point2 from = (!aboveDoor) ? (fol->from) : (aboveDoor->from);
|
||||
const Point2 to = (!aboveDoor) ? (fol->to) : (aboveDoor->to);
|
||||
const Point2 cen2 = (from+to)/2;
|
||||
|
||||
const float rad = std::atan2(to.y - from.y, to.x - from.x);
|
||||
const float deg = rad * 180 / M_PI;
|
||||
|
||||
// cube's destination center
|
||||
const float _height = (fol->height_m > 0) ? (fol->height_m) : (fpos.height); // use either floor's height or user height
|
||||
const double height = (!aboveDoor) ? (_height) : (fpos.height - aboveDoor->height);
|
||||
const double cenZ = (!aboveDoor) ? (fpos.z1 + height/2) : (fpos.z1 + aboveDoor->height + height/2);// (fpos.z2 - (fpos.height - aboveDoor->height) / 2);
|
||||
const Point3 pos(cen2.x, cen2.y, cenZ);
|
||||
|
||||
// div by 2.01 to prevent overlapps and z-fighting
|
||||
const float sx = from.getDistance(to) / 2;
|
||||
const float sy = thickness_m / 2;
|
||||
const float sz = height / 2.01f; // prevent overlaps
|
||||
const Point3 size(sx, sy, sz);
|
||||
const Point3 rot(0,0,deg);
|
||||
|
||||
// build
|
||||
Cube cube(pos, size, rot, cubeParts);
|
||||
|
||||
// done
|
||||
Obstacle3D res(getType(fol), fol->material);
|
||||
res.triangles = cube.getTriangles();
|
||||
vec.push_back(res);
|
||||
|
||||
}
|
||||
|
||||
const std::vector<Obstacle3D>& get() override {
|
||||
return vec;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FLOORPLAN_3D_LINES_H
|
||||
62
floorplan/3D/Objects.h
Normal file
62
floorplan/3D/Objects.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef FLOORPLAN_3D_OBJECTS_H
|
||||
#define FLOORPLAN_3D_OBJECTS_H
|
||||
|
||||
#include "Obstacle3.h"
|
||||
#include "misc.h"
|
||||
#include "objects/OBJPool.h"
|
||||
|
||||
namespace Floorplan3D {
|
||||
|
||||
class Objects {
|
||||
|
||||
public:
|
||||
|
||||
std::vector<Obstacle3D> getObjects(const Floorplan::Floor* f) {
|
||||
std::vector<Obstacle3D> res;
|
||||
for (const Floorplan::FloorObstacle* o: f->obstacles) {
|
||||
const Floorplan::FloorObstacleObject* obj = dynamic_cast<const Floorplan::FloorObstacleObject*>(o);
|
||||
if (obj) {
|
||||
res.push_back(getObject(f, obj));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/** 3D Obstacle from .obj 3D mesh */
|
||||
Obstacle3D getObject(const Floorplan::Floor* f, const Floorplan::FloorObstacleObject* foo) const {
|
||||
|
||||
FloorPos fpos(f);
|
||||
|
||||
const std::string& name = foo->file;
|
||||
Obstacle3D obs = OBJPool::get().getObject(name);
|
||||
|
||||
// perform sanity checks
|
||||
if (!obs.isValid()) {
|
||||
throw std::runtime_error("invalid obstacle-data detected");
|
||||
}
|
||||
|
||||
// apply scaling/rotation/translation
|
||||
obs = obs.scaled(foo->scale);
|
||||
obs = obs.rotated_deg( Point3(foo->rot.x, foo->rot.y, foo->rot.z) );
|
||||
obs = obs.translated(foo->pos + Point3(0,0,fpos.z1));
|
||||
obs.type = Obstacle3D::Type::OBJECT;
|
||||
|
||||
return obs;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FLOORPLAN_3D_OBJECTS_H
|
||||
158
floorplan/3D/Obstacle3.h
Normal file
158
floorplan/3D/Obstacle3.h
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef FLOORPLAN_3D_OBSTACLE3_H
|
||||
#define FLOORPLAN_3D_OBSTACLE3_H
|
||||
|
||||
#include <vector>
|
||||
#include "../../geo/Triangle3.h"
|
||||
#include "../../geo/Sphere3.h"
|
||||
#include "../../geo/TriangleStrip3.h"
|
||||
|
||||
#include "../v2/Floorplan.h"
|
||||
|
||||
namespace Floorplan3D {
|
||||
|
||||
/**
|
||||
* 3D obstacle
|
||||
* based on multiple triangles
|
||||
* has a material and a type
|
||||
*/
|
||||
struct Obstacle3D {
|
||||
|
||||
enum class Type {
|
||||
UNKNOWN,
|
||||
GROUND_INDOOR,
|
||||
GROUND_OUTDOOR,
|
||||
STAIR,
|
||||
HANDRAIL,
|
||||
DOOR,
|
||||
WALL,
|
||||
WINDOW,
|
||||
OBJECT,
|
||||
ERROR,
|
||||
};
|
||||
|
||||
Type type;
|
||||
Floorplan::Material mat;
|
||||
std::vector<Triangle3> triangles;
|
||||
|
||||
/** empty ctor */
|
||||
Obstacle3D() : type(Type::UNKNOWN), mat() {;}
|
||||
|
||||
/** ctor */
|
||||
Obstacle3D(Type type, Floorplan::Material mat) : type(type), mat(mat) {;}
|
||||
|
||||
|
||||
/** append a new triangle. REFERENCE ONLY VALID UNTIL NEXT ADD */
|
||||
Triangle3& addTriangle(const Point3 p1, const Point3 p2, const Point3 p3, const bool reverse) {
|
||||
Triangle3 t(p1, p2, p3);
|
||||
if (reverse) {t.reverse();}
|
||||
triangles.push_back(t);
|
||||
return triangles.back();
|
||||
}
|
||||
|
||||
void addTriangleStrip(std::initializer_list<Point3> pts, bool reverse = false) {
|
||||
TriangleStrip3 strip;
|
||||
for (const Point3& p : pts) {strip.add(p);}
|
||||
for (Triangle3 t : strip.toTriangles()) {
|
||||
if (reverse) {t.reverse();}
|
||||
triangles.push_back(t);
|
||||
}
|
||||
}
|
||||
|
||||
void addTriangleStrip(const std::vector<Point3>& pts, bool reverse = false) {
|
||||
TriangleStrip3 strip;
|
||||
for (const Point3& p : pts) {strip.add(p);}
|
||||
for (Triangle3 t : strip.toTriangles()) {
|
||||
if (reverse) {t.reverse();}
|
||||
triangles.push_back(t);
|
||||
}
|
||||
}
|
||||
|
||||
/** append a new quad by splitting into two triangles */
|
||||
void addQuad(const Point3 p1, const Point3 p2, const Point3 p3, const Point3 p4, const bool reverse = false) {
|
||||
addTriangle(p1, p2, p3, reverse);
|
||||
addTriangle(p1, p3, p4, reverse);
|
||||
}
|
||||
|
||||
/** reverse all faces (CW<->CCW) */
|
||||
void reverseFaces() {
|
||||
for (Triangle3& t : triangles) {
|
||||
t.reverse();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** scaled copy */
|
||||
Obstacle3D scaled(const Point3 scale) const {
|
||||
Obstacle3D copy = *this;
|
||||
for (Triangle3& tria : copy.triangles) {
|
||||
tria *= scale;
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
/** translated copy */
|
||||
Obstacle3D translated(const Point3 pos) const {
|
||||
Obstacle3D copy = *this;
|
||||
for (Triangle3& tria : copy.triangles) {
|
||||
tria += pos;
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
/** rotated [around (0,0,0)] copy */
|
||||
Obstacle3D rotated_deg(const Point3 rot) const {
|
||||
Obstacle3D copy = *this;
|
||||
for (Triangle3& tria : copy.triangles) {
|
||||
// tria.p1 = tria.p1.rot(rot.x/180.0f*M_PI, rot.y/180.0f*M_PI, rot.z/180.0f*M_PI);
|
||||
// tria.p2 = tria.p2.rot(rot.x/180.0f*M_PI, rot.y/180.0f*M_PI, rot.z/180.0f*M_PI);
|
||||
// tria.p3 = tria.p3.rot(rot.x/180.0f*M_PI, rot.y/180.0f*M_PI, rot.z/180.0f*M_PI);
|
||||
tria.rotate_deg(rot);
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
/** get all triangle-edge-points (x,y) within the obstacle */
|
||||
std::vector<Point2> getPoints2D() const {
|
||||
std::vector<Point2> res;
|
||||
for (const Triangle3& tria : triangles) {
|
||||
res.push_back(tria.p1.xy());
|
||||
res.push_back(tria.p2.xy());
|
||||
res.push_back(tria.p3.xy());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/** get all triangle-edge-points (x,y,z) within the obstacle */
|
||||
std::vector<Point3> getPoints3D() const {
|
||||
std::vector<Point3> res;
|
||||
for (const Triangle3& tria : triangles) {
|
||||
res.push_back(tria.p1);
|
||||
res.push_back(tria.p2);
|
||||
res.push_back(tria.p3);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/** perform sanity checks */
|
||||
bool isValid() const {
|
||||
for (const Triangle3& t : triangles) {
|
||||
if (!t.isValid()) {return false;}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FLOORPLAN_3D_OBSTACLE3_H
|
||||
109
floorplan/3D/Outline.h
Normal file
109
floorplan/3D/Outline.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef FLOORPLAN_3D_OUTLINE_H
|
||||
#define FLOORPLAN_3D_OUTLINE_H
|
||||
|
||||
#include "Obstacle3.h"
|
||||
#include "misc.h"
|
||||
#include <unordered_map>
|
||||
#include "../../geo/GPCPolygon2.h"
|
||||
|
||||
namespace Floorplan3D {
|
||||
|
||||
class Outline {
|
||||
|
||||
public:
|
||||
|
||||
/** convert a floor (floor/ceiling) into triangles */
|
||||
std::vector<Obstacle3D> get(const Floorplan::Floor* f) {
|
||||
|
||||
FloorPos fpos(f);
|
||||
|
||||
std::vector<Obstacle3D> res;
|
||||
if (!f->enabled) {return res;}
|
||||
if (!f->outline.enabled) {return res;}
|
||||
|
||||
// floor uses an outline based on "add" and "remove" areas
|
||||
// we need to create the apropriate triangles to model the polygon
|
||||
// including all holes (remove-areas)
|
||||
|
||||
// process all "add" regions by type
|
||||
// [this allows for overlaps of the same type]
|
||||
std::unordered_map<std::string, GPCPolygon2> types;
|
||||
for (Floorplan::FloorOutlinePolygon* fop : f->outline) {
|
||||
if (fop->method == Floorplan::OutlineMethod::ADD) {
|
||||
if (fop->outdoor) {
|
||||
types["outdoor"].add(fop->poly);
|
||||
} else {
|
||||
types["indoor"].add(fop->poly);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// remove the "remove" regions from EVERY "add" region added within the previous step
|
||||
for (Floorplan::FloorOutlinePolygon* fop : f->outline) {
|
||||
if (fop->method == Floorplan::OutlineMethod::REMOVE) {
|
||||
for (auto& it : types) {
|
||||
it.second.remove(fop->poly);
|
||||
}
|
||||
}
|
||||
// allow for overlapping outdoor/indoor regions -> outdoor wins [remove outdoor part from indoor parts]
|
||||
if (fop->outdoor) {
|
||||
types["indoor"].remove(fop->poly);
|
||||
}
|
||||
}
|
||||
|
||||
// create an obstacle for each type (indoor, outdoor)
|
||||
for (auto& it : types) {
|
||||
|
||||
// TODO: variable type?
|
||||
Obstacle3D::Type type = (it.first == "indoor") ? (Obstacle3D::Type::GROUND_INDOOR) : (Obstacle3D::Type::GROUND_OUTDOOR);
|
||||
Obstacle3D obs(type, Floorplan::Material::CONCRETE);
|
||||
|
||||
// convert them into polygons
|
||||
std::vector<std::vector<Point3>> polys = it.second.get(fpos.z1);
|
||||
|
||||
// convert polygons (GL_TRIANGLE_STRIP) to triangles
|
||||
for (const std::vector<Point3>& pts : polys) {
|
||||
for (int i = 0; i < (int)pts.size() - 2; ++i) {
|
||||
|
||||
// floor must be double-sided
|
||||
Triangle3 tria1 (pts[i+0], pts[i+1], pts[i+2]);
|
||||
Triangle3 tria2 (pts[i+2], pts[i+1], pts[i+0]);
|
||||
|
||||
// ensure the triangle with the normal pointing downwards (towards bulding's cellar)
|
||||
// is below the triangle that points upwards (towards the sky)
|
||||
if (tria1.getNormal().z < 0) {std::swap(tria1, tria2);}
|
||||
|
||||
// tria2 = ceiling of previous floor
|
||||
tria2 -= Point3(0,0,fpos.fh);
|
||||
|
||||
// add both
|
||||
obs.triangles.push_back(tria1);
|
||||
obs.triangles.push_back(tria2);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
res.push_back(obs);
|
||||
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FLOORPLAN_3D_OUTLINE_H
|
||||
61
floorplan/3D/Pillars.h
Normal file
61
floorplan/3D/Pillars.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef FLOORPLAN_3D_PILLARS_H
|
||||
#define FLOORPLAN_3D_PILLARS_H
|
||||
|
||||
#include "Obstacle3.h"
|
||||
#include "misc.h"
|
||||
#include "primitives/Cylinder.h"
|
||||
|
||||
namespace Floorplan3D {
|
||||
|
||||
class Pillars {
|
||||
|
||||
public:
|
||||
|
||||
std::vector<Obstacle3D> getPillars(const Floorplan::Floor* f) {
|
||||
std::vector<Obstacle3D> res;
|
||||
for (const Floorplan::FloorObstacle* o: f->obstacles) {
|
||||
const Floorplan::FloorObstacleCircle* circ = dynamic_cast<const Floorplan::FloorObstacleCircle*>(o);
|
||||
if (circ) {
|
||||
res.push_back(getPillar(f, circ));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
Obstacle3D getPillar(const Floorplan::Floor* f, const Floorplan::FloorObstacleCircle* foc) {
|
||||
|
||||
FloorPos fpos(f);
|
||||
|
||||
// attributes
|
||||
const float r = foc->radius;
|
||||
const float h = (foc->height > 0) ? (foc->height) : (fpos.height); // use either floor's height or user height
|
||||
const Point3 pos(foc->center.x, foc->center.y, fpos.z1 + h/2);
|
||||
|
||||
// build
|
||||
Cylinder cyl;
|
||||
cyl.add(r, h/2, true);
|
||||
cyl.translate(pos);
|
||||
|
||||
// done
|
||||
Obstacle3D res(getType(foc), foc->material);
|
||||
res.triangles = cyl.getTriangles();
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FLOORPLAN_3D_PILLARS_H
|
||||
92
floorplan/3D/Stairs.h
Normal file
92
floorplan/3D/Stairs.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef FLOORPLAN_3D_STAIRS_H
|
||||
#define FLOORPLAN_3D_STAIRS_H
|
||||
|
||||
#include "Obstacle3.h"
|
||||
#include "primitives/Cube.h"
|
||||
|
||||
namespace Floorplan3D {
|
||||
|
||||
class Stairs {
|
||||
|
||||
public:
|
||||
|
||||
std::vector<Obstacle3D> getStairs(const Floorplan::Floor* f) {
|
||||
std::vector<Obstacle3D> res;
|
||||
for (const Floorplan::Stair* stair : f->stairs) {
|
||||
res.push_back(getStair(f, stair));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Obstacle3D getStair(const Floorplan::Floor* f, const Floorplan::Stair* s) {
|
||||
|
||||
Obstacle3D res(Obstacle3D::Type::STAIR, Floorplan::Material::CONCRETE);
|
||||
|
||||
std::vector<Floorplan::Quad3> quads = Floorplan::getQuads(s->getParts(), f);
|
||||
for (const Floorplan::Quad3& quad : quads) {
|
||||
|
||||
if (quad.isLeveled()) {
|
||||
|
||||
const float h = 0.2;
|
||||
const Point3 ph(0,0,h);
|
||||
const Cube cube = Cube::fromBottomAndHeight(quad.p1-ph, quad.p2-ph, quad.p3-ph, quad.p4-ph, 0.2);
|
||||
|
||||
const std::vector<Triangle3> tmp = cube.getTriangles();
|
||||
res.triangles.insert(res.triangles.end(), tmp.begin(), tmp.end());
|
||||
|
||||
} else {
|
||||
|
||||
const Point3 dir1 = quad.p3 - quad.p2;
|
||||
const Point3 dir2 = quad.p4 - quad.p1;
|
||||
float stepH = 0.20;
|
||||
const float totalH = quad.p3.z - quad.p1.z;
|
||||
const int numStairs = std::round(totalH / stepH);
|
||||
stepH = totalH / numStairs;
|
||||
|
||||
for (int i = 0; i < numStairs; ++i) {
|
||||
|
||||
//const float y1 = quad.p1.z + (stepH * i);
|
||||
//const float y2 = y1 + stepH;
|
||||
|
||||
Point3 p1b = quad.p1 + dir1 * (i+0) / numStairs; p1b.z -= stepH;
|
||||
Point3 p2b = quad.p2 + dir2 * (i+0) / numStairs; p2b.z -= stepH;
|
||||
|
||||
const Point3 p3t = quad.p2 + dir2 * (i+1) / numStairs;
|
||||
const Point3 p4t = quad.p1 + dir1 * (i+1) / numStairs;
|
||||
|
||||
const Point3 p1t(p1b.x, p1b.y, p4t.z);
|
||||
const Point3 p2t(p2b.x, p2b.y, p3t.z);
|
||||
|
||||
const Point3 p3b(p3t.x, p3t.y, p2b.z+stepH);
|
||||
const Point3 p4b(p4t.x, p4t.y, p1b.z+stepH);
|
||||
|
||||
const Cube cube = Cube::fromVertices(p1t, p2t, p3t, p4t, p1b, p2b, p3b, p4b);
|
||||
const std::vector<Triangle3> tmp = cube.getTriangles();
|
||||
res.triangles.insert(res.triangles.end(), tmp.begin(), tmp.end());
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FLOORPLAN_3D_STAIRS_H
|
||||
32
floorplan/3D/Walls.h
Normal file
32
floorplan/3D/Walls.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef FLOORPLAN_3D_WALLS_H
|
||||
#define FLOORPLAN_3D_WALLS_H
|
||||
|
||||
#include "Obstacle3.h"
|
||||
|
||||
namespace Floorplan3D {
|
||||
|
||||
class Walls {
|
||||
|
||||
public:
|
||||
|
||||
virtual void clear() = 0;
|
||||
|
||||
virtual void add(const Floorplan::Floor* f, const Floorplan::FloorObstacleLine* fol, const Floorplan::FloorObstacleDoor* aboveDoor) = 0;
|
||||
|
||||
virtual const std::vector<Obstacle3D>& get() = 0;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FLOORPLAN_3D_WALLS_H
|
||||
79
floorplan/3D/WallsViaCubes.h
Normal file
79
floorplan/3D/WallsViaCubes.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef FLOORPLAN_3D_WALLSVIACUBE_H
|
||||
#define FLOORPLAN_3D_WALLSVIACUBE_H
|
||||
|
||||
#include "Walls.h"
|
||||
#include "misc.h"
|
||||
#include "primitives/Cube.h"
|
||||
|
||||
namespace Floorplan3D {
|
||||
|
||||
/**
|
||||
* simply use one 3D cube per wall
|
||||
* if walls intersect in the 2D view, cubes will also intersect
|
||||
*/
|
||||
class WallsViaCubes : public Walls {
|
||||
|
||||
Cube::Part cubeParts = (Cube::Part) 63; // leftright,topbottom,rearfront
|
||||
|
||||
std::vector<Obstacle3D> vec;
|
||||
|
||||
public:
|
||||
|
||||
void clear() override {
|
||||
vec.clear();
|
||||
}
|
||||
|
||||
void add(const Floorplan::Floor* f, const Floorplan::FloorObstacleLine* fol, const Floorplan::FloorObstacleDoor* aboveDoor) override {
|
||||
|
||||
FloorPos fpos(f);
|
||||
|
||||
const float thickness_m = fol->thickness_m;
|
||||
const Point2 from = (!aboveDoor) ? (fol->from) : (aboveDoor->from);
|
||||
const Point2 to = (!aboveDoor) ? (fol->to) : (aboveDoor->to);
|
||||
const Point2 cen2 = (from+to)/2;
|
||||
|
||||
const float rad = std::atan2(to.y - from.y, to.x - from.x);
|
||||
const float deg = rad * 180 / M_PI;
|
||||
|
||||
// cube's destination center
|
||||
const float _height = (fol->height_m > 0) ? (fol->height_m) : (fpos.height); // use either floor's height or user height
|
||||
const double height = (!aboveDoor) ? (_height) : (fpos.height - aboveDoor->height);
|
||||
const double cenZ = (!aboveDoor) ? (fpos.z1 + height/2) : (fpos.z1 + aboveDoor->height + height/2);// (fpos.z2 - (fpos.height - aboveDoor->height) / 2);
|
||||
const Point3 pos(cen2.x, cen2.y, cenZ);
|
||||
|
||||
// div by 2.01 to prevent overlapps and z-fighting
|
||||
const float sx = from.getDistance(to) / 2;
|
||||
const float sy = thickness_m / 2;
|
||||
const float sz = height / 2.01f; // prevent overlaps
|
||||
const Point3 size(sx, sy, sz);
|
||||
const Point3 rot(0,0,deg);
|
||||
|
||||
// build
|
||||
Cube cube(pos, size, rot, cubeParts);
|
||||
|
||||
// done
|
||||
Obstacle3D res(getType(fol), fol->material);
|
||||
res.triangles = cube.getTriangles();
|
||||
vec.push_back(res);
|
||||
|
||||
}
|
||||
|
||||
const std::vector<Obstacle3D>& get() override {
|
||||
return vec;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FLOORPLAN_3D_WALLSVIACUBE_H
|
||||
745
floorplan/3D/WallsViaCuttedQuads.h
Normal file
745
floorplan/3D/WallsViaCuttedQuads.h
Normal file
@@ -0,0 +1,745 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef FLOORPLAN_3D_WALLSVIACUTTEDQUADS_H
|
||||
#define FLOORPLAN_3D_WALLSVIACUTTEDQUADS_H
|
||||
|
||||
#include "../../geo/Line2.h"
|
||||
#include "../../geo/Polygon2.h"
|
||||
#include "../../geo/GPCPolygon2.h"
|
||||
|
||||
//#include "Walls.h"
|
||||
#include "misc.h"
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
|
||||
namespace Floorplan3D {
|
||||
|
||||
/**
|
||||
* interpret walls als quads (polygons)
|
||||
* intersect them with each other to prevent overlaps
|
||||
*/
|
||||
class WallsViaCuttedQuads {
|
||||
|
||||
private:
|
||||
|
||||
struct Wall {
|
||||
|
||||
/** algorithms set error flag here */
|
||||
bool error = false;
|
||||
|
||||
/** original line from floorplan */
|
||||
const Floorplan::FloorObstacleWall* line;
|
||||
|
||||
/** outlines after applying thickness */
|
||||
Line2 l1;
|
||||
Line2 l2;
|
||||
|
||||
Wall(const Floorplan::FloorObstacleWall* line) : line(line) {
|
||||
|
||||
const Point2 from = line->from;
|
||||
const Point2 to = line->to;
|
||||
const Point2 dir = (to-from).normalized();
|
||||
const Point2 dirP = dir.perpendicular();
|
||||
const float w = line->thickness_m;
|
||||
const float w2 = w/2;
|
||||
|
||||
const Point2 p1 = from + dirP * w2;
|
||||
const Point2 p2 = from - dirP * w2;
|
||||
const Point2 p3 = to - dirP * w2;
|
||||
const Point2 p4 = to + dirP * w2;
|
||||
|
||||
l1 = Line2(p1, p4);
|
||||
l2 = Line2(p2, p3);
|
||||
|
||||
}
|
||||
|
||||
/** get points for CCW wall outline (p1->p2->p3->p4) */
|
||||
Point2 getP1() const {return l1.p1;}
|
||||
Point2 getP2() const {return l1.p2;}
|
||||
Point2 getP3() const {return l2.p2;}
|
||||
Point2 getP4() const {return l2.p1;}
|
||||
|
||||
Point2& getP1() {return l1.p1;}
|
||||
Point2& getP2() {return l1.p2;}
|
||||
Point2& getP3() {return l2.p2;}
|
||||
Point2& getP4() {return l2.p1;}
|
||||
|
||||
struct CutRes {
|
||||
Point2 p;
|
||||
Line2* l1; // affected line within this wall
|
||||
Line2* l2; // affected line within the other wall
|
||||
CutRes(Point2 p, Line2* l1, Line2* l2) : p(p), l1(l1), l2(l2) {;}
|
||||
};
|
||||
|
||||
/** get all intersecting points between the two walls */
|
||||
std::vector<CutRes> getIntersections(Wall& o, bool limit = true) {
|
||||
std::vector<CutRes> res;
|
||||
Point2 p;
|
||||
if (intersects(l1, o.l1, limit, p)) {res.push_back(CutRes(p,&l1, &o.l1));}
|
||||
if (intersects(l1, o.l2, limit, p)) {res.push_back(CutRes(p,&l1, &o.l2));}
|
||||
if (intersects(l2, o.l1, limit, p)) {res.push_back(CutRes(p,&l2, &o.l1));}
|
||||
if (intersects(l2, o.l2, limit, p)) {res.push_back(CutRes(p,&l2, &o.l2));}
|
||||
return res;
|
||||
}
|
||||
|
||||
/** is this wall directly attached to the given wall? */
|
||||
bool directlyConnectedTo(const Wall& o) const {
|
||||
const float d = 0.001;
|
||||
return
|
||||
(line->from.eq( o.line->from, d)) ||
|
||||
(line->to.eq( o.line->from, d)) ||
|
||||
(line->from.eq( o.line->to, d)) ||
|
||||
(line->to.eq( o.line->to, d));
|
||||
}
|
||||
|
||||
/** does this wall end within the given wall? */
|
||||
bool endsWithin(const Wall& o) const {
|
||||
return o.containsPoint(line->from) || o.containsPoint(line->to);
|
||||
}
|
||||
|
||||
/** does this wall contain the given point */
|
||||
bool containsPoint(const Point2 p) const {
|
||||
return polygonContainsPoint({getP1(), getP2(), getP3(), getP4()}, p);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
void cropLine(Line2* l, Point2 p) {
|
||||
|
||||
// determine which line-end to crop
|
||||
if (p.getDistance(l->p1) < p.getDistance(l->p2)) {
|
||||
l->p1 = p;
|
||||
} else {
|
||||
l->p2 = p;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void cutInMiddle(Point2& pa1, Point2& pa2, Point2& pb1, Point2& pb2) {
|
||||
|
||||
// line from pa1->pa2, and pb1->pb2
|
||||
// intersection expected near pa2|pb1
|
||||
|
||||
Line2 l1(pa1, pa2); l1 = l1.longerAtEnd(10);
|
||||
Line2 l2(pb1, pb2); l2 = l2.longerAtStart(10);
|
||||
|
||||
Point2 pi;
|
||||
if (intersects(l1, l2, true, pi)) {
|
||||
pa2 = pi; // replace end of line1
|
||||
pb1 = pi; // replace start of line2
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::vector<Wall> walls;
|
||||
const Floorplan::Floor* floor;
|
||||
std::vector<Obstacle3D> obs;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
void clear() {
|
||||
walls.clear();
|
||||
}
|
||||
|
||||
void add(const Floorplan::Floor* f, const Floorplan::FloorObstacleWall* fow) {
|
||||
if (fow->type != Floorplan::ObstacleType::WALL) {return;}
|
||||
this->floor = f;
|
||||
walls.push_back(Wall(fow));
|
||||
}
|
||||
|
||||
virtual const std::vector<Obstacle3D>& get() {
|
||||
std::vector<Wall> tmp = walls;
|
||||
tmp = cutConnected(tmp);
|
||||
tmp = cutProtruding(tmp);
|
||||
obs = toObstacle(tmp, floor);
|
||||
return obs;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/** convert all walls to obstacles */
|
||||
std::vector<Obstacle3D> toObstacle(const std::vector<Wall>& walls, const Floorplan::Floor* f) {
|
||||
std::vector<Obstacle3D> res;
|
||||
for (const Wall& w : walls) {
|
||||
res.push_back(toObstacle(w, f));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
Obstacle3D toObstacle(const Wall& w, const Floorplan::Floor* f) {
|
||||
|
||||
FloorPos fp(f);
|
||||
|
||||
const Floorplan::FloorObstacleWall* wall = w.line;
|
||||
Obstacle3D::Type type = (w.error) ? (Obstacle3D::Type::ERROR) : (getType(wall));
|
||||
Obstacle3D obs(type, wall->material);
|
||||
|
||||
const float z1 = fp.z1;
|
||||
const float z2 = (wall->height_m > 0) ? (fp.z1 + wall->height_m) : (fp.z2);
|
||||
|
||||
const Point3 p1 = Point3(w.getP1().x, w.getP1().y, z1); // front
|
||||
const Point3 p2 = Point3(w.getP2().x, w.getP2().y, z1); // front
|
||||
const Point3 p3 = Point3(w.getP3().x, w.getP3().y, z1); // back
|
||||
const Point3 p4 = Point3(w.getP4().x, w.getP4().y, z1); // back
|
||||
|
||||
const Point3 p1u = Point3(w.getP1().x, w.getP1().y, z2); // front upper
|
||||
const Point3 p2u = Point3(w.getP2().x, w.getP2().y, z2); // front upper
|
||||
const Point3 p3u = Point3(w.getP3().x, w.getP3().y, z2); // back upper
|
||||
const Point3 p4u = Point3(w.getP4().x, w.getP4().y, z2); // back upper
|
||||
|
||||
const Point2 dir = (wall->to - wall->from).normalized();
|
||||
const Point2 perp = dir.perpendicular().normalized() * (wall->thickness_m/2);
|
||||
|
||||
// move from wall's center line towards front (- half thickness)
|
||||
auto toFront = [perp] (const Point2 pt, const float z) {
|
||||
const Point2 tmp = pt + perp;
|
||||
return Point3(tmp.x, tmp.y, z);
|
||||
};
|
||||
|
||||
// move from wall's center line towards back (+ half thickness)
|
||||
auto toBack = [perp] (const Point2 pt, const float z) {
|
||||
const Point2 tmp = pt - perp;
|
||||
return Point3(tmp.x, tmp.y, z);
|
||||
};
|
||||
|
||||
// above window, below window, window frame
|
||||
for (const Floorplan::FloorObstacleWallWindow* win : wall->windows) {
|
||||
|
||||
// quad below the window (1,2,3,4) CCW
|
||||
// quad above the window (5,6,7,8) CCW
|
||||
// -> window = (4,3,6,5) CCW
|
||||
|
||||
const float za = z1 + win->startsAtHeight;
|
||||
const float zb = za + win->height;
|
||||
|
||||
const Point3 p1f = toFront(win->getStart(wall), z1);
|
||||
const Point3 p2f = toFront(win->getEnd(wall), z1);
|
||||
const Point3 p3f = toFront(win->getEnd(wall), za);
|
||||
const Point3 p4f = toFront(win->getStart(wall), za);
|
||||
|
||||
const Point3 p1b = toBack(win->getStart(wall), z1);
|
||||
const Point3 p2b = toBack(win->getEnd(wall), z1);
|
||||
const Point3 p3b = toBack(win->getEnd(wall), za);
|
||||
const Point3 p4b = toBack(win->getStart(wall), za);
|
||||
|
||||
const Point3 p5f = toFront(win->getStart(wall), zb);
|
||||
const Point3 p6f = toFront(win->getEnd(wall), zb);
|
||||
const Point3 p7f = toFront(win->getEnd(wall), z2);
|
||||
const Point3 p8f = toFront(win->getStart(wall), z2);
|
||||
|
||||
const Point3 p5b = toBack(win->getStart(wall), zb);
|
||||
const Point3 p6b = toBack(win->getEnd(wall), zb);
|
||||
const Point3 p7b = toBack(win->getEnd(wall), z2);
|
||||
const Point3 p8b = toBack(win->getStart(wall), z2);
|
||||
|
||||
// quad below the window
|
||||
obs.addQuad(p1f, p2f, p3f, p4f, true); // front
|
||||
obs.addQuad(p1b, p2b, p3b, p4b, false); // back
|
||||
|
||||
// quad above the window
|
||||
obs.addQuad(p5f, p6f, p7f, p8f, true); // front
|
||||
obs.addQuad(p5b, p6b, p7b, p8b, false); // back
|
||||
|
||||
// window frame
|
||||
obs.addTriangleStrip({p4f, p4b, p3f, p3b, p6f, p6b, p5f, p5b, p4f, p4b});
|
||||
|
||||
}
|
||||
|
||||
// start mantle surface (zig-zag around the wall)
|
||||
std::vector<Point3> ptsLateralSurface;
|
||||
ptsLateralSurface.push_back(p1);
|
||||
ptsLateralSurface.push_back(p4);
|
||||
|
||||
|
||||
// sort doors along the wall
|
||||
std::vector<Floorplan::FloorObstacleWallDoor*> doors = wall->doors;
|
||||
std::sort(doors.begin(), doors.end(), [] (const Floorplan::FloorObstacleWallDoor* d1, const Floorplan::FloorObstacleWallDoor* d2) {
|
||||
return d1->atLinePos < d2->atLinePos;
|
||||
});
|
||||
|
||||
// above door, append lateral surface around wall
|
||||
for (const Floorplan::FloorObstacleWallDoor* door : doors) {
|
||||
|
||||
Point2 ps = door->getStart(wall);
|
||||
Point2 pe = door->getEnd(wall);
|
||||
if (door->leftRight) {std::swap(ps,pe);}
|
||||
|
||||
const float zb = z1 + door->height;
|
||||
|
||||
{
|
||||
|
||||
const Point3 p1f = toFront(ps, zb);
|
||||
const Point3 p2f = toFront(pe, zb);
|
||||
const Point3 p3f = toFront(pe, z2);
|
||||
const Point3 p4f = toFront(ps, z2);
|
||||
|
||||
const Point3 p1b = toBack(ps, zb);
|
||||
const Point3 p2b = toBack(pe, zb);
|
||||
const Point3 p3b = toBack(pe, z2);
|
||||
const Point3 p4b = toBack(ps, z2);
|
||||
|
||||
// quad above the door (front)
|
||||
obs.addQuad(p1f, p2f, p3f, p4f, true);
|
||||
|
||||
// quad above the door (back)
|
||||
obs.addQuad(p1b, p2b, p3b, p4b, false);
|
||||
|
||||
}
|
||||
|
||||
const Point3 p1f = toFront(ps, z1);
|
||||
const Point3 p2f = toFront(pe, z1);
|
||||
const Point3 p3f = toFront(pe, zb);
|
||||
const Point3 p4f = toFront(ps, zb);
|
||||
|
||||
const Point3 p1b = toBack(ps, z1);
|
||||
const Point3 p2b = toBack(pe, z1);
|
||||
const Point3 p3b = toBack(pe, zb);
|
||||
const Point3 p4b = toBack(ps, zb);
|
||||
|
||||
// continue mantle surface
|
||||
ptsLateralSurface.push_back(p1f);
|
||||
ptsLateralSurface.push_back(p1b);
|
||||
ptsLateralSurface.push_back(p4f);
|
||||
ptsLateralSurface.push_back(p4b);
|
||||
ptsLateralSurface.push_back(p3f);
|
||||
ptsLateralSurface.push_back(p3b);
|
||||
ptsLateralSurface.push_back(p2f);
|
||||
ptsLateralSurface.push_back(p2b);
|
||||
|
||||
}
|
||||
|
||||
// complete mantle surface
|
||||
ptsLateralSurface.push_back(p2);
|
||||
ptsLateralSurface.push_back(p3);
|
||||
ptsLateralSurface.push_back(p2u);
|
||||
ptsLateralSurface.push_back(p3u);
|
||||
ptsLateralSurface.push_back(p1u);
|
||||
ptsLateralSurface.push_back(p4u);
|
||||
ptsLateralSurface.push_back(p1);
|
||||
ptsLateralSurface.push_back(p4);
|
||||
obs.addTriangleStrip(ptsLateralSurface, true);
|
||||
|
||||
// get all points along the wall start, doorstart,doorend, doorstart,doorend, .., end
|
||||
std::vector<Point3> ptsFront;
|
||||
ptsFront.push_back(p1);
|
||||
ptsFront.push_back(p2);
|
||||
|
||||
std::vector<Point3> ptsBack;
|
||||
ptsBack.push_back(p3);
|
||||
ptsBack.push_back(p4);
|
||||
|
||||
for (const Floorplan::FloorObstacleWallDoor* door : wall->doors) {
|
||||
ptsFront.push_back(toFront(door->getStart(wall), z1));
|
||||
ptsFront.push_back(toFront(door->getEnd(wall), z1));
|
||||
ptsBack.push_back(toBack(door->getStart(wall), z1));
|
||||
ptsBack.push_back(toBack(door->getEnd(wall), z1));
|
||||
}
|
||||
for (const Floorplan::FloorObstacleWallWindow* window : wall->windows) {
|
||||
ptsFront.push_back(toFront(window->getStart(wall), z1));
|
||||
ptsFront.push_back(toFront(window->getEnd(wall), z1));
|
||||
ptsBack.push_back(toBack(window->getStart(wall), z1));
|
||||
ptsBack.push_back(toBack(window->getEnd(wall), z1));
|
||||
}
|
||||
|
||||
// sort all points by distance from start (correct on-off-on-off-on order)
|
||||
auto compFront = [p1] (const Point3 _p1, const Point3 _p2) { return p1.getDistance(_p1) < p1.getDistance(_p2); };
|
||||
std::sort(ptsFront.begin(), ptsFront.end(), compFront);
|
||||
auto compBack = [p4] (const Point3 _p1, const Point3 _p2) { return p4.getDistance(_p1) < p4.getDistance(_p2); };
|
||||
std::sort(ptsBack.begin(), ptsBack.end(), compBack);
|
||||
|
||||
// from wall segment to wall segment, excluding doors
|
||||
|
||||
for (size_t i = 0; i < ptsFront.size(); i += 2) {
|
||||
const Point3 p1 = ptsFront[i+0];
|
||||
const Point3 p2 = ptsFront[i+1];
|
||||
const Point3 p3(p2.x, p2.y, z2);
|
||||
const Point3 p4(p1.x, p1.y, z2);
|
||||
obs.addQuad(p1, p2, p3, p4, true);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ptsBack.size(); i += 2) {
|
||||
const Point3 p1 = ptsBack[i+0];
|
||||
const Point3 p2 = ptsBack[i+1];
|
||||
const Point3 p3(p2.x, p2.y, z2);
|
||||
const Point3 p4(p1.x, p1.y, z2);
|
||||
obs.addQuad(p1, p2, p3, p4, false);
|
||||
}
|
||||
|
||||
|
||||
// obs.addQuad(p1, p2, p2u, p1u);
|
||||
// obs.addQuad(p2, p3, p3u, p2u);
|
||||
// obs.addQuad(p3, p4, p4u, p3u);
|
||||
// obs.addQuad(p4, p1, p1u, p4u);
|
||||
|
||||
// obs.addQuad(p1u, p2u, p3u, p4u);
|
||||
// obs.addQuad(p4, p3, p2, p1);
|
||||
|
||||
// obs.reverseFaces();
|
||||
|
||||
return obs;
|
||||
}
|
||||
|
||||
/*
|
||||
Obstacle3D toObstacle(const Wall& wall, const Floorplan::Floor* f) {
|
||||
|
||||
FloorPos fp(f);
|
||||
|
||||
Obstacle3D::Type type = (wall.error) ? (Obstacle3D::Type::ERROR) : (getType(wall.line));
|
||||
Obstacle3D obs(type, wall.line->material);
|
||||
|
||||
const float z1 = fp.z1;
|
||||
const float z2 = (wall.line->height_m > 0) ? (fp.z1 + wall.line->height_m) : (fp.z2);
|
||||
|
||||
const Point3 p1 = Point3(wall.getP1().x, wall.getP1().y, z1);
|
||||
const Point3 p2 = Point3(wall.getP2().x, wall.getP2().y, z1);
|
||||
const Point3 p3 = Point3(wall.getP3().x, wall.getP3().y, z1);
|
||||
const Point3 p4 = Point3(wall.getP4().x, wall.getP4().y, z1);
|
||||
|
||||
const Point3 p1u = Point3(wall.getP1().x, wall.getP1().y, z2);
|
||||
const Point3 p2u = Point3(wall.getP2().x, wall.getP2().y, z2);
|
||||
const Point3 p3u = Point3(wall.getP3().x, wall.getP3().y, z2);
|
||||
const Point3 p4u = Point3(wall.getP4().x, wall.getP4().y, z2);
|
||||
|
||||
obs.addQuad(p1, p2, p2u, p1u);
|
||||
obs.addQuad(p2, p3, p3u, p2u);
|
||||
obs.addQuad(p3, p4, p4u, p3u);
|
||||
obs.addQuad(p4, p1, p1u, p4u);
|
||||
|
||||
obs.addQuad(p1u, p2u, p3u, p4u);
|
||||
obs.addQuad(p4, p3, p2, p1);
|
||||
|
||||
obs.reverseFaces();
|
||||
|
||||
return obs;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/** convert one wall into an obstacle */
|
||||
/*
|
||||
Obstacle3D toObstacle(const Wall& wall, const Floorplan::Floor* f) {
|
||||
|
||||
FloorPos fp(f);
|
||||
|
||||
Obstacle3D::Type type = (wall.error) ? (Obstacle3D::Type::ERROR) : (getType(wall.line));
|
||||
Obstacle3D obs(type, wall.line->material);
|
||||
|
||||
const float z1 = fp.z1;
|
||||
const float z2 = (wall.line->height_m > 0) ? (fp.z1 + wall.line->height_m) : (fp.z2);
|
||||
|
||||
|
||||
|
||||
Point3 p0 = Point3(wall.line->from.x, wall.line->from.y, z1);
|
||||
|
||||
Point3 p1 = Point3(wall.getP1().x, wall.getP1().y, z1);
|
||||
Point3 p2 = Point3(wall.getP2().x, wall.getP2().y, z1);
|
||||
Point3 p3 = Point3(wall.getP3().x, wall.getP3().y, z1);
|
||||
Point3 p4 = Point3(wall.getP4().x, wall.getP4().y, z1);
|
||||
|
||||
Point3 p1u = Point3(wall.getP1().x, wall.getP1().y, z2);
|
||||
Point3 p2u = Point3(wall.getP2().x, wall.getP2().y, z2);
|
||||
Point3 p3u = Point3(wall.getP3().x, wall.getP3().y, z2);
|
||||
Point3 p4u = Point3(wall.getP4().x, wall.getP4().y, z2);
|
||||
|
||||
Point3 o = p0;
|
||||
float t = wall.line->thickness_m / 2;
|
||||
|
||||
const Point3 o1;// = p1;
|
||||
const Point3 o2;// = p4;
|
||||
const float a = std::atan2(wall.getP2().y - wall.getP1().y, wall.getP2().x - wall.getP1().x);
|
||||
|
||||
auto flatten = [a,o] (const Point3 p) {return (p - o).rotZ(-a).xz();};
|
||||
//auto flatten2 = [a,o] (const Point3 p) {return (p - o).rotZ(-a).xz();};
|
||||
|
||||
auto unFlattenFront = [o,t,a] (const Point3 p) {return Point3(p.x, +t, p.y).rotZ(a)+o;};
|
||||
auto unFlattenBack = [o,t,a] (const Point3 p) {return Point3(p.x, -t, p.y).rotZ(a)+o;};
|
||||
|
||||
auto unFlattenFront2 = [o,t,a] (const Point2 p) {return Point3(p.x, +t, p.y).rotZ(a)+o;};
|
||||
auto unFlattenBack2 = [o,t,a] (const Point2 p) {return Point3(p.x, -t, p.y).rotZ(a)+o;};
|
||||
|
||||
const Point2 fp1 = flatten(p1);
|
||||
const Point2 fp2 = flatten(p2);
|
||||
const Point2 fp3 = flatten(p3);
|
||||
const Point2 fp4 = flatten(p4);
|
||||
const Point2 fp1u = flatten(p1u);
|
||||
const Point2 fp2u = flatten(p2u);
|
||||
const Point2 fp3u = flatten(p3u);
|
||||
const Point2 fp4u = flatten(p4u);
|
||||
|
||||
|
||||
Polygon2 front;
|
||||
front.add({fp1, fp2, fp2u, fp1u});
|
||||
|
||||
GPCPolygon2 gpFront;
|
||||
gpFront.add(front);
|
||||
|
||||
Polygon2 back;
|
||||
back.add({fp3, fp4, fp4u, fp3u});
|
||||
|
||||
GPCPolygon2 gpBack;
|
||||
gpBack.add(back);
|
||||
|
||||
// sort doors by their position within the wall (first comes first)
|
||||
std::vector<Floorplan::FloorObstacleWallDoor*> doors = wall.line->doors;
|
||||
auto compDoors = [] (const Floorplan::FloorObstacleWallDoor* d1, Floorplan::FloorObstacleWallDoor* d2) {
|
||||
return d1->atLinePos < d2->atLinePos;
|
||||
};
|
||||
std::sort(doors.begin(), doors.end(), compDoors);
|
||||
|
||||
|
||||
TriangleStrip strip;
|
||||
|
||||
strip.add(p1);
|
||||
strip.add(p4);
|
||||
|
||||
for (const Floorplan::FloorObstacleWallDoor* door : doors) {
|
||||
|
||||
Point2 pds = door->getStart(wall.line);
|
||||
Point2 pde = door->getEnd(wall.line);
|
||||
|
||||
// inverted door, swap start and end for correct triangle order
|
||||
if (door->leftRight) {
|
||||
std::swap(pds, pde);
|
||||
}
|
||||
|
||||
const Point3 dp1(pds.x, pds.y, z1);
|
||||
const Point3 dp2(pde.x, pde.y, z1);
|
||||
const Point3 dp2u(pde.x, pde.y, z1+door->height);
|
||||
const Point3 dp1u(pds.x, pds.y, z1+door->height);
|
||||
|
||||
Polygon2 pDoor;
|
||||
pDoor.add(flatten(dp1));
|
||||
pDoor.add(flatten(dp2));
|
||||
pDoor.add(flatten(dp2u));
|
||||
pDoor.add(flatten(dp1u));
|
||||
|
||||
// wall cutout (front/back)
|
||||
gpFront.remove(pDoor);
|
||||
gpBack.remove(pDoor);
|
||||
|
||||
// 3D framing
|
||||
strip.add(unFlattenFront2(pDoor[0]));
|
||||
strip.add(unFlattenBack2(pDoor[0]));
|
||||
|
||||
strip.add(unFlattenFront2(pDoor[3]));
|
||||
strip.add(unFlattenBack2(pDoor[3]));
|
||||
|
||||
strip.add(unFlattenFront2(pDoor[2]));
|
||||
strip.add(unFlattenBack2(pDoor[2]));
|
||||
|
||||
strip.add(unFlattenFront2(pDoor[1]));
|
||||
strip.add(unFlattenBack2(pDoor[1]));
|
||||
|
||||
}
|
||||
|
||||
strip.add(p2);
|
||||
strip.add(p3);
|
||||
strip.add(p2u);
|
||||
strip.add(p3u);
|
||||
strip.add(p1u);
|
||||
strip.add(p4u);
|
||||
strip.add(p1);
|
||||
strip.add(p4);
|
||||
|
||||
for (Triangle3 t : strip.toTriangles()) {
|
||||
t.reverse();
|
||||
obs.triangles.push_back(t);
|
||||
}
|
||||
|
||||
// process all windows within the wall
|
||||
|
||||
for (const Floorplan::FloorObstacleWallWindow* win : wall.line->windows) {
|
||||
|
||||
const Point2 pws = win->getStart(wall.line);
|
||||
const Point2 pwe = win->getEnd(wall.line);
|
||||
|
||||
const float wz1 = z1 + win->startsAtHeight;
|
||||
const float wz2 = z1 + win->startsAtHeight + win->height;
|
||||
|
||||
const Point3 dp1(pws.x, pws.y, wz1);
|
||||
const Point3 dp2(pwe.x, pwe.y, wz1);
|
||||
const Point3 dp2u(pwe.x, pwe.y, wz2);
|
||||
const Point3 dp1u(pws.x, pws.y, wz2);
|
||||
|
||||
Polygon2 pWindow;
|
||||
pWindow.add(flatten(dp1));
|
||||
pWindow.add(flatten(dp2));
|
||||
pWindow.add(flatten(dp2u));
|
||||
pWindow.add(flatten(dp1u));
|
||||
|
||||
// wall cutout (front/back)
|
||||
gpFront.remove(pWindow);
|
||||
gpBack.remove(pWindow);
|
||||
|
||||
// 3D framing
|
||||
TriangleStrip strip;
|
||||
|
||||
strip.add(unFlattenFront2(pWindow[0]));
|
||||
strip.add(unFlattenBack2(pWindow[0]));
|
||||
|
||||
strip.add(unFlattenFront2(pWindow[3]));
|
||||
strip.add(unFlattenBack2(pWindow[3]));
|
||||
|
||||
strip.add(unFlattenFront2(pWindow[2]));
|
||||
strip.add(unFlattenBack2(pWindow[2]));
|
||||
|
||||
strip.add(unFlattenFront2(pWindow[1]));
|
||||
strip.add(unFlattenBack2(pWindow[1]));
|
||||
|
||||
strip.add(unFlattenFront2(pWindow[0]));
|
||||
strip.add(unFlattenBack2(pWindow[0]));
|
||||
|
||||
for (Triangle3 t : strip.toTriangles()) {
|
||||
t.reverse();
|
||||
obs.triangles.push_back(t);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Frontseite triangulieren
|
||||
std::vector<Triangle3> triasFront = gpFront.getTriangles();
|
||||
|
||||
for (Triangle3 tria : triasFront) {
|
||||
fixTria(tria, unFlattenFront);
|
||||
tria.reverse();
|
||||
obs.triangles.push_back(tria);
|
||||
}
|
||||
|
||||
// Rückseite triangulieren
|
||||
std::vector<Triangle3> triasBack = gpBack.getTriangles();
|
||||
|
||||
for (Triangle3 tria : triasBack) {
|
||||
fixTria(tria, unFlattenBack);
|
||||
obs.triangles.push_back(tria);
|
||||
}
|
||||
|
||||
|
||||
// for (const Floorplan::FloorObstacleWallDoor* door : wall.line->doors) {
|
||||
// const Point2 p1 = door->getStart(wall.line);
|
||||
// const Point2 p2 = door->getEnd(wall.line);
|
||||
|
||||
// }
|
||||
|
||||
return obs;
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
void fixTria(Triangle3& t, std::function<Point3(Point3)> func) {
|
||||
t.p1 = func(t.p1);
|
||||
t.p2 = func(t.p2);
|
||||
t.p3 = func(t.p3);
|
||||
}
|
||||
|
||||
/** cut off walls ending within another wall */
|
||||
std::vector<Wall> cutProtruding(std::vector<Wall> walls) {
|
||||
|
||||
// if one wall ends within another one cut it off
|
||||
|
||||
for (size_t i = 0; i < walls.size(); ++i) {
|
||||
Wall& w1 = walls[i];
|
||||
|
||||
for (size_t j = i+1; j < walls.size(); ++j) {
|
||||
Wall& w2 = walls[j];
|
||||
|
||||
if (i == j) {continue;}
|
||||
|
||||
// if the two walls are directly connected (share one node) -> ignore this case here!
|
||||
if (w1.directlyConnectedTo(w2)) {continue;}
|
||||
|
||||
// not the case we are looking for?
|
||||
if (!w1.endsWithin(w2) && !w2.endsWithin(w1)) {continue;}
|
||||
|
||||
// get all intersection points between the two walls
|
||||
std::vector<Wall::CutRes> isects = w1.getIntersections(w2);
|
||||
|
||||
// this should be 0 (no intersections) or 2 (one for each outline)
|
||||
if (!isects.empty() && isects.size() != 2) {
|
||||
w1.error = true;
|
||||
w2.error = true;
|
||||
std::cout << "detected strange wall intersection" << std::endl;
|
||||
}
|
||||
|
||||
int cut = 0;
|
||||
|
||||
// check the (2) detected intersections
|
||||
for (const auto isect : isects) {
|
||||
|
||||
// if one of the line-ends p1/p2 from wall1 ends within wall2, crop it by setting it to the intersection
|
||||
if (w2.containsPoint(isect.l1->p1)) {isect.l1->p1 = isect.p; ++cut;}
|
||||
if (w2.containsPoint(isect.l1->p2)) {isect.l1->p2 = isect.p; ++cut;}
|
||||
|
||||
// if one of the line-ends p1/p2 from wall2 ends within wall1, crop it by setting it to the intersection
|
||||
if (w1.containsPoint(isect.l2->p1)) {isect.l2->p1 = isect.p; ++cut;}
|
||||
if (w1.containsPoint(isect.l2->p2)) {isect.l2->p2 = isect.p; ++cut;}
|
||||
|
||||
}
|
||||
|
||||
// 2 lines should have been cut. if not, potential issue!
|
||||
if (cut != 2) {
|
||||
w1.error = true;
|
||||
w2.error = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return walls;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** if two walls share one node, cut both ends in the middle (like 45 degree) */
|
||||
std::vector<Wall> cutConnected(std::vector<Wall> walls) {
|
||||
|
||||
for (size_t i = 0; i < walls.size(); ++i) {
|
||||
Wall& w1 = walls[i];
|
||||
|
||||
for (size_t j = i+1; j < walls.size(); ++j) {
|
||||
Wall& w2 = walls[j];
|
||||
|
||||
if (i == j) {continue;}
|
||||
|
||||
// if the two walls are note directly connected -> ignore
|
||||
if (!w1.directlyConnectedTo(w2)) {continue;}
|
||||
|
||||
const float d = 0.001;
|
||||
if (w1.line->to.eq(w2.line->from, d)) {
|
||||
cutInMiddle(w1.getP1(), w1.getP2(), w2.getP1(), w2.getP2());
|
||||
cutInMiddle(w1.getP4(), w1.getP3(), w2.getP4(), w2.getP3());
|
||||
} else if (w1.line->to.eq(w2.line->to, d)) {
|
||||
cutInMiddle(w1.getP1(), w1.getP2(), w2.getP3(), w2.getP4());
|
||||
cutInMiddle(w1.getP4(), w1.getP3(), w2.getP2(), w2.getP1());
|
||||
} else if (w1.line->from.eq(w2.line->to, d)) {
|
||||
cutInMiddle(w1.getP3(), w1.getP4(), w2.getP3(), w2.getP4());
|
||||
cutInMiddle(w1.getP2(), w1.getP1(), w2.getP2(), w2.getP1());
|
||||
} else if (w1.line->from.eq(w2.line->from, d)) {
|
||||
cutInMiddle(w1.getP3(), w1.getP4(), w2.getP1(), w2.getP2());
|
||||
cutInMiddle(w1.getP2(), w1.getP1(), w2.getP4(), w2.getP3());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return walls;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FLOORPLAN_3D_WALLSVIACUTTEDQUADS_H
|
||||
52
floorplan/3D/misc.h
Normal file
52
floorplan/3D/misc.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef FLOORPLAN_3D_MISC_H
|
||||
#define FLOORPLAN_3D_MISC_H
|
||||
|
||||
#include "Obstacle3.h"
|
||||
|
||||
namespace Floorplan3D {
|
||||
|
||||
/** used to model ceiling thickness */
|
||||
struct FloorPos {
|
||||
float fh;
|
||||
float z1;
|
||||
float z2;
|
||||
float height;
|
||||
FloorPos(const Floorplan::Floor* f) : fh(0.01), z1(f->getStartingZ()), z2(f->getEndingZ()-fh), height(z2-z1) {;}
|
||||
};
|
||||
|
||||
static Obstacle3D::Type getType(const Floorplan::FloorObstacleLine* l) {
|
||||
switch (l->type) {
|
||||
case Floorplan::ObstacleType::WALL: return Obstacle3D::Type::WALL;
|
||||
case Floorplan::ObstacleType::WINDOW: return Obstacle3D::Type::WINDOW;
|
||||
case Floorplan::ObstacleType::HANDRAIL: return Obstacle3D::Type::HANDRAIL;
|
||||
default: return Obstacle3D::Type::UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
static Obstacle3D::Type getType(const Floorplan::FloorObstacleWall* l) {
|
||||
switch (l->type) {
|
||||
case Floorplan::ObstacleType::WALL: return Obstacle3D::Type::WALL;
|
||||
case Floorplan::ObstacleType::WINDOW: return Obstacle3D::Type::WINDOW;
|
||||
case Floorplan::ObstacleType::HANDRAIL: return Obstacle3D::Type::HANDRAIL;
|
||||
default: return Obstacle3D::Type::UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
static Obstacle3D::Type getType(const Floorplan::FloorObstacleCircle* c) {
|
||||
(void) c;
|
||||
return Obstacle3D::Type::WALL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // FLOORPLAN_3D_MISC_H
|
||||
145
floorplan/3D/objects/MTLReader.h
Normal file
145
floorplan/3D/objects/MTLReader.h
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef MTLREADER_H
|
||||
#define MTLREADER_H
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include "../../../geo/Point2.h"
|
||||
#include "../../../geo/Point3.h"
|
||||
|
||||
/**
|
||||
* prase .mtl files
|
||||
*/
|
||||
class MTLReader {
|
||||
|
||||
public:
|
||||
|
||||
struct Material {
|
||||
std::string textureFile = "";
|
||||
Point3 diffuse = Point3(1,1,1);
|
||||
float alpha = 1.0;
|
||||
};
|
||||
|
||||
Material* cur = nullptr;
|
||||
std::unordered_map<std::string, Material> map;
|
||||
|
||||
/** ctor. use readXYZ() */
|
||||
MTLReader() {
|
||||
;
|
||||
}
|
||||
|
||||
/** read .mtl from the given file */
|
||||
void readFile(const std::string& file) {
|
||||
std::ifstream is(file);
|
||||
std::string line;
|
||||
while(getline(is, line)) {parseLine(line);}
|
||||
is.close();
|
||||
}
|
||||
|
||||
/** read mtl from the given data string (.mtl file contents) */
|
||||
void readData(const char* data) {
|
||||
readData(std::string(data));
|
||||
}
|
||||
|
||||
/** read mtl from the given data string (.mtl file contents) */
|
||||
void readData(const std::string& data) {
|
||||
std::stringstream is(data);
|
||||
std::string line;
|
||||
while(getline(is, line)) {parseLine(line);}
|
||||
}
|
||||
|
||||
/** get the given material */
|
||||
const Material& getMaterial(const std::string& mat) const {
|
||||
const auto& it = map.find(mat);
|
||||
if (it == map.end()) {throw Exception("material not available");}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
template<typename Out>
|
||||
void split(const std::string &s, char delim, Out result) {
|
||||
std::stringstream ss(s);
|
||||
std::string item;
|
||||
while (std::getline(ss, item, delim)) {
|
||||
*(result++) = item;
|
||||
}
|
||||
}
|
||||
|
||||
void replaceAll(std::string& str, const std::string& from, const std::string& to) {
|
||||
size_t start_pos = 0;
|
||||
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
||||
size_t end_pos = start_pos + from.length();
|
||||
str.replace(start_pos, end_pos, to);
|
||||
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
|
||||
}
|
||||
}
|
||||
|
||||
/** remove empty strings from the vector */
|
||||
std::vector<std::string> nonEmpty(const std::vector<std::string>& src) {
|
||||
std::vector<std::string> res;
|
||||
for (const std::string& s : src) {
|
||||
if (!s.empty()) {res.push_back(s);}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<std::string> split(const std::string &s, char delim) {
|
||||
std::vector<std::string> elems;
|
||||
split(s, delim, std::back_inserter(elems));
|
||||
return elems;
|
||||
}
|
||||
|
||||
/** parse one line of the .obj file */
|
||||
void parseLine(std::string line) {
|
||||
|
||||
if (line.length() < 2) {return;}
|
||||
|
||||
// remove leading "#"
|
||||
while (line[0] == ' ' || line[0] == '\t') {
|
||||
line.erase(line.begin());
|
||||
}
|
||||
|
||||
// remove other linebreaks
|
||||
replaceAll(line, "\r", "");
|
||||
|
||||
const std::vector<std::string> tokens = nonEmpty(split(line, ' '));
|
||||
const std::string token = tokens.front();
|
||||
|
||||
if ("newmtl" == token) {
|
||||
const std::string id = tokens[1];
|
||||
map[id] = Material();
|
||||
cur = &map[id];
|
||||
} else if ("map_Ka" == token) {
|
||||
const std::string texFile = tokens[1];
|
||||
cur->textureFile = texFile;
|
||||
} else if ("map_Kd" == token) {
|
||||
const std::string texFile = tokens[1];
|
||||
cur->textureFile = texFile;
|
||||
} else if ("Kd" == token) {
|
||||
cur->diffuse.x = std::stof(tokens[1]);
|
||||
cur->diffuse.y = std::stof(tokens[2]);
|
||||
cur->diffuse.z = std::stof(tokens[3]);
|
||||
} else if ("d" == token) {
|
||||
cur->alpha = std::stof(tokens[1]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // MTLREADER_H
|
||||
145
floorplan/3D/objects/OBJPool.h
Normal file
145
floorplan/3D/objects/OBJPool.h
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef FLOORPLAN_3D_OBJPOOL_H
|
||||
#define FLOORPLAN_3D_OBJPOOL_H
|
||||
|
||||
#include <vector>
|
||||
#include "../../../geo/Triangle3.h"
|
||||
#include <unordered_map>
|
||||
|
||||
#include "OBJReader.h"
|
||||
#include "../Obstacle3.h"
|
||||
|
||||
// LINUX ONLY
|
||||
//#include <dirent.h>
|
||||
//#include <stdio.h>
|
||||
#include "../../../data/File.h"
|
||||
|
||||
|
||||
#include "../../../misc/Debug.h"
|
||||
|
||||
|
||||
namespace Floorplan3D {
|
||||
|
||||
/**
|
||||
* load several named 3D models for quick re-use
|
||||
*/
|
||||
class OBJPool {
|
||||
|
||||
private:
|
||||
|
||||
static constexpr const char* name = "OBJ-Pool";
|
||||
|
||||
/** singleton */
|
||||
OBJPool() {;}
|
||||
|
||||
bool initDone = false;
|
||||
std::unordered_map<std::string, Obstacle3D> cache;
|
||||
|
||||
public:
|
||||
|
||||
/** singleton access */
|
||||
static OBJPool& get() {
|
||||
static OBJPool instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/** init with *.obj data folder */
|
||||
void init(const std::string& folder) {
|
||||
|
||||
scanFolder(folder);
|
||||
initDone = true;
|
||||
|
||||
}
|
||||
|
||||
/** init with multiple *.obj data folder */
|
||||
void init(const std::initializer_list<std::string>& folders) {
|
||||
|
||||
for (const std::string& folder : folders) {
|
||||
try {
|
||||
scanFolder(folder);
|
||||
} catch (...) {;}
|
||||
}
|
||||
initDone = true;
|
||||
|
||||
}
|
||||
|
||||
/** get all triangles for the given object (if known) */
|
||||
const Obstacle3D& getObject(const std::string& name) {
|
||||
|
||||
// ensure the cache is initialized
|
||||
if (!initDone) {
|
||||
throw Exception("OBJPool: not initialized. call init(folder) first");
|
||||
}
|
||||
|
||||
static Obstacle3D empty;
|
||||
|
||||
// find the entry
|
||||
const auto& it = cache.find(name);
|
||||
if (it == cache.end()) {return empty;}
|
||||
return it->second;
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/** scan the given folder for all *.obj files */
|
||||
void scanFolder(const std::string& folder) {
|
||||
|
||||
FS::File d(folder);
|
||||
if (!d.exists()) {
|
||||
throw Exception("OBJPool: folder not found: " + folder);
|
||||
}
|
||||
|
||||
for (const FS::File& f : d.listFiles()) {
|
||||
std::string name = f.getFilename();
|
||||
if (endsWith(name, ".obj")) {
|
||||
//std::string name = entry.path().filename().string();
|
||||
name = name.substr(0, name.length() - 4); // without extension
|
||||
load(f.getPath(), name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inline bool endsWith(std::string const & value, std::string const & ending) {
|
||||
if (ending.size() > value.size()) return false;
|
||||
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
|
||||
}
|
||||
|
||||
/** load the given .obj file into the cache */
|
||||
void load(const std::string& absName, const std::string& name) {
|
||||
|
||||
OBJReader reader;
|
||||
reader.readFile(absName);
|
||||
//reader.readFile("/mnt/vm/paper/diss/code/IndoorMap/res/mdl/" + file + ".obj"); // todo
|
||||
|
||||
// create triangles
|
||||
Obstacle3D obs;
|
||||
for (const OBJReader::Object& obj : reader.getData().objects) {
|
||||
for (const OBJReader::Face& face : obj.faces) {
|
||||
const Triangle3 tria(face.vnt[0].vertex, face.vnt[1].vertex, face.vnt[2].vertex);
|
||||
obs.triangles.push_back(tria);
|
||||
}
|
||||
}
|
||||
|
||||
Log::add(this->name, "loaded: " + absName + " [" + std::to_string(obs.triangles.size()) + " triangles]", true);
|
||||
|
||||
// store
|
||||
cache[name] = obs;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FLOORPLAN_3D_OBJPOOL_H
|
||||
228
floorplan/3D/objects/OBJReader.h
Normal file
228
floorplan/3D/objects/OBJReader.h
Normal file
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef OBJREADER_H
|
||||
#define OBJREADER_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include "../../../geo/Point2.h"
|
||||
#include "../../../geo/Point3.h"
|
||||
|
||||
/**
|
||||
* prase .obj files
|
||||
*/
|
||||
class OBJReader {
|
||||
|
||||
public:
|
||||
|
||||
/** group vertex+normal+texture */
|
||||
struct VNT {
|
||||
int idxVertex;
|
||||
int idxNormal;
|
||||
int idxTexture;
|
||||
Point3 vertex;
|
||||
Point3 normal;
|
||||
Point2 texture;
|
||||
};
|
||||
|
||||
/** one triangle */
|
||||
struct Face {
|
||||
VNT vnt[3];
|
||||
Face(VNT v1, VNT v2, VNT v3) : vnt{v1,v2,v3} {;}
|
||||
};
|
||||
|
||||
/** one object within the file */
|
||||
struct Object {
|
||||
std::string material;
|
||||
std::string name;
|
||||
std::vector<Face> faces;
|
||||
};
|
||||
|
||||
/** internal data */
|
||||
struct Data {
|
||||
std::vector<Point3> vertices;
|
||||
std::vector<Point2> texCoords;
|
||||
std::vector<Point3> normals;
|
||||
std::vector<std::string> materialFiles;
|
||||
std::vector<Object> objects;
|
||||
Object& curObj() {
|
||||
if (objects.empty()) {objects.push_back(Object());}
|
||||
return objects.back();
|
||||
}
|
||||
} data;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor. use readXYZ() */
|
||||
OBJReader() {
|
||||
;
|
||||
}
|
||||
|
||||
/** read .obj from the given file */
|
||||
void readFile(const std::string& file) {
|
||||
std::ifstream is(file);
|
||||
std::string line;
|
||||
while(getline(is, line)) {parseLine(line);}
|
||||
is.close();
|
||||
}
|
||||
|
||||
/** read obj from the given data string (.obj file contents) */
|
||||
void readData(const char* data) {
|
||||
readData(std::string(data));
|
||||
}
|
||||
|
||||
/** read obj from the given data string (.obj file contents) */
|
||||
void readData(const std::string& data) {
|
||||
std::stringstream is(data);
|
||||
std::string line;
|
||||
while(getline(is, line)) {parseLine(line);}
|
||||
}
|
||||
|
||||
/** get the parsed data */
|
||||
const Data& getData() const {return data;}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
void replaceAll(std::string& str, const std::string& from, const std::string& to) {
|
||||
size_t start_pos = 0;
|
||||
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
||||
size_t end_pos = start_pos + from.length();
|
||||
str.replace(start_pos, end_pos, to);
|
||||
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
|
||||
}
|
||||
}
|
||||
|
||||
/** remove empty strings from the vector */
|
||||
std::vector<std::string> nonEmpty(const std::vector<std::string>& src) {
|
||||
std::vector<std::string> res;
|
||||
for (const std::string& s : src) {
|
||||
if (!s.empty()) {res.push_back(s);}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
template<typename Out>
|
||||
void split(const std::string &s, char delim, Out result) {
|
||||
std::stringstream ss(s);
|
||||
std::string item;
|
||||
while (std::getline(ss, item, delim)) {
|
||||
*(result++) = item;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> split(const std::string &s, char delim) {
|
||||
std::vector<std::string> elems;
|
||||
split(s, delim, std::back_inserter(elems));
|
||||
return elems;
|
||||
}
|
||||
|
||||
/** parse one line of the .obj file */
|
||||
void parseLine(std::string line) {
|
||||
|
||||
if (line.length() < 2) {return;}
|
||||
|
||||
// remove other linebreaks
|
||||
replaceAll(line, "\r", "");
|
||||
|
||||
const std::vector<std::string> tokens = nonEmpty(split(line, ' '));
|
||||
const std::string token = tokens.front();
|
||||
|
||||
if ("mtllib" == token) {data.materialFiles.push_back(tokens[1]);}
|
||||
if ("usemtl" == token) {data.curObj().material = tokens[1];}
|
||||
if ("v" == token) {parseVertex(tokens);}
|
||||
if ("vt" == token) {parseTexCoord(tokens);}
|
||||
if ("vn" == token) {parseNormal(tokens);}
|
||||
if ("f" == token) {parseFace(tokens);}
|
||||
if ("g" == token) {newObject(tokens[1]);}
|
||||
if ("o" == token) {newObject(tokens[1]);}
|
||||
|
||||
}
|
||||
|
||||
/** allocate a new object */
|
||||
void newObject(const std::string& name) {
|
||||
Object o;
|
||||
o.name = name;
|
||||
data.objects.push_back(o);
|
||||
}
|
||||
|
||||
/** parse one vertex from the tokenizer */
|
||||
void parseVertex(const std::vector<std::string>& t) {
|
||||
const float x = std::stof(t[1]);
|
||||
const float y = std::stof(t[2]);
|
||||
const float z = std::stof(t[3]);
|
||||
data.vertices.push_back(Point3(x,y,z));
|
||||
}
|
||||
|
||||
/** parse one texture-coordinate from the tokenizer */
|
||||
void parseTexCoord(const std::vector<std::string>& t) {
|
||||
const float u = std::stof(t[1]);
|
||||
const float v = std::stof(t[2]);
|
||||
data.texCoords.push_back(Point2(u, -v));
|
||||
}
|
||||
|
||||
/** parse one normal from the tokenizer */
|
||||
void parseNormal(const std::vector<std::string>& t) {
|
||||
const float x = std::stof(t[1]);
|
||||
const float y = std::stof(t[2]);
|
||||
const float z = std::stof(t[3]);
|
||||
data.normals.push_back(Point3(x,y,z));
|
||||
}
|
||||
|
||||
/** parse one face from the tokenizer */
|
||||
void parseFace(const std::vector<std::string>& t) {
|
||||
|
||||
std::vector<VNT> indices;
|
||||
|
||||
int numVertices = 0;
|
||||
for (size_t i = 1; i < t.size(); ++i) {
|
||||
|
||||
// one V/T/N
|
||||
const std::string entry = t[i];
|
||||
const std::vector<std::string> vtn = split(entry, '/');
|
||||
|
||||
++numVertices;
|
||||
const std::string v = vtn[0];
|
||||
const std::string vt = (vtn.size() > 1) ? (vtn[1]) : ("");
|
||||
const std::string vn = (vtn.size() > 2) ? (vtn[2]) : ("");
|
||||
//const std::string vt = t2.getToken('/', false);
|
||||
//const std::string vn = t2.getToken('/', false);
|
||||
|
||||
// create a new vertex/normal/texture combination
|
||||
VNT vnt;
|
||||
vnt.idxVertex = (std::stoi(v) - 1);
|
||||
vnt.idxNormal = (vn.empty()) ? (-1) : (std::stoi(vn) - 1);
|
||||
vnt.idxTexture = (vt.empty()) ? (-1) : (std::stoi(vt) - 1);
|
||||
|
||||
if (vnt.idxVertex >= 0) {vnt.vertex = data.vertices[vnt.idxVertex];}
|
||||
if (vnt.idxNormal >= 0) {vnt.normal = data.normals[vnt.idxNormal];}
|
||||
if (vnt.idxTexture >= 0) {vnt.texture = data.texCoords[vnt.idxTexture];}
|
||||
|
||||
indices.push_back(vnt);
|
||||
|
||||
}
|
||||
|
||||
// this will both, create normal triangles and triangulate polygons
|
||||
// see: http://www.mathopenref.com/polygontriangles.html
|
||||
for (int i = 1; i < (int) indices.size()-1; ++i) {
|
||||
Face face(indices[0], indices[1], indices[i+1]);
|
||||
data.curObj().faces.push_back(face);
|
||||
}
|
||||
|
||||
// sanity check
|
||||
// if (numVertices != 3) {throw "this face is not a triangle!";}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // OBJREADER_H
|
||||
176
floorplan/3D/primitives/Cube.h
Normal file
176
floorplan/3D/primitives/Cube.h
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef FLOORPLAN_3D_CUBE_H
|
||||
#define FLOORPLAN_3D_CUBE_H
|
||||
|
||||
|
||||
#include "../../../math/Matrix4.h"
|
||||
#include "Mesh.h"
|
||||
|
||||
namespace Floorplan3D {
|
||||
|
||||
class Cube : public Mesh {
|
||||
|
||||
private:
|
||||
|
||||
/** ctor */
|
||||
Cube() {
|
||||
//unitCube(true);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
|
||||
enum Part {
|
||||
CUBE_TOP = 1,
|
||||
CUBE_BOTTOM = 2,
|
||||
CUBE_LEFT = 4,
|
||||
CUBE_RIGHT = 8,
|
||||
CUBE_FRONT = 16,
|
||||
CUBE_BACK = 32,
|
||||
};
|
||||
|
||||
// Cube (const Point3 p1, const Point3 p2, const Point3 p3, const Point3 p4, const float h) {
|
||||
//// const Point3 ph(0,0,h);
|
||||
//// addQuad(p1+ph, p2+ph, p3+ph, p4+ph); // top
|
||||
//// addQuad(p4, p3, p2, p1); // bottom
|
||||
//// addQuad(p3+ph, p2+ph, p2, p3); // right
|
||||
//// addQuad(p1+ph, p4+ph, p4, p1); // left
|
||||
//// addQuad(p2+ph, p1+ph, p1, p2); // front
|
||||
//// addQuad(p4+ph, p3+ph, p3, p4); // back
|
||||
// addQuad();
|
||||
// }
|
||||
|
||||
/** ctor with position, size and rotation */
|
||||
Cube(const Point3 pos, const Point3 size, const Point3 rot_deg, const Part parts = (Part)63) {
|
||||
unitCube(parts);
|
||||
transform(pos, size, rot_deg);
|
||||
}
|
||||
|
||||
|
||||
/** get a transformed version */
|
||||
Cube transformed(const Matrix4& mat) const {
|
||||
Cube res = *this;
|
||||
res.transform(mat);
|
||||
return res;
|
||||
}
|
||||
|
||||
/** get a transformed version */
|
||||
Cube transformed(const Point3 pos, const Point3 size, const Point3 rot_deg) const {
|
||||
Cube res = *this;
|
||||
res.transform(pos, size, rot_deg);
|
||||
return res;
|
||||
}
|
||||
|
||||
static Cube unit(const Part parts = (Part) 63) {
|
||||
Cube cube;
|
||||
cube.unitCube(parts);
|
||||
return cube;
|
||||
}
|
||||
|
||||
/** cube from 8 vertices (upper 4, lower 4) */
|
||||
static Cube fromVertices(const Point3 pt1, const Point3 pt2, const Point3 pt3, const Point3 pt4, const Point3 pb1, const Point3 pb2, const Point3 pb3, const Point3 pb4) {
|
||||
Cube cube;
|
||||
cube.addQuad(pt1, pt2, pt3, pt4); // top
|
||||
cube.addQuad(pb4, pb3, pb2, pb1); // bottom
|
||||
cube.addQuad(pt3, pt2, pb2, pb3); // right
|
||||
cube.addQuad(pt1, pt4, pb4, pb1); // left
|
||||
cube.addQuad(pt2, pt1, pb1, pb2); // front
|
||||
cube.addQuad(pt4, pt3, pb3, pb4); // back
|
||||
return cube;
|
||||
}
|
||||
|
||||
/** cube from 8 vertices (upper 4, lower 4) */
|
||||
static Cube fromBottomAndHeight(const Point3 pb1, const Point3 pb2, const Point3 pb3, const Point3 pb4, const float h) {
|
||||
const Point3 ph(0,0,h);
|
||||
return Cube::fromVertices(pb1+ph, pb2+ph, pb3+ph, pb4+ph, pb1, pb2, pb3, pb4);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/** build unit-cube faces */
|
||||
void unitCube(const Part parts) {
|
||||
|
||||
const float s = 1.0f;
|
||||
|
||||
|
||||
|
||||
// left?
|
||||
if (parts & CUBE_LEFT) {
|
||||
addQuad(
|
||||
Point3(+s, -s, -s),
|
||||
Point3(+s, -s, +s),
|
||||
Point3(-s, -s, +s),
|
||||
Point3(-s, -s, -s)
|
||||
);
|
||||
}
|
||||
|
||||
// right?
|
||||
if (parts & CUBE_RIGHT) {
|
||||
addQuad(
|
||||
Point3(-s, +s, -s),
|
||||
Point3(-s, +s, +s),
|
||||
Point3(+s, +s, +s),
|
||||
Point3(+s, +s, -s)
|
||||
);
|
||||
}
|
||||
|
||||
// front
|
||||
if (parts & CUBE_FRONT) {
|
||||
addQuad(
|
||||
Point3(-s, -s, -s),
|
||||
Point3(-s, -s, +s),
|
||||
Point3(-s, +s, +s),
|
||||
Point3(-s, +s, -s)
|
||||
);
|
||||
}
|
||||
|
||||
// back
|
||||
if (parts & CUBE_BACK) {
|
||||
addQuad(
|
||||
Point3(+s, +s, -s),
|
||||
Point3(+s, +s, +s),
|
||||
Point3(+s, -s, +s),
|
||||
Point3(+s, -s, -s)
|
||||
);
|
||||
}
|
||||
|
||||
// top
|
||||
if (parts & CUBE_TOP) {
|
||||
addQuad(
|
||||
Point3(+s, +s, +s),
|
||||
Point3(-s, +s, +s),
|
||||
Point3(-s, -s, +s),
|
||||
Point3(+s, -s, +s)
|
||||
);
|
||||
}
|
||||
|
||||
// bottom
|
||||
if (parts & CUBE_BOTTOM) {
|
||||
addQuad(
|
||||
Point3(+s, -s, -s),
|
||||
Point3(-s, -s, -s),
|
||||
Point3(-s, +s, -s),
|
||||
Point3(+s, +s, -s)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FLOORPLAN_3D_CUBE_H
|
||||
92
floorplan/3D/primitives/Cylinder.h
Normal file
92
floorplan/3D/primitives/Cylinder.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef FLOORPLAN_3D_CYLINDER_H
|
||||
#define FLOORPLAN_3D_CYLINDER_H
|
||||
|
||||
|
||||
#include "../../../math/Matrix4.h"
|
||||
#include "Mesh.h"
|
||||
|
||||
namespace Floorplan3D {
|
||||
|
||||
/** walled cylinder */
|
||||
class Cylinder : public Mesh {
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
Cylinder() {
|
||||
;
|
||||
}
|
||||
|
||||
/** get a transformed version */
|
||||
Cylinder transformed(const Matrix4& mat) const {
|
||||
Cylinder res = *this;
|
||||
res.transform(mat);
|
||||
return res;
|
||||
}
|
||||
|
||||
/** build */
|
||||
void add(const float rOuter, const float h, bool topAndBottom) {
|
||||
|
||||
const int tiles = 8;
|
||||
const float deg_per_tile = 360.0f / tiles;
|
||||
const float rad_per_tile = deg_per_tile / 180.0f * M_PI;
|
||||
|
||||
for (int i = 0; i < tiles; ++i) {
|
||||
|
||||
const float startRad = (i+0) * rad_per_tile;
|
||||
const float endRad = (i+1) * rad_per_tile;
|
||||
|
||||
const float xo0 = std::cos(startRad) * rOuter;
|
||||
const float yo0 = std::sin(startRad) * rOuter;
|
||||
const float xo1 = std::cos(endRad) * rOuter;
|
||||
const float yo1 = std::sin(endRad) * rOuter;
|
||||
const float cx = 0;
|
||||
const float cy = 0;
|
||||
|
||||
// outer
|
||||
addQuad(
|
||||
Point3(xo0, yo0, -h),
|
||||
Point3(xo1, yo1, -h),
|
||||
Point3(xo1, yo1, +h),
|
||||
Point3(xo0, yo0, +h)
|
||||
);
|
||||
|
||||
if (topAndBottom) {
|
||||
|
||||
// top
|
||||
addTriangle(
|
||||
Point3(cx, cy, h),
|
||||
Point3(xo0, yo0, h),
|
||||
Point3(xo1, yo1, h)
|
||||
);
|
||||
|
||||
// bottom
|
||||
addTriangle(
|
||||
Point3(cx, cy, -h),
|
||||
Point3(xo1, yo1, -h),
|
||||
Point3(xo0, yo0, -h)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FLOORPLAN_3D_CYLINDER_H
|
||||
79
floorplan/3D/primitives/Mesh.h
Normal file
79
floorplan/3D/primitives/Mesh.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef FLOORPLAN_3D_MESH_H
|
||||
#define FLOORPLAN_3D_MESH_H
|
||||
|
||||
#include <vector>
|
||||
#include "../../../geo/Triangle3.h"
|
||||
#include "../../../math/Matrix4.h"
|
||||
|
||||
namespace Floorplan3D {
|
||||
|
||||
class Mesh {
|
||||
|
||||
protected:
|
||||
|
||||
std::vector<Triangle3> trias;
|
||||
|
||||
public:
|
||||
|
||||
/** get the mesh's triangles */
|
||||
const std::vector<Triangle3>& getTriangles() const {
|
||||
return trias;
|
||||
}
|
||||
|
||||
void transform(const Point3 pos, Point3 size, Point3 rot_deg) {
|
||||
const Matrix4 mRot = Matrix4::getRotationDeg(rot_deg.x, rot_deg.y, rot_deg.z);
|
||||
const Matrix4 mSize = Matrix4::getScale(size.x, size.y, size.z);
|
||||
const Matrix4 mPos = Matrix4::getTranslation(pos.x, pos.y, pos.z);
|
||||
const Matrix4 mat = mPos * mRot * mSize;
|
||||
transform(mat);
|
||||
}
|
||||
|
||||
void translate(const Point3 pos) {
|
||||
const Matrix4 mPos = Matrix4::getTranslation(pos.x, pos.y, pos.z);
|
||||
transform(mPos);
|
||||
}
|
||||
|
||||
void transform(const Matrix4& mat) {
|
||||
|
||||
for (Triangle3& tria : trias) {
|
||||
|
||||
Vector4 v1(tria.p1.x, tria.p1.y, tria.p1.z, 1);
|
||||
Vector4 v2(tria.p2.x, tria.p2.y, tria.p2.z, 1);
|
||||
Vector4 v3(tria.p3.x, tria.p3.y, tria.p3.z, 1);
|
||||
|
||||
v1 = mat*v1;
|
||||
v2 = mat*v2;
|
||||
v3 = mat*v3;
|
||||
|
||||
tria.p1 = Point3(v1.x, v1.y, v1.z);
|
||||
tria.p2 = Point3(v2.x, v2.y, v2.z);
|
||||
tria.p3 = Point3(v3.x, v3.y, v3.z);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void addQuad(Point3 p1, Point3 p2, Point3 p3, Point3 p4) {
|
||||
trias.push_back( Triangle3(p1,p2,p3) );
|
||||
trias.push_back( Triangle3(p1,p3,p4) );
|
||||
}
|
||||
|
||||
void addTriangle(Point3 p1, Point3 p2, Point3 p3) {
|
||||
trias.push_back( Triangle3(p1,p2,p3) );
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FLOORPLAN_3D_MESH_H
|
||||
132
floorplan/3D/primitives/Tube.h
Normal file
132
floorplan/3D/primitives/Tube.h
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef FLOORPLAN_3D_TUBE_H
|
||||
#define FLOORPLAN_3D_TUBE_H
|
||||
|
||||
|
||||
#include "../../../math/Matrix4.h"
|
||||
#include "Mesh.h"
|
||||
|
||||
namespace Floorplan3D {
|
||||
|
||||
/** walled cylinder */
|
||||
class Tube : public Mesh {
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
Tube() {
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
/** get a transformed version */
|
||||
Tube transformed(const Matrix4& mat) const {
|
||||
Tube res = *this;
|
||||
res.transform(mat);
|
||||
return res;
|
||||
}
|
||||
|
||||
/** build */
|
||||
void addSegment(const float from_deg, const float to_deg, const float rInner, const float rOuter, const float h, bool closeSides, bool topAndBottom) {
|
||||
|
||||
const int tiles = 32;
|
||||
const float deg_per_tile = 360.0f / tiles;
|
||||
const float rad_per_tile = deg_per_tile / 180.0f * M_PI;
|
||||
const int startTile = std::round(from_deg / deg_per_tile);
|
||||
const int endTile = std::round(to_deg / deg_per_tile);
|
||||
|
||||
for (int i = startTile; i < endTile; ++i) {
|
||||
|
||||
const float startRad = (i+0) * rad_per_tile;
|
||||
const float endRad = (i+1) * rad_per_tile;
|
||||
|
||||
const float xo0 = std::cos(startRad) * rOuter;
|
||||
const float yo0 = std::sin(startRad) * rOuter;
|
||||
const float xo1 = std::cos(endRad) * rOuter;
|
||||
const float yo1 = std::sin(endRad) * rOuter;
|
||||
|
||||
const float xi0 = std::cos(startRad) * rInner;
|
||||
const float yi0 = std::sin(startRad) * rInner;
|
||||
const float xi1 = std::cos(endRad) * rInner;
|
||||
const float yi1 = std::sin(endRad) * rInner;
|
||||
|
||||
if (closeSides) {
|
||||
|
||||
// close start of segment
|
||||
if (i == startTile) {
|
||||
addQuad(
|
||||
Point3(xi0, yi0, -h),
|
||||
Point3(xo0, yo0, -h),
|
||||
Point3(xo0, yo0, +h),
|
||||
Point3(xi0, yi0, +h)
|
||||
);
|
||||
}
|
||||
|
||||
// close end of segment
|
||||
if (i == endTile-1) {
|
||||
addQuad(
|
||||
Point3(xi1, yi1, +h),
|
||||
Point3(xo1, yo1, +h),
|
||||
Point3(xo1, yo1, -h),
|
||||
Point3(xi1, yi1, -h)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// outer
|
||||
addQuad(
|
||||
Point3(xo0, yo0, -h),
|
||||
Point3(xo1, yo1, -h),
|
||||
Point3(xo1, yo1, +h),
|
||||
Point3(xo0, yo0, +h)
|
||||
);
|
||||
|
||||
// innser
|
||||
addQuad(
|
||||
Point3(xi0, yi0, +h),
|
||||
Point3(xi1, yi1, +h),
|
||||
Point3(xi1, yi1, -h),
|
||||
Point3(xi0, yi0, -h)
|
||||
);
|
||||
|
||||
if (topAndBottom) {
|
||||
|
||||
// top
|
||||
addQuad(
|
||||
Point3(xi0, yi0, h),
|
||||
Point3(xo0, yo0, h),
|
||||
Point3(xo1, yo1, h),
|
||||
Point3(xi1, yi1, h)
|
||||
);
|
||||
|
||||
// bottom
|
||||
addQuad(
|
||||
Point3(xi1, yi1, -h),
|
||||
Point3(xo1, yo1, -h),
|
||||
Point3(xo0, yo0, -h),
|
||||
Point3(xi0, yi0, -h)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FLOORPLAN_3D_TUBE_H
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef FLOOR_H
|
||||
#define FLOOR_H
|
||||
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef FLOORPLANFACTORYSVG_H
|
||||
#define FLOORPLANFACTORYSVG_H
|
||||
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef PLATFORMSTAIR_H
|
||||
#define PLATFORMSTAIR_H
|
||||
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef STAIR_H
|
||||
#define STAIR_H
|
||||
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef STAIRS_H
|
||||
#define STAIRS_H
|
||||
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Einige Aenderungen beigetragen von Toni Fetzer
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef FLOORPLAN2_H
|
||||
#define FLOORPLAN2_H
|
||||
|
||||
@@ -130,6 +141,10 @@ namespace Floorplan {
|
||||
default: throw Exception("out of bounds");
|
||||
}
|
||||
}
|
||||
/** same z-value for all points? */
|
||||
bool isLeveled() const {
|
||||
return (p1.z == p2.z) && (p2.z == p3.z) && (p3.z == p4.z);
|
||||
}
|
||||
};
|
||||
|
||||
/** additional type-info for obstacles */
|
||||
@@ -153,6 +168,12 @@ namespace Floorplan {
|
||||
_END,
|
||||
};
|
||||
|
||||
/** available window types */
|
||||
enum class WindowType {
|
||||
UNKNOWN,
|
||||
_END,
|
||||
};
|
||||
|
||||
/** all supported material types */
|
||||
enum class Material {
|
||||
UNKNOWN,
|
||||
@@ -160,6 +181,8 @@ namespace Floorplan {
|
||||
WOOD,
|
||||
DRYWALL,
|
||||
GLASS,
|
||||
METAL,
|
||||
METALLIZED_GLAS,
|
||||
_END,
|
||||
};
|
||||
|
||||
@@ -179,27 +202,54 @@ namespace Floorplan {
|
||||
struct FloorObstacle;
|
||||
struct AccessPoint;
|
||||
struct Beacon;
|
||||
struct FingerprintLocation;
|
||||
struct FloorRegion;
|
||||
struct UnderlayImage;
|
||||
struct POI;
|
||||
struct Stair;
|
||||
struct Elevator;
|
||||
struct GroundTruthPoint;
|
||||
struct GroundTruthPoint;
|
||||
struct FloorObstacleWallDoor;
|
||||
struct FloorObstacleWallWindow;
|
||||
|
||||
using FloorOutline = std::vector<FloorOutlinePolygon*>;
|
||||
using FloorObstacles = std::vector<FloorObstacle*>;
|
||||
using FloorAccessPoints = std::vector<AccessPoint*>;
|
||||
using FloorBeacons = std::vector<Beacon*>;
|
||||
using FloorRegions = std::vector<FloorRegion*>;
|
||||
using FloorUnderlays = std::vector<UnderlayImage*>;
|
||||
using FloorPOIs = std::vector<POI*>;
|
||||
using FloorStairs = std::vector<Stair*>;
|
||||
using FloorElevators = std::vector<Elevator*>;
|
||||
using FloorGroundTruthPoints = std::vector<GroundTruthPoint*>;
|
||||
struct FloorOutline : public std::vector<FloorOutlinePolygon*> {
|
||||
bool enabled = true;
|
||||
};
|
||||
struct FloorObstacles : public std::vector<FloorObstacle*> {
|
||||
bool enabled = true;
|
||||
};
|
||||
struct FloorAccessPoints : public std::vector<AccessPoint*> {
|
||||
bool enabled = true;
|
||||
};
|
||||
struct FloorBeacons : public std::vector<Beacon*> {
|
||||
bool enabled = true;
|
||||
};
|
||||
struct FloorFingerprintLocations : public std::vector<FingerprintLocation*> {
|
||||
bool enabled = true;
|
||||
};
|
||||
struct FloorRegions : public std::vector<FloorRegion*> {
|
||||
bool enabled = true;
|
||||
};
|
||||
struct FloorUnderlays : public std::vector<UnderlayImage*> {
|
||||
bool enabled = true;
|
||||
};
|
||||
struct FloorPOIs : public std::vector<POI*> {
|
||||
bool enabled = true;
|
||||
};
|
||||
struct FloorStairs : public std::vector<Stair*> {
|
||||
bool enabled = true;
|
||||
};
|
||||
struct FloorElevators : public std::vector<Elevator*> {
|
||||
bool enabled = true;
|
||||
};
|
||||
struct FloorGroundTruthPoints : public std::vector<GroundTruthPoint*> {
|
||||
bool enabled = true;
|
||||
};
|
||||
|
||||
/** describes one floor within the map, starting at a given height */
|
||||
struct Floor {
|
||||
|
||||
bool enabled = true;
|
||||
float atHeight; // the floor's starting height
|
||||
float height; // the floor's total height (from start)
|
||||
std::string name; // the floor's name
|
||||
@@ -208,11 +258,12 @@ namespace Floorplan {
|
||||
FloorRegions regions; // all regions within the floor (rooms, ...)
|
||||
FloorAccessPoints accesspoints;
|
||||
FloorBeacons beacons;
|
||||
FloorFingerprintLocations fpLocations; // potential fingerprint locations
|
||||
FloorUnderlays underlays; // underlay images (used for map-building)
|
||||
FloorPOIs pois; // POIs within the floor
|
||||
FloorStairs stairs; // all stairs within one floor
|
||||
FloorElevators elevators; // all elevators within one floor
|
||||
FloorGroundTruthPoints gtpoints; // all ground truth points within one floor
|
||||
FloorGroundTruthPoints gtpoints; // all ground truth points within one floor
|
||||
//FloorKeyValue other; // other, free elements
|
||||
|
||||
Floor() {;}
|
||||
@@ -225,7 +276,15 @@ namespace Floorplan {
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** location for fingerprint measurements */
|
||||
struct FingerprintLocation : public HasMeta {
|
||||
std::string name;
|
||||
Point2 posOnFloor;
|
||||
float heightAboveFloor = 0;
|
||||
FingerprintLocation() {;}
|
||||
FingerprintLocation(const std::string& name, const Point2 posOnFloor, const float heightAboveFloor) : name(name), posOnFloor(posOnFloor), heightAboveFloor(heightAboveFloor) {;}
|
||||
Point3 getPosition(const Floor& floor) const {return Point3(posOnFloor.x, posOnFloor.y, floor.atHeight + heightAboveFloor);}
|
||||
};
|
||||
|
||||
/** a POI located somewhere on a floor */
|
||||
struct POI {
|
||||
@@ -237,14 +296,15 @@ namespace Floorplan {
|
||||
bool operator == (const POI& o) const {return (o.type == type) && (o.name == name) && (o.pos == pos);}
|
||||
};
|
||||
|
||||
/** a GroundTruthPoint located somewhere on a floor */
|
||||
struct GroundTruthPoint {
|
||||
int id; //TODO: this value can be changed and isn't set incremental within the indoormap
|
||||
Point2 pos;
|
||||
GroundTruthPoint() : id(), pos() {;}
|
||||
GroundTruthPoint(const int& id, const Point2& pos) : id(id), pos(pos) {;}
|
||||
bool operator == (const GroundTruthPoint& o) const {return (o.id == id) && (o.pos == pos);}
|
||||
};
|
||||
/** a GroundTruthPoint located somewhere on a floor */
|
||||
struct GroundTruthPoint {
|
||||
int id; //TODO: this value can be changed and isn't set incremental within the indoormap
|
||||
Point3 pos; // TODO: splint into 2D position + float for "heightAboveGround" [waypoints' height is relative to the floor's height!
|
||||
GroundTruthPoint() : id(), pos() {;}
|
||||
GroundTruthPoint(const int id, const Point3& pos) : id(id), pos(pos) {;}
|
||||
const Point3 getPosition(const Floor& f) const {return pos + Point3(0,0,f.atHeight);}
|
||||
bool operator == (const GroundTruthPoint& o) const {return (o.id == id) && (o.pos == pos);}
|
||||
};
|
||||
|
||||
/** an AccessPoint located somewhere on a floor */
|
||||
struct AccessPoint : public HasMeta {
|
||||
@@ -278,7 +338,7 @@ namespace Floorplan {
|
||||
Beacon() : name(), mac(), pos() {;}
|
||||
Beacon(const std::string& name, const std::string& mac, const Point3& pos) : name(name), mac(mac), pos(pos) {;}
|
||||
bool operator == (const Beacon& o) const {return (o.name == name) && (o.mac == mac) && (o.pos == pos);}
|
||||
Point3 getPos(const Floor* f) const {return pos + Point3(0,0,f->atHeight);} // relative to the floor's ground
|
||||
Point3 getPos(const Floor* f) const {return pos + Point3(0,0,f->atHeight);} // relative to the floor's ground
|
||||
};
|
||||
|
||||
|
||||
@@ -287,15 +347,13 @@ namespace Floorplan {
|
||||
OutlineMethod method;
|
||||
std::string name;
|
||||
Polygon2 poly;
|
||||
FloorOutlinePolygon() : method(OutlineMethod::ADD), name(), poly() {;}
|
||||
FloorOutlinePolygon(const OutlineMethod method, const std::string& name, const Polygon2& poly) : method(method), name(name), poly(poly) {;}
|
||||
bool operator == (const FloorOutlinePolygon& o) const {return (o.method == method) && (o.name == name) && (o.poly == poly);}
|
||||
bool outdoor; // special marker
|
||||
FloorOutlinePolygon() : method(OutlineMethod::ADD), name(), poly(), outdoor(false) {;}
|
||||
FloorOutlinePolygon(const OutlineMethod method, const std::string& name, const Polygon2& poly, bool outdoor) : method(method), name(name), poly(poly), outdoor(outdoor) {;}
|
||||
bool operator == (const FloorOutlinePolygon& o) const {return (o.method == method) && (o.name == name) && (o.poly == poly) && (o.outdoor == outdoor);}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** base-class for one obstacle (wall, door, window, pillar, ..) within a floor */
|
||||
struct FloorObstacle {
|
||||
Material material;
|
||||
@@ -309,16 +367,20 @@ namespace Floorplan {
|
||||
ObstacleType type;
|
||||
Point2 from;
|
||||
Point2 to;
|
||||
FloorObstacleLine(const ObstacleType type, const Material material, const Point2 from, const Point2 to) : FloorObstacle(material), type(type), from(from), to(to) {;}
|
||||
FloorObstacleLine(const ObstacleType type, const Material material, const float x1, const float y1, const float x2, const float y2) : FloorObstacle(material), type(type), from(x1,y1), to(x2,y2) {;}
|
||||
float thickness_m;
|
||||
float height_m = 0; // 0 = floor's height
|
||||
FloorObstacleLine(const ObstacleType type, const Material material, const Point2 from, const Point2 to, const float thickness_m = 0.2f, const float height_m = 0) : FloorObstacle(material), type(type), from(from), to(to), thickness_m(thickness_m), height_m(height_m) {;}
|
||||
FloorObstacleLine(const ObstacleType type, const Material material, const float x1, const float y1, const float x2, const float y2, const float thickness_m = 0.2f, const float height_m = 0) : FloorObstacle(material), type(type), from(x1,y1), to(x2,y2), thickness_m(thickness_m), height_m(height_m) {;}
|
||||
};
|
||||
|
||||
/** circle obstacle */
|
||||
struct FloorObstacleCircle : public FloorObstacle {
|
||||
Point2 center;
|
||||
float radius;
|
||||
FloorObstacleCircle(const Material material, const Point2 center, const float radius) : FloorObstacle(material), center(center), radius(radius) {;}
|
||||
FloorObstacleCircle(const Material material, const float cx, const float cy, const float radius) : FloorObstacle(material), center(cx,cy), radius(radius) {;}
|
||||
float height = 0; // 0 = floor's height
|
||||
FloorObstacleCircle(const Material material, const Point2 center, const float radius, const float height=0) : FloorObstacle(material), center(center), radius(radius), height(height) {;}
|
||||
FloorObstacleCircle(const Material material, const float cx, const float cy, const float radius, const float height=0) : FloorObstacle(material), center(cx,cy), radius(radius), height(height) {;}
|
||||
//float getHeight(const Floor* f) const {return (height > 0) ? (height) : (f->height);}
|
||||
};
|
||||
|
||||
/** door obstacle */
|
||||
@@ -334,6 +396,69 @@ namespace Floorplan {
|
||||
};
|
||||
|
||||
|
||||
/** wall obstacle */
|
||||
struct FloorObstacleWall: public FloorObstacle {
|
||||
ObstacleType type;
|
||||
Point2 from;
|
||||
Point2 to;
|
||||
float thickness_m;
|
||||
float height_m = 0; // 0 = floor's height
|
||||
std::vector<FloorObstacleWallDoor*> doors;
|
||||
std::vector<FloorObstacleWallWindow*> windows;
|
||||
FloorObstacleWall(const ObstacleType type, const Material material, const Point2 from, const Point2 to, const float thickness_m = 0.2f, const float height_m = 0) : FloorObstacle(material), type(type), from(from), to(to), thickness_m(thickness_m), height_m(height_m) {;}
|
||||
FloorObstacleWall(const ObstacleType type, const Material material, const float x1, const float y1, const float x2, const float y2, const float thickness_m = 0.2f, const float height_m = 0) : FloorObstacle(material), type(type), from(x1,y1), to(x2,y2), thickness_m(thickness_m), height_m(height_m) {;}
|
||||
};
|
||||
|
||||
/** wall->door obstacle */
|
||||
struct FloorObstacleWallDoor : public FloorObstacle {
|
||||
DoorType type;
|
||||
float atLinePos;
|
||||
float width;
|
||||
float height;
|
||||
bool leftRight = false;
|
||||
bool inOut = false;
|
||||
FloorObstacleWallDoor(const DoorType type, const Material material, const float atLinePos, const float width, const float height, const bool lr = false, const bool io = false) : FloorObstacle(material), type(type), atLinePos(atLinePos), width(width), height(height), leftRight(lr), inOut(io) {;}
|
||||
Point2 getStart(const FloorObstacleWall* wall) const {
|
||||
const Point2 dir = wall->to - wall->from;
|
||||
return wall->from + dir * atLinePos;
|
||||
}
|
||||
Point2 getEnd(const FloorObstacleWall* wall) const {
|
||||
const Point2 dir = wall->to - wall->from;
|
||||
return getStart(wall) + dir.normalized() * (leftRight ? -width : +width);
|
||||
}
|
||||
};
|
||||
|
||||
/** wall->window obstacle */
|
||||
struct FloorObstacleWallWindow : public FloorObstacle {
|
||||
WindowType type;
|
||||
float atLinePos;
|
||||
float startsAtHeight;
|
||||
float width;
|
||||
float height;
|
||||
bool inOut = false;
|
||||
FloorObstacleWallWindow(const WindowType type, const Material material, const float atLinePos, const float startsAtHeight, const float width, const float height, const bool io = false) : FloorObstacle(material), type(type), atLinePos(atLinePos), startsAtHeight(startsAtHeight), width(width), height(height), inOut(io) {;}
|
||||
Point2 getStart(const FloorObstacleWall* wall) const {
|
||||
const Point2 dir = wall->to - wall->from;
|
||||
const Point2 cen = wall->from + dir * atLinePos;
|
||||
return cen - dir.normalized() * width/2;
|
||||
}
|
||||
Point2 getEnd(const FloorObstacleWall* wall) const {
|
||||
const Point2 dir = wall->to - wall->from;
|
||||
const Point2 cen = wall->from + dir * atLinePos;
|
||||
return cen + dir.normalized() * width/2;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** 3D obstacle */
|
||||
struct FloorObstacleObject : public FloorObstacle {
|
||||
std::string file;
|
||||
Point3 pos;
|
||||
Point3 rot;
|
||||
Point3 scale = Point3(1,1,1);
|
||||
FloorObstacleObject(const std::string& file, const Point3 pos, const Point3 rot, const Point3 scale) : file(file), pos(pos), rot(rot), scale(scale) {;}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -401,7 +526,7 @@ namespace Floorplan {
|
||||
|
||||
|
||||
/** snap quad-edges together if their distance is below the given maximum. is used to close minor gaps */
|
||||
static void snapQuads(std::vector<Floorplan::Quad3>& quads, const float maxDist) {
|
||||
static inline void snapQuads(std::vector<Floorplan::Quad3>& quads, const float maxDist) {
|
||||
|
||||
for (Floorplan::Quad3& quad1 : quads) {
|
||||
for (int i1 = 0; i1 < 4; ++i1) {
|
||||
@@ -428,7 +553,7 @@ namespace Floorplan {
|
||||
}
|
||||
|
||||
/** convert stair-parts to quads. the scaling factor may be used to slightly grow each quad. e.g. needed to ensure that the quads overlap */
|
||||
static std::vector<Quad3> getQuads(const std::vector<StairPart>& parts, const Floor* floor, const float s = 1.0f) {
|
||||
static inline std::vector<Quad3> getQuads(const std::vector<StairPart>& parts, const Floor* floor, const float s = 1.0f) {
|
||||
|
||||
std::vector<Quad3> vec;
|
||||
|
||||
@@ -497,6 +622,9 @@ namespace Floorplan {
|
||||
/** the elevator's rotation (in radians) */
|
||||
float rotation;
|
||||
|
||||
/** the elevator's height (from its starting position) */
|
||||
float height_m;
|
||||
|
||||
/** get the 4 corner points for the elevator */
|
||||
Polygon2 getPoints() const {
|
||||
const Point2 p1 = Point2(+width/2, +depth/2).rotated(rotation) + center;
|
||||
@@ -536,6 +664,9 @@ namespace Floorplan {
|
||||
|
||||
Point3 posOnMap_m;
|
||||
|
||||
/** empty ctor */
|
||||
EarthPosMapPos() : posOnEarth(), posOnMap_m() {;}
|
||||
|
||||
/** ctor */
|
||||
EarthPosMapPos(const EarthPos posOnEarth, const Point3 posOnMap_m) : posOnEarth(posOnEarth), posOnMap_m(posOnMap_m) {;}
|
||||
|
||||
@@ -545,8 +676,10 @@ namespace Floorplan {
|
||||
/** describe the floorplan's location on earth */
|
||||
struct EarthRegistration {
|
||||
|
||||
bool enabled = true;
|
||||
|
||||
/** all available correspondences: earth <-> map */
|
||||
std::vector<EarthPosMapPos> correspondences;
|
||||
std::vector<EarthPosMapPos*> correspondences;
|
||||
|
||||
};
|
||||
|
||||
|
||||
164
floorplan/v2/FloorplanCeilings.h
Normal file
164
floorplan/v2/FloorplanCeilings.h
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Einige Aenderungen beigetragen von Toni Fetzer
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef FLOORPLANCEILINGS_H
|
||||
#define FLOORPLANCEILINGS_H
|
||||
|
||||
#include "Floorplan.h"
|
||||
|
||||
namespace Floorplan {
|
||||
|
||||
/**
|
||||
* helper-class for floorplan ceilings
|
||||
* e.g. to determine the number of ceilings between two given positions
|
||||
*/
|
||||
class Ceilings {
|
||||
|
||||
private:
|
||||
|
||||
/** position (height) of all ceilings (in meter) */
|
||||
std::vector<float> ceilingsAtHeight_m;
|
||||
|
||||
public:
|
||||
|
||||
/** empty ctor */
|
||||
Ceilings() {
|
||||
;
|
||||
}
|
||||
|
||||
/** ctor with the map to work with */
|
||||
Ceilings(const IndoorMap* map) {
|
||||
|
||||
// sanity checks
|
||||
Assert::isTrue(map->floors.size() >= 1, "map has no floors?!");
|
||||
|
||||
// get position of all ceilings
|
||||
for (Floorplan::Floor* f : map->floors) {
|
||||
|
||||
const float h1 = f->atHeight;
|
||||
const float h2 = f->atHeight + f->height;
|
||||
|
||||
if (std::find(ceilingsAtHeight_m.begin(), ceilingsAtHeight_m.end(), h1) == ceilingsAtHeight_m.end()) {
|
||||
ceilingsAtHeight_m.push_back(h1);
|
||||
}
|
||||
if (std::find(ceilingsAtHeight_m.begin(), ceilingsAtHeight_m.end(), h2) == ceilingsAtHeight_m.end()) {
|
||||
ceilingsAtHeight_m.push_back(h2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::vector<float> getCeilings() const {
|
||||
return ceilingsAtHeight_m;
|
||||
}
|
||||
|
||||
void addCeiling(const float height_m) {
|
||||
ceilingsAtHeight_m.push_back(height_m);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
ceilingsAtHeight_m.clear();
|
||||
}
|
||||
|
||||
/** get a fading number of floors between ap and person using sigmod in the area where the ceiling is */
|
||||
float numCeilingsBetweenFloat(const Point3 ap, const Point3 person) const {
|
||||
|
||||
// sanity checks
|
||||
Assert::isNot0(ceilingsAtHeight_m.size(), "no ceilings available for testing! incorrect map?");
|
||||
|
||||
// fading between floors using sigmoid
|
||||
const float near = 1.0;
|
||||
float cnt = 0;
|
||||
|
||||
|
||||
for (const float z : ceilingsAtHeight_m) {
|
||||
|
||||
const float myDistToCeil = (ap.z < person.z) ? (person.z - z) : (z - person.z);
|
||||
if ( std::abs(myDistToCeil) < near ) {
|
||||
cnt += sigmoid(myDistToCeil * 6);
|
||||
} else if (ap.z < z && person.z >= z+near) { // AP below celing, me above ceiling
|
||||
cnt += 1;
|
||||
} else if (ap.z > z && person.z <= z-near) { // AP above ceiling, me below ceiling
|
||||
cnt += 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return cnt;
|
||||
|
||||
}
|
||||
|
||||
float numCeilingsBetweenLinearInt(const Point3 ap, const Point3 person) const {
|
||||
|
||||
// sanity checks
|
||||
Assert::isNot0(ceilingsAtHeight_m.size(), "no ceilings available for testing! incorrect map?");
|
||||
|
||||
int cnt = 0;
|
||||
float sum = 0;
|
||||
for (float z = -1.0; z <= +1.0; z+= 0.25) {
|
||||
sum += numCeilingsBetween(ap, person + Point3(0,0,z));
|
||||
++cnt;
|
||||
}
|
||||
|
||||
return sum/cnt;
|
||||
|
||||
}
|
||||
|
||||
/** get the number of ceilings between z1 and z2 */
|
||||
int numCeilingsBetween(const Point3 pos1, const Point3 pos2) const {
|
||||
|
||||
// sanity checks
|
||||
Assert::isNot0(ceilingsAtHeight_m.size(), "no ceilings available for testing! incorrect map?");
|
||||
|
||||
// find min and max height given the to-be-compared points
|
||||
const float zMin = std::min(pos1.z, pos2.z);
|
||||
const float zMax = std::max(pos1.z, pos2.z);
|
||||
|
||||
#ifdef WITH_ASSERTIONS
|
||||
|
||||
static uint64_t numNear = 0;
|
||||
static uint64_t numFar = 0;
|
||||
|
||||
for (const float z : ceilingsAtHeight_m) {
|
||||
const float diff = std::min( std::abs(z-zMin), std::abs(z-zMax) );
|
||||
if (diff < 0.1) {++numNear;} else {++numFar;}
|
||||
}
|
||||
if ((numNear + numFar) > 150000) {
|
||||
Assert::isTrue(numNear < numFar*0.5,
|
||||
"many requests to Floorplan::Ceilings::numCeilingsBetween address nodes (very) near to a ground! \
|
||||
due to rounding issues, determining the number of floors between AP and point-in-question is NOT possible! \
|
||||
expect very wrong outputs! \
|
||||
consider adding the person's height to the questioned positions: p += Point3(0,0,1.3) "
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
int cnt = 0;
|
||||
for (const float z : ceilingsAtHeight_m) {
|
||||
if (zMin < z && zMax > z) {++cnt;}
|
||||
}
|
||||
|
||||
return cnt;
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
static inline float sigmoid(const float val) {
|
||||
return 1.0f / (1.0f + std::exp(-val));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FLOORPLANCEILINGS_H
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef FLOORPLANHELPER2_H
|
||||
#define FLOORPLANHELPER2_H
|
||||
|
||||
@@ -7,11 +17,88 @@
|
||||
|
||||
#include "Floorplan.h"
|
||||
|
||||
#include "../../sensors/MACAddress.h"
|
||||
|
||||
/**
|
||||
* helper methods for the floorplan
|
||||
*/
|
||||
class FloorplanHelper {
|
||||
|
||||
public:
|
||||
|
||||
|
||||
/** get the AP for the given MAC [if available] */
|
||||
static std::pair<Floorplan::AccessPoint*, Floorplan::Floor*> getAP(const Floorplan::IndoorMap* map, const MACAddress& mac) {
|
||||
for (Floorplan::Floor* f : map->floors) {
|
||||
for (Floorplan::AccessPoint* ap : f->accesspoints) {
|
||||
if (MACAddress(ap->mac) == mac) {
|
||||
return std::make_pair(ap, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::make_pair(nullptr, nullptr);
|
||||
}
|
||||
|
||||
/** get the AP for the given Name [if available] */
|
||||
static std::pair<Floorplan::AccessPoint*, Floorplan::Floor*> getAPByName(const Floorplan::IndoorMap* map, const std::string& name) {
|
||||
for (Floorplan::Floor* f : map->floors) {
|
||||
for (Floorplan::AccessPoint* ap : f->accesspoints) {
|
||||
if (name == ap->name) {
|
||||
return std::make_pair(ap, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::make_pair(nullptr, nullptr);
|
||||
}
|
||||
|
||||
/** get all APs within the map */
|
||||
static std::vector<std::pair<Floorplan::AccessPoint*, Floorplan::Floor*>> getAPs(const Floorplan::IndoorMap* map) {
|
||||
std::vector<std::pair<Floorplan::AccessPoint*, Floorplan::Floor*>> res;
|
||||
for (Floorplan::Floor* f : map->floors) {
|
||||
for (Floorplan::AccessPoint* ap : f->accesspoints) {
|
||||
res.push_back(std::make_pair(ap,f));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/** get the Fingerprint-Loation for the given Name [if available] */
|
||||
static std::pair<Floorplan::FingerprintLocation*, Floorplan::Floor*> getFingerprintLocationByName(const Floorplan::IndoorMap* map, const std::string& name) {
|
||||
for (Floorplan::Floor* f : map->floors) {
|
||||
for (Floorplan::FingerprintLocation* fpl : f->fpLocations) {
|
||||
if (name == fpl->name) {
|
||||
return std::make_pair(fpl, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::make_pair(nullptr, nullptr);
|
||||
}
|
||||
|
||||
/** get all ground-truth points within the map as hash-map: id->pos */
|
||||
static std::unordered_map<int, Point3> getGroundTruthPoints(const Floorplan::IndoorMap* map) {
|
||||
std::unordered_map<int, Point3> res;
|
||||
for (const Floorplan::Floor* f : map->floors) {
|
||||
for (const Floorplan::GroundTruthPoint* gtp : f->gtpoints) {
|
||||
res[gtp->id] = gtp->getPosition(*f);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/** get all ground-truth points, identified by the given indices */
|
||||
static std::vector<Point3> getGroundTruth(const Floorplan::IndoorMap* map, const std::vector<int> indices) {
|
||||
|
||||
// get a map id->pos for all ground-truth-points within the map
|
||||
const std::unordered_map<int, Point3> src = getGroundTruthPoints(map);
|
||||
std::vector<Point3> res;
|
||||
for (const int idx : indices) {
|
||||
const auto& it = src.find(idx);
|
||||
if (it == src.end()) {throw Exception("map does not contain a ground-truth-point with ID " + std::to_string(idx));}
|
||||
res.push_back(it->second);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/** align all floorplan values to the given grid size. needed for the grid factory */
|
||||
|
||||
278
floorplan/v2/FloorplanLINT.h
Normal file
278
floorplan/v2/FloorplanLINT.h
Normal file
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef FLOORPLANLINT_H
|
||||
#define FLOORPLANLINT_H
|
||||
|
||||
#include "Floorplan.h"
|
||||
#include "../../geo/BBox2.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
namespace Floorplan {
|
||||
|
||||
class LINT {
|
||||
|
||||
public:
|
||||
|
||||
/** possible issue types */
|
||||
enum class Type {
|
||||
WARN,
|
||||
ERR,
|
||||
};
|
||||
|
||||
/** type -> string */
|
||||
static std::string getTypeStr(const Type t) {
|
||||
switch(t) {
|
||||
case Type::WARN: return "WARNING";
|
||||
case Type::ERR: return "ERROR";
|
||||
default: throw Exception("code error. invalid type. todo!");
|
||||
}
|
||||
}
|
||||
|
||||
/** a detected issue */
|
||||
struct Issue {
|
||||
|
||||
Type type;
|
||||
const Floor* floor;
|
||||
std::string desc;
|
||||
|
||||
Issue(const Type type, const Floor* floor, const std::string& desc) : type(type), floor(floor), desc(desc) {;}
|
||||
|
||||
/** issue to string */
|
||||
std::string asString() const {
|
||||
return getTypeStr(type) + ": " + "floor '" + floor->name + "': " + desc;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
using Issues = std::vector<Issue>;
|
||||
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/** throw in case of errors within the map */
|
||||
static void assertOK(IndoorMap* map) {
|
||||
|
||||
const Issues issues = check(map);
|
||||
int err = 0;
|
||||
for (const Issue& i : issues) {
|
||||
std::cout << i.asString() << std::endl;
|
||||
if (i.type == Type::ERR) {++err;}
|
||||
}
|
||||
if (err > 0) {
|
||||
// throw Exception("detected floorplan errors");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** get all errors within the map */
|
||||
static Issues check(IndoorMap* map) {
|
||||
|
||||
Issues res;
|
||||
|
||||
for (const Floor* floor : map->floors) {
|
||||
|
||||
// outline present?
|
||||
if (floor->outline.empty()) {
|
||||
res.push_back(Issue(Type::ERR, floor, "has no outline"));
|
||||
}
|
||||
|
||||
// check outline
|
||||
for (const FloorOutlinePolygon* poly : floor->outline) {
|
||||
checkOutline(res, floor, poly);
|
||||
}
|
||||
|
||||
// check obstacles
|
||||
for (const FloorObstacle* obs : floor->obstacles) {
|
||||
checkObstacle(res, map, floor, obs);
|
||||
}
|
||||
|
||||
// check fingerprints
|
||||
for (const FingerprintLocation* fpl : floor->fpLocations) {
|
||||
checkFingerprintLoc(res, floor, fpl);
|
||||
}
|
||||
|
||||
// check stairs
|
||||
for (const Stair* s : floor->stairs) {
|
||||
checkStair(res, map, floor, s);
|
||||
}
|
||||
|
||||
// check elevators
|
||||
for (const Elevator* e : floor->elevators) {
|
||||
checkElevator(res, map, floor, e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// done
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/** check a floor's outline */
|
||||
static void checkOutline(Issues& res, const Floor* floor, const FloorOutlinePolygon* poly) {
|
||||
|
||||
// number of points valid?
|
||||
if (poly->poly.points.size() < 3) {res.push_back(Issue(Type::ERR, floor, "' outline '" + poly->name + "' needs at least 3 edges"));}
|
||||
if (poly->poly.points.size() > 1024) {res.push_back(Issue(Type::ERR, floor, "' outline '" + poly->name + "' has too many edges"));}
|
||||
|
||||
// outline size [bbox] valid?
|
||||
BBox2 outline;
|
||||
for (const Point2 pt : poly->poly.points) { outline.add(pt); }
|
||||
const Point2 size = outline.getSize();
|
||||
if (size.x < 1.0 || size.y < 1.0) {res.push_back(Issue(Type::ERR, floor, "' outline '" + poly->name + "' seems too small"));}
|
||||
|
||||
}
|
||||
|
||||
/** check walls, doors, ... */
|
||||
static void checkObstacle(Issues& res, const IndoorMap* map, const Floor* floor, const FloorObstacle* fo) {
|
||||
|
||||
// line? -> check
|
||||
const FloorObstacleLine* line = dynamic_cast<const FloorObstacleLine*>(fo);
|
||||
if (line) {
|
||||
const float len_m = line->from.getDistance(line->to);
|
||||
if (len_m < 0.15) {
|
||||
res.push_back(Issue(Type::WARN, floor, "' line-obstacle is too short: " + std::to_string(len_m) + " meter from " + line->from.asString() + " to " + line->to.asString()));
|
||||
}
|
||||
}
|
||||
|
||||
// door? -> check
|
||||
const FloorObstacleDoor* door = dynamic_cast<const FloorObstacleDoor*>(fo);
|
||||
if (door) {
|
||||
const float len_m = door->from.getDistance(door->to);
|
||||
if (len_m < 0.40) {
|
||||
res.push_back(Issue(Type::ERR, floor, "' door is too narrow: " + std::to_string(len_m) + " meter from " + door->from.asString() + " to " + door->to.asString()));
|
||||
}
|
||||
|
||||
#pragma message ("TODO!")
|
||||
// try {
|
||||
// Ray3D::ModelFactory fac(map);
|
||||
// fac.getDoorAbove(floor, door);
|
||||
// } catch (Exception e) {
|
||||
// res.push_back(Issue(Type::ERR, floor, std::string(e.what()) + "[from" + door->from.asString() + " to " + door->to.asString() + "]"));
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
// pillar? -> check
|
||||
const FloorObstacleCircle* circle = dynamic_cast<const FloorObstacleCircle*>(fo);
|
||||
if (circle) {
|
||||
const float len_m = circle->radius;
|
||||
if (len_m < 0.20) {
|
||||
res.push_back(Issue(Type::ERR, floor, "' pillar is too narrow: " + std::to_string(len_m) + " meter at " + circle->center.asString()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void checkFingerprintLoc(Issues& res, const Floor* floor, const FingerprintLocation* fpl) {
|
||||
|
||||
const std::string note = "does it belong to a stair? if so: fine! Note: fingerprints are currently measured using smartphones and smartphone are held by the pedestian. thus: fingerprints are ~1.3 meter above ground";
|
||||
if (fpl->heightAboveFloor < 0.8) {
|
||||
res.push_back(Issue(Type::ERR, floor, std::string() + "fingerprint " + fpl->name + " @ " + fpl->getPosition(*floor).asString() + " is too near to the floor. " + note));
|
||||
} else if (fpl->heightAboveFloor > 2.0) {
|
||||
res.push_back(Issue(Type::WARN, floor, std::string() + "fingerprint " + fpl->name + " @ " + fpl->getPosition(*floor).asString() + " is too high above the floor. " + note));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void checkStair(Issues& res, const IndoorMap* map, const Floor* floor, const Stair* stair) {
|
||||
|
||||
// list of all heights where there is a floor;
|
||||
std::vector<int> floorAtHeight_cm;
|
||||
for (const Floor* f : map->floors) {
|
||||
const int floorZ_cm = std::round(f->atHeight * 100);
|
||||
floorAtHeight_cm.push_back(floorZ_cm); // integer height in cm
|
||||
}
|
||||
|
||||
if (stair->getParts().empty()) {
|
||||
res.push_back(Issue(Type::ERR, floor, "stair does not contain any parts! [empty stair]"));
|
||||
return;
|
||||
}
|
||||
|
||||
const std::vector<Floorplan::StairPart> parts = stair->getParts();
|
||||
const std::vector<Floorplan::Quad3> quads = Floorplan::getQuads(parts, floor);
|
||||
|
||||
const Floorplan::Quad3& quadS = quads.front();
|
||||
const Floorplan::Quad3& quadE = quads.back();
|
||||
|
||||
// start == end?
|
||||
if (quadS.p1.z == quadE.p3.z) {
|
||||
res.push_back(Issue(Type::ERR, floor, "stair start and end must not belong to the same floor!"));
|
||||
}
|
||||
|
||||
// disconnected start? (MUST belong to the floor the stair is attached to)
|
||||
if (quadS.p1.z != floor->getStartingZ()) {
|
||||
res.push_back(Issue(Type::ERR, floor, "stair is not connected to the starting floor's ground! [open stair start]"));
|
||||
}
|
||||
|
||||
// disconnected end? (must be long to ANY other floor within the map)
|
||||
//if (quadE.p3.z != floor->getEndingZ()) {
|
||||
const int stairEndingZ_cm = std::round( quadE.p3.z * 100 );
|
||||
if(std::find(floorAtHeight_cm.begin(), floorAtHeight_cm.end(), stairEndingZ_cm) == floorAtHeight_cm.end()) {
|
||||
res.push_back(Issue(Type::ERR, floor, "stair is not connected to the ending floor's ground! [open stair end]"));
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < (int) parts.size(); ++i) {
|
||||
|
||||
//const Floorplan::Quad3& quad = quads[i];
|
||||
|
||||
// disconnected within?
|
||||
if (i > 0) {
|
||||
if (quads[i-1].p4.z != quads[i-0].p1.z) {
|
||||
res.push_back(Issue(Type::ERR, floor, "stair is disconnected within!"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void checkElevator(Issues& res, const IndoorMap* map, const Floor* floor, const Elevator* e) {
|
||||
|
||||
if (e->depth < 0.5) {
|
||||
res.push_back(Issue(Type::ERR, floor, "elevator's @" + e->center.asString() + ": depth is too small: " + std::to_string(e->depth) + "m"));
|
||||
}
|
||||
|
||||
if (e->width < 0.5) {
|
||||
res.push_back(Issue(Type::ERR, floor, "elevator's @" + e->center.asString() + ": width is too small: " + std::to_string(e->width) + "m"));
|
||||
}
|
||||
|
||||
if (e->height_m < 0.1) {
|
||||
res.push_back(Issue(Type::ERR, floor, "elevator's @" + e->center.asString() + ": height is too small: " + std::to_string(e->height_m) + "m"));
|
||||
}
|
||||
|
||||
// list of all heights where there is a floor;
|
||||
std::vector<int> floorAtHeight_cm;
|
||||
for (const Floor* f : map->floors) {
|
||||
const int floorZ_cm = std::round(f->atHeight * 100);
|
||||
floorAtHeight_cm.push_back(floorZ_cm); // integer height in cm
|
||||
}
|
||||
|
||||
// disconnected end? (must be long to ANY other floor within the map)
|
||||
const int elevEndZ_cm = std::round( (floor->getStartingZ() + e->height_m) * 100 );
|
||||
if(std::find(floorAtHeight_cm.begin(), floorAtHeight_cm.end(), elevEndZ_cm) == floorAtHeight_cm.end()) {
|
||||
res.push_back(Issue(Type::ERR, floor, "elevator @" + e->center.asString() + " is not connected to the ending floor's ground! [open elevator end]"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // FLOORPLANLINT_H
|
||||
@@ -1,9 +1,21 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Einige Aenderungen beigetragen von Toni Fetzer
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef FLOORPLANREADER_H
|
||||
#define FLOORPLANREADER_H
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "Floorplan.h"
|
||||
#include "FloorplanLINT.h"
|
||||
|
||||
#include "../../misc/Debug.h"
|
||||
#include "../../Assertions.h"
|
||||
@@ -36,10 +48,10 @@ namespace Floorplan {
|
||||
throw Exception(
|
||||
std::string() + "error while loading XML " + file + "\n" +
|
||||
((doc.GetErrorStr1()) ? (doc.GetErrorStr1()) : ("")) + "\n" +
|
||||
((doc.GetErrorStr2()) ? (doc.GetErrorStr2()) : (""))
|
||||
);
|
||||
((doc.GetErrorStr2()) ? (doc.GetErrorStr2()) : ("")));
|
||||
}
|
||||
return parse(doc);
|
||||
IndoorMap* map = parse(doc);
|
||||
return map;
|
||||
}
|
||||
|
||||
/** read an IndoorMap from the given XMl-string */
|
||||
@@ -55,12 +67,13 @@ namespace Floorplan {
|
||||
((doc.GetErrorStr2()) ? (doc.GetErrorStr2()) : (""))
|
||||
);
|
||||
}
|
||||
return parse(doc);
|
||||
IndoorMap* map = parse(doc);
|
||||
return map;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
#define FOREACH_NODE(out, in) for( const XMLElem* out = (XMLElem*) in->FirstChild(); out; out = (XMLElem*) out->NextSibling() )
|
||||
#define FOREACH_NODE(out, in) for( const XMLElem* out = in->FirstChildElement(); out; out = out->NextSiblingElement() )
|
||||
|
||||
static void assertNode(const std::string& node, const XMLElem* el) {
|
||||
std::string err = std::string("unexpected node '") + el->Name() + "' expected '" + node + "'";
|
||||
@@ -69,7 +82,7 @@ namespace Floorplan {
|
||||
|
||||
/** parse the complete document */
|
||||
static IndoorMap* parse(tinyxml2::XMLDocument& doc) {
|
||||
return parseMap((XMLElem*)doc.FirstChild());
|
||||
return parseMap(doc.FirstChildElement());
|
||||
}
|
||||
|
||||
/** parse the <map> node */
|
||||
@@ -78,12 +91,36 @@ namespace Floorplan {
|
||||
IndoorMap* map = new IndoorMap();
|
||||
map->width = el->FloatAttribute("width");
|
||||
map->depth = el->FloatAttribute("depth");
|
||||
|
||||
FOREACH_NODE(n, el) {
|
||||
if (std::string("floors") == n->Name()) {map->floors = parseFloors(n);}
|
||||
if (std::string("floors") == n->Name()) {map->floors = parseFloors(n);}
|
||||
if (std::string("earthReg") == n->Name()) {map->earthReg = parseEarthReg(n);}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/** parse the <earthReg> node */
|
||||
static EarthRegistration parseEarthReg(const XMLElem* el) {
|
||||
EarthRegistration reg;
|
||||
FOREACH_NODE(n, el) {
|
||||
if (std::string("correspondences") == n->Name()) {
|
||||
FOREACH_NODE(n2, n) {
|
||||
if (std::string("point") == n2->Name()) {
|
||||
Floorplan::EarthPosMapPos* pos = new Floorplan::EarthPosMapPos();
|
||||
pos->posOnMap_m.x = n2->FloatAttribute("mx");
|
||||
pos->posOnMap_m.y = n2->FloatAttribute("my");
|
||||
pos->posOnMap_m.z = n2->FloatAttribute("mz");
|
||||
pos->posOnEarth.lat = n2->FloatAttribute("lat");
|
||||
pos->posOnEarth.lon = n2->FloatAttribute("lon");
|
||||
pos->posOnEarth.height = n2->FloatAttribute("alt");
|
||||
reg.correspondences.push_back(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return reg;
|
||||
}
|
||||
|
||||
/** parse the <floors> node */
|
||||
static std::vector<Floor*> parseFloors(const XMLElem* el) {
|
||||
std::vector<Floor*> floors;
|
||||
@@ -105,20 +142,21 @@ namespace Floorplan {
|
||||
if (std::string("obstacles") == n->Name()) {floor->obstacles = parseFloorObstacles(n);}
|
||||
if (std::string("accesspoints") == n->Name()) {floor->accesspoints = parseFloorAccessPoints(n);}
|
||||
if (std::string("beacons") == n->Name()) {floor->beacons = parseFloorBeacons(n);}
|
||||
if (std::string("fingerprints") == n->Name()) {floor->fpLocations = parseFingerprintLocations(n);}
|
||||
if (std::string("regions") == n->Name()) {floor->regions = parseFloorRegions(n);}
|
||||
if (std::string("underlays") == n->Name()) {floor->underlays = parseFloorUnderlays(n);}
|
||||
if (std::string("pois") == n->Name()) {floor->pois = parseFloorPOIs(n);}
|
||||
if (std::string("stairs") == n->Name()) {floor->stairs = parseFloorStairs(n);}
|
||||
if (std::string("elevators") == n->Name()) {floor->elevators = parseFloorElevators(n);}
|
||||
if (std::string("gtpoints") == n->Name()) {floor->gtpoints = parseFloorGroundTruthPoints(n);}
|
||||
if (std::string("gtpoints") == n->Name()) {floor->gtpoints = parseFloorGroundTruthPoints(n);}
|
||||
}
|
||||
return floor;
|
||||
}
|
||||
|
||||
|
||||
/** parse the <elevators> tag */
|
||||
static std::vector<Elevator*> parseFloorElevators(const XMLElem* el) {
|
||||
std::vector<Elevator*> vec;
|
||||
static FloorElevators parseFloorElevators(const XMLElem* el) {
|
||||
FloorElevators vec;
|
||||
FOREACH_NODE(n, el) {
|
||||
if (std::string("elevator") == n->Name()) { vec.push_back(parseFloorElevator(n)); }
|
||||
}
|
||||
@@ -126,8 +164,8 @@ namespace Floorplan {
|
||||
}
|
||||
|
||||
/** parse the <stairs> tag */
|
||||
static std::vector<Stair*> parseFloorStairs(const XMLElem* el) {
|
||||
std::vector<Stair*> vec;
|
||||
static FloorStairs parseFloorStairs(const XMLElem* el) {
|
||||
FloorStairs vec;
|
||||
FOREACH_NODE(n, el) {
|
||||
if (std::string("stair") == n->Name()) { vec.push_back(parseFloorStair(n)); }
|
||||
}
|
||||
@@ -140,6 +178,7 @@ namespace Floorplan {
|
||||
elev->center = Point2(el->FloatAttribute("cx"), el->FloatAttribute("cy"));
|
||||
elev->depth = el->FloatAttribute("depth");
|
||||
elev->width = el->FloatAttribute("width");
|
||||
elev->height_m = el->FloatAttribute("height");
|
||||
elev->rotation = el->FloatAttribute("rotation");
|
||||
return elev;
|
||||
}
|
||||
@@ -179,8 +218,8 @@ namespace Floorplan {
|
||||
|
||||
|
||||
/** parse the <pois> tag */
|
||||
static std::vector<POI*> parseFloorPOIs(const XMLElem* el) {
|
||||
std::vector<POI*> vec;
|
||||
static FloorPOIs parseFloorPOIs(const XMLElem* el) {
|
||||
FloorPOIs vec;
|
||||
FOREACH_NODE(n, el) {
|
||||
if (std::string("poi") == n->Name()) { vec.push_back(parseFloorPOI(n)); }
|
||||
}
|
||||
@@ -197,27 +236,26 @@ namespace Floorplan {
|
||||
}
|
||||
|
||||
|
||||
/** parse the <gtpoints> tag */
|
||||
static std::vector<GroundTruthPoint*> parseFloorGroundTruthPoints(const XMLElem* el) {
|
||||
std::vector<GroundTruthPoint*> vec;
|
||||
FOREACH_NODE(n, el) {
|
||||
if (std::string("gtpoint") == n->Name()) { vec.push_back(parseFloorGroundTruthPoint(n)); }
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
/** parse a <gtpoint> tag */
|
||||
static GroundTruthPoint* parseFloorGroundTruthPoint(const XMLElem* el) {
|
||||
GroundTruthPoint* gtp = new GroundTruthPoint();
|
||||
gtp->id = el->IntAttribute("id");
|
||||
gtp->pos = parsePoint2(el);
|
||||
return gtp;
|
||||
}
|
||||
/** parse the <gtpoints> tag */
|
||||
static FloorGroundTruthPoints parseFloorGroundTruthPoints(const XMLElem* el) {
|
||||
FloorGroundTruthPoints vec;
|
||||
FOREACH_NODE(n, el) {
|
||||
if (std::string("gtpoint") == n->Name()) { vec.push_back(parseFloorGroundTruthPoint(n)); }
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
/** parse a <gtpoint> tag */
|
||||
static GroundTruthPoint* parseFloorGroundTruthPoint(const XMLElem* el) {
|
||||
GroundTruthPoint* gtp = new GroundTruthPoint();
|
||||
gtp->id = el->IntAttribute("id");
|
||||
gtp->pos = parsePoint3(el);
|
||||
return gtp;
|
||||
}
|
||||
|
||||
/** parse the <accesspoints> tag */
|
||||
static std::vector<AccessPoint*> parseFloorAccessPoints(const XMLElem* el) {
|
||||
std::vector<AccessPoint*> vec;
|
||||
static FloorAccessPoints parseFloorAccessPoints(const XMLElem* el) {
|
||||
FloorAccessPoints vec;
|
||||
FOREACH_NODE(n, el) {
|
||||
if (std::string("accesspoint") == n->Name()) { vec.push_back(parseAccessPoint(n)); }
|
||||
}
|
||||
@@ -265,7 +303,9 @@ namespace Floorplan {
|
||||
const XMLElem* sub = n->FirstChildElement();
|
||||
while(sub) {
|
||||
// <entry key="123">abc</entry>
|
||||
elem->add(sub->Attribute("key"), sub->FirstChild()->Value());
|
||||
const std::string key = sub->Attribute("key");
|
||||
const std::string val = sub->GetText();
|
||||
elem->add(key, val);
|
||||
sub = sub->NextSiblingElement();
|
||||
}
|
||||
return elem;
|
||||
@@ -287,8 +327,8 @@ namespace Floorplan {
|
||||
|
||||
|
||||
/** parse the <beacons> tag */
|
||||
static std::vector<Beacon*> parseFloorBeacons(const XMLElem* el) {
|
||||
std::vector<Beacon*> vec;
|
||||
static FloorBeacons parseFloorBeacons(const XMLElem* el) {
|
||||
FloorBeacons vec;
|
||||
FOREACH_NODE(n, el) {
|
||||
if (std::string("beacon") == n->Name()) { vec.push_back(parseBeacon(n)); }
|
||||
}
|
||||
@@ -310,9 +350,31 @@ namespace Floorplan {
|
||||
return b;
|
||||
}
|
||||
|
||||
/** parse <fingerprints> <location>s */
|
||||
static FloorFingerprintLocations parseFingerprintLocations(const XMLElem* el) {
|
||||
assertNode("fingerprints", el);
|
||||
FloorFingerprintLocations vec;
|
||||
FOREACH_NODE(n, el) {
|
||||
if (std::string("location") == n->Name()) { vec.push_back(parseFingerprintLocation(n)); }
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
static std::vector<FloorRegion*> parseFloorRegions(const XMLElem* el) {
|
||||
std::vector<FloorRegion*> vec;
|
||||
/** parse one fingerprint <location> */
|
||||
static FingerprintLocation* parseFingerprintLocation(const XMLElem* n) {
|
||||
assertNode("location", n);
|
||||
FingerprintLocation* fpl = new FingerprintLocation();
|
||||
fpl->name = n->Attribute("name");
|
||||
fpl->posOnFloor.x = n->FloatAttribute("x");
|
||||
fpl->posOnFloor.y = n->FloatAttribute("y");
|
||||
fpl->heightAboveFloor = n->FloatAttribute("dz");
|
||||
const XMLElem* meta = n->FirstChildElement("meta");
|
||||
if (meta) {fpl->setMeta(parseMetaElement(meta));}
|
||||
return fpl;
|
||||
}
|
||||
|
||||
static FloorRegions parseFloorRegions(const XMLElem* el) {
|
||||
FloorRegions vec;
|
||||
FOREACH_NODE(n, el) {
|
||||
if (std::string("region") == n->Name()) { vec.push_back(parseFloorRegion(n)); }
|
||||
}
|
||||
@@ -328,29 +390,81 @@ namespace Floorplan {
|
||||
}
|
||||
|
||||
/** parse the <obstacles> tag */
|
||||
static std::vector<FloorObstacle*> parseFloorObstacles(const XMLElem* el) {
|
||||
static FloorObstacles parseFloorObstacles(const XMLElem* el) {
|
||||
assertNode("obstacles", el);
|
||||
std::vector<FloorObstacle*> obstacles;
|
||||
FloorObstacles obstacles;
|
||||
FOREACH_NODE(n, el) {
|
||||
// if (std::string("wall") == n->Name()) {obstacles.push_back(parseFloorObstacleWall(n));}
|
||||
// if (std::string("door") == n->Name()) {obstacles.push_back(parseFloorObstacleDoor(n));}
|
||||
// if (std::string("window") == n->Name()) {obstacles.push_back(parseFloorObstacleWindow(n));}
|
||||
// if (std::string("pillar") == n->Name()) {obstacles.push_back(parseFloorObstaclePillar(n));}
|
||||
//if (std::string("obstacle") == n->Name()) {obstacles.push_back(parseFloorObstacleLine(n));} // OLD
|
||||
if (std::string("wall") == n->Name()) {obstacles.push_back(parseFloorObstacleWall(n));}
|
||||
if (std::string("line") == n->Name()) {obstacles.push_back(parseFloorObstacleLine(n));}
|
||||
if (std::string("circle") == n->Name()) {obstacles.push_back(parseFloorObstacleCircle(n));}
|
||||
if (std::string("door") == n->Name()) {obstacles.push_back(parseFloorObstacleDoor(n));}
|
||||
if (std::string("object") == n->Name()) {obstacles.push_back(parseFloorObstacleObject(n));}
|
||||
}
|
||||
return obstacles;
|
||||
}
|
||||
|
||||
/** parse one wall */
|
||||
static FloorObstacleWall* parseFloorObstacleWall(const XMLElem* el) {
|
||||
|
||||
FloorObstacleWall* wall = new FloorObstacleWall(
|
||||
parseObstacleType(el->Attribute("type")),
|
||||
parseMaterial(el->Attribute("material")),
|
||||
el->FloatAttribute("x1"), el->FloatAttribute("y1"),
|
||||
el->FloatAttribute("x2"), el->FloatAttribute("y2"),
|
||||
(el->FloatAttribute("thickness") > 0) ? (el->FloatAttribute("thickness")) : (0.15), // default wall thickness in m
|
||||
el->FloatAttribute("height")
|
||||
);
|
||||
|
||||
// doors
|
||||
FOREACH_NODE(n, el) {
|
||||
if (std::string("door") == n->Name()) {
|
||||
FloorObstacleWallDoor* door = new FloorObstacleWallDoor(
|
||||
parseDoorType(n->Attribute("type")),
|
||||
parseMaterial(n->Attribute("material")),
|
||||
n->FloatAttribute("x01"),
|
||||
n->FloatAttribute("width"),
|
||||
n->FloatAttribute("height"),
|
||||
n->BoolAttribute("lr"),
|
||||
n->BoolAttribute("io")
|
||||
);
|
||||
wall->doors.push_back(door);
|
||||
}
|
||||
}
|
||||
|
||||
// windows
|
||||
FOREACH_NODE(n, el) {
|
||||
if (std::string("window") == n->Name()) {
|
||||
FloorObstacleWallWindow* win = new FloorObstacleWallWindow(
|
||||
parseWindowType(n->Attribute("type")),
|
||||
parseMaterial(n->Attribute("material")),
|
||||
n->FloatAttribute("x01"),
|
||||
n->FloatAttribute("y"),
|
||||
n->FloatAttribute("width"),
|
||||
n->FloatAttribute("height"),
|
||||
n->BoolAttribute("io")
|
||||
);
|
||||
wall->windows.push_back(win);
|
||||
}
|
||||
}
|
||||
|
||||
return wall;
|
||||
|
||||
}
|
||||
|
||||
/** parse one line */
|
||||
static FloorObstacleLine* parseFloorObstacleLine(const XMLElem* el) {
|
||||
return new FloorObstacleLine(
|
||||
parseObstacleType(el->Attribute("type")),
|
||||
parseMaterial(el->Attribute("material")),
|
||||
el->FloatAttribute("x1"), el->FloatAttribute("y1"),
|
||||
el->FloatAttribute("x2"), el->FloatAttribute("y2")
|
||||
el->FloatAttribute("x2"), el->FloatAttribute("y2"),
|
||||
(el->FloatAttribute("thickness") > 0) ? (el->FloatAttribute("thickness")) : (0.15), // default wall thickness in m
|
||||
el->FloatAttribute("height")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -359,7 +473,8 @@ namespace Floorplan {
|
||||
return new FloorObstacleCircle(
|
||||
parseMaterial(el->Attribute("material")),
|
||||
el->FloatAttribute("cx"), el->FloatAttribute("cy"),
|
||||
el->FloatAttribute("radius")
|
||||
el->FloatAttribute("radius"),
|
||||
el->FloatAttribute("height")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -374,12 +489,22 @@ namespace Floorplan {
|
||||
);
|
||||
}
|
||||
|
||||
/** parse one object */
|
||||
static FloorObstacleObject* parseFloorObstacleObject(const XMLElem* el) {
|
||||
return new FloorObstacleObject(
|
||||
el->Attribute("file"),
|
||||
Point3(el->FloatAttribute("x"), el->FloatAttribute("y"), el->FloatAttribute("z")),
|
||||
Point3(el->FloatAttribute("rx", 0), el->FloatAttribute("ry", 0), el->FloatAttribute("rz", 0)),
|
||||
Point3(el->FloatAttribute("sx", 1), el->FloatAttribute("sy", 1), el->FloatAttribute("sz", 1))
|
||||
);
|
||||
}
|
||||
|
||||
/** parse a floor's <outline> tag */
|
||||
static FloorOutline parseFloorOutline(const XMLElem* el) {
|
||||
FloorOutline outline;
|
||||
FOREACH_NODE(n, el) {
|
||||
if (std::string("polygon") == n->Name()) {
|
||||
outline.push_back(parseFloorPolygon(n)); // TODO
|
||||
outline.push_back(parseFloorPolygon(n));
|
||||
}
|
||||
}
|
||||
return outline;
|
||||
@@ -389,6 +514,7 @@ namespace Floorplan {
|
||||
static FloorOutlinePolygon* parseFloorPolygon(const XMLElem* el) {
|
||||
FloorOutlinePolygon* poly = new FloorOutlinePolygon();
|
||||
poly->name = el->Attribute("name");
|
||||
poly->outdoor = el->BoolAttribute("outdoor");
|
||||
poly->method = parseOutlineMethod(el->Attribute("method"));
|
||||
poly->poly = parsePoly2(el);
|
||||
return poly;
|
||||
@@ -435,6 +561,10 @@ namespace Floorplan {
|
||||
try { return (DoorType) std::stoi(type); } catch (...) { return DoorType::UNKNOWN; }
|
||||
}
|
||||
|
||||
static WindowType parseWindowType(const std::string type) {
|
||||
try { return (WindowType) std::stoi(type); } catch (...) { return WindowType::UNKNOWN; }
|
||||
}
|
||||
|
||||
static Material parseMaterial(const std::string material) {
|
||||
try {
|
||||
return (Material) std::stoi(material);
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Einige Aenderungen beigetragen von Toni Fetzer
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef FLOORPLANWRITER_H
|
||||
#define FLOORPLANWRITER_H
|
||||
|
||||
@@ -38,6 +49,7 @@ namespace Floorplan {
|
||||
private:
|
||||
|
||||
static std::string toString(const ObstacleType t) { return std::to_string((int)t); }
|
||||
static std::string toString(const WindowType t) { return std::to_string((int)t); }
|
||||
static std::string toString(const DoorType t) { return std::to_string((int)t); }
|
||||
static std::string toString(const Material m) { return std::to_string((int)m); }
|
||||
static std::string toString(const OutlineMethod m) { return std::to_string((int)m); }
|
||||
@@ -49,11 +61,34 @@ namespace Floorplan {
|
||||
root->SetAttribute("width", map->width);
|
||||
root->SetAttribute("depth", map->depth);
|
||||
|
||||
// add earth registration to the map
|
||||
addEarthReg(doc, root, map);
|
||||
|
||||
// add all floors to the map
|
||||
addFloors(doc, root, map);
|
||||
|
||||
}
|
||||
|
||||
/** add earth registration to the map */
|
||||
static void addEarthReg(XMLDoc& doc, XMLElem* root, const IndoorMap* map) {
|
||||
XMLElem* earthReg = doc.NewElement("earthReg"); {
|
||||
XMLElem* correspondences = doc.NewElement("correspondences");
|
||||
for (const Floorplan::EarthPosMapPos* reg : map->earthReg.correspondences) {
|
||||
XMLElem* point = doc.NewElement("point");
|
||||
point->SetAttribute("lat", reg->posOnEarth.lat);
|
||||
point->SetAttribute("lon", reg->posOnEarth.lon);
|
||||
point->SetAttribute("alt", reg->posOnEarth.height);
|
||||
point->SetAttribute("mx", reg->posOnMap_m.x);
|
||||
point->SetAttribute("my", reg->posOnMap_m.y);
|
||||
point->SetAttribute("mz", reg->posOnMap_m.z);
|
||||
correspondences->InsertEndChild(point);
|
||||
}
|
||||
earthReg->InsertEndChild(correspondences);
|
||||
} root->InsertEndChild(earthReg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** add all floors to the map */
|
||||
static void addFloors(XMLDoc& doc, XMLElem* root, const IndoorMap* map) {
|
||||
XMLElem* floors = doc.NewElement("floors");
|
||||
@@ -92,6 +127,7 @@ namespace Floorplan {
|
||||
elem->SetAttribute("cy", elevator->center.y);
|
||||
elem->SetAttribute("width", elevator->width);
|
||||
elem->SetAttribute("depth", elevator->depth);
|
||||
elem->SetAttribute("height", elevator->height_m);
|
||||
elem->SetAttribute("rotation", elevator->rotation);
|
||||
elevators->InsertEndChild(elem);
|
||||
}
|
||||
@@ -131,6 +167,7 @@ namespace Floorplan {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** add all sorts of POI to the floor */
|
||||
static void addFloorPOI(XMLDoc& doc, XMLElem* floor, const Floor* mf) {
|
||||
|
||||
@@ -145,15 +182,16 @@ namespace Floorplan {
|
||||
}
|
||||
floor->InsertEndChild(pois);
|
||||
|
||||
XMLElem* gtpoints = doc.NewElement("gtpoints");
|
||||
for (const GroundTruthPoint* gtp : mf->gtpoints) {
|
||||
XMLElem* elem = doc.NewElement("gtpoint");
|
||||
elem->SetAttribute("id", gtp->id);
|
||||
elem->SetAttribute("x", gtp->pos.x);
|
||||
elem->SetAttribute("y", gtp->pos.y);
|
||||
gtpoints->InsertEndChild(elem);
|
||||
}
|
||||
floor->InsertEndChild(gtpoints);
|
||||
XMLElem* gtpoints = doc.NewElement("gtpoints");
|
||||
for (const GroundTruthPoint* gtp : mf->gtpoints) {
|
||||
XMLElem* elem = doc.NewElement("gtpoint");
|
||||
elem->SetAttribute("id", gtp->id);
|
||||
elem->SetAttribute("x", gtp->pos.x);
|
||||
elem->SetAttribute("y", gtp->pos.y);
|
||||
elem->SetAttribute("z", gtp->pos.z);
|
||||
gtpoints->InsertEndChild(elem);
|
||||
}
|
||||
floor->InsertEndChild(gtpoints);
|
||||
|
||||
XMLElem* accesspoints = doc.NewElement("accesspoints");
|
||||
for (const AccessPoint* ap : mf->accesspoints) {
|
||||
@@ -190,6 +228,18 @@ namespace Floorplan {
|
||||
floor->InsertEndChild(beacons);
|
||||
|
||||
|
||||
XMLElem* fingerprints = doc.NewElement("fingerprints");
|
||||
for (const FingerprintLocation* fpl : mf->fpLocations) {
|
||||
XMLElem* efpl = doc.NewElement("location");
|
||||
efpl->SetAttribute("name", fpl->name.c_str());
|
||||
efpl->SetAttribute("x", fpl->posOnFloor.x);
|
||||
efpl->SetAttribute("y", fpl->posOnFloor.y);
|
||||
efpl->SetAttribute("dz", fpl->heightAboveFloor);
|
||||
addMetaElement(doc, efpl, fpl->getMeta());
|
||||
fingerprints->InsertEndChild(efpl);
|
||||
}
|
||||
floor->InsertEndChild(fingerprints);
|
||||
|
||||
}
|
||||
|
||||
static void addFloorOutline(XMLDoc& doc, XMLElem* floor, const Floor* mf) {
|
||||
@@ -214,6 +264,7 @@ namespace Floorplan {
|
||||
XMLElem* polygon = doc.NewElement("polygon");
|
||||
polygon->SetAttribute("name", poly->name.c_str());
|
||||
polygon->SetAttribute("method", method.c_str());
|
||||
polygon->SetAttribute("outdoor", poly->outdoor);
|
||||
dst->InsertEndChild(polygon);
|
||||
|
||||
for (Point2 p : poly->poly.points) {
|
||||
@@ -272,10 +323,14 @@ namespace Floorplan {
|
||||
for (FloorObstacle* fo : mf->obstacles) {
|
||||
if (dynamic_cast<FloorObstacleLine*>(fo)) {
|
||||
addFloorObstacleLine(doc, obstacles, (FloorObstacleLine*)fo);
|
||||
} else if (dynamic_cast<FloorObstacleWall*>(fo)) {
|
||||
addFloorObstacleWall(doc, obstacles, (FloorObstacleWall*)fo);
|
||||
} else if (dynamic_cast<FloorObstacleCircle*>(fo)) {
|
||||
addFloorObstacleCircle(doc, obstacles, (FloorObstacleCircle*)fo);
|
||||
} else if (dynamic_cast<FloorObstacleDoor*>(fo)) {
|
||||
addFloorObstacleDoor(doc, obstacles, (FloorObstacleDoor*)fo);
|
||||
} else if (dynamic_cast<FloorObstacleObject*>(fo)) {
|
||||
addFloorObstacleObject(doc, obstacles, (FloorObstacleObject*)fo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,7 +338,56 @@ namespace Floorplan {
|
||||
|
||||
}
|
||||
|
||||
/** write a line obstacle (wall, handrail, ..) */
|
||||
/** write a wall obstacle (wall) */
|
||||
static void addFloorObstacleWall(XMLDoc& doc, XMLElem* obstacles, FloorObstacleWall* wall) {
|
||||
|
||||
XMLElem* oWall = doc.NewElement("wall");
|
||||
obstacles->InsertEndChild(oWall);
|
||||
|
||||
oWall->SetAttribute("material", toString(wall->material).c_str());
|
||||
oWall->SetAttribute("type", toString(wall->type).c_str());
|
||||
oWall->SetAttribute("x1", wall->from.x);
|
||||
oWall->SetAttribute("y1", wall->from.y);
|
||||
oWall->SetAttribute("x2", wall->to.x);
|
||||
oWall->SetAttribute("y2", wall->to.y);
|
||||
oWall->SetAttribute("thickness", wall->thickness_m);
|
||||
if (wall->height_m != 0) {oWall->SetAttribute("height", wall->height_m);}
|
||||
|
||||
// doors?
|
||||
for (const FloorObstacleWallDoor* door : wall->doors) {
|
||||
|
||||
XMLElem* oDoor = doc.NewElement("door");
|
||||
oWall->InsertEndChild(oDoor);
|
||||
|
||||
oDoor->SetAttribute("type", toString(door->type).c_str());
|
||||
oDoor->SetAttribute("material", toString(door->material).c_str());
|
||||
oDoor->SetAttribute("x01", door->atLinePos);
|
||||
oDoor->SetAttribute("width", door->width);
|
||||
oDoor->SetAttribute("height", door->height);
|
||||
oDoor->SetAttribute("io", door->inOut);
|
||||
oDoor->SetAttribute("lr", door->leftRight);
|
||||
|
||||
}
|
||||
|
||||
// windows?
|
||||
for (const FloorObstacleWallWindow* win : wall->windows) {
|
||||
|
||||
XMLElem* oDoor = doc.NewElement("window");
|
||||
oWall->InsertEndChild(oDoor);
|
||||
|
||||
oDoor->SetAttribute("type", toString(win->type).c_str());
|
||||
oDoor->SetAttribute("material", toString(win->material).c_str());
|
||||
oDoor->SetAttribute("x01", win->atLinePos);
|
||||
oDoor->SetAttribute("y", win->startsAtHeight);
|
||||
oDoor->SetAttribute("width", win->width);
|
||||
oDoor->SetAttribute("height", win->height);
|
||||
oDoor->SetAttribute("io", win->inOut);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** write a line obstacle (old walls, handrail, ..) */
|
||||
static void addFloorObstacleLine(XMLDoc& doc, XMLElem* obstacles, FloorObstacleLine* line) {
|
||||
XMLElem* obstacle = doc.NewElement("line");
|
||||
obstacle->SetAttribute("material", toString(line->material).c_str());
|
||||
@@ -292,6 +396,8 @@ namespace Floorplan {
|
||||
obstacle->SetAttribute("y1", line->from.y);
|
||||
obstacle->SetAttribute("x2", line->to.x);
|
||||
obstacle->SetAttribute("y2", line->to.y);
|
||||
obstacle->SetAttribute("thickness", line->thickness_m);
|
||||
if (line->height_m != 0) {obstacle->SetAttribute("height", line->height_m);}
|
||||
obstacles->InsertEndChild(obstacle);
|
||||
}
|
||||
|
||||
@@ -302,6 +408,7 @@ namespace Floorplan {
|
||||
obstacle->SetAttribute("cx", circle->center.x);
|
||||
obstacle->SetAttribute("cy", circle->center.y);
|
||||
obstacle->SetAttribute("radius", circle->radius);
|
||||
if (circle->height != 0) {obstacle->SetAttribute("height", circle->height);}
|
||||
obstacles->InsertEndChild(obstacle);
|
||||
}
|
||||
|
||||
@@ -319,6 +426,22 @@ namespace Floorplan {
|
||||
obstacles->InsertEndChild(obstacle);
|
||||
}
|
||||
|
||||
/** write an object-obstacle */
|
||||
static void addFloorObstacleObject(XMLDoc& doc, XMLElem* obstacles, FloorObstacleObject* obj) {
|
||||
XMLElem* obstacle = doc.NewElement("object");
|
||||
obstacle->SetAttribute("file", obj->file.c_str());
|
||||
obstacle->SetAttribute("x", obj->pos.x);
|
||||
obstacle->SetAttribute("y", obj->pos.y);
|
||||
obstacle->SetAttribute("z", obj->pos.z);
|
||||
if (obj->rot.x != 0) {obstacle->SetAttribute("rx", obj->rot.x);}
|
||||
if (obj->rot.y != 0) {obstacle->SetAttribute("ry", obj->rot.y);}
|
||||
if (obj->rot.z != 0) {obstacle->SetAttribute("rz", obj->rot.z);}
|
||||
if (obj->scale.x != 1) {obstacle->SetAttribute("sx", obj->scale.x);}
|
||||
if (obj->scale.y != 1) {obstacle->SetAttribute("sy", obj->scale.y);}
|
||||
if (obj->scale.z != 1) {obstacle->SetAttribute("sz", obj->scale.z);}
|
||||
obstacles->InsertEndChild(obstacle);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
21
geo/Angle.h
21
geo/Angle.h
@@ -1,9 +1,20 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef ANGLE_H
|
||||
#define ANGLE_H
|
||||
|
||||
#include <cmath>
|
||||
#include "../Assertions.h"
|
||||
#include "Point2.h"
|
||||
#include "../math/speed.h"
|
||||
|
||||
#define PI ((float) M_PI)
|
||||
|
||||
@@ -29,6 +40,13 @@ public:
|
||||
return radToDeg(getRAD_2PI(x1,y1,x2,y2));
|
||||
}
|
||||
|
||||
/** ensure the given radians-value is within [0:2pi] */
|
||||
static float makeSafe_2PI(float rad) {
|
||||
while(rad < 0) {rad += 2*PI;}
|
||||
while(rad >= 2*PI) {rad -= 2*PI;}
|
||||
return rad;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gets the angular difference between
|
||||
@@ -44,13 +62,14 @@ public:
|
||||
|
||||
/**
|
||||
* gets the angular difference between
|
||||
* "angular change from r1 to r2"
|
||||
* - the given radians [0:2PI]
|
||||
* - as a change-in-direction between [-PI:+PI]
|
||||
*/
|
||||
static float getSignedDiffRAD_2PI(const float r1, const float r2) {
|
||||
Assert::isBetween(r1, 0.0f, (float)(2*PI), "r1 out of bounds"); // [0:360] deg
|
||||
Assert::isBetween(r2, 0.0f, (float)(2*PI), "r2 out of bounds"); // [0:360] deg
|
||||
float diff = r1-r2;
|
||||
float diff = r2-r1;
|
||||
if (diff > +PI) {diff = -(2*PI - diff);}
|
||||
else if (diff < -PI) {diff = +(2*PI + diff);}
|
||||
Assert::isBetween(diff, -PI, (float)(+PI), "result out of bounds"); // [-180:+180] deg
|
||||
|
||||
117
geo/BBox2.h
117
geo/BBox2.h
@@ -1,15 +1,27 @@
|
||||
#ifndef BBOX2_H
|
||||
#define BBOX2_H
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Einige Aenderungen beigetragen von Toni Fetzer
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef GEO_BBOX2_H
|
||||
#define GEO_BBOX2_H
|
||||
|
||||
#include "Point2.h"
|
||||
#include "Line2.h"
|
||||
#include <vector>
|
||||
|
||||
class BBox2 {
|
||||
|
||||
protected:
|
||||
|
||||
static constexpr float MAX = +99999;
|
||||
static constexpr float MIN = -99999;
|
||||
static constexpr float MAX = +99999999;
|
||||
static constexpr float MIN = -99999999;
|
||||
|
||||
/** minimum */
|
||||
Point2 p1;
|
||||
@@ -25,19 +37,30 @@ public:
|
||||
/** ctor */
|
||||
BBox2(const Point2& p1, const Point2& p2) : p1(p1), p2(p2) {;}
|
||||
|
||||
/** ctor */
|
||||
BBox2(const float x1, const float y1, const float x2, const float y2) : p1(x1,y1), p2(x2,y2) {;}
|
||||
|
||||
/** adjust the bounding-box by adding this point */
|
||||
void add(const Point2& p) {
|
||||
add(p.x, p.y);
|
||||
}
|
||||
|
||||
if (p.x > p2.x) {p2.x = p.x;}
|
||||
if (p.y > p2.y) {p2.y = p.y;}
|
||||
/** adjust the bounding-box by adding this point */
|
||||
void add(const float x, const float y) {
|
||||
|
||||
if (p.x < p1.x) {p1.x = p.x;}
|
||||
if (p.y < p1.y) {p1.y = p.y;}
|
||||
if (x > p2.x) {p2.x = x;}
|
||||
if (y > p2.y) {p2.y = y;}
|
||||
|
||||
if (x < p1.x) {p1.x = x;}
|
||||
if (y < p1.y) {p1.y = y;}
|
||||
|
||||
}
|
||||
|
||||
/** the area spanned by the bbox */
|
||||
float getArea() const {return getSize().x * getSize().y;}
|
||||
|
||||
/** returns true if the bbox is not yet configured */
|
||||
const bool isInvalid() const {
|
||||
bool isInvalid() const {
|
||||
return p1.x == MAX || p1.y == MAX || p2.x == MIN || p2.y == MIN;
|
||||
}
|
||||
|
||||
@@ -47,10 +70,19 @@ public:
|
||||
/** get the bbox's maximum */
|
||||
const Point2& getMax() const {return p2;}
|
||||
|
||||
/** get the bbox's size [max-min] */
|
||||
const Point2 getSize() const {return p2-p1;}
|
||||
|
||||
/** get the bbox's center point */
|
||||
Point2 getCenter() const { return (p1+p2) / 2; }
|
||||
|
||||
|
||||
Point2 getCorner1() const {return Point2(p1.x, p1.y);}
|
||||
Point2 getCorner2() const {return Point2(p2.x, p1.y);}
|
||||
Point2 getCorner3() const {return Point2(p1.x, p2.y);}
|
||||
Point2 getCorner4() const {return Point2(p2.x, p2.y);}
|
||||
|
||||
|
||||
/** equal? */
|
||||
bool operator == (const BBox2& o) const {
|
||||
return (p1.x == o.p1.x) &&
|
||||
@@ -59,6 +91,44 @@ public:
|
||||
(p2.y == o.p2.y);
|
||||
}
|
||||
|
||||
bool intersects(const BBox2& o) const {
|
||||
// TODO is this correct?
|
||||
if (o.p2.x < p1.x) {return false;}
|
||||
if (o.p1.x > p2.x) {return false;}
|
||||
if (o.p2.y < p1.y) {return false;}
|
||||
if (o.p1.y > p2.y) {return false;}
|
||||
return true;
|
||||
// return (p1.x <= o.p2.x) &&
|
||||
// (p1.y <= o.p2.y) &&
|
||||
// (p2.x >= o.p1.x) &&
|
||||
// (p2.y >= o.p1.y);
|
||||
}
|
||||
|
||||
BBox2 combine(const BBox2& o) {
|
||||
|
||||
// TODO is this correct?
|
||||
const float x1 = std::min(p1.x, o.p1.x);
|
||||
const float x2 = std::max(p2.x, o.p2.x);
|
||||
|
||||
const float y1 = std::min(p1.y, o.p1.y);
|
||||
const float y2 = std::max(p2.y, o.p2.y);
|
||||
|
||||
return BBox2(x1,y1, x2,y2);
|
||||
|
||||
}
|
||||
|
||||
BBox2 intersection(const BBox2& o) const {
|
||||
// TODO is this correct?
|
||||
const float x1 = std::max(p1.x, o.p1.x);
|
||||
const float x2 = std::min(p2.x, o.p2.x);
|
||||
|
||||
const float y1 = std::max(p1.y, o.p1.y);
|
||||
const float y2 = std::min(p2.y, o.p2.y);
|
||||
|
||||
return BBox2(x1,y1, x2,y2);
|
||||
|
||||
}
|
||||
|
||||
/** does the BBox intersect with the given line? */
|
||||
bool intersects (const Line2& l) const {
|
||||
const Line2 l1(p1.x, p1.y, p2.x, p1.y); // upper
|
||||
@@ -83,10 +153,14 @@ public:
|
||||
}
|
||||
|
||||
bool contains(const Point2& p) const {
|
||||
if (p.x < p1.x) {return false;}
|
||||
if (p.x > p2.x) {return false;}
|
||||
if (p.y < p1.y) {return false;}
|
||||
if (p.y > p2.y) {return false;}
|
||||
return contains(p.x, p.y);
|
||||
}
|
||||
|
||||
bool contains(const float x, const float y) const {
|
||||
if (x < p1.x) {return false;}
|
||||
if (x > p2.x) {return false;}
|
||||
if (y < p1.y) {return false;}
|
||||
if (y > p2.y) {return false;}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -102,6 +176,21 @@ public:
|
||||
p2 += Point2(val, val); // increase maximum
|
||||
}
|
||||
|
||||
/** grow the bbox by the amount given for each dimension */
|
||||
void growRel(const float val) {
|
||||
const Point2 center = (p1+p2)/2;
|
||||
p1 += (p1-center)*val; // decrease minimum
|
||||
p2 += (p2-center)*val; // increase maximum
|
||||
}
|
||||
|
||||
|
||||
/** combine two bboxes */
|
||||
static BBox2 join(const BBox2& bb1, const BBox2& bb2) {
|
||||
const Point2 min( std::min(bb1.p1.x, bb2.p1.x), std::min(bb1.p1.y, bb2.p1.y) );
|
||||
const Point2 max( std::max(bb1.p2.x, bb2.p2.x), std::max(bb1.p2.y, bb2.p2.y) );
|
||||
return BBox2(min, max);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // BBOX2_H
|
||||
#endif // GEO_BBOX2_H
|
||||
|
||||
92
geo/BBox3.h
92
geo/BBox3.h
@@ -1,31 +1,49 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef BBOX3_H
|
||||
#define BBOX3_H
|
||||
|
||||
#include "Point3.h"
|
||||
|
||||
class BBox3 {
|
||||
template <typename Scalar>
|
||||
class _BBox3 {
|
||||
|
||||
private:
|
||||
|
||||
static constexpr float MAX = +99999;
|
||||
static constexpr float MIN = -99999;
|
||||
|
||||
/** minimum */
|
||||
Point3 p1;
|
||||
_Point3<Scalar> p1;
|
||||
|
||||
/** maximum */
|
||||
Point3 p2;
|
||||
_Point3<Scalar> p2;
|
||||
|
||||
public:
|
||||
|
||||
/** empty ctor */
|
||||
BBox3() : p1(MAX,MAX,MAX), p2(MIN,MIN,MIN) {;}
|
||||
/** empty ctor */
|
||||
_BBox3() : p1(std::numeric_limits<Scalar>::max(),
|
||||
std::numeric_limits<Scalar>::max(),
|
||||
std::numeric_limits<Scalar>::max()),
|
||||
p2(std::numeric_limits<Scalar>::lowest(),
|
||||
std::numeric_limits<Scalar>::lowest(),
|
||||
std::numeric_limits<Scalar>::lowest())
|
||||
{;}
|
||||
|
||||
/** ctor with min and max */
|
||||
BBox3(const Point3 min, const Point3 max) : p1(min), p2(max) {;}
|
||||
/** ctor with min and max */
|
||||
_BBox3(const _Point3<Scalar> min, const _Point3<Scalar> max) : p1(min), p2(max) {;}
|
||||
|
||||
/** create a bbox around the given point */
|
||||
static _BBox3 around(const _Point3<Scalar> center, const _Point3<Scalar> size) {
|
||||
return _BBox3(center-size/2, center+size/2);
|
||||
}
|
||||
|
||||
/** adjust the bounding-box by adding this point */
|
||||
void add(const Point3& p) {
|
||||
void add(const _Point3<Scalar>& p) {
|
||||
|
||||
if (p.x > p2.x) {p2.x = p.x;}
|
||||
if (p.y > p2.y) {p2.y = p.y;}
|
||||
@@ -38,19 +56,25 @@ public:
|
||||
}
|
||||
|
||||
/** add the given bounding-box to this one */
|
||||
void add(const BBox3& bb) {
|
||||
void add(const _BBox3& bb) {
|
||||
add(bb.getMin());
|
||||
add(bb.getMax());
|
||||
}
|
||||
|
||||
/** get the bbox's minimum */
|
||||
const Point3& getMin() const {return p1;}
|
||||
const _Point3<Scalar>& getMin() const {return p1;}
|
||||
|
||||
/** get the bbox's maximum */
|
||||
const Point3& getMax() const {return p2;}
|
||||
const _Point3<Scalar>& getMax() const {return p2;}
|
||||
|
||||
/** get the bbox's size */
|
||||
const _Point3<Scalar> getSize() const {return p2-p1;}
|
||||
|
||||
/** get the boox's center */
|
||||
const _Point3<Scalar> getCenter() const {return (p2+p1)/2;}
|
||||
|
||||
/** equal? */
|
||||
bool operator == (const BBox3& o) const {
|
||||
bool operator == (const _BBox3& o) const {
|
||||
return (p1.x == o.p1.x) &&
|
||||
(p1.y == o.p1.y) &&
|
||||
(p1.z == o.p1.z) &&
|
||||
@@ -60,35 +84,38 @@ public:
|
||||
}
|
||||
|
||||
/** shrink the bbox in each dimension by the given amount */
|
||||
void shrink(const float v) {
|
||||
shrink(Point3(v,v,v));
|
||||
void shrink(const Scalar v) {
|
||||
shrink(_Point3<Scalar>(v,v,v));
|
||||
}
|
||||
|
||||
/** shrink the bbox by the amount given for each dimension */
|
||||
void shrink(const Point3& p) {
|
||||
void shrink(const _Point3<Scalar>& p) {
|
||||
p1 += p; // increase minimum
|
||||
p2 -= p; // decrease maximum
|
||||
}
|
||||
|
||||
/** grow the bbox by the amount given for each dimension */
|
||||
void grow(const float v) {
|
||||
grow(Point3(v,v,v));
|
||||
void grow(const Scalar v) {
|
||||
grow(_Point3<Scalar>(v,v,v));
|
||||
}
|
||||
|
||||
/** grow the bbox by the amount given for each dimension */
|
||||
void grow(const Point3& p) {
|
||||
void grow(const _Point3<Scalar>& p) {
|
||||
p1 -= p; // decrease minimum
|
||||
p2 += p; // increase maximum
|
||||
}
|
||||
|
||||
/** set both, min/max z to the same value */
|
||||
void setZ(const float z) {
|
||||
p1.z = z;
|
||||
p2.z = z;
|
||||
}
|
||||
/** set both, min/max z to the same value */
|
||||
void setZ(const Scalar z) {
|
||||
p1.z = z;
|
||||
p2.z = z;
|
||||
}
|
||||
|
||||
void setMinZ(const Scalar z) {this->p1.z = z;}
|
||||
void setMaxZ(const Scalar z) {this->p2.z = z;}
|
||||
|
||||
/** does the bbox contain the given point? */
|
||||
bool contains(const Point3& p) const {
|
||||
bool contains(const _Point3<Scalar>& p) const {
|
||||
if (p.x < p1.x) {return false;}
|
||||
if (p.x > p2.x) {return false;}
|
||||
if (p.y < p1.y) {return false;}
|
||||
@@ -98,6 +125,15 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
/** combine two bboxes */
|
||||
static _BBox3 join(const _BBox3& bb1, const _BBox3& bb2) {
|
||||
const _Point3<Scalar> min( std::min(bb1.p1.x, bb2.p1.x), std::min(bb1.p1.y, bb2.p1.y), std::min(bb1.p1.z, bb2.p1.z) );
|
||||
const _Point3<Scalar> max( std::max(bb1.p2.x, bb2.p2.x), std::max(bb1.p2.y, bb2.p2.y), std::max(bb1.p2.z, bb2.p2.z) );
|
||||
return _BBox3(min,max);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
using BBox3 = _BBox3<float>;
|
||||
|
||||
#endif // BBOX3_H
|
||||
|
||||
55
geo/BBoxes3.h
Normal file
55
geo/BBoxes3.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef BBOXES3_H
|
||||
#define BBOXES3_H
|
||||
|
||||
#include "BBox3.h"
|
||||
#include <vector>
|
||||
|
||||
class BBoxes3 {
|
||||
|
||||
private:
|
||||
|
||||
/** all contained bboxes */
|
||||
std::vector<BBox3> bboxes;
|
||||
|
||||
public:
|
||||
|
||||
/** empty ctor */
|
||||
BBoxes3() {;}
|
||||
|
||||
/** ctor with entries */
|
||||
BBoxes3(const std::vector<BBox3>& bboxes) : bboxes(bboxes) {;}
|
||||
|
||||
|
||||
/** add the given bbox */
|
||||
void add(const BBox3& bbox) {
|
||||
bboxes.push_back(bbox);
|
||||
}
|
||||
|
||||
/** get all contained bboxes */
|
||||
const std::vector<BBox3>& get() const {
|
||||
return bboxes;
|
||||
}
|
||||
|
||||
/** does the compound contain the given point? */
|
||||
bool contains(const Point3& p) const {
|
||||
for (const BBox3& bb : bboxes) {
|
||||
if (bb.contains(p)) {return true;}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // BBOXES3_H
|
||||
333
geo/Circle2.h
Normal file
333
geo/Circle2.h
Normal file
@@ -0,0 +1,333 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef CIRCLE2_H
|
||||
#define CIRCLE2_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "Point2.h"
|
||||
#include "Ray2.h"
|
||||
#include "Line2.h"
|
||||
|
||||
#include "../Assertions.h"
|
||||
|
||||
//#include <KLib/misc/gnuplot/Gnuplot.h>
|
||||
//#include <KLib/misc/gnuplot/GnuplotPlot.h>
|
||||
//#include <KLib/misc/gnuplot/GnuplotPlotElementLines.h>
|
||||
//#include <KLib/misc/gnuplot/GnuplotPlotElementPoints.h>
|
||||
|
||||
struct Circle2 {
|
||||
|
||||
Point2 center;
|
||||
|
||||
float radius;
|
||||
|
||||
public:
|
||||
|
||||
/** empty ctor */
|
||||
Circle2() : center(), radius(0) {;}
|
||||
|
||||
/** ctor */
|
||||
Circle2(const Point2 center, const float radius) : center(center), radius(radius) {;}
|
||||
|
||||
|
||||
/** does this circle contain the given point? */
|
||||
bool contains(const Point2 p) const {
|
||||
return center.getDistance(p) <= radius;
|
||||
}
|
||||
|
||||
/** does this circle contain the given point? */
|
||||
bool containsAll(const std::vector<Point2>& pts) const {
|
||||
for (const Point2& p : pts) {
|
||||
if (!contains(p)) {return false;}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** get a point on the circle for the given radians */
|
||||
Point2 getPointAt(const float rad) const {
|
||||
return center + Point2(std::cos(rad), std::sin(rad)) * radius;
|
||||
}
|
||||
|
||||
|
||||
/** does this circle contain the given circle? */
|
||||
bool contains(const Circle2 c) const {
|
||||
return (center.getDistance(c.center)+c.radius) <= radius;
|
||||
}
|
||||
|
||||
/** does this circle intersect with the given ray? */
|
||||
bool intersects(const Ray2 ray) const {
|
||||
|
||||
// https://math.stackexchange.com/questions/311921/get-location-of-vector-circle-intersection
|
||||
const float a = ray.dir.x*ray.dir.x + ray.dir.y*ray.dir.y;
|
||||
const float b = 2 * ray.dir.x * (ray.start.x-center.x) + 2 * ray.dir.y * (ray.start.y - center.y);
|
||||
const float c = (ray.start.x-center.x) * (ray.start.x-center.x) + (ray.start.y - center.y)*(ray.start.y - center.y) - radius*radius;
|
||||
const float discr = b*b - 4*a*c;
|
||||
|
||||
return discr >= 0;
|
||||
|
||||
}
|
||||
|
||||
/** does this circle intersect with the given ray? */
|
||||
bool intersects(const Line2 line) const {
|
||||
|
||||
// https://math.stackexchange.com/questions/311921/get-location-of-vector-circle-intersection
|
||||
Point2 dir = line.p2 - line.p1;
|
||||
Point2 start = line.p1;
|
||||
const float a = dir.x*dir.x + dir.y*dir.y;
|
||||
const float b = 2 * dir.x * (start.x-center.x) + 2 * dir.y * (start.y - center.y);
|
||||
const float c = (start.x-center.x) * (start.x-center.x) + (start.y - center.y)*(start.y - center.y) - radius*radius;
|
||||
const float discr = b*b - 4*a*c;
|
||||
|
||||
if (discr < 0) {return false;}
|
||||
|
||||
const float t = (2*c) / (-b + std::sqrt(discr));
|
||||
return (t <= 1) && (t >= 0);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** configure this sphere to contain the given point-set */
|
||||
void adjustToPointSet(const std::vector<Point2>& lst) {
|
||||
|
||||
//adjustToPointSetAPX(lst);
|
||||
adjustToPointSetExact(lst);
|
||||
|
||||
// validate
|
||||
for (const Point2& p : lst) {
|
||||
Assert::isTrue(this->contains(p), "calculated circle seems incorrect");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** combine two circles into a new one containing both */
|
||||
static Circle2 join(const Circle2& a, const Circle2& b) {
|
||||
|
||||
// if one already contains the other, just return it as-is
|
||||
if (a.contains(b)) {return a;}
|
||||
if (b.contains(a)) {return b;}
|
||||
|
||||
// create both maximum ends
|
||||
const Point2 out1 = a.center + (a.center-b.center).normalized() * a.radius;
|
||||
const Point2 out2 = b.center + (b.center-a.center).normalized() * b.radius;
|
||||
|
||||
// center is within both ends, so is the radius
|
||||
Point2 newCen = (out1 + out2) / 2;
|
||||
float newRad = out1.getDistance(out2) / 2 * 1.0001; // slightly larger to prevent rounding issues
|
||||
Circle2 res(newCen, newRad);
|
||||
|
||||
// check
|
||||
Assert::isTrue(res.contains(a), "sphere joining error. base-spheres are not contained.");
|
||||
Assert::isTrue(res.contains(b), "sphere joining error. base-spheres are not contained.");
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
float getArea() const {
|
||||
return M_PI * (radius*radius);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/*
|
||||
void show(std::vector<Point2> pts, const Point2 P, const Point2 Q, const Point2 R = Point2(NAN, NAN)) {
|
||||
|
||||
static K::Gnuplot gp;
|
||||
|
||||
K::GnuplotPlot plot;
|
||||
K::GnuplotPlotElementPoints gPoints; plot.add(&gPoints); gPoints.setColor(K::GnuplotColor::fromHexStr("#0000ff")); gPoints.setPointSize(1);
|
||||
K::GnuplotPlotElementLines gLines; plot.add(&gLines);
|
||||
|
||||
for (const Point2 p : pts) {
|
||||
K::GnuplotPoint2 p2(p.x, p.y);
|
||||
gPoints.add(p2);
|
||||
}
|
||||
|
||||
K::GnuplotPoint2 gP(P.x, P.y);
|
||||
K::GnuplotPoint2 gQ(Q.x, Q.y);
|
||||
gLines.addSegment(gP, gQ);
|
||||
|
||||
K::GnuplotPoint2 gR(R.x, R.y);
|
||||
//gLines.addSegment(gP, gR);
|
||||
gLines.addSegment(gQ, gR);
|
||||
|
||||
for (float f = 0; f < M_PI*2; f += 0.1) {
|
||||
Point2 p = center + Point2(std::cos(f), std::sin(f)) * radius;
|
||||
K::GnuplotPoint2 gp (p.x, p.y);
|
||||
gLines.add(gp);
|
||||
}
|
||||
|
||||
gp << "set size ratio -1\n";
|
||||
gp.draw(plot);
|
||||
gp.flush();
|
||||
|
||||
int i = 0; (void) i;
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
// Graphic Gems 2 - Jon Rokne
|
||||
void adjustToPointSetExact(const std::vector<Point2>& _lst) {
|
||||
|
||||
if (_lst.size() == 2) {
|
||||
this->center = (_lst[0] + _lst[1]) / 2;
|
||||
this->radius = _lst[0].getDistance(_lst[1]) / 2 * 1.0001f;
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<Point2> lst = _lst;
|
||||
|
||||
// find point P having min(p.y)
|
||||
// NOTE: seems like the original work uses another coordinate system. so we search for max(p.y) instead!
|
||||
auto compMinY = [] (const Point2 p1, const Point2 p2) {return p1.y < p2.y;};
|
||||
auto it1 = std::max_element(lst.begin(), lst.end(), compMinY);
|
||||
Point2 P = *it1;
|
||||
lst.erase(it1);
|
||||
|
||||
// find a point Q such that the angle of the line segment
|
||||
// PQ with the x axis is minimal
|
||||
auto compMinAngleX = [&] (const Point2 p1, const Point2 p2) {
|
||||
|
||||
const Point2 PQ1 = p1 - P;
|
||||
const Point2 PQ2 = p2 - P;
|
||||
|
||||
const float angle1 = dot(PQ1.normalized(), Point2(0,1));
|
||||
const float angle2 = dot(PQ2.normalized(), Point2(0,1));
|
||||
|
||||
return std::acos(angle1) < std::acos(angle2);
|
||||
|
||||
};
|
||||
auto it2 = std::min_element(lst.begin(), lst.end(), compMinAngleX);
|
||||
Point2 Q = *it2;
|
||||
lst.erase(it2);
|
||||
|
||||
// get the angle abc which is the angle at "b"
|
||||
auto angle = [] (const Point2 a, const Point2 b, const Point2 c) {
|
||||
const Point2 ba = a - b;
|
||||
const Point2 bc = c - b;
|
||||
return std::acos(dot(ba.normalized(), bc.normalized()));
|
||||
};
|
||||
|
||||
// TODO: how many loops?
|
||||
for (size_t xx = 0; xx < lst.size()*10; ++xx) {
|
||||
|
||||
auto compMinAnglePRQ = [P,Q,angle] (const Point2 p1, const Point2 p2) {
|
||||
return std::abs(angle(P,p1,Q)) < std::abs(angle(P,p2,Q));
|
||||
};
|
||||
|
||||
auto it3 = std::min_element(lst.begin(), lst.end(), compMinAnglePRQ);
|
||||
|
||||
Point2 R = *it3;
|
||||
lst.erase(it3);
|
||||
|
||||
const float anglePRQ = angle(P,R,Q);
|
||||
const float angleRPQ = angle(R,P,Q);
|
||||
const float anglePQR = angle(P,Q,R);
|
||||
|
||||
//check for case 1 (angle PRQ is obtuse), the circle is determined
|
||||
//by two points, P and Q. radius = |(P-Q)/2|, center = (P+Q)/2
|
||||
if (anglePRQ > M_PI/2) {
|
||||
|
||||
this->radius = P.getDistance(Q) / 2 * 1.001f;
|
||||
this->center = (P+Q)/2;
|
||||
//if (!containsAll(_lst)) {show(_lst, P, Q, R);}
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (angleRPQ > M_PI/2) {
|
||||
lst.push_back(P);
|
||||
P = R;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (anglePQR > M_PI/2) {
|
||||
lst.push_back(Q);
|
||||
Q = R;
|
||||
continue;
|
||||
}
|
||||
|
||||
const Point2 mPQ = (P+Q)/2;
|
||||
const Point2 mQR = (Q+R)/2;
|
||||
|
||||
const float numer = -(-mPQ.y * R.y + mPQ.y * Q.y + mQR.y * R.y - mQR.y * Q.y - mPQ.x * R.x + mPQ.x * Q.x + mQR.x * R.x - mQR.x * Q.x);
|
||||
const float denom = (-Q.x * R.y + P.x * R.y - P.x * Q.y + Q.y * R.x - P.y * R.x + P.y * Q.x);
|
||||
const float t = numer / denom;
|
||||
|
||||
const float cx = -t * (Q.y - P.y) + mPQ.x;
|
||||
const float cy = t * (Q.x - P.x) + mPQ.y;
|
||||
this->center = Point2(cx, cy);
|
||||
this->radius = this->center.getDistance(P) * 1.001f;
|
||||
|
||||
//if (!containsAll(_lst)) {show(_lst, P, Q, R);}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
throw Exception("should not happen");
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
void adjustToPointSetRefine(const std::vector<Point2>& lst) {
|
||||
|
||||
float bestArea = 99999999;
|
||||
|
||||
for (size_t i = 0; i < lst.size(); ++i) {
|
||||
for (size_t j = 0; j < lst.size(); ++j) {
|
||||
if (i == j) {continue;}
|
||||
|
||||
const Point2 center = (lst[i] + lst[j]) / 3;
|
||||
const float radius = lst[i].getDistance(lst[j]);
|
||||
const Circle2 test(center, radius);
|
||||
|
||||
if (test.containsAll(lst)) {
|
||||
if (test.getArea() < bestArea) {
|
||||
bestArea = test.getArea();
|
||||
this->radius = test.radius;
|
||||
this->center = test.center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
void adjustToPointSetAPX(const std::vector<Point2>& lst) {
|
||||
|
||||
// NOT OPTIMAL but fast
|
||||
|
||||
// calculate the point set's center
|
||||
Point2 sum(0,0);
|
||||
for (const Point2 p : lst) {sum += p;}
|
||||
const Point2 center = sum / lst.size();
|
||||
|
||||
// calculate the sphere's radius (maximum distance from center
|
||||
float radius = 0;
|
||||
for (const Point2 p : lst) {
|
||||
const float dist = center.getDistance(p);
|
||||
if (dist > radius) {radius = dist;}
|
||||
}
|
||||
|
||||
this->radius = radius;
|
||||
this->center = center;
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // CIRCLE2_H
|
||||
74
geo/ConvexHull2.h
Normal file
74
geo/ConvexHull2.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef GEO_CONVEXHULL2_H
|
||||
#define GEO_CONVEXHULL2_H
|
||||
|
||||
#include "Point2.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* get a convex-hull around a set of 2D points
|
||||
* https://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain
|
||||
*/
|
||||
class ConvexHull2 {
|
||||
|
||||
public:
|
||||
|
||||
//using namespace std;
|
||||
|
||||
typedef double coord_t; // coordinate type
|
||||
typedef double coord2_t; // must be big enough to hold 2*max(|coordinate|)^2
|
||||
|
||||
// 2D cross product of OA and OB vectors, i.e. z-component of their 3D cross product.
|
||||
// Returns a positive value, if OAB makes a counter-clockwise turn,
|
||||
// negative for clockwise turn, and zero if the points are collinear.
|
||||
static inline coord2_t cross(const Point2 O, const Point2 A, const Point2 B) {
|
||||
return (A.x - O.x) * (B.y - O.y) - (A.y - O.y) * (B.x - O.x);
|
||||
}
|
||||
|
||||
// Returns a list of points on the convex hull in counter-clockwise order.
|
||||
// Note: the last point in the returned list is the same as the first one.
|
||||
static inline std::vector<Point2> get(std::vector<Point2> P) {
|
||||
|
||||
auto comp = [] (const Point2 p1, const Point2 p2) {
|
||||
return p1.x < p2.x || (p1.x == p2.x && p1.y < p2.y);
|
||||
};
|
||||
|
||||
int n = P.size(), k = 0;
|
||||
if (n == 1) return P;
|
||||
std::vector<Point2> H(2*n);
|
||||
|
||||
// Sort points lexicographically
|
||||
std::sort(P.begin(), P.end(), comp);
|
||||
|
||||
// Build lower hull
|
||||
for (int i = 0; i < n; ++i) {
|
||||
while (k >= 2 && cross(H[k-2], H[k-1], P[i]) <= 0) k--;
|
||||
H[k++] = P[i];
|
||||
}
|
||||
|
||||
// Build upper hull
|
||||
for (int i = n-2, t = k+1; i >= 0; i--) {
|
||||
while (k >= t && cross(H[k-2], H[k-1], P[i]) <= 0) k--;
|
||||
H[k++] = P[i];
|
||||
}
|
||||
|
||||
H.resize(k-1);
|
||||
return H;
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // GEO_CONVEXHULL2_H
|
||||
@@ -1,8 +1,21 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef EARTHMAPPING_H
|
||||
#define EARTHMAPPING_H
|
||||
|
||||
#include "Point3.h"
|
||||
#include "EarthPos.h"
|
||||
#include "../floorplan/v2/Floorplan.h"
|
||||
#include "../floorplan/v2/FloorplanHelper.h"
|
||||
#include "../misc/Debug.h"
|
||||
|
||||
/**
|
||||
* mapping between positions on earth and positions within the floorplan
|
||||
@@ -23,30 +36,119 @@ private:
|
||||
double m_per_deg_lat;
|
||||
double m_per_deg_lon;
|
||||
|
||||
static constexpr const char* NAME = "EarthMap";
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/** ctor with parameters */
|
||||
EarthMapping(Point3 center_map_m, EarthPos center_earth, float rotation_deg) :
|
||||
center_map_m(center_map_m), center_earth(center_earth), rotation_deg(rotation_deg) {
|
||||
|
||||
precalc();
|
||||
|
||||
}
|
||||
|
||||
/** ctor for a given map */
|
||||
EarthMapping(Floorplan::IndoorMap* map) {
|
||||
|
||||
// get the map<->earth correspondences from the floorplan
|
||||
const std::vector<Floorplan::EarthPosMapPos*> cor = map->earthReg.correspondences;
|
||||
|
||||
// sanity check
|
||||
if (cor.size() < 3) {
|
||||
throw Exception("for EarthMapping to work, the map needs at least 3 correspondence points");
|
||||
}
|
||||
|
||||
Log::add(NAME, "calculating map<->earth correspondence using " + std::to_string(cor.size()) + " reference points");
|
||||
|
||||
// 1)
|
||||
// to reduce errors, use the average of all correspondces for earth<->map mapping
|
||||
Point3 _mapSum(0,0,0);
|
||||
EarthPos _earthSum(0,0,0);
|
||||
for (const Floorplan::EarthPosMapPos* epmp : cor) {
|
||||
_mapSum += epmp->posOnMap_m;
|
||||
_earthSum.lat += epmp->posOnEarth.lat;
|
||||
_earthSum.lon += epmp->posOnEarth.lon;
|
||||
_earthSum.height += epmp->posOnEarth.height;
|
||||
}
|
||||
const Point3 _mapAvg = _mapSum / cor.size();
|
||||
const EarthPos _earthAvg = EarthPos(_earthSum.lat/cor.size(), _earthSum.lon/cor.size(), _earthSum.height/cor.size());
|
||||
|
||||
// 2)
|
||||
// initialize the mapper with those values
|
||||
// this allows a first mapping, but the map is not facing towards the north!
|
||||
rotation_deg = 0; // currently unkown
|
||||
center_map_m = _mapAvg;
|
||||
center_earth = _earthAvg;
|
||||
precalc();
|
||||
|
||||
Log::add(NAME, "avg. reference points: " + _mapAvg.asString() + " <-> " + _earthAvg.asString());
|
||||
|
||||
// 3)
|
||||
// now we use this initial setup to determine the rotation angle between map and world
|
||||
float deltaAngleSum = 0;
|
||||
for (Floorplan::EarthPosMapPos* epmp : cor) {
|
||||
|
||||
// angle between mapAvg and mapCorrespondencePoint
|
||||
const float angleMap = std::atan2(_mapAvg.y - epmp->posOnMap_m.y, _mapAvg.x - epmp->posOnMap_m.x);
|
||||
|
||||
// use the current setup to convert from map to world, WITHOUT correct rotation
|
||||
const Point3 repro = this->worldToMap(epmp->posOnEarth);
|
||||
|
||||
// get the angle between mapAvg and projectedCorrespondencePoint
|
||||
const float angleEarth = std::atan2(_mapAvg.y - repro.y, _mapAvg.x - repro.x);
|
||||
|
||||
// the difference between angleMap and angleEarth contains the angle needed to let the map face northwards
|
||||
// we use the average of all those angles determined by each correspondence
|
||||
const float dx_rad = angleEarth - angleMap;
|
||||
float dx_deg = (dx_rad * 180 / M_PI);
|
||||
if (dx_deg < 0) {dx_deg = 360 + dx_deg;}
|
||||
deltaAngleSum += dx_deg;
|
||||
|
||||
}
|
||||
const float deltaSumAvg = deltaAngleSum / cor.size();
|
||||
|
||||
Log::add(NAME, "avg angular difference [north-rotation]: " + std::to_string(deltaSumAvg));
|
||||
|
||||
// 4)
|
||||
// the current center is not the rotation center we actually need:
|
||||
// e.g. when correspondence points were outside of the building, the rotation center is wrong
|
||||
// as real rotation center, we use the building's bbox center and the correspondence lon/lat on earth
|
||||
const BBox3 bbox = FloorplanHelper::getBBox(map);
|
||||
const Point2 _mapCenter2 = ((bbox.getMax() - bbox.getMin()) / 2).xy();
|
||||
const Point3 _mapCenter3 = Point3(_mapCenter2.x, _mapCenter2.y, this->center_map_m.z); // keep original z!
|
||||
this->center_earth = mapToWorld(_mapCenter3);
|
||||
this->center_map_m = _mapCenter3;
|
||||
|
||||
Log::add(NAME, "setting rotation center from bbox: " + center_map_m.asString() + " <-> " + center_earth.asString());
|
||||
|
||||
// 5)
|
||||
// finally, let the mapper know the north-angle
|
||||
this->rotation_deg = deltaSumAvg;
|
||||
|
||||
}
|
||||
|
||||
void build() {
|
||||
|
||||
// TODO
|
||||
precalc();
|
||||
|
||||
}
|
||||
|
||||
/** convert from map-coordinates to earth-coordinates */
|
||||
EarthPos mapToWorld(const Point3 mapPos_m) const {
|
||||
|
||||
Point3 pos = mapPos_m;
|
||||
|
||||
// move to (0,0,0)
|
||||
pos -= center_map_m;
|
||||
Point3 pos = mapPos_m - center_map_m;
|
||||
|
||||
// undo the rotation
|
||||
const Point2 xy = pos.xy().rotated(-rotation_deg / 180.0 * (float) M_PI);
|
||||
const Point2 xy = pos.xy().rotated(degToRad(+rotation_deg));
|
||||
|
||||
// convert this "delta to (0,0,0)" to lon/lat and move it to the lon/lat-center
|
||||
const double lat = cenLat + (xy.y / m_per_deg_lat);
|
||||
const double lon = cenLon + (xy.x / m_per_deg_lon);
|
||||
const float height = pos.z;
|
||||
const double lat = center_earth.lat + (xy.y / m_per_deg_lat);
|
||||
const double lon = center_earth.lon + (xy.x / m_per_deg_lon);
|
||||
const float height = center_earth.height + pos.z;
|
||||
|
||||
// done
|
||||
return EarthPos(lat, lon, height);
|
||||
@@ -56,18 +158,36 @@ public:
|
||||
/** convert from earth-coordinates to map-coordinates */
|
||||
Point3 worldToMap(const EarthPos earthPos) const {
|
||||
|
||||
const double y_m = +(lat-cenLat) * m_per_deg_lat;
|
||||
const double x_m = +(lon-cenLon) * m_per_deg_lon;
|
||||
// move to center and scale
|
||||
const double y_m = +(earthPos.lat - center_earth.lat) * m_per_deg_lat;
|
||||
const double x_m = +(earthPos.lon - center_earth.lon) * m_per_deg_lon;
|
||||
const double z_m = (earthPos.height - center_earth.height);
|
||||
|
||||
// rotate (our map is axis aligned)
|
||||
Point2 pos(x_m, y_m);
|
||||
pos = pos.rotated(rotDeg / 180 * M_PI);
|
||||
Point2 xy(x_m, y_m);
|
||||
xy = xy.rotated(degToRad(-rotation_deg));
|
||||
|
||||
// apply movement
|
||||
pos += mapCenter_m;
|
||||
// append height
|
||||
Point3 pos3(xy.x, xy.y, z_m);
|
||||
|
||||
return pos;
|
||||
// move from center
|
||||
pos3 += center_map_m;
|
||||
|
||||
return pos3;
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/** perform some pre-calculations to speed things up */
|
||||
void precalc() {
|
||||
const double refLat = center_earth.lat / 180.0 * M_PI;
|
||||
m_per_deg_lat = 111132.954 - 559.822 * std::cos( 2.0 * refLat ) + 1.175 * std::cos( 4.0 * refLat);
|
||||
m_per_deg_lon = 111132.954 * std::cos ( refLat );
|
||||
}
|
||||
|
||||
static inline float degToRad(const float deg) {
|
||||
return deg / 180.0f * (float) M_PI;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef EARTHPOS_H
|
||||
#define EARTHPOS_H
|
||||
|
||||
@@ -11,11 +21,20 @@ struct EarthPos {
|
||||
/** height above sea level */
|
||||
float height;
|
||||
|
||||
/** ctor with values */
|
||||
EarthPos(const double lat, const double lon, const float height) : lon(lon), lat(lat), height(height) {
|
||||
/** empty ctor */
|
||||
EarthPos() : lat(NAN), lon(NAN), height(NAN) {
|
||||
;
|
||||
}
|
||||
|
||||
/** ctor with values */
|
||||
EarthPos(const double lat, const double lon, const float height) : lat(lat), lon(lon), height(height) {
|
||||
;
|
||||
}
|
||||
|
||||
std::string asString() const {
|
||||
return "(lat: " + std::to_string(lat) + "°, lon: " + std::to_string(lon) + "°, alt: " + std::to_string(height) + ")";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // EARTHPOS_H
|
||||
|
||||
235
geo/GPCPolygon2.h
Normal file
235
geo/GPCPolygon2.h
Normal file
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef GPCPOLYGON2_H
|
||||
#define GPCPOLYGON2_H
|
||||
|
||||
#include "../lib/gpc/gpc.cpp.h"
|
||||
#include "Polygon2.h"
|
||||
#include "Triangle3.h"
|
||||
|
||||
#include "TriangleStrip3.h"
|
||||
|
||||
class GPCPolygon2 {
|
||||
|
||||
struct GPCPolygon : gpc_polygon {
|
||||
GPCPolygon() {
|
||||
num_contours = 0;
|
||||
contour = nullptr;
|
||||
hole = nullptr;
|
||||
}
|
||||
|
||||
// no copy or move
|
||||
GPCPolygon(const GPCPolygon& o) = delete;
|
||||
GPCPolygon(GPCPolygon&& o) = delete;
|
||||
|
||||
~GPCPolygon() {
|
||||
if (contour) {
|
||||
gpc_free_polygon(this);
|
||||
//free(contour->vertex); contour->vertex = nullptr;
|
||||
}
|
||||
free(contour); contour = nullptr;
|
||||
free(hole); hole = nullptr;
|
||||
}
|
||||
|
||||
GPCPolygon& operator = (const GPCPolygon& o) = delete;
|
||||
GPCPolygon& operator = (GPCPolygon&& o) = delete;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
GPCPolygon state;
|
||||
float z;
|
||||
|
||||
public:
|
||||
|
||||
GPCPolygon2() : z(0) {
|
||||
;
|
||||
}
|
||||
|
||||
GPCPolygon2(float z) : z(z) {
|
||||
;
|
||||
}
|
||||
|
||||
void add(const Polygon2& poly) {
|
||||
GPCPolygon cur;
|
||||
toGPC(poly, cur);
|
||||
gpc_polygon_clip(GPC_UNION, &state, &cur, &state);
|
||||
}
|
||||
void add(const Floorplan::Polygon2& poly) {
|
||||
GPCPolygon cur;
|
||||
toGPC(poly, cur);
|
||||
gpc_polygon_clip(GPC_UNION, &state, &cur, &state);
|
||||
}
|
||||
|
||||
void remove(const Floorplan::Polygon2& poly) {
|
||||
GPCPolygon cur;
|
||||
toGPC(poly, cur);
|
||||
gpc_polygon_clip(GPC_DIFF, &state, &cur, &state);
|
||||
}
|
||||
void remove(const Polygon2& poly) {
|
||||
GPCPolygon cur;
|
||||
toGPC(poly, cur);
|
||||
gpc_polygon_clip(GPC_DIFF, &state, &cur, &state);
|
||||
}
|
||||
|
||||
std::vector<std::vector<Point3>> get() {
|
||||
|
||||
gpc_tristrip res;
|
||||
res.num_strips = 0;
|
||||
res.strip = nullptr;
|
||||
|
||||
//res.strip = (gpc_vertex_list*) malloc(1024);
|
||||
gpc_polygon_to_tristrip(&state, &res);
|
||||
|
||||
std::vector<std::vector<Point3>> trias;
|
||||
|
||||
for (int i = 0; i < res.num_strips; ++i) {
|
||||
gpc_vertex_list lst = res.strip[i];
|
||||
for (int j = 2; j < lst.num_vertices; ++j) {
|
||||
std::vector<Point3> tria;
|
||||
gpc_vertex& v1 = lst.vertex[j - 2];
|
||||
gpc_vertex& v2 = lst.vertex[j - 1];
|
||||
gpc_vertex& v3 = lst.vertex[j];
|
||||
tria.push_back(Point3(v1.x, v1.y, z));
|
||||
tria.push_back(Point3(v2.x, v2.y, z));
|
||||
tria.push_back(Point3(v3.x, v3.y, z));
|
||||
trias.push_back(tria);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
gpc_free_tristrip(&res);
|
||||
|
||||
return std::move(trias);
|
||||
|
||||
}
|
||||
|
||||
std::vector<Triangle3> getTriangles() {
|
||||
|
||||
gpc_tristrip res;
|
||||
res.num_strips = 0;
|
||||
res.strip = nullptr;
|
||||
|
||||
//res.strip = (gpc_vertex_list*) malloc(1024);
|
||||
gpc_polygon_to_tristrip(&state, &res);
|
||||
|
||||
std::vector<Triangle3> trias;
|
||||
|
||||
for (int i = 0; i < res.num_strips; ++i) {
|
||||
gpc_vertex_list lst = res.strip[i];
|
||||
|
||||
TriangleStrip3 strip;
|
||||
for (int j = 0; j < lst.num_vertices; ++j) {
|
||||
gpc_vertex& v = lst.vertex[j];
|
||||
strip.add(Point3(v.x, v.y, z));
|
||||
}
|
||||
strip.toTriangles(trias);
|
||||
|
||||
// for (int j = 2; j < lst.num_vertices; ++j) {
|
||||
|
||||
// gpc_vertex& v1 = lst.vertex[j - 2];
|
||||
// gpc_vertex& v2 = lst.vertex[j - 1];
|
||||
// gpc_vertex& v3 = lst.vertex[j];
|
||||
|
||||
// // https://en.wikipedia.org/wiki/Triangle_strip
|
||||
// // GL_TRIANGLE_STRIP
|
||||
// // Draws a series of triangles (three-sided polygons) using vertices v0, v1, v2, then v2, v1, v3 (note the order), then v2, v3, v4, and so on. The ordering is to ensure that the triangles are all drawn with the same orientation so that the strip can correctly form part of a surface.
|
||||
// // For odd n, vertices n, n+1, and n+2 define triangle n. For even n, vertices n+1, n, and n+2 define triangle n. N-2 triangles are drawn.
|
||||
|
||||
// if (j % 2 == 0) {
|
||||
// Triangle3 tria(
|
||||
// Point3(v1.x, v1.y, z),
|
||||
// Point3(v2.x, v2.y, z),
|
||||
// Point3(v3.x, v3.y, z)
|
||||
// );
|
||||
// trias.push_back(tria);
|
||||
// } else {
|
||||
// Triangle3 tria(
|
||||
// Point3(v2.x, v2.y, z),
|
||||
// Point3(v1.x, v1.y, z),
|
||||
// Point3(v3.x, v3.y, z)
|
||||
// );
|
||||
// trias.push_back(tria);
|
||||
// }
|
||||
|
||||
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
gpc_free_tristrip(&res);
|
||||
|
||||
return std::move(trias);
|
||||
|
||||
}
|
||||
|
||||
std::vector<std::vector<Point3>> get(float z) {
|
||||
|
||||
gpc_tristrip res;
|
||||
res.num_strips = 0;
|
||||
res.strip = nullptr;
|
||||
|
||||
//res.strip = (gpc_vertex_list*) malloc(1024);
|
||||
gpc_polygon_to_tristrip(&state, &res);
|
||||
|
||||
std::vector<std::vector<Point3>> trias;
|
||||
|
||||
for (int i = 0; i < res.num_strips; ++i) {
|
||||
gpc_vertex_list lst = res.strip[i];
|
||||
std::vector<Point3> tria;
|
||||
for (int j = 0; j < lst.num_vertices; ++j) {
|
||||
gpc_vertex& vert = lst.vertex[j];
|
||||
Point3 p3(vert.x, vert.y, z);
|
||||
tria.push_back(p3);
|
||||
}
|
||||
trias.push_back(tria);
|
||||
}
|
||||
|
||||
gpc_free_tristrip(&res);
|
||||
|
||||
return std::move(trias);
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void toGPC(const Floorplan::Polygon2& poly, GPCPolygon& result) {
|
||||
|
||||
std::vector<gpc_vertex> verts;
|
||||
for (Point2 p2 : poly.points) {
|
||||
gpc_vertex vert; vert.x = p2.x; vert.y = p2.y;
|
||||
verts.push_back(vert);
|
||||
}
|
||||
|
||||
gpc_vertex_list list;
|
||||
list.num_vertices = verts.size();
|
||||
list.vertex = verts.data();
|
||||
gpc_add_contour(&result, &list, 0);
|
||||
|
||||
}
|
||||
|
||||
void toGPC(const Polygon2& poly, GPCPolygon& result) {
|
||||
|
||||
std::vector<gpc_vertex> verts;
|
||||
for (Point2 p2 : poly) {
|
||||
gpc_vertex vert; vert.x = p2.x; vert.y = p2.y;
|
||||
verts.push_back(vert);
|
||||
}
|
||||
|
||||
gpc_vertex_list list;
|
||||
list.num_vertices = verts.size();
|
||||
list.vertex = verts.data();
|
||||
gpc_add_contour(&result, &list, 0);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
#endif
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef HEADING_H
|
||||
#define HEADING_H
|
||||
|
||||
@@ -27,14 +37,22 @@ public:
|
||||
Assert::isBetween(rad, 0.0f, (float)_2PI, "radians out of bounds");
|
||||
}
|
||||
|
||||
/** ctor from(x,y) -> to(x,y) */
|
||||
Heading(const Point2 from, const Point2 to) : Heading(from.x, from.y, to.x, to.y) {
|
||||
;
|
||||
}
|
||||
|
||||
/** POSITIVE angular difference [0:PI] */
|
||||
float getDiffHalfRAD(const Heading other) const {
|
||||
return Angle::getDiffRAD_2PI_PI(rad, other.rad);
|
||||
}
|
||||
|
||||
/** signled angular difference [-PI:+PI] */
|
||||
float getSignedDiff(const Heading other) const {
|
||||
return Angle::getSignedDiffRAD_2PI(rad, other.rad);
|
||||
// float getSignedDiff(const Heading other) const {
|
||||
// return Angle::getSignedDiffRAD_2PI(other.rad, rad);
|
||||
// }
|
||||
static float getSignedDiff(const Heading from, const Heading to) {
|
||||
return Angle::getSignedDiffRAD_2PI(from.rad, to.rad);
|
||||
}
|
||||
|
||||
/** update the angle but ensure we stay within [0:2PI] */
|
||||
@@ -74,10 +92,10 @@ public:
|
||||
}
|
||||
|
||||
|
||||
Heading& operator = (const float _rad) {
|
||||
rad = _rad;
|
||||
return *this;
|
||||
}
|
||||
Heading& operator = (const float _rad) {
|
||||
rad = _rad;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** compare two headings */
|
||||
bool operator == (const Heading other) const {return rad == other.rad;}
|
||||
@@ -92,6 +110,11 @@ public:
|
||||
|
||||
float getRAD() const {return rad;}
|
||||
|
||||
/** convert heading into a direction-vector */
|
||||
Point2 asVector() const {
|
||||
return Point2(std::cos(rad), std::sin(rad));
|
||||
}
|
||||
|
||||
// /** get a random heading */
|
||||
// static Heading rnd() {
|
||||
// static std::minstd_rand gen(1234);
|
||||
|
||||
10
geo/Length.h
10
geo/Length.h
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef LENGTH_H
|
||||
#define LENGTH_H
|
||||
|
||||
|
||||
188
geo/Line2.h
188
geo/Line2.h
@@ -1,8 +1,19 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef LINE2D_H
|
||||
#define LINE2D_H
|
||||
|
||||
//#include <KLib/geo/Line.h>
|
||||
#include "Point2.h"
|
||||
#include "Ray2.h"
|
||||
|
||||
class Line2 {
|
||||
|
||||
@@ -14,44 +25,71 @@ public:
|
||||
|
||||
public:
|
||||
|
||||
/** empty ctor */
|
||||
/** empty ctor */
|
||||
Line2() : p1(), p2() {;}
|
||||
|
||||
/** value ctor */
|
||||
Line2(const Point2 p1, const Point2 p2) : p1(p1), p2(p2) {;}
|
||||
/** value ctor */
|
||||
Line2(const Point2 p1, const Point2 p2) : p1(p1), p2(p2) {;}
|
||||
|
||||
/** value ctor */
|
||||
/** value ctor */
|
||||
Line2(const float x1, const float y1, const float x2, const float y2) : p1(x1,y1), p2(x2,y2) {;}
|
||||
|
||||
|
||||
Line2 operator * (const float v) const {return Line2(p1*v, p2*v);}
|
||||
|
||||
// bool getSegmentIntersection(const Line& other) const {
|
||||
// static K::Point p;
|
||||
// return K::Line::getSegmentIntersection(other, p);
|
||||
// }
|
||||
|
||||
|
||||
/** get intersection between these two lines (unlimited length!) */
|
||||
bool getLineIntersection(const Line2& other, Point2& result) const {
|
||||
float getAngle() const {
|
||||
return std::atan2(p2.y-p1.y, p2.x-p1.x);
|
||||
}
|
||||
|
||||
double bx = p2.x - p1.x;
|
||||
double by = p2.y - p1.y;
|
||||
void reverse() {
|
||||
std::swap(p1, p2);
|
||||
}
|
||||
|
||||
double dx = other.p2.x - other.p1.x;
|
||||
double dy = other.p2.y - other.p1.y;
|
||||
float getLength() const {
|
||||
return p1.getDistance(p2);
|
||||
}
|
||||
|
||||
double b_dot_d_perp = bx*dy - by*dx;
|
||||
/** keep the starting point, but make the line longer by the given length */
|
||||
Line2 longerAtEnd(const float l) {
|
||||
Point2 dir = p2-p1;
|
||||
return Line2(p1, p1+dir+dir.normalized()*l);
|
||||
}
|
||||
|
||||
if(b_dot_d_perp == 0) {return false;}
|
||||
/** keep the ending point, but make the line longer by the given length */
|
||||
Line2 longerAtStart(const float l) {
|
||||
Point2 dir = p1-p2;
|
||||
return Line2(p2, p2+dir+dir.normalized()*l);
|
||||
}
|
||||
|
||||
double cx = other.p1.x - p1.x;
|
||||
double cy = other.p1.y - p1.y;
|
||||
double t = (cx*dy - cy*dx) / b_dot_d_perp;
|
||||
/** get intersection between these two lines (unlimited length!) */
|
||||
bool getLineIntersection(const Line2& other, Point2& result) const {
|
||||
|
||||
result.x = p1.x + t * bx;
|
||||
result.y = p1.y + t * by;
|
||||
double bx = p2.x - p1.x;
|
||||
double by = p2.y - p1.y;
|
||||
|
||||
return true;
|
||||
double dx = other.p2.x - other.p1.x;
|
||||
double dy = other.p2.y - other.p1.y;
|
||||
|
||||
}
|
||||
double b_dot_d_perp = bx*dy - by*dx;
|
||||
|
||||
if(b_dot_d_perp == 0) {return false;}
|
||||
|
||||
double cx = other.p1.x - p1.x;
|
||||
double cy = other.p1.y - p1.y;
|
||||
double t = (cx*dy - cy*dx) / b_dot_d_perp;
|
||||
|
||||
result.x = p1.x + t * bx;
|
||||
result.y = p1.y + t * by;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool getSegmentIntersection(const Line2& other) const {
|
||||
|
||||
@@ -138,6 +176,118 @@ public:
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool getSegmentIntersectionInt(const Line2& other, Point2& result) const {
|
||||
|
||||
int mul = 100;
|
||||
|
||||
const float p0_x = std::round(p1.x*mul), p1_x = std::round(p2.x*mul), p2_x = std::round(other.p1.x*mul), p3_x = std::round(other.p2.x*mul);
|
||||
const float p0_y = std::round(p1.y*mul), p1_y = std::round(p2.y*mul), p2_y = std::round(other.p1.y*mul), p3_y = std::round(other.p2.y*mul);
|
||||
|
||||
const float s1_x = p1_x - p0_x;
|
||||
const float s1_y = p1_y - p0_y;
|
||||
const float s2_x = p3_x - p2_x;
|
||||
const float s2_y = p3_y - p2_y;
|
||||
|
||||
const float s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y);
|
||||
const float t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);
|
||||
|
||||
if (s >= 0 && s <= 1 && t >= 0 && t <= 1) {
|
||||
result.x = (p0_x + (t * s1_x)) / mul;
|
||||
result.y = (p0_y + (t * s1_y)) / mul;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/** does the line intersect with the given ray? */
|
||||
bool intersects(const Ray2& ray, Point2& result) const {
|
||||
|
||||
//https://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect/565282#565282
|
||||
|
||||
const float p0_x = p1.x, p1_x = p2.x;
|
||||
const float p0_y = p1.y, p1_y = p2.y;
|
||||
|
||||
const float p2_x = ray.start.x;//, p3_x = other.p2.x;
|
||||
const float p2_y = ray.start.y;//, p3_y = other.p2.y;
|
||||
|
||||
const float s1_x = p1_x - p0_x;
|
||||
const float s1_y = p1_y - p0_y;
|
||||
|
||||
const float s2_x = ray.dir.x; // p3_x - p2_x;
|
||||
const float s2_y = ray.dir.y; // p3_y - p2_y;
|
||||
|
||||
// ray_start + s * ray_dir
|
||||
const float s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y);
|
||||
|
||||
// before the ray's start?
|
||||
if (s < 0) {return false;}
|
||||
|
||||
// line.p1 + t * (line.p2-line.p1)
|
||||
const float t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);
|
||||
|
||||
// t must be between 0 and 1, otherwise we are before the line's start / after the line's end
|
||||
if (t < 0 || t > 1) {return false;}
|
||||
|
||||
// intersection
|
||||
result.x = (p0_x + (t * s1_x));
|
||||
result.y = (p0_y + (t * s1_y));
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
static inline bool intersects(const Line2& l1, const Line2& l2, bool limit, Point2& pos, float* _u = nullptr) {
|
||||
|
||||
// (sx1,sy1) + (dx1, dy1)*u = (sx2, sy2) + (dx2, dy2)*v
|
||||
// solve((c+d*v-a)/b = (g+h*v-e)/f, v)
|
||||
// solve((a+b*u-c)/d = (e+f*u-g)/h, u)
|
||||
// bf != 0
|
||||
// dh != 0
|
||||
// df != bh // kollinear (b/d != f/h)
|
||||
|
||||
const float sx1 = l1.p1.x; // a
|
||||
const float sy1 = l1.p1.y; // e
|
||||
|
||||
const float dx1 = l1.p2.x - l1.p1.x; // b
|
||||
const float dy1 = l1.p2.y - l1.p1.y; // f
|
||||
|
||||
const float sx2 = l2.p1.x; // c
|
||||
const float sy2 = l2.p1.y; // g
|
||||
|
||||
const float dx2 = l2.p2.x - l2.p1.x; // d
|
||||
const float dy2 = l2.p2.y - l2.p1.y; // h
|
||||
|
||||
// collinear?
|
||||
const float a1 = dx2*dy1;
|
||||
const float a2 = dx1*dy2;
|
||||
if (std::abs(a1-a2) < 0.0001) {return false;}
|
||||
|
||||
const float v = (dy1*(sx1-sx2) + dx1*(sy2-sy1)) / (dx2*dy1 - dx1*dy2);
|
||||
const float u = (dy2*(sx1-sx2) + dx2*(sy2-sy1)) / (dx2*dy1 - dx1*dy2);
|
||||
|
||||
//const float x = sx2 + v*dx2;
|
||||
//const float y = sy2 + v*dy2;
|
||||
|
||||
const float x = sx1 + u*dx1;
|
||||
const float y = sy1 + u*dy1;
|
||||
|
||||
if (!limit || (u >= 0 && v >= 0 && u <= 1 && v <= 1)) {
|
||||
pos = Point2(x,y);
|
||||
if (_u) {*_u = u;}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // LINE2D_H
|
||||
|
||||
36
geo/Plane3.h
Normal file
36
geo/Plane3.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef PLANE3_H
|
||||
#define PLANE3_H
|
||||
|
||||
#include "Point3.h"
|
||||
|
||||
class Plane3 {
|
||||
|
||||
private:
|
||||
|
||||
Point3 p1;
|
||||
Point3 p2;
|
||||
Point3 p3;
|
||||
Point3 p4;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
Plane3(Point3 p1, Point3 p2, Point3 p3, Point3 p4) : p1(p1), p2(p2), p3(p3), p4(p4) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // PLANE3_H
|
||||
42
geo/Point2.h
42
geo/Point2.h
@@ -1,7 +1,19 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef POINT2_H
|
||||
#define POINT2_H
|
||||
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* 2D Point
|
||||
@@ -42,6 +54,7 @@ struct Point2 {
|
||||
|
||||
bool operator != (const Point2& o) const {return x!=o.x || y!=o.y;}
|
||||
|
||||
bool eq (const Point2& o, const float delta) const { return eq(x,o.x,delta) && eq(y,o.y,delta); }
|
||||
|
||||
Point2 perpendicular() const {return Point2(-y, x);}
|
||||
|
||||
@@ -60,11 +73,40 @@ struct Point2 {
|
||||
return std::sqrt(dx*dx + dy*dy);
|
||||
}
|
||||
|
||||
std::string asString() const {
|
||||
return "(" + std::to_string(x) + "," + std::to_string(y) + ")";
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
static inline bool eq(const float a, const float b, const float delta) {return std::abs(a-b) <= delta;}
|
||||
static inline bool ne(const float a, const float b, const float delta) {return std::abs(a-b) > delta;}
|
||||
|
||||
};
|
||||
|
||||
inline float dot(const Point2 p1, const Point2 p2) {
|
||||
return (p1.x*p2.x) + (p1.y*p2.y);
|
||||
}
|
||||
|
||||
inline float determinant(const Point2 p1, const Point2 p2) {
|
||||
return (p1.x*p2.y) - (p1.y*p2.x);
|
||||
}
|
||||
|
||||
inline void swap(Point2& p1, Point2& p2) {
|
||||
std::swap(p1.x, p2.x);
|
||||
std::swap(p1.y, p2.y);
|
||||
}
|
||||
|
||||
namespace std {
|
||||
|
||||
template <> struct hash<Point2> {
|
||||
std::size_t operator()(const Point2& p) const {
|
||||
uint32_t x = *((uint32_t*)&(p.x));
|
||||
uint32_t y = *((uint32_t*)&(p.y));
|
||||
return std::hash<uint32_t>()(x^y);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // POINT2_H
|
||||
|
||||
104
geo/Point3.h
104
geo/Point3.h
@@ -1,5 +1,15 @@
|
||||
#ifndef POINT3_H
|
||||
#define POINT3_H
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef GEO_Point3_H
|
||||
#define GEO_Point3_H
|
||||
|
||||
#include "../Assertions.h"
|
||||
#include <cmath>
|
||||
@@ -8,76 +18,77 @@
|
||||
/**
|
||||
* 3D Point
|
||||
*/
|
||||
struct Point3 {
|
||||
template <typename Scalar> struct _Point3 {
|
||||
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
Scalar x;
|
||||
Scalar y;
|
||||
Scalar z;
|
||||
|
||||
/** ctor */
|
||||
Point3() : x(0), y(0), z(0) {;}
|
||||
_Point3() : x(0), y(0), z(0) {;}
|
||||
|
||||
/** ctor */
|
||||
Point3(const float x, const float y, const float z) : x(x), y(y), z(z) {;}
|
||||
_Point3(const Scalar x, const Scalar y, const Scalar z) : x(x), y(y), z(z) {;}
|
||||
|
||||
|
||||
Point3 operator - () const {return Point3(-x, -y, -z);}
|
||||
_Point3 operator - () const {return _Point3(-x, -y, -z);}
|
||||
|
||||
|
||||
Point3 operator + (const Point3& o) const {return Point3(x+o.x, y+o.y, z+o.z);}
|
||||
_Point3 operator + (const _Point3& o) const {return _Point3(x+o.x, y+o.y, z+o.z);}
|
||||
|
||||
Point3 operator - (const Point3& o) const {return Point3(x-o.x, y-o.y, z-o.z);}
|
||||
_Point3 operator - (const _Point3& o) const {return _Point3(x-o.x, y-o.y, z-o.z);}
|
||||
|
||||
Point3 operator * (const Point3& o) const {return Point3(x*o.x, y*o.y, z*o.z);}
|
||||
_Point3 operator * (const _Point3& o) const {return _Point3(x*o.x, y*o.y, z*o.z);}
|
||||
|
||||
Point3 operator * (const float v) const {return Point3(v*x, v*y, v*z);}
|
||||
_Point3 operator * (const Scalar v) const {return _Point3(v*x, v*y, v*z);}
|
||||
|
||||
Point3 operator / (const float v) const {return Point3(x/v, y/v, z/v);}
|
||||
_Point3 operator / (const Scalar v) const {return _Point3(x/v, y/v, z/v);}
|
||||
|
||||
|
||||
Point3& operator *= (const float v) {x*=v; y*=v; z*=v; return *this;}
|
||||
_Point3& operator *= (const Scalar v) {x*=v; y*=v; z*=v; return *this;}
|
||||
|
||||
Point3& operator /= (const float v) {x/=v; y/=v; z/=v; return *this;}
|
||||
_Point3& operator /= (const Scalar v) {x/=v; y/=v; z/=v; return *this;}
|
||||
|
||||
|
||||
Point3& operator += (const Point3& o) {x+=o.x; y+=o.y; z+=o.z; return *this;}
|
||||
_Point3& operator += (const _Point3& o) {x+=o.x; y+=o.y; z+=o.z; return *this;}
|
||||
|
||||
Point3& operator -= (const Point3& o) {x-=o.x; y-=o.y; z-=o.z; return *this;}
|
||||
_Point3& operator -= (const _Point3& o) {x-=o.x; y-=o.y; z-=o.z; return *this;}
|
||||
|
||||
Point3& operator *= (const Point3& o) {x*=o.x; y*=o.y; z*=o.z; return *this;}
|
||||
_Point3& operator *= (const _Point3& o) {x*=o.x; y*=o.y; z*=o.z; return *this;}
|
||||
|
||||
Point3& operator /= (const Point3& o) {x/=o.x; y/=o.y; z/=o.z; return *this;}
|
||||
_Point3& operator /= (const _Point3& o) {x/=o.x; y/=o.y; z/=o.z; return *this;}
|
||||
|
||||
|
||||
bool operator < (const Point3& o) const {return x<o.x && y<o.y && z<o.z;}
|
||||
bool operator < (const _Point3& o) const {return x<o.x && y<o.y && z<o.z;}
|
||||
|
||||
bool operator == (const Point3& o) const {return x==o.x && y==o.y && z==o.z;}
|
||||
bool operator == (const _Point3& o) const {return x==o.x && y==o.y && z==o.z;}
|
||||
|
||||
bool operator != (const Point3& o) const {return x!=o.x || y!=o.y || z!=o.z;}
|
||||
bool operator != (const _Point3& o) const {return x!=o.x || y!=o.y || z!=o.z;}
|
||||
|
||||
bool eq (const Point3& o, const float delta) const { return eq(x,o.x,delta) && eq(y,o.y,delta) && eq(z,o.z,delta); }
|
||||
bool eq (const _Point3& o, const Scalar delta) const { return eq(x,o.x,delta) && eq(y,o.y,delta) && eq(z,o.z,delta); }
|
||||
|
||||
|
||||
Point2 xy() const {return Point2(x,y);}
|
||||
Point2 xz() const {return Point2(x,z);}
|
||||
|
||||
|
||||
Point3 rotX(const float r) const {
|
||||
return Point3(x, y*cos(r) - z*sin(r), y*sin(r) + z*cos(r));
|
||||
_Point3 rotX(const Scalar r) const {
|
||||
return _Point3(x, y*cos(r) - z*sin(r), y*sin(r) + z*cos(r));
|
||||
}
|
||||
Point3 rotY(const float r) const {
|
||||
return Point3(z*sin(r) + x*cos(r), y, z*cos(r) - x*sin(r));
|
||||
_Point3 rotY(const Scalar r) const {
|
||||
return _Point3(z*sin(r) + x*cos(r), y, z*cos(r) - x*sin(r));
|
||||
}
|
||||
Point3 rotZ(const float r) const {
|
||||
return Point3(x*cos(r) - y*sin(r), x*sin(r) + y*cos(r), z);
|
||||
_Point3 rotZ(const Scalar r) const {
|
||||
return _Point3(x*cos(r) - y*sin(r), x*sin(r) + y*cos(r), z);
|
||||
}
|
||||
Point3 rot(const float rx, const float ry, const float rz) const {
|
||||
_Point3 rot(const Scalar rx, const Scalar ry, const Scalar rz) const {
|
||||
return rotX(rx).rotY(ry).rotZ(rz);
|
||||
//return rotZ(rz).rotY(ry).rotX(rx);
|
||||
}
|
||||
|
||||
|
||||
/** read-only array access */
|
||||
float operator [] (const int idx) const {
|
||||
Scalar operator [] (const int idx) const {
|
||||
Assert::isBetween(idx, 0, 2, "index out of bounds");
|
||||
if (0 == idx) {return x;}
|
||||
if (1 == idx) {return y;}
|
||||
@@ -85,19 +96,19 @@ struct Point3 {
|
||||
}
|
||||
|
||||
/** get the distance between this point and the other one */
|
||||
float getDistance(const Point3& o) const {
|
||||
const float dx = x - o.x;
|
||||
const float dy = y - o.y;
|
||||
const float dz = z - o.z;
|
||||
Scalar getDistance(const _Point3& o) const {
|
||||
const Scalar dx = x - o.x;
|
||||
const Scalar dy = y - o.y;
|
||||
const Scalar dz = z - o.z;
|
||||
return std::sqrt(dx*dx + dy*dy + dz*dz);
|
||||
}
|
||||
|
||||
/** get a normalized copy */
|
||||
Point3 normalized() const {return *this / this->length();}
|
||||
_Point3 normalized() const {return *this / this->length();}
|
||||
|
||||
float length() const {return std::sqrt(x*x + y*y + z*z);}
|
||||
Scalar length() const {return std::sqrt(x*x + y*y + z*z);}
|
||||
|
||||
float length(const float norm) const {
|
||||
Scalar length(const Scalar norm) const {
|
||||
return std::pow(
|
||||
(std::pow(std::abs(x),norm) +
|
||||
std::pow(std::abs(y),norm) +
|
||||
@@ -111,16 +122,19 @@ struct Point3 {
|
||||
|
||||
private:
|
||||
|
||||
static inline bool eq(const float a, const float b, const float delta) {return std::abs(a-b) <= delta;}
|
||||
static inline bool ne(const float a, const float b, const float delta) {return std::abs(a-b) > delta;}
|
||||
static inline bool eq(const Scalar a, const Scalar b, const Scalar delta) {return std::abs(a-b) <= delta;}
|
||||
static inline bool ne(const Scalar a, const Scalar b, const Scalar delta) {return std::abs(a-b) > delta;}
|
||||
|
||||
};
|
||||
|
||||
inline bool dot(const Point3& p1, const Point3& p2) {
|
||||
return p1.x*p2.x + p1.y*p2.y + p1.z*p2.z;
|
||||
//using Point3 = _Point3<double>;
|
||||
using Point3 = _Point3<float>;
|
||||
|
||||
inline double dot(const Point3 p1, const Point3 p2) {
|
||||
return (p1.x*p2.x) + (p1.y*p2.y) + (p1.z*p2.z);
|
||||
}
|
||||
|
||||
inline Point3 cross(const Point3& a, const Point3& b) {
|
||||
inline Point3 cross(const Point3 a, const Point3 b) {
|
||||
return Point3(
|
||||
a.y*b.z - a.z*b.y,
|
||||
a.z*b.x - a.x*b.z,
|
||||
@@ -128,4 +142,4 @@ inline Point3 cross(const Point3& a, const Point3& b) {
|
||||
);
|
||||
}
|
||||
|
||||
#endif // POINT3_H
|
||||
#endif // GEO__Point3_H
|
||||
|
||||
139
geo/Polygon2.h
Normal file
139
geo/Polygon2.h
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef POLYGON2_H
|
||||
#define POLYGON2_H
|
||||
|
||||
#include "Point2.h"
|
||||
#include <vector>
|
||||
#include "Line2.h"
|
||||
#include "BBox2.h"
|
||||
|
||||
static bool polygonContainsPoint(const std::vector<Point2>& poly, const Point2 p);
|
||||
|
||||
class Polygon2 {
|
||||
|
||||
std::vector<Point2> pts;
|
||||
|
||||
public:
|
||||
|
||||
void add(const Point2 p) {
|
||||
pts.push_back(p);
|
||||
}
|
||||
|
||||
void add(std::initializer_list<Point2> lst) {
|
||||
for (const Point2 p : lst) {add(p);}
|
||||
}
|
||||
|
||||
std::vector<Point2>::iterator begin() {return pts.begin();}
|
||||
std::vector<Point2>::iterator end() {return pts.end();}
|
||||
|
||||
std::vector<Point2>::const_iterator begin() const {return pts.begin();}
|
||||
std::vector<Point2>::const_iterator end() const {return pts.end();}
|
||||
|
||||
std::vector<Point2>& getVector() {return pts;}
|
||||
|
||||
Point2& operator [] (const size_t idx) {
|
||||
return pts[idx];
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** get polygon as list of lines */
|
||||
std::vector<Line2> getLines() const {
|
||||
std::vector<Line2> res;
|
||||
for (size_t i = 0; i < pts.size(); ++i) {
|
||||
const Point2 p1 = pts[(i+0)];
|
||||
const Point2 p2 = pts[(i+1)%pts.size()];
|
||||
res.push_back(Line2(p1, p2));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
struct CutRes {
|
||||
Point2 p;
|
||||
size_t i1,i2; // line, given by two points, within this poly
|
||||
size_t j1,j2; // line, given by two points, within other, given poly
|
||||
CutRes(Point2 p, size_t i1, size_t i2, size_t j1, size_t j2) : p(p), i1(i1), i2(i2), j1(j1), j2(j2) {;}
|
||||
};
|
||||
|
||||
|
||||
BBox2 getBBox() const {
|
||||
BBox2 bb;
|
||||
for (const Point2 p : pts) {bb.add(p);}
|
||||
return bb;
|
||||
}
|
||||
|
||||
|
||||
bool contains(const Point2 p) const {
|
||||
return polygonContainsPoint(pts, p);
|
||||
}
|
||||
|
||||
std::vector<CutRes> getIntersections(const Polygon2& o, bool limit = true) const {
|
||||
|
||||
std::vector<CutRes> res;
|
||||
for (size_t i = 0; i < pts.size(); ++i) {
|
||||
const size_t i1 = (i+0);
|
||||
const size_t i2 = (i+1) % pts.size();
|
||||
const Line2 l1(pts[i1], pts[i2]);
|
||||
|
||||
for (size_t j = 0; j < o.pts.size(); ++j) {
|
||||
const size_t j1 = (j+0);
|
||||
const size_t j2 = (j+1) % o.pts.size();
|
||||
const Line2 l2(o.pts[j1], o.pts[j2]);
|
||||
|
||||
Point2 p;
|
||||
//if (l1.getSegmentIntersection(l2, p)) {
|
||||
if (intersects(l1, l2, limit, p)) {
|
||||
res.push_back(CutRes(p, i1,i2, j1,j2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/** does the given polygon (list of points) contain the given point? */
|
||||
static bool polygonContainsPoint(const std::vector<Point2>& poly, const Point2 p) {
|
||||
|
||||
// https://en.wikipedia.org/wiki/Winding_number
|
||||
// http://geomalgorithms.com/a03-_inclusion.html
|
||||
|
||||
// BBox2 bb
|
||||
// bb.grow(Point2(0.001, 0.001));
|
||||
// if (!bb.contains(p)) {return false;}
|
||||
|
||||
double sum = 0;
|
||||
for (size_t i = 0; i < poly.size(); ++i) {
|
||||
|
||||
const Point2 p1 = poly[i];
|
||||
const Point2 p2 = poly[(i+1)%poly.size()];
|
||||
|
||||
const Point2 d1 = (p1-p).normalized();
|
||||
const Point2 d2 = (p2-p).normalized();
|
||||
|
||||
sum += std::acos(dot(d1, d2));
|
||||
|
||||
}
|
||||
|
||||
// normalize (x = number of windings)
|
||||
const double x = sum / M_PI / 2;
|
||||
|
||||
// only look at the fractional part
|
||||
//const double y = x - std::floor(x);
|
||||
|
||||
return std::abs(x) >= 0.995;// && (std::abs(y) >= 0.9 || std::abs(y) < 0.1);
|
||||
|
||||
}
|
||||
|
||||
#endif // POLYGON2_H
|
||||
39
geo/Ray2.h
Normal file
39
geo/Ray2.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef GEO_RAY2_H
|
||||
#define GEO_RAY2_H
|
||||
|
||||
|
||||
#include "Point2.h"
|
||||
|
||||
struct Ray2 {
|
||||
|
||||
/** starting point */
|
||||
Point2 start;
|
||||
|
||||
/** ray's direction */
|
||||
Point2 dir;
|
||||
|
||||
public:
|
||||
|
||||
/** empty */
|
||||
Ray2() : start(), dir() {
|
||||
;
|
||||
}
|
||||
|
||||
/** ctor */
|
||||
Ray2(Point2 start, Point2 dir) : start(start), dir(dir) {
|
||||
;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // GEO_RAY2_H
|
||||
38
geo/Ray3.h
Normal file
38
geo/Ray3.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef GEO_RAY3_H
|
||||
#define GEO_RAY3_H
|
||||
|
||||
#include "Point3.h"
|
||||
|
||||
struct Ray3 {
|
||||
|
||||
/** starting point */
|
||||
Point3 start;
|
||||
|
||||
/** ray's direction */
|
||||
Point3 dir;
|
||||
|
||||
public:
|
||||
|
||||
/** empty */
|
||||
Ray3() : start(), dir() {
|
||||
;
|
||||
}
|
||||
|
||||
/** ctor */
|
||||
Ray3(Point3 start, Point3 dir) : start(start), dir(dir) {
|
||||
;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // GEO_RAY3_H
|
||||
198
geo/Sphere3.h
Normal file
198
geo/Sphere3.h
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef GEO_SPHERE3_H
|
||||
#define GEO_SPHERE3_H
|
||||
|
||||
#include "Point3.h"
|
||||
#include "Ray3.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
||||
struct Sphere3 {
|
||||
|
||||
Point3 center;
|
||||
|
||||
float radius;
|
||||
|
||||
/** empty ctor */
|
||||
Sphere3() : center(), radius(0) {;}
|
||||
|
||||
/** ctor */
|
||||
Sphere3(const Point3 center, const float radius) : center(center), radius(radius) {;}
|
||||
|
||||
|
||||
|
||||
/** does this sphere contain the given sphere? */
|
||||
bool contains(const Sphere3& o) const {
|
||||
return (o.center.getDistance(this->center) + o.radius) <= this->radius;
|
||||
}
|
||||
|
||||
/** does the sphere contain the given point? */
|
||||
bool contains(const Point3& p) const {
|
||||
return center.getDistance(p) <= radius;
|
||||
}
|
||||
|
||||
/** does the sphere intersect with the given sphere? */
|
||||
bool intersects(const Sphere3& other) const {
|
||||
return center.getDistance(other.center) < (radius + other.radius);
|
||||
}
|
||||
|
||||
/** does the sphere intersect with the given ray? */
|
||||
bool intersects(const Ray3& ray) const {
|
||||
|
||||
// if the sphere contains the ray's start -> done
|
||||
//if (contains(ray.start)) {return true;}
|
||||
|
||||
|
||||
// https://stackoverflow.com/questions/6533856/ray-sphere-intersection
|
||||
|
||||
/*
|
||||
const float xA = ray.start.x;
|
||||
const float yA = ray.start.y;
|
||||
const float zA = ray.start.z;
|
||||
|
||||
const float xB = (ray.start + ray.dir).x;
|
||||
const float yB = (ray.start + ray.dir).y;
|
||||
const float zB = (ray.start + ray.dir).z;
|
||||
|
||||
const float xC = center.x;
|
||||
const float yC = center.y;
|
||||
const float zC = center.z;
|
||||
|
||||
const float a = (xB-xA)*(xB-xA)+(yB-yA)*(yB-yA)+(zB-zA)*(zB-zA);
|
||||
const float b = 2*((xB-xA)*(xA-xC)+(yB-yA)*(yA-yC)+(zB-zA)*(zA-zC));
|
||||
const float c = (xA-xC)*(xA-xC)+(yA-yC)*(yA-yC)+(zA-zC)*(zA-zC)-(radius*radius);
|
||||
|
||||
const float delta = (b*b) - (4*a*c);
|
||||
|
||||
// intersection?
|
||||
return delta >= 0;
|
||||
*/
|
||||
|
||||
|
||||
// http://www.ccs.neu.edu/home/fell/CSU540/programs/RayTracingFormulas.htm
|
||||
/*
|
||||
const float x0 = ray.start.x;
|
||||
const float y0 = ray.start.y;
|
||||
const float z0 = ray.start.z;
|
||||
|
||||
const float cx = center.x;
|
||||
const float cy = center.y;
|
||||
const float cz = center.z;
|
||||
|
||||
const float dx = ray.dir.x;
|
||||
const float dy = ray.dir.y;
|
||||
const float dz = ray.dir.z;
|
||||
|
||||
const float a = dx*dx + dy*dy + dz*dz;
|
||||
const float b = 2*dx*(x0-cx) + 2*dy*(y0-cy) + 2*dz*(z0-cz);
|
||||
const float c = cx*cx + cy*cy + cz*cz + x0*x0 + y0*y0 + z0*z0 + -2*(cx*x0 + cy*y0 + cz*z0) - radius*radius;
|
||||
|
||||
const float discriminant = (b*b) - (4*a*c);
|
||||
return discriminant >= 0;
|
||||
*/
|
||||
|
||||
|
||||
//https://gamedev.stackexchange.com/questions/96459/fast-ray-sphere-collision-code
|
||||
|
||||
const Point3 m = ray.start - center;
|
||||
const float b = dot(m, ray.dir);
|
||||
const float c = dot(m, m) - radius*radius;
|
||||
|
||||
// Exit if r’s origin outside s (c > 0) and r pointing away from s (b > 0)
|
||||
if (c > 0.0f && b > 0.0f) {return false;}
|
||||
const float discr = b*b - c;
|
||||
|
||||
// A negative discriminant corresponds to ray missing sphere
|
||||
if (discr < 0.0f) {return false;}
|
||||
|
||||
return true;
|
||||
|
||||
// // Ray now found to intersect sphere, compute smallest t value of intersection
|
||||
// t = -b - Sqrt(discr);
|
||||
|
||||
// // If t is negative, ray started inside sphere so clamp t to zero
|
||||
// if (t < 0.0f) t = 0.0f;
|
||||
// q = p + t * d;
|
||||
|
||||
// return 1;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/** configure this sphere to contain the given point-set */
|
||||
void adjustToPointSet(const std::vector<Point3>& lst) {
|
||||
|
||||
// NOT OPTIMAL but fast
|
||||
|
||||
// calculate the point set's center
|
||||
Point3 sum(0,0,0);
|
||||
for (const Point3 p : lst) {sum += p;}
|
||||
const Point3 center = sum / lst.size();
|
||||
|
||||
// calculate the sphere's radius (maximum distance from center
|
||||
float radius = 0;
|
||||
for (const Point3 p : lst) {
|
||||
const float dist = center.getDistance(p);
|
||||
if (dist > radius) {radius = dist;}
|
||||
}
|
||||
|
||||
this->radius = radius;
|
||||
this->center = center;
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/** create a sphere that fully contains the given point-set */
|
||||
static Sphere3 around(const std::vector<Point3>& lst) {
|
||||
Sphere3 sphere;
|
||||
sphere.adjustToPointSet(lst);
|
||||
return sphere;
|
||||
}
|
||||
|
||||
/** combine two spheres into a new one containing both */
|
||||
static Sphere3 join(const Sphere3& a, const Sphere3& b) {
|
||||
|
||||
// if one already contains the other, just return it as-is
|
||||
if (a.contains(b)) {return a;}
|
||||
if (b.contains(a)) {return b;}
|
||||
|
||||
// calculate the new center and radius
|
||||
// Point3 newCen = (a.center + b.center) / 2;
|
||||
// float newRad = (a.center.getDistance(b.center) + std::max(a.radius, b.radius) * 2) / 2;
|
||||
// return Sphere3(newCen, newRad);
|
||||
|
||||
// Point3 newCen = (a.center*a.radius + b.center*b.radius) / (a.radius+b.radius);
|
||||
// float newRad = (a.center.getDistance(b.center) + a.radius + b.radius) / 2 * 1.02f; // slightly larger to prevent rounding issues
|
||||
// Sphere3 res(newCen, newRad);
|
||||
|
||||
// create both maximum ends
|
||||
const Point3 out1 = a.center + (a.center-b.center).normalized() * a.radius;
|
||||
const Point3 out2 = b.center + (b.center-a.center).normalized() * b.radius;
|
||||
|
||||
// center is within both ends, so is the radius
|
||||
Point3 newCen = (out1 + out2) / 2;
|
||||
float newRad = out1.getDistance(out2) / 2 * 1.0001; // slightly larger to prevent rounding issues
|
||||
Sphere3 res(newCen, newRad);
|
||||
|
||||
// check
|
||||
Assert::isTrue(res.contains(a), "sphere joining error. base-spheres are not contained.");
|
||||
Assert::isTrue(res.contains(b), "sphere joining error. base-spheres are not contained.");
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // GEO_SPHERE3_H
|
||||
216
geo/Triangle3.h
Normal file
216
geo/Triangle3.h
Normal file
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef TRIANGLE3_H
|
||||
#define TRIANGLE3_H
|
||||
|
||||
#include "Point3.h"
|
||||
#include "Ray3.h"
|
||||
|
||||
struct Triangle3 {
|
||||
|
||||
Point3 p1;
|
||||
Point3 p2;
|
||||
Point3 p3;
|
||||
|
||||
public:
|
||||
|
||||
/** empty ctor */
|
||||
Triangle3() : p1(), p2(), p3() {;}
|
||||
|
||||
/** ctor */
|
||||
Triangle3(Point3 p1, Point3 p2, Point3 p3) : p1(p1), p2(p2), p3(p3) {;}
|
||||
|
||||
|
||||
// Point3 cross(Point3 b, Point3 c) const {
|
||||
// return Point3(
|
||||
// b.y*c.z - c.y*b.z,
|
||||
// b.z*c.x - c.z*b.x,
|
||||
// b.x*c.y - c.x*b.y
|
||||
// );
|
||||
// }
|
||||
|
||||
// float dot(Point3 b, Point3 c) const {
|
||||
// return b.x*c.x + b.y*c.y + b.z*c.z;
|
||||
// }
|
||||
|
||||
Triangle3 operator - (const Point3 o) const {
|
||||
return Triangle3(p1-o, p2-o, p3-o);
|
||||
}
|
||||
Triangle3& operator += (const Point3 o) {
|
||||
p1 += o;
|
||||
p2 += o;
|
||||
p3 += o;
|
||||
return *this;
|
||||
}
|
||||
Triangle3& operator -= (const Point3 o) {
|
||||
p1 -= o;
|
||||
p2 -= o;
|
||||
p3 -= o;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** scale using 3 individual components */
|
||||
Triangle3& operator *= (const Point3 o) {
|
||||
p1 *= o;
|
||||
p2 *= o;
|
||||
p3 *= o;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** switch between CW<->CCW */
|
||||
void reverse() {
|
||||
std::swap(p2, p3);
|
||||
}
|
||||
|
||||
void rotate_deg(const Point3 rot) {
|
||||
p1 = p1.rot(rot.x/180.0f*M_PI, rot.y/180.0f*M_PI, rot.z/180.0f*M_PI);
|
||||
p2 = p2.rot(rot.x/180.0f*M_PI, rot.y/180.0f*M_PI, rot.z/180.0f*M_PI);
|
||||
p3 = p3.rot(rot.x/180.0f*M_PI, rot.y/180.0f*M_PI, rot.z/180.0f*M_PI);
|
||||
}
|
||||
|
||||
// void ensureCCW() {
|
||||
// Point3 norm = cross(p3-p1, p2-p1);
|
||||
// if (norm.z < 0) {
|
||||
// reverse();
|
||||
// }
|
||||
// }
|
||||
|
||||
Point3 getNormal() const {
|
||||
return cross( p2-p1, p3-p1 ).normalized();
|
||||
}
|
||||
|
||||
|
||||
// http://www.lighthouse3d.com/tutorials/maths/ray-triangle-intersection/
|
||||
bool intersects(const Ray3& ray, Point3& pos) const {
|
||||
|
||||
const Point3 e1 = p2-p1;
|
||||
const Point3 e2 = p3-p1;
|
||||
|
||||
const Point3 h = cross(ray.dir, e2);
|
||||
const double a = dot(e1, h);
|
||||
|
||||
if (a > -0.00001 && a < 0.00001) {return false;}
|
||||
|
||||
const double f = 1/a;
|
||||
|
||||
const Point3 s = ray.start - p1;
|
||||
const double u = f * dot(s,h);
|
||||
|
||||
if (u < 0.0 || u > 1.0) {return false;}
|
||||
|
||||
const Point3 q = cross(s, e1);
|
||||
const double v = f * dot(ray.dir, q);
|
||||
|
||||
if (v < 0.0 || u + v > 1.0) {return false;}
|
||||
const double t = f * dot(e2,q);
|
||||
|
||||
|
||||
if (t > 0.00001) {
|
||||
pos = ray.start + (ray.dir * t);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/** add some slight delta to prevent rounding issues */
|
||||
bool intersectsDelta(const Ray3& ray, const double delta, Point3& pos) const {
|
||||
|
||||
const Point3 e1 = p2-p1;
|
||||
const Point3 e2 = p3-p1;
|
||||
|
||||
// make delta an absolute value (independent of the triangle's size)
|
||||
// larger triangle -> smaller delta, as u,v are [0:1]
|
||||
//double deltaU = delta/e2.length();
|
||||
//double deltaV = delta/e1.length();
|
||||
|
||||
const double deltaU = delta;
|
||||
const double deltaV = delta;
|
||||
|
||||
const Point3 h = cross(ray.dir, e2);
|
||||
const double a = dot(e1, h);
|
||||
|
||||
if (a > -0.00001 && a < 0.00001) {return false;}
|
||||
|
||||
const double f = 1/a;
|
||||
|
||||
const Point3 s = ray.start - p1;
|
||||
const double u = f * dot(s,h);
|
||||
|
||||
if (u < 0.0-deltaU || u > 1.0+deltaU) {return false;}
|
||||
|
||||
const Point3 q = cross(s, e1);
|
||||
const double v = f * dot(ray.dir, q);
|
||||
|
||||
if (v < 0.0-deltaV || u + v > 1.0+deltaV) {return false;}
|
||||
const double t = f * dot(e2,q);
|
||||
|
||||
|
||||
if (t > 0.00001) {
|
||||
pos = ray.start + (ray.dir * t);
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/** perform some sanity checks */
|
||||
bool isValid() const {
|
||||
if (p1 == p2) {return false;}
|
||||
if (p1 == p3) {return false;}
|
||||
if (p2 == p3) {return false;}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
int rayIntersectsTriangle(float *p, float *d,
|
||||
float *v0, float *v1, float *v2) {
|
||||
|
||||
float e1[3],e2[3],h[3],s[3],q[3];
|
||||
float a,f,u,v;
|
||||
|
||||
//crossProduct(h,d,e2);
|
||||
a = innerProduct(e1,h);
|
||||
|
||||
if (a > -0.00001 && a < 0.00001)
|
||||
return(false);
|
||||
|
||||
f = 1/a;
|
||||
vector(s,p,v0);
|
||||
u = f * (innerProduct(s,h));
|
||||
|
||||
if (u < 0.0 || u > 1.0)
|
||||
return(false);
|
||||
|
||||
crossProduct(q,s,e1);
|
||||
v = f * innerProduct(d,q);
|
||||
|
||||
if (v < 0.0 || u + v > 1.0)
|
||||
return(false);
|
||||
|
||||
// at this stage we can compute t to find out where
|
||||
// the intersection point is on the line
|
||||
t = f * innerProduct(e2,q);
|
||||
|
||||
if (t > 0.00001) // ray intersection
|
||||
return(true);
|
||||
|
||||
else // this means that there is a line intersection
|
||||
// but not a ray intersection
|
||||
return (false);
|
||||
|
||||
}
|
||||
*/
|
||||
};
|
||||
|
||||
#endif // TRIANGLE3_H
|
||||
69
geo/TriangleStrip3.h
Normal file
69
geo/TriangleStrip3.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef TRIANGLESTRIP3_H
|
||||
#define TRIANGLESTRIP3_H
|
||||
|
||||
#include <vector>
|
||||
#include "Point3.h"
|
||||
#include "Triangle3.h"
|
||||
|
||||
class TriangleStrip3 {
|
||||
|
||||
private:
|
||||
std::vector<Point3> pts;
|
||||
|
||||
public:
|
||||
|
||||
void add(const Point3 p) {
|
||||
pts.push_back(p);
|
||||
}
|
||||
|
||||
void set(const std::vector<Point3>& pts) {
|
||||
this->pts = pts;
|
||||
}
|
||||
|
||||
void toTriangles(std::vector<Triangle3>& trias) const {
|
||||
|
||||
// https://en.wikipedia.org/wiki/Triangle_strip
|
||||
// GL_TRIANGLE_STRIP
|
||||
// Draws a series of triangles (three-sided polygons) using vertices v0, v1, v2, then v2, v1, v3 (note the order), then v2, v3, v4, and so on. The ordering is to ensure that the triangles are all drawn with the same orientation so that the strip can correctly form part of a surface.
|
||||
// For odd n, vertices n, n+1, and n+2 define triangle n. For even n, vertices n+1, n, and n+2 define triangle n. N-2 triangles are drawn.
|
||||
|
||||
for (size_t j = 2; j < pts.size(); ++j) {
|
||||
|
||||
if (j % 2 == 0) {
|
||||
Triangle3 tria(
|
||||
pts[j-2],
|
||||
pts[j-1],
|
||||
pts[j]
|
||||
);
|
||||
trias.push_back(tria);
|
||||
} else {
|
||||
Triangle3 tria(
|
||||
pts[j-1],
|
||||
pts[j-2],
|
||||
pts[j]
|
||||
);
|
||||
trias.push_back(tria);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::vector<Triangle3> toTriangles() const {
|
||||
std::vector<Triangle3> trias;
|
||||
toTriangles(trias);
|
||||
return trias;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // TRIANGLESTRIP3_H
|
||||
10
geo/Units.h
10
geo/Units.h
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef UNITS_H
|
||||
#define UNITS_H
|
||||
|
||||
|
||||
317
geo/volume/BVH.h
Normal file
317
geo/volume/BVH.h
Normal file
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef BOUNDINGVOLUMEHIERARCHY_H
|
||||
#define BOUNDINGVOLUMEHIERARCHY_H
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
#include "../Ray2.h"
|
||||
#include "../Ray3.h"
|
||||
|
||||
#include "BoundingVolume.h"
|
||||
|
||||
#include "BoundingVolumeAABB2.h"
|
||||
#include "BoundingVolumeCircle2.h"
|
||||
|
||||
#include "BoundingVolumeAABB3.h"
|
||||
#include "BoundingVolumeSphere3.h"
|
||||
|
||||
|
||||
|
||||
template <typename Element, typename Ray, typename Point, typename Volume, typename Wrapper> class BVH {
|
||||
|
||||
protected:
|
||||
|
||||
/** one node within the tree */
|
||||
struct BVHNode {
|
||||
bool isLeaf;
|
||||
bool check;
|
||||
Volume boundingVolume;
|
||||
std::vector<BVHNode*> childNodes;
|
||||
BVHNode(bool isLeaf = false, bool check = true) : isLeaf(isLeaf), check(check) {;}
|
||||
};
|
||||
|
||||
/** one leaf within the tree */
|
||||
struct BVHLeaf : public BVHNode {
|
||||
Element element;
|
||||
BVHLeaf(const Element& e, const bool check) : BVHNode(true, check), element(e) {;}
|
||||
};
|
||||
|
||||
/** the tree's root */
|
||||
BVHNode root;
|
||||
|
||||
public:
|
||||
|
||||
/** get the tree's root node */
|
||||
const BVHNode& getRoot() const {
|
||||
return root;
|
||||
}
|
||||
|
||||
/** add a new volume to the tree */
|
||||
void add(const Element& element, const bool leafCheck = true) {
|
||||
|
||||
// create a new leaf for this element
|
||||
BVHLeaf* leaf = new BVHLeaf(element, leafCheck);
|
||||
|
||||
// get the element's boundin volume
|
||||
leaf->boundingVolume = getBoundingVolume(element);
|
||||
|
||||
// add the leaf to the tree
|
||||
root.childNodes.push_back(leaf);
|
||||
|
||||
}
|
||||
|
||||
/** optimize the tree */
|
||||
int optimize(const int max = 9999) {
|
||||
for (int i = 0; i < max; ++i) {
|
||||
//const bool did = concat(); // faster
|
||||
const bool did = combineBest(); // better
|
||||
if (!did) {return i;}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
|
||||
void getHits(const Ray& ray, const std::function<void(const Element&)>& func) const {
|
||||
getHits(ray, &root, func);
|
||||
}
|
||||
|
||||
// this one has to be as fast as possible!
|
||||
static void getHits(const Ray& ray, const BVHNode* node, const std::function<void(const Element&)>& func) {
|
||||
for (const BVHNode* sub : node->childNodes) {
|
||||
if (!sub->check || sub->boundingVolume.intersects(ray)) {
|
||||
if (sub->isLeaf) {
|
||||
const BVHLeaf* leaf = static_cast<const BVHLeaf*>(sub);
|
||||
func(leaf->element);
|
||||
} else {
|
||||
getHits(ray, sub, func);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** get the tree's depth */
|
||||
int getDepth() const {
|
||||
return getDepth(&root, 1);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/** call the given function for each leaf within the given subtree */
|
||||
void forEachLeaf(const BVHNode* n, std::function<void(const BVHNode*)> func) const {
|
||||
if (n->isLeaf) {
|
||||
func(n);
|
||||
} else {
|
||||
for (BVHNode* child : n->childNodes) {
|
||||
forEachLeaf(child, func);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** determine/approximate a new bounding volume around n1+n2 */
|
||||
Volume getVolAround(const BVHNode* n1, const BVHNode* n2) const {
|
||||
//return getVolAroundExact(n1, n2);
|
||||
return getVolAroundAPX(n1, n2);
|
||||
}
|
||||
|
||||
/** determine the bounding-volume around n1 and n2 by (slowly) calculating a new, exact volume based on all leaf-elements */
|
||||
Volume getVolAroundExact(const BVHNode* n1, const BVHNode* n2) const {
|
||||
std::vector<Point> verts;
|
||||
auto onLeaf = [&] (const BVHNode* n) {
|
||||
BVHLeaf* leaf = (BVHLeaf*) n;
|
||||
std::vector<Point> subVerts = Wrapper::getVertices(leaf->element);
|
||||
verts.insert(verts.end(), subVerts.begin(), subVerts.end());
|
||||
};
|
||||
forEachLeaf(n1, onLeaf);
|
||||
forEachLeaf(n2, onLeaf);
|
||||
return Volume::fromVertices(verts);
|
||||
}
|
||||
|
||||
/** approximate the bounding-volume around n1 and n2 by (quickly) joining their current volumes. the result might be unnecessarily large */
|
||||
Volume getVolAroundAPX(const BVHNode* n1, const BVHNode* n2) const {
|
||||
return Volume::join(n1->boundingVolume, n2->boundingVolume);
|
||||
}
|
||||
|
||||
|
||||
bool combineBest() {
|
||||
|
||||
// nothing to do?
|
||||
if (root.childNodes.size() < 2) {return false;}
|
||||
|
||||
struct Best {
|
||||
BVHNode* n1 = nullptr;
|
||||
BVHNode* n2 = nullptr;
|
||||
Volume vol;
|
||||
float volSize = 99999999;
|
||||
} best;
|
||||
|
||||
for (size_t i = 0; i < root.childNodes.size(); ++i) {
|
||||
for (size_t j = 0; j < root.childNodes.size(); ++j) {
|
||||
|
||||
if (i == j) {continue;}
|
||||
|
||||
BVHNode* n1 = root.childNodes[i];
|
||||
BVHNode* n2 = root.childNodes[j];
|
||||
|
||||
const Volume newVol = getVolAround(n1,n2);
|
||||
const float newVolSize = newVol.getVolumeSize();
|
||||
if (newVolSize < best.volSize) {
|
||||
best.vol = newVol;
|
||||
best.volSize = newVolSize;
|
||||
best.n1 = n1;
|
||||
best.n2 = n2;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
root.childNodes.erase(std::remove(root.childNodes.begin(), root.childNodes.end(), best.n1), root.childNodes.end());
|
||||
root.childNodes.erase(std::remove(root.childNodes.begin(), root.childNodes.end(), best.n2), root.childNodes.end());
|
||||
|
||||
// combine both into a new node
|
||||
BVHNode* newNode = new BVHNode();
|
||||
newNode->childNodes.push_back(best.n1);
|
||||
newNode->childNodes.push_back(best.n2);
|
||||
newNode->boundingVolume = best.vol;
|
||||
|
||||
// does the newly created node contain any other nodes?
|
||||
// THIS SHOULD NEVER BE THE CASE!
|
||||
// for (size_t i = 0; i < root.childNodes.size(); ++i) {
|
||||
// BVHNode* n3 = root.childNodes[i];
|
||||
// if (newNode->boundingVolume.contains(n3->boundingVolume)) {
|
||||
// newNode->childNodes.push_back(n3);
|
||||
// root.childNodes.erase(root.childNodes.begin()+i);
|
||||
// --i;
|
||||
// }
|
||||
// }
|
||||
|
||||
// attach the node
|
||||
root.childNodes.push_back(newNode);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool concat() {
|
||||
|
||||
// nothing to do?
|
||||
if (root.childNodes.size() < 2) {return false;}
|
||||
|
||||
|
||||
bool concated = false;
|
||||
|
||||
// first, sort all elements by volume (smallest first)
|
||||
auto compVolume = [] (const BVHNode* n1, const BVHNode* n2) {
|
||||
return n1->boundingVolume.getVolumeSize() < n2->boundingVolume.getVolumeSize();
|
||||
};
|
||||
std::sort(root.childNodes.begin(), root.childNodes.end(), compVolume);
|
||||
|
||||
|
||||
// elements will be grouped into this new root
|
||||
BVHNode newRoot;
|
||||
|
||||
// combine nearby elements
|
||||
while(true) {
|
||||
|
||||
// get [and remove] the next element
|
||||
BVHNode* n0 = (BVHNode*) root.childNodes[0];
|
||||
root.childNodes.erase(root.childNodes.begin()+0);
|
||||
|
||||
// find another element that yields minimal increase in volume
|
||||
auto compNear = [n0] (const BVHNode* n1, const BVHNode* n2) {
|
||||
const float v1 = Volume::join(n0->boundingVolume, n1->boundingVolume).getVolumeSize();
|
||||
const float v2 = Volume::join(n0->boundingVolume, n2->boundingVolume).getVolumeSize();
|
||||
return v1 < v2;
|
||||
};
|
||||
auto it = std::min_element(root.childNodes.begin(), root.childNodes.end(), compNear);
|
||||
BVHNode* n1 = *it;
|
||||
|
||||
// calculate the resulting increment in volume
|
||||
const Volume joined = Volume::join(n0->boundingVolume, n1->boundingVolume);
|
||||
const float increment = joined.getVolumeSize() / n0->boundingVolume.getVolumeSize();
|
||||
const bool intersects = n0->boundingVolume.intersects(n1->boundingVolume);
|
||||
|
||||
const bool combine = true; //(intersects); //(increment < 15.0);
|
||||
|
||||
if (combine) {
|
||||
|
||||
// remove from current root
|
||||
root.childNodes.erase(it);
|
||||
|
||||
// combine both into a new node
|
||||
BVHNode* node = new BVHNode();
|
||||
node->childNodes.push_back(n0);
|
||||
node->childNodes.push_back(n1);
|
||||
node->boundingVolume = joined;
|
||||
newRoot.childNodes.push_back(node);
|
||||
|
||||
concated = true;
|
||||
|
||||
} else {
|
||||
|
||||
BVHNode* node = new BVHNode();
|
||||
node->childNodes.push_back(n0);
|
||||
node->boundingVolume = n0->boundingVolume;
|
||||
newRoot.childNodes.push_back(node);
|
||||
|
||||
}
|
||||
|
||||
// done?
|
||||
if (root.childNodes.size() == 1) {
|
||||
BVHNode* node = new BVHNode();
|
||||
node->childNodes.push_back(root.childNodes.front());
|
||||
node->boundingVolume = root.childNodes.front()->boundingVolume;
|
||||
newRoot.childNodes.push_back(node);
|
||||
break;
|
||||
} else if (root.childNodes.size() == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
root = newRoot;
|
||||
return concated;
|
||||
|
||||
}
|
||||
|
||||
int getDepth(const BVHNode* node, const int cur) const {
|
||||
if (node->isLeaf) {
|
||||
return cur;
|
||||
} else {
|
||||
int res = cur;
|
||||
for (const BVHNode* sub : node->childNodes) {
|
||||
const int subDepth = getDepth(sub, cur+1);
|
||||
if (subDepth > res) {res = subDepth;}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
/** get a bounding-volume for the given element */
|
||||
Volume getBoundingVolume(const Element& element) {
|
||||
const std::vector<Point> verts = Wrapper::getVertices(element);
|
||||
return Volume::fromVertices(verts);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename Element, typename Volume, typename Wrapper> class BVH3 : public BVH<Element, Ray3, Point3, Volume, Wrapper> {
|
||||
|
||||
};
|
||||
|
||||
template <typename Element, typename Volume, typename Wrapper> class BVH2 : public BVH<Element, Ray2, Point2, Volume, Wrapper> {
|
||||
|
||||
};
|
||||
|
||||
#endif // BOUNDINGVOLUMEHIERARCHY_H
|
||||
252
geo/volume/BVHDebug.h
Normal file
252
geo/volume/BVHDebug.h
Normal file
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef BVHDEBUG_H
|
||||
#define BVHDEBUG_H
|
||||
|
||||
#include "BVH.h"
|
||||
|
||||
|
||||
#include <KLib/misc/gnuplot/Gnuplot.h>
|
||||
|
||||
#include <KLib/misc/gnuplot/GnuplotSplot.h>
|
||||
#include <KLib/misc/gnuplot/GnuplotSplotElementPoints.h>
|
||||
#include <KLib/misc/gnuplot/GnuplotSplotElementColorPoints.h>
|
||||
#include <KLib/misc/gnuplot/GnuplotSplotElementLines.h>
|
||||
|
||||
#include <KLib/misc/gnuplot/GnuplotPlot.h>
|
||||
#include <KLib/misc/gnuplot/GnuplotPlotElementPoints.h>
|
||||
#include <KLib/misc/gnuplot/GnuplotPlotElementColorLines.h>
|
||||
#include <KLib/misc/gnuplot/GnuplotPlotElementLines.h>
|
||||
|
||||
#include "../BBox3.h"
|
||||
#include <random>
|
||||
|
||||
///** adds some debug helpers to the BVH */
|
||||
//template <typename Element, typename Ray, typename Point, typename Volume, typename Wrapper> class BVHDebug : public BVH<Element, Ray, Point, Volume, Wrapper> {
|
||||
|
||||
|
||||
//};
|
||||
|
||||
template <typename Element, typename Volume, typename Wrapper> class BVH3Debug : public BVH<Element, Ray3, Point3, Volume, Wrapper> {
|
||||
|
||||
using BVHNode = typename BVH<Element, Ray3, Point3, Volume, Wrapper>::BVHNode;
|
||||
using BVHLeaf = typename BVH<Element, Ray3, Point3, Volume, Wrapper>::BVHLeaf;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
void show(int maxPts = 1500, bool showLeafs = true) {
|
||||
|
||||
std::stringstream out;
|
||||
|
||||
static K::Gnuplot gp;
|
||||
K::GnuplotSplot plot;
|
||||
|
||||
K::GnuplotSplotElementColorPoints pVol; plot.add(&pVol); //pVol.setColor(K::GnuplotColor::fromRGB(128,128,128));
|
||||
K::GnuplotSplotElementPoints pElemPoints; plot.add(&pElemPoints); pElemPoints.setColor(K::GnuplotColor::fromRGB(0,0,255));
|
||||
K::GnuplotSplotElementLines pElemLines; plot.add(&pElemLines); pElemLines.getStroke().setColor(K::GnuplotColor::fromRGB(0,0,255));
|
||||
|
||||
const int maxDepth = this->getDepth();
|
||||
recurse(maxPts, showLeafs, 0, &this->root, pVol, pElemPoints, pElemLines);
|
||||
|
||||
plot.getAxisCB().setRange(0, maxDepth);
|
||||
|
||||
gp << "set view equal xyz\n";
|
||||
gp.draw(plot);
|
||||
gp.flush();
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Point3 getRandomPoint(BoundingVolumeSphere3 sphere) {
|
||||
static std::minstd_rand gen;
|
||||
std::uniform_real_distribution<float> dist(-1, +1);
|
||||
Point3 dir = Point3(dist(gen), dist(gen), dist(gen)).normalized() * sphere.radius;
|
||||
return sphere.center + dir;
|
||||
}
|
||||
|
||||
void addLines(const Element& elem, K::GnuplotSplotElementLines& elemLines) {
|
||||
|
||||
std::vector<Point3> pts = Wrapper::getDebugLines(elem);
|
||||
|
||||
for (size_t i = 0; i< pts.size(); i+=2) {
|
||||
|
||||
const Point3 p1 = pts[i+0];
|
||||
const Point3 p2 = pts[i+1];
|
||||
|
||||
K::GnuplotPoint3 gp1(p1.x, p1.y, p1.z);
|
||||
K::GnuplotPoint3 gp2(p2.x, p2.y, p2.z);
|
||||
|
||||
elemLines.addSegment(gp1, gp2);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int recurse(int maxPts, bool showLeafs, int curDepth, const BVHNode* node, K::GnuplotSplotElementColorPoints& vol, K::GnuplotSplotElementPoints& pElemPoints, K::GnuplotSplotElementLines& elemLines) {
|
||||
|
||||
int resDepth = curDepth;
|
||||
|
||||
for (BVHNode* sub : node->childNodes) {
|
||||
resDepth = recurse(maxPts, showLeafs, curDepth+1, sub, vol, pElemPoints, elemLines);
|
||||
}
|
||||
|
||||
if (!node->isLeaf || showLeafs) {
|
||||
|
||||
const int numPts = maxPts / (curDepth+1);
|
||||
|
||||
for (int i = 0; i < numPts; ++i) {
|
||||
const Point3 p = getRandomPoint(node->boundingVolume);
|
||||
vol.add(K::GnuplotPoint3(p.x, p.y, p.z), curDepth);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (node->isLeaf) {
|
||||
BVHLeaf* leaf = (BVHLeaf*) node;
|
||||
std::vector<Point3> verts = Wrapper::getVertices(leaf->element);
|
||||
for (const Point3 p : verts) {
|
||||
pElemPoints.add(K::GnuplotPoint3(p.x, p.y, p.z));
|
||||
}
|
||||
addLines(leaf->element, elemLines);
|
||||
}
|
||||
|
||||
return resDepth;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename Element, typename Volume, typename Wrapper> class BVH2Debug : public BVH<Element, Ray2, Point2, Volume, Wrapper> {
|
||||
|
||||
using BVHNode = typename BVH<Element, Ray2, Point2, Volume, Wrapper>::BVHNode;
|
||||
using BVHLeaf = typename BVH<Element, Ray2, Point2, Volume, Wrapper>::BVHLeaf;
|
||||
|
||||
public:
|
||||
|
||||
void show(int maxPts = 1500, bool showLeafs = true) {
|
||||
|
||||
std::stringstream out;
|
||||
|
||||
static K::Gnuplot gp;
|
||||
K::GnuplotPlot plot;
|
||||
|
||||
|
||||
K::GnuplotPlotElementColorLines pVolLines; plot.add(&pVolLines);
|
||||
K::GnuplotPlotElementPoints pElemPoints; plot.add(&pElemPoints); pElemPoints.setColor(K::GnuplotColor::fromRGB(0,0,255));
|
||||
K::GnuplotPlotElementLines pElemLines; plot.add(&pElemLines); pElemLines.getStroke().setColor(K::GnuplotColor::fromRGB(0,0,255));
|
||||
|
||||
const int maxDepth = this->getDepth();
|
||||
recurse(maxDepth, showLeafs, 0, &this->root, plot, pVolLines, pElemPoints, pElemLines);
|
||||
|
||||
plot.getObjects().reOrderByZIndex();
|
||||
plot.getAxisCB().setRange(0, maxDepth);
|
||||
|
||||
gp << "set size ratio -1\n";
|
||||
gp.draw(plot);
|
||||
gp.flush();
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
|
||||
void addLines(const Element& elem, K::GnuplotPlotElementLines& elemLines) {
|
||||
|
||||
std::vector<Point2> pts = Wrapper::getDebugLines(elem);
|
||||
|
||||
for (size_t i = 0; i< pts.size(); i+=2) {
|
||||
|
||||
const Point2 p1 = pts[i+0];
|
||||
const Point2 p2 = pts[i+1];
|
||||
|
||||
K::GnuplotPoint2 gp1(p1.x, p1.y);
|
||||
K::GnuplotPoint2 gp2(p2.x, p2.y);
|
||||
|
||||
elemLines.addSegment(gp1, gp2);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::vector<std::string> colors = {
|
||||
"#888800", "#444400", "#008888", "#004444", "#880088", "#440044", "#ee0000", "#880000", "#00ee00", "#008800", "#0000ee", "#000088",
|
||||
"#888800", "#444400", "#008888", "#004444", "#880088", "#440044", "#ee0000", "#880000", "#00ee00", "#008800", "#0000ee", "#000088",
|
||||
"#888800", "#444400", "#008888", "#004444", "#880088", "#440044", "#ee0000", "#880000", "#00ee00", "#008800", "#0000ee", "#000088"
|
||||
};
|
||||
|
||||
void showVolume(const BoundingVolumeCircle2& circle, int maxDepth, int curDepth, K::GnuplotPlot& plot, K::GnuplotPlotElementColorLines& pVolLines) {
|
||||
K::GnuplotObjectPolygon* poly = new K::GnuplotObjectPolygon();
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
const float f = M_PI*2 * i / 19;
|
||||
const Point2 p = circle.getPointAt(f);
|
||||
poly->add(K::GnuplotCoordinate2(p.x, p.y, K::GnuplotCoordinateSystem::FIRST));
|
||||
poly->getFill().setColor(K::GnuplotColor::fromHexStr(colors[maxDepth-curDepth]));
|
||||
poly->getFill().setStyle(K::GnuplotFillStyle::SOLID);
|
||||
poly->setZIndex(curDepth);
|
||||
}
|
||||
plot.getObjects().add(poly);
|
||||
}
|
||||
|
||||
void showVolume(const BoundingVolumeAABB2& _aabb, int maxDepth, int curDepth, K::GnuplotPlot& plot, K::GnuplotPlotElementColorLines& pVolLines) {
|
||||
BBox2 bbox2 = _aabb;
|
||||
bbox2.grow( (10-curDepth) / 100.0f );
|
||||
// pVolLines.add(K::GnuplotPoint2(bbox2.getMin().x, bbox2.getMin().y), curDepth);
|
||||
// pVolLines.add(K::GnuplotPoint2(bbox2.getMax().x, bbox2.getMin().y), curDepth);
|
||||
// pVolLines.add(K::GnuplotPoint2(bbox2.getMax().x, bbox2.getMax().y), curDepth);
|
||||
// pVolLines.add(K::GnuplotPoint2(bbox2.getMin().x, bbox2.getMax().y), curDepth);
|
||||
// pVolLines.add(K::GnuplotPoint2(bbox2.getMin().x, bbox2.getMin().y), curDepth);
|
||||
// pVolLines.splitFace();
|
||||
K::GnuplotObjectPolygon* poly = new K::GnuplotObjectPolygon();
|
||||
poly->getStroke().setColor(K::GnuplotColor::fromHexStr(colors[maxDepth-curDepth]));
|
||||
//poly->getFill().setColor(K::GnuplotColor::fromHexStr(colors[maxDepth-curDepth]));
|
||||
//poly->getFill().setStyle(K::GnuplotFillStyle::SOLID);
|
||||
poly->add(K::GnuplotCoordinate2(bbox2.getMin().x, bbox2.getMin().y, K::GnuplotCoordinateSystem::FIRST));
|
||||
poly->add(K::GnuplotCoordinate2(bbox2.getMax().x, bbox2.getMin().y, K::GnuplotCoordinateSystem::FIRST));
|
||||
poly->add(K::GnuplotCoordinate2(bbox2.getMax().x, bbox2.getMax().y, K::GnuplotCoordinateSystem::FIRST));
|
||||
poly->add(K::GnuplotCoordinate2(bbox2.getMin().x, bbox2.getMax().y, K::GnuplotCoordinateSystem::FIRST));
|
||||
poly->close();
|
||||
poly->setZIndex(curDepth);
|
||||
plot.getObjects().add(poly);
|
||||
}
|
||||
|
||||
int recurse(int maxDepth, bool showLeafs, int curDepth, const BVHNode* node, K::GnuplotPlot& plot, K::GnuplotPlotElementColorLines& pVolLines, K::GnuplotPlotElementPoints& pElemPoints, K::GnuplotPlotElementLines& elemLines) {
|
||||
|
||||
int resDepth = curDepth;
|
||||
|
||||
for (BVHNode* sub : node->childNodes) {
|
||||
resDepth = recurse(maxDepth, showLeafs, curDepth+1, sub, plot, pVolLines, pElemPoints, elemLines);
|
||||
}
|
||||
|
||||
if (!node->isLeaf || showLeafs) {
|
||||
if (node != &this->root) {
|
||||
//const int numPts = maxPts / (curDepth+1);
|
||||
showVolume(node->boundingVolume, maxDepth, curDepth, plot, pVolLines);
|
||||
}
|
||||
}
|
||||
|
||||
if (node->isLeaf) {
|
||||
BVHLeaf* leaf = (BVHLeaf*) node;
|
||||
std::vector<Point2> verts = Wrapper::getVertices(leaf->element);
|
||||
for (const Point2 p : verts) {
|
||||
pElemPoints.add(K::GnuplotPoint2(p.x, p.y));
|
||||
}
|
||||
addLines(leaf->element, elemLines);
|
||||
}
|
||||
|
||||
return resDepth;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // BVHDEBUG_H
|
||||
38
geo/volume/BoundingVolume.h
Normal file
38
geo/volume/BoundingVolume.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef BOUNDINGVOLUME_H
|
||||
#define BOUNDINGVOLUME_H
|
||||
|
||||
#include "../Point3.h"
|
||||
#include "../Ray3.h"
|
||||
|
||||
class BoundingVolume {
|
||||
|
||||
public:
|
||||
|
||||
// /** get the volume's size (something like m^3) */
|
||||
// virtual float getVolumeSize() const = 0;
|
||||
|
||||
// /** does the volume contain the given point? */
|
||||
// virtual bool contains(const Point3 p) const = 0;
|
||||
|
||||
// /** does the volume contain the given volume? */
|
||||
// virtual bool contains(const BoundingVolume& other) const = 0;
|
||||
|
||||
// /** does the volume intersect with the given ray? */
|
||||
// virtual bool intersects(const Ray3& ray) const = 0;
|
||||
|
||||
// /** does the volume intersect with the given volume? */
|
||||
// virtual bool intersects(const BoundingVolume& other) const = 0;
|
||||
|
||||
};
|
||||
|
||||
#endif // BOUNDINGVOLUME_H
|
||||
46
geo/volume/BoundingVolumeAABB2.h
Normal file
46
geo/volume/BoundingVolumeAABB2.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef BOUNDINGVOLUMEAABB2_H
|
||||
#define BOUNDINGVOLUMEAABB2_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "BoundingVolume.h"
|
||||
#include "../BBox2.h"
|
||||
|
||||
class BoundingVolumeAABB2 : public BBox2 {
|
||||
|
||||
public:
|
||||
|
||||
BoundingVolumeAABB2() {;}
|
||||
|
||||
BoundingVolumeAABB2(const BBox2& bb) : BBox2(bb) {;}
|
||||
|
||||
float getVolumeSize() const {
|
||||
const float dx = getMax().x - getMin().x;
|
||||
const float dy = getMax().y - getMin().y;
|
||||
return (dx*dy);
|
||||
}
|
||||
|
||||
/** construct a volume around the given point-set */
|
||||
static BoundingVolumeAABB2 fromVertices(const std::vector<Point2>& verts) {
|
||||
BoundingVolumeAABB2 bvs;
|
||||
for (const Point2 p : verts) {bvs.add(p);}
|
||||
return bvs;
|
||||
}
|
||||
|
||||
static BoundingVolumeAABB2 join(const BoundingVolumeAABB2 a, const BoundingVolumeAABB2 b) {
|
||||
return BoundingVolumeAABB2(BBox2::join(a, b));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // BOUNDINGVOLUMEAABB2_H
|
||||
40
geo/volume/BoundingVolumeAABB3.h
Normal file
40
geo/volume/BoundingVolumeAABB3.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef BOUNDINGVOLUMEBOX_H
|
||||
#define BOUNDINGVOLUMEBOX_H
|
||||
|
||||
#include "BoundingVolume.h"
|
||||
#include "../Point3.h"
|
||||
|
||||
class BoundingVolumeAABB : public BoundingVolume {
|
||||
|
||||
static constexpr float MAX = +99999;
|
||||
static constexpr float MIN = -99999;
|
||||
|
||||
/** minimum */
|
||||
Point3 p1;
|
||||
|
||||
/** maximum */
|
||||
Point3 p2;
|
||||
|
||||
public:
|
||||
|
||||
/** empty ctor */
|
||||
BoundingVolumeAABB() : p1(MAX,MAX,MAX), p2(MIN,MIN,MIN) {;}
|
||||
|
||||
float getVolumeSize() const {
|
||||
return (p2.x-p1.x) * (p2.y-p1.y) * (p2.z-p1.z);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // BOUNDINGVOLUMEBOX_H
|
||||
48
geo/volume/BoundingVolumeCircle2.h
Normal file
48
geo/volume/BoundingVolumeCircle2.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef BOUDINGVOLUMECIRCLE2_H
|
||||
#define BOUDINGVOLUMECIRCLE2_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "BoundingVolume.h"
|
||||
#include "../Circle2.h"
|
||||
|
||||
class BoundingVolumeCircle2 : public Circle2 {
|
||||
|
||||
public:
|
||||
|
||||
BoundingVolumeCircle2() {;}
|
||||
|
||||
BoundingVolumeCircle2(const Circle2& c) : Circle2(c) {;}
|
||||
|
||||
float getVolumeSize() const {
|
||||
return M_PI * (radius*radius);
|
||||
}
|
||||
|
||||
bool intersects(const Ray2 ray) const {
|
||||
return Circle2::intersects(ray);
|
||||
}
|
||||
|
||||
/** construct a volume around the given point-set */
|
||||
static BoundingVolumeCircle2 fromVertices(const std::vector<Point2>& verts) {
|
||||
BoundingVolumeCircle2 bvs;
|
||||
bvs.adjustToPointSet(verts);
|
||||
return bvs;
|
||||
}
|
||||
|
||||
static BoundingVolumeCircle2 join(const BoundingVolumeCircle2 a, const BoundingVolumeCircle2 b) {
|
||||
return BoundingVolumeCircle2(Circle2::join(a, b));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // BOUDINGVOLUMECIRCLE2_H
|
||||
63
geo/volume/BoundingVolumeSphere3.h
Normal file
63
geo/volume/BoundingVolumeSphere3.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef BOUNDINGVOLUMESPHERE_H
|
||||
#define BOUNDINGVOLUMESPHERE_H
|
||||
|
||||
#include "BoundingVolume.h"
|
||||
#include "../Sphere3.h"
|
||||
#include "../Point3.h"
|
||||
|
||||
class BoundingVolumeSphere3 : public BoundingVolume, public Sphere3 {
|
||||
|
||||
public:
|
||||
|
||||
BoundingVolumeSphere3() {;}
|
||||
|
||||
BoundingVolumeSphere3(const Sphere3& s) : Sphere3(s) {;}
|
||||
|
||||
float getVolumeSize() const {
|
||||
return (4.0f / 3.0f) * M_PI * (radius*radius*radius);
|
||||
}
|
||||
|
||||
bool contains(const Point3 p) const {
|
||||
return Sphere3::contains(p);
|
||||
}
|
||||
|
||||
bool intersects(const Ray3& ray) const {
|
||||
return Sphere3::intersects(ray);
|
||||
}
|
||||
|
||||
/** does the volume intersect with the given volume? */
|
||||
bool intersects(const BoundingVolume& other) const {
|
||||
const BoundingVolumeSphere3& sphere = (const BoundingVolumeSphere3&) other;
|
||||
return Sphere3::intersects(sphere);
|
||||
}
|
||||
|
||||
/** does the volume contain the given volume? */
|
||||
bool contains(const BoundingVolume& other) const {
|
||||
const BoundingVolumeSphere3& sphere = (const BoundingVolumeSphere3&) other;
|
||||
return Sphere3::contains(sphere);
|
||||
}
|
||||
|
||||
/** construct a volume around the given point-set */
|
||||
static BoundingVolumeSphere3 fromVertices(const std::vector<Point3>& verts) {
|
||||
BoundingVolumeSphere3 bvs;
|
||||
bvs.adjustToPointSet(verts);
|
||||
return bvs;
|
||||
}
|
||||
|
||||
static BoundingVolumeSphere3 join(const BoundingVolumeSphere3 a, const BoundingVolumeSphere3 b) {
|
||||
return BoundingVolumeSphere3(Sphere3::join(a, b));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // BOUNDINGVOLUMESPHERE_H
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef DEFAULTGRIDNODE_H
|
||||
#define DEFAULTGRIDNODE_H
|
||||
|
||||
|
||||
82
grid/Grid.h
82
grid/Grid.h
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef GRID_H
|
||||
#define GRID_H
|
||||
|
||||
@@ -16,6 +26,11 @@
|
||||
#include "../geo/BBox3.h"
|
||||
#include "../misc/Debug.h"
|
||||
|
||||
#define GM_BOX 1
|
||||
#define GM_HOBEYCOMB 2
|
||||
#define GRID_MODE GM_BOX
|
||||
|
||||
|
||||
/**
|
||||
* grid of a given-size, storing some user-data-value which
|
||||
* - extends GridPoint and GridNode
|
||||
@@ -68,6 +83,11 @@ public:
|
||||
hashes.clear();
|
||||
}
|
||||
|
||||
/** is the grid empty? */
|
||||
bool isEmpty() const {
|
||||
return nodes.empty();
|
||||
}
|
||||
|
||||
/** no-assign */
|
||||
void operator = (const Grid& o) = delete;
|
||||
|
||||
@@ -177,10 +197,11 @@ public:
|
||||
}
|
||||
|
||||
/** get the center-node the given Point belongs to */
|
||||
const T& getNodeFor(const GridPoint& p) {
|
||||
const UID uid = getUID(p);
|
||||
Assert::isTrue(hashes.find(uid) != hashes.end(), "element not found!");
|
||||
return nodes[hashes[uid]];
|
||||
const T& getNodeFor(const GridPoint& p) const {
|
||||
//const UID uid = getUID(p);
|
||||
auto it = hashes.find(getUID(p));
|
||||
Assert::isTrue(it != hashes.end(), "element not found!");
|
||||
return nodes[it->second];
|
||||
}
|
||||
|
||||
/** get the center-node the given Point belongs to. or nullptr if not present */
|
||||
@@ -213,6 +234,14 @@ public:
|
||||
return GridNodeBBox(node, gridSize_cm);
|
||||
}
|
||||
|
||||
/** is this node part of a non-plain stair/escalator */
|
||||
bool isPlain(const T& n1) const {
|
||||
for (const T& n2 : neighbors(n1)) {
|
||||
if (n2.z_cm != n1.z_cm) {return false;}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* get an UID for the given point.
|
||||
* this works only for aligned points.
|
||||
@@ -231,14 +260,32 @@ public:
|
||||
const uint64_t center = 1 << 19;
|
||||
|
||||
// build
|
||||
const uint64_t x = center + (int64_t) std::round((p.x_cm) / (float)gridSize_cm);
|
||||
const uint64_t y = center + (int64_t) std::round((p.y_cm) / (float)gridSize_cm);
|
||||
const uint64_t z = center + (int64_t) std::round((p.z_cm) / (float)gridSize_cm * 5); // z is usually much lower and not always aligned -> allow more room for hashes
|
||||
#if (GRID_MODE == GM_HOBEYCOMB)
|
||||
const int xx = ((int)std::round(p.y_cm / gridSize_cm) % 2 == 0) ? (0) : (gridSize_cm/2);
|
||||
const uint64_t x = center + (int64_t) idxX(p.x_cm-xx);
|
||||
const uint64_t y = center + (int64_t) idxY(p.y_cm);
|
||||
const uint64_t z = center + (int64_t) idxZ(p.z_cm);
|
||||
#elif (GRID_MODE == GM_BOX)
|
||||
const uint64_t x = center + (int64_t) idxX(p.x_cm);
|
||||
const uint64_t y = center + (int64_t) idxY(p.y_cm);
|
||||
const uint64_t z = center + (int64_t) idxZ(p.z_cm);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
return (z << 40) | (y << 20) | (x << 0);
|
||||
|
||||
}
|
||||
|
||||
inline int idxX(const int x_cm) const {return std::round(x_cm / (float)gridSize_cm);}
|
||||
inline int idxY(const int y_cm) const {return std::round(y_cm / (float)gridSize_cm);}
|
||||
inline int idxZ(const int z_cm) const {return std::round(z_cm / 20.0f);} // * 5?? // z is usually much lower and not always aligned -> allow more room for hashes
|
||||
|
||||
inline int snapX(const int x_cm) const {return std::round(x_cm / (float)gridSize_cm) * gridSize_cm;}
|
||||
inline int snapY(const int y_cm) const {return std::round(y_cm / (float)gridSize_cm) * gridSize_cm;}
|
||||
inline int snapZ(const int z_cm) const {return std::round(z_cm / 20.0f) * 20;} // * 5?? // z is usually much lower and not always aligned -> allow more room for hashes
|
||||
|
||||
|
||||
/** array access */
|
||||
T& operator [] (const int idx) {
|
||||
Assert::isBetween(idx, 0, getNumNodes()-1, "index out of bounds");
|
||||
@@ -268,7 +315,7 @@ public:
|
||||
}
|
||||
|
||||
/** remove the connection from n1 to n2 (not the other way round!) */
|
||||
void disconnectUniDir(T& n1, T& n2) {
|
||||
void disconnectUniDir(T& n1, const T& n2) {
|
||||
for (int n = 0; n < n1._numNeighbors; ++n) {
|
||||
if (n1._neighbors[n] == n2._idx) {
|
||||
arrayRemove(n1._neighbors, n, n1._numNeighbors);
|
||||
@@ -278,6 +325,11 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/** convert to a GridPoint coordinate (in cm) */
|
||||
GridPoint toGridPoint(const Point3 pos_m) const {
|
||||
return GridPoint( snapX(pos_m.x*100), snapY(pos_m.y*100), snapZ(pos_m.z*100) );
|
||||
}
|
||||
|
||||
/** remove the given array-index by moving all follwing elements down by one */
|
||||
template <typename X> void arrayRemove(X* arr, const int idxToRemove, const int arrayLen) {
|
||||
for (int i = idxToRemove+1; i < arrayLen; ++i) {
|
||||
@@ -301,12 +353,16 @@ public:
|
||||
void remove(T& node) {
|
||||
|
||||
// disconnect from all neighbors
|
||||
int cnt = 0;
|
||||
while (node._numNeighbors) {
|
||||
|
||||
// by removing one neighbor, all others are shifted to close the gap within the array
|
||||
// its therefor ok to always delete array index [0]
|
||||
disconnectBiDir(node._idx, node._neighbors[0]);
|
||||
|
||||
if (++cnt > node.MAX_NEIGHBORS) {
|
||||
Log::add(name, "WARNING! found unsolveable neighboring! " + node.asString(), true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// remove from hash-list
|
||||
@@ -456,11 +512,11 @@ public:
|
||||
|
||||
|
||||
|
||||
NeighborForEach neighbors(const int idx) {
|
||||
NeighborForEach neighbors(const int idx) const {
|
||||
return neighbors(nodes[idx]);
|
||||
}
|
||||
|
||||
NeighborForEach neighbors(const T& node) {
|
||||
NeighborForEach neighbors(const T& node) const {
|
||||
return NeighborForEach(*this, node._idx);
|
||||
}
|
||||
|
||||
@@ -500,9 +556,13 @@ private:
|
||||
|
||||
/** asssert that the given element is aligned to the grid */
|
||||
void assertAligned(const T& elem) {
|
||||
#if (GRID_MODE == GM_HOBEYCOMB)
|
||||
|
||||
#elif (GRID_MODE == GM_BOX)
|
||||
if (((int)elem.x_cm % gridSize_cm) != 0) {throw Exception("element's x is not aligned!");}
|
||||
if (((int)elem.y_cm % gridSize_cm) != 0) {throw Exception("element's y is not aligned!");}
|
||||
//if (((int)elem.z_cm % gridSize_cm) != 0) {throw Exception("element's z is not aligned!");}
|
||||
#endif
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef GRIDNEIGHBORITERATOR_H
|
||||
#define GRIDNEIGHBORITERATOR_H
|
||||
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef GRIDNODE_H
|
||||
#define GRIDNODE_H
|
||||
|
||||
@@ -52,6 +62,9 @@ public:
|
||||
static const uint8_t TYPE_ELEVATOR = 2;
|
||||
static const uint8_t TYPE_DOOR = 3;
|
||||
|
||||
static const uint8_t TYPE_OUTDOOR = 100;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
@@ -61,6 +74,9 @@ public:
|
||||
/** get the node's index within its grid */
|
||||
int getIdx() const {return _idx;}
|
||||
|
||||
/** get the x-th neighbor's index within its grid */
|
||||
int getNeighborIdx(const int x) const {return _neighbors[x];}
|
||||
|
||||
/** get the number of neighbors for this node */
|
||||
int getNumNeighbors() const {return _numNeighbors;}
|
||||
|
||||
@@ -81,6 +97,7 @@ public:
|
||||
/** set the node's semantic type */
|
||||
void setType(const uint8_t type) {this->_type = type;}
|
||||
|
||||
|
||||
// /** get the n-th neighbor for this node */
|
||||
// template <int gridSize_cm, typename T> inline T& getNeighbor(const int nth, const Grid<gridSize_cm, T>& grid) const {
|
||||
// return grid.getNeighbor(_idx, nth);
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef GRIDNODEBBOX_H
|
||||
#define GRIDNODEBBOX_H
|
||||
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef GRIDPOINT_H
|
||||
#define GRIDPOINT_H
|
||||
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef GRIDFACTORY_H
|
||||
#define GRIDFACTORY_H
|
||||
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef GRIDIMPORTANCE_H
|
||||
#define GRIDIMPORTANCE_H
|
||||
|
||||
@@ -11,7 +21,7 @@
|
||||
#include "../../nav/dijkstra/Dijkstra.h"
|
||||
#include "../../nav/dijkstra/DijkstraPath.h"
|
||||
|
||||
#include "../../math/Distributions.h"
|
||||
#include "../../math/distribution/Normal.h"
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef GRID_ELEVATORS_H
|
||||
#define GRID_ELEVATORS_H
|
||||
|
||||
@@ -50,10 +60,15 @@ public:
|
||||
std::vector<IntPos> nodesWithin;
|
||||
const HelperPoly poly(elevator->getPoints());
|
||||
|
||||
// elevator starts at the current floor, but where does the elevator end?
|
||||
const int f1_cm = grid.snapZ(floor->getStartingZ()*100);
|
||||
// elevator's end is given by its height
|
||||
const int f2_cm = grid.snapZ( (floor->getStartingZ() + elevator->height_m) * 100);
|
||||
|
||||
auto callback = [&] (const int x_cm, const int y_cm) {
|
||||
|
||||
const GridPoint gp1(x_cm, y_cm, floor->getStartingZ()*100); // starting floor
|
||||
const GridPoint gp2(x_cm, y_cm, floor->getEndingZ()*100); // the floor above
|
||||
const GridPoint gp1(x_cm, y_cm, f1_cm); // starting floor
|
||||
const GridPoint gp2(x_cm, y_cm, f2_cm); // the floor above
|
||||
|
||||
// ensure such a node is present in both floors (and thus a connection is possible)
|
||||
if (grid.hasNodeFor(gp1) && grid.hasNodeFor(gp2)) {
|
||||
@@ -63,29 +78,85 @@ public:
|
||||
};
|
||||
poly.forEachGridPoint(gs_cm, callback);
|
||||
|
||||
if (nodesWithin.empty()) {
|
||||
throw Exception("faild to determine starting and ending nodes for elevator. disconnected?");
|
||||
}
|
||||
|
||||
// now create the interconnection in z-direction
|
||||
const int z1_cm = std::ceil((floor->getStartingZ()*100+1) / gs_cm) * gs_cm; // the next node above the current flor
|
||||
const int z2_cm = std::floor((floor->getEndingZ()*100-1) / gs_cm) * gs_cm; // the last node below the next floor
|
||||
//const int z1_cm = std::ceil((floor->getStartingZ()*100+1) / gs_cm) * gs_cm; // the next node above the current flor
|
||||
//const int z2_cm = std::floor((floor->getEndingZ()*100-1) / gs_cm) * gs_cm; // the last node below the next floor
|
||||
|
||||
int z1_cm = std::ceil((f1_cm+1.0f) / gs_cm) * gs_cm;
|
||||
int z2_cm = std::floor((f2_cm-1.0f) / gs_cm) * gs_cm;
|
||||
|
||||
// ensure the nodes between (z1,z2) are not directly the floor (f1,f2)
|
||||
//if (grid.snapZ(z1_cm) == grid.snapZ(f1_cm)) {z1_cm += gs_cm;}
|
||||
//if (grid.snapZ(z2_cm) == grid.snapZ(f2_cm)) {z2_cm -= gs_cm;}
|
||||
|
||||
|
||||
for (const IntPos nodePos : nodesWithin) {
|
||||
|
||||
// create nodes BETWEEN the two floors (skip the floors themselves! -> floor1+gridSize <-> floor2-gridSize
|
||||
for (int z_cm = z1_cm; z_cm <= z2_cm; z_cm += gs_cm) {
|
||||
const GridPoint gp1(nodePos.x_cm, nodePos.y_cm, z_cm); // the to-be-added node
|
||||
Assert::isFalse(grid.hasNodeFor(gp1), "elevator collission"); // such a node must not yet exist! otherwise we e.g. collide with a stari
|
||||
const int idx = grid.add(T(gp1.x_cm, gp1.y_cm, gp1.z_cm)); // create the node
|
||||
grid[idx].setType(GridNode::TYPE_ELEVATOR); // set the node-type
|
||||
//Assert::isFalse(grid.hasNodeFor(gp1), "elevator collission"); // such a node must not yet exist! otherwise we e.g. collide with a stari
|
||||
if (grid.hasNodeFor(gp1)) {
|
||||
const int idx = grid.getNodeFor(gp1).getIdx();
|
||||
grid[idx].setType(GridNode::TYPE_ELEVATOR); // set the node-type
|
||||
} else {
|
||||
const int idx = grid.add(T(gp1.x_cm, gp1.y_cm, gp1.z_cm)); // create the node
|
||||
grid[idx].setType(GridNode::TYPE_ELEVATOR); // set the node-type
|
||||
}
|
||||
}
|
||||
|
||||
// connect each of the new nodes with the node below it. NOW ALSO EXAMINE THE floor above (z2_cm + gs_cm)
|
||||
for (int z_cm = z1_cm; z_cm <= z2_cm + gs_cm; z_cm += gs_cm) {
|
||||
const GridPoint gpBelow(nodePos.x_cm, nodePos.y_cm, z_cm-gs_cm);
|
||||
const GridPoint gp(nodePos.x_cm, nodePos.y_cm, z_cm);
|
||||
Assert::isTrue(grid.hasNodeFor(gpBelow), "missing node");
|
||||
Assert::isTrue(grid.hasNodeFor(gp), "missing node");
|
||||
T& n1 = (T&) grid.getNodeFor(gpBelow);
|
||||
T& n2 = (T&) grid.getNodeFor(gp);
|
||||
grid.connectBiDir(n1, n2);
|
||||
// connect each of the new nodes with the node below it
|
||||
// also connect the elevator to the starting and ending floor
|
||||
for (int z_cm = z1_cm; z_cm <= z2_cm; z_cm += gs_cm) {
|
||||
|
||||
// directly above starting floor
|
||||
if (z_cm == z1_cm) {
|
||||
|
||||
// connect with floor below
|
||||
const GridPoint gpBelow(nodePos.x_cm, nodePos.y_cm, f1_cm);
|
||||
const GridPoint gp(nodePos.x_cm, nodePos.y_cm, z_cm);
|
||||
Assert::isTrue(grid.hasNodeFor(gpBelow), "missing node");
|
||||
Assert::isTrue(grid.hasNodeFor(gp), "missing node");
|
||||
const T& n1 = grid.getNodeFor(gpBelow);
|
||||
const T& n2 = grid.getNodeFor(gp);
|
||||
grid.connectBiDir(n1.getIdx(), n2.getIdx());
|
||||
grid[n1.getIdx()].setType(GridNode::TYPE_ELEVATOR);
|
||||
|
||||
} else {
|
||||
|
||||
// connect with node below
|
||||
const GridPoint gpBelow(nodePos.x_cm, nodePos.y_cm, z_cm-gs_cm);
|
||||
const GridPoint gp(nodePos.x_cm, nodePos.y_cm, z_cm);
|
||||
Assert::isTrue(grid.hasNodeFor(gpBelow), "missing node");
|
||||
Assert::isTrue(grid.hasNodeFor(gp), "missing node");
|
||||
const T& n1 = grid.getNodeFor(gpBelow);
|
||||
const T& n2 = grid.getNodeFor(gp);
|
||||
grid.connectBiDir(n1.getIdx(), n2.getIdx());
|
||||
|
||||
}
|
||||
|
||||
// directly below ending floor
|
||||
if (z_cm == z2_cm) {
|
||||
|
||||
// connect with floor above
|
||||
const GridPoint gpAbove(nodePos.x_cm, nodePos.y_cm, f2_cm);
|
||||
const GridPoint gp(nodePos.x_cm, nodePos.y_cm, z_cm);
|
||||
Assert::isTrue(grid.hasNodeFor(gpAbove), "missing node");
|
||||
Assert::isTrue(grid.hasNodeFor(gp), "missing node");
|
||||
const T& n1 = grid.getNodeFor(gpAbove);
|
||||
const T& n2 = grid.getNodeFor(gp);
|
||||
|
||||
//if (n1.getIdx() == n2.getIdx()) {continue;}
|
||||
|
||||
grid.connectBiDir(n1.getIdx(), n2.getIdx());
|
||||
grid[n1.getIdx()].setType(GridNode::TYPE_ELEVATOR);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef GRIDFACTORY_V2_H
|
||||
#define GRIDFACTORY_V2_H
|
||||
|
||||
@@ -5,6 +15,8 @@
|
||||
#include <unordered_set>
|
||||
#include <set>
|
||||
|
||||
#include "../../../math/Math.h"
|
||||
|
||||
#include "../../../floorplan/v2/Floorplan.h"
|
||||
|
||||
#include "Helper.h"
|
||||
@@ -12,12 +24,15 @@
|
||||
#include "Elevators.h"
|
||||
|
||||
#include "../../../geo/Units.h"
|
||||
#include "../../../geo/Circle2.h"
|
||||
#include "../../GridNodeBBox.h"
|
||||
#include "../../Grid.h"
|
||||
|
||||
#include "../../../misc/Debug.h"
|
||||
#include <functional>
|
||||
|
||||
#include "../../../floorplan/3D/objects/OBJPool.h"
|
||||
#include "../../../geo/ConvexHull2.h"
|
||||
|
||||
#include "GridFactoryListener.h"
|
||||
|
||||
@@ -42,8 +57,12 @@ private:
|
||||
Elevators<T> elevators;
|
||||
|
||||
|
||||
std::vector<GridPoint> withinRemovePolygon;
|
||||
|
||||
bool _buildStairs = true;
|
||||
bool _removeIsolated = true;
|
||||
bool _addTightToObstacle = false;
|
||||
bool _abortOnError = true;
|
||||
|
||||
public:
|
||||
|
||||
@@ -52,6 +71,9 @@ public:
|
||||
|
||||
}
|
||||
|
||||
void setAbortOnError(const bool abort) {this->_abortOnError = abort;}
|
||||
|
||||
void setAddTightToObstacle(const bool tight) {this->_addTightToObstacle = tight;}
|
||||
|
||||
/** whether or not to build stairs */
|
||||
void setBuildStairs(const bool build) {this->_buildStairs = build;}
|
||||
@@ -60,6 +82,14 @@ public:
|
||||
void setRemoveIsolated(const bool remove) {this->_removeIsolated = remove;}
|
||||
|
||||
|
||||
/** does the given grid-node have a stair-neighbor? */
|
||||
bool hasStairNeighbor(const T& node) const {
|
||||
for (const T& n : grid.neighbors(node)) {
|
||||
if (n.getType() == GridNode::TYPE_STAIR) {return true;}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** build using the given map */
|
||||
void build(const Floorplan::IndoorMap* map, GridFactoryListener* listener = nullptr) {
|
||||
|
||||
@@ -79,7 +109,7 @@ public:
|
||||
if (listener) {listener->onGridBuildUpdateMajor("adding stairs");}
|
||||
if (_buildStairs) {
|
||||
for (Floorplan::Floor* f : map->floors) {
|
||||
buildStairs(f, listener);
|
||||
buildStairs(map, f, listener);
|
||||
if (listener) {listener->onGridBuildUpdateMajor(total, ++cur);}
|
||||
}
|
||||
}
|
||||
@@ -93,6 +123,27 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// remove nodes within remove-OutlinePolygons
|
||||
// this must happen AFTER stairs have been added, otherwise stairs might not be connectable due to removed/missing nodes.
|
||||
// also, within this loop we prevent the deltion of nodes that are a direct neighbor of a stair!
|
||||
// [otherwise the same thing would happen again!]
|
||||
if (true) {
|
||||
|
||||
for (const GridPoint& gp : withinRemovePolygon) {
|
||||
T* n = (T*) grid.getNodePtrFor(gp);
|
||||
|
||||
// delete if node is present and is no direct neighbor of a stair
|
||||
if (n && !hasStairNeighbor(*n)) {
|
||||
grid.remove(*n);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// remove all nodes that were just marked for removal
|
||||
grid.cleanup();
|
||||
|
||||
}
|
||||
|
||||
// remove isolated nodes
|
||||
if (_removeIsolated) {
|
||||
if (listener) {listener->onGridBuildUpdateMajor("removing isolated nodes");}
|
||||
@@ -114,21 +165,45 @@ public:
|
||||
return bb;
|
||||
}
|
||||
|
||||
bool isPartOfFloorOutline(const int x_cm, const int y_cm, const Floorplan::FloorOutline& outline) {
|
||||
struct PartOfOutline {
|
||||
bool contained = false; // contained within at-least one polygon?
|
||||
bool markedForRemove = false; // part of a "to-be-removed" polygon?
|
||||
bool outdoor = false; // otherwise: indoor
|
||||
};
|
||||
|
||||
bool add = false;
|
||||
/** get the part of outline the given location belongs to. currently: none, indoor, outdoor */
|
||||
static PartOfOutline isPartOfFloorOutline(const int x_cm, const int y_cm, const Floorplan::FloorOutline& outline) {
|
||||
|
||||
// assume the point is not part of the outline
|
||||
PartOfOutline res;
|
||||
res.contained = false;
|
||||
res.markedForRemove = false;
|
||||
|
||||
// process every outline polygon
|
||||
for (Floorplan::FloorOutlinePolygon* poly : outline) {
|
||||
if (poly->method != Floorplan::OutlineMethod::ADD) {continue;}
|
||||
HelperPoly pol(*poly);
|
||||
if (pol.contains(Point2(x_cm, y_cm))) {add = true;}
|
||||
if (pol.contains(Point2(x_cm, y_cm))) {
|
||||
|
||||
// mark as "contained"
|
||||
res.contained = true;
|
||||
|
||||
// belongs to a "remove" polygon? -> directly ignore this location!
|
||||
if (poly->method == Floorplan::OutlineMethod::REMOVE) {
|
||||
res.markedForRemove = true;
|
||||
}
|
||||
|
||||
// belongs to a "add" polygon? -> remember until all polygons were checked
|
||||
// [might still belong to a "remove" polygon]
|
||||
if (poly->outdoor) {
|
||||
res.outdoor = true; // belonging to an outdoor region overwrites all other belongings
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (!add) {return false;}
|
||||
for (Floorplan::FloorOutlinePolygon* poly : outline) {
|
||||
if (poly->method != Floorplan::OutlineMethod::REMOVE) {continue;}
|
||||
HelperPoly pol(*poly);
|
||||
if (pol.contains(Point2(x_cm, y_cm))) {add = false;} // TODO
|
||||
}
|
||||
return add;
|
||||
|
||||
// done
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -144,32 +219,79 @@ public:
|
||||
const int x2 = helper.align(bbox.getMax().x);
|
||||
const int y1 = helper.align(bbox.getMin().y);
|
||||
const int y2 = helper.align(bbox.getMax().y);
|
||||
const int z_cm = (floor->atHeight*100);
|
||||
const int z_cm = std::round(floor->atHeight*100);
|
||||
|
||||
const int total = (x2-x1) / helper.gridSize();
|
||||
int cur = 0;
|
||||
int numNodes = 0;
|
||||
|
||||
// all 3D objects within the floor
|
||||
std::vector<HelperPoly> objObstacles;
|
||||
for (const Floorplan::FloorObstacle* fo : floor->obstacles) {
|
||||
|
||||
// process all object-obstalces
|
||||
const Floorplan::FloorObstacleObject* foo = dynamic_cast<const Floorplan::FloorObstacleObject*>(fo);
|
||||
if (foo) {
|
||||
|
||||
// get the obstacle
|
||||
const Floorplan3D::Obstacle3D obs = Floorplan3D::OBJPool::get().getObject(foo->file).scaled(foo->scale).rotated_deg(foo->rot).translated(foo->pos);
|
||||
|
||||
// construct its 2D convex hull (in centimter)
|
||||
HelperPoly poly;
|
||||
for (const Point2 p : ConvexHull2::get(obs.getPoints2D())) {poly.add(p*100);}
|
||||
objObstacles.push_back(poly);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// does any of the obj-obstalces contain the given point?
|
||||
auto isPartOfObject = [&objObstacles, this] (const GridNodeBBox& bb) {
|
||||
for (HelperPoly poly : objObstacles) {
|
||||
//if (!_addTightToObstacle) {
|
||||
if (poly.contains(bb.getCorner1())) {return true;}
|
||||
if (poly.contains(bb.getCorner2())) {return true;}
|
||||
if (poly.contains(bb.getCorner3())) {return true;}
|
||||
if (poly.contains(bb.getCorner4())) {return true;}
|
||||
//} else {
|
||||
// //poly.shrink(1);
|
||||
// if (poly.contains(bb.getCenter())) {return true;}
|
||||
//}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// build grid-points for floor-outline
|
||||
for(int x_cm = x1; x_cm < x2; x_cm += helper.gridSize()) {
|
||||
for (int y_cm = y1; y_cm < y2; y_cm += helper.gridSize()) {
|
||||
|
||||
// does the outline-polygon contain this position?
|
||||
if (!isPartOfFloorOutline(x_cm, y_cm, floor->outline)) {continue;}
|
||||
const PartOfOutline part = isPartOfFloorOutline(x_cm, y_cm, floor->outline);
|
||||
if (!part.contained) {continue;}
|
||||
|
||||
// check intersection with the floorplan
|
||||
// bbox to check intersection with the floorplan
|
||||
GridNodeBBox bbox(GridPoint(x_cm, y_cm, z_cm), helper.gridSize());
|
||||
|
||||
// slightly grow the bbox to ensure even obstacles that are directly aligned to the bbox are hit
|
||||
bbox.grow(0.42345);
|
||||
if (intersects(bbox, floor)) {continue;}
|
||||
bbox.grow(0.1337);
|
||||
if (!_addTightToObstacle) {
|
||||
if (intersects(bbox, floor)) {continue;}
|
||||
}
|
||||
|
||||
// add to the grid
|
||||
// intersection with objects?
|
||||
if (isPartOfObject(bbox)) {continue;}
|
||||
|
||||
// add to the grid [once]
|
||||
T t(x_cm, y_cm, z_cm);
|
||||
t.setType(getType(bbox, floor));
|
||||
if (grid.hasNodeFor(t)) {continue;}
|
||||
updateType(t, part, bbox, floor);
|
||||
grid.add(t);
|
||||
|
||||
// node part of remove-region?
|
||||
// if so, remove >>AFTER<< stairs have been added
|
||||
if (part.markedForRemove) {
|
||||
withinRemovePolygon.push_back(t);
|
||||
}
|
||||
|
||||
// debug
|
||||
++numNodes;
|
||||
|
||||
@@ -186,7 +308,9 @@ public:
|
||||
|
||||
}
|
||||
|
||||
void buildStairs(const Floorplan::Floor* floor, GridFactoryListener* listener = nullptr) {
|
||||
void buildStairs(const Floorplan::IndoorMap* map, const Floorplan::Floor* floor, GridFactoryListener* listener = nullptr) {
|
||||
|
||||
stairs.setAbortOnError(_abortOnError);
|
||||
|
||||
const int total = floor->stairs.size();
|
||||
int cur = 0;
|
||||
@@ -194,7 +318,7 @@ public:
|
||||
// process each stair within the floor
|
||||
for (const Floorplan::Stair* stair : floor->stairs) {
|
||||
if (listener) {listener->onGridBuildUpdateMinor("adding " + floor->name + " stair " + std::to_string(cur+1));}
|
||||
stairs.build(floor, stair);
|
||||
stairs.build(map, floor, stair);
|
||||
if (listener) {listener->onGridBuildUpdateMinor(total, ++cur);}
|
||||
}
|
||||
|
||||
@@ -278,8 +402,15 @@ public:
|
||||
// does the grid contain the potential neighbor?
|
||||
const T* n2 = grid.getNodePtrFor(p);
|
||||
if (n2 != nullptr) {
|
||||
|
||||
if (isBlocked(floor, n1, *n2)) {continue;} // is there a (e.g. small) obstacle between the two?
|
||||
grid.connectUniDir(n1, *n2); // UNI-dir connection as EACH node is processed!
|
||||
|
||||
// NOTE
|
||||
// if you have two floors at the >same< height, and their outlines overlap, this one is needed!
|
||||
if (!n1.hasNeighbor(n2->getIdx())) {
|
||||
grid.connectUniDir(n1, *n2); // UNI-dir connection as EACH node is processed!
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -352,6 +483,9 @@ public:
|
||||
|
||||
void removeIsolatedNodes() {
|
||||
|
||||
//std::cout << "todo: remove" << std::endl;
|
||||
//return;
|
||||
|
||||
// try to start at the first stair
|
||||
for (T& n : grid) {
|
||||
if (n.getType() == GridNode::TYPE_STAIR) {removeIsolatedNodes(n); return;}
|
||||
@@ -372,11 +506,29 @@ public:
|
||||
getConnected(n1, set);
|
||||
Log::tock();
|
||||
|
||||
//const int numToRemove = grid.getNumNodes() - set.size();
|
||||
//int numRemoved = 0;
|
||||
|
||||
// remove all other
|
||||
Log::add(name, "removing all nodes NOT connected to " + (std::string) n1, false);
|
||||
Log::tick();
|
||||
for (T& n2 : grid) {
|
||||
if (set.find(n2.getIdx()) == set.end()) {grid.remove(n2);}
|
||||
if (set.find(n2.getIdx()) == set.end()) {
|
||||
|
||||
// sanity check
|
||||
// wouldn't make sense that a stair-node is removed..
|
||||
// maybe something went wrong elsewhere???
|
||||
Assert::notEqual(n2.getType(), GridNode::TYPE_STAIR, "detected an isolated stair?!");
|
||||
Assert::notEqual(n2.getType(), GridNode::TYPE_ELEVATOR, "detected an isolated elevator?!");
|
||||
//Assert::notEqual(n2.getType(), GridNode::TYPE_DOOR, "detected an isolated door?!");
|
||||
|
||||
// proceed ;)
|
||||
grid.remove(n2);
|
||||
|
||||
//++numRemoved;
|
||||
//std::cout << numRemoved << ":" << numToRemove << std::endl;
|
||||
|
||||
}
|
||||
}
|
||||
Log::tock();
|
||||
|
||||
@@ -415,7 +567,23 @@ private:
|
||||
|
||||
private:
|
||||
|
||||
|
||||
/** as line-obstacles have a thickness, we need 4 lines for the intersection test! */
|
||||
static std::vector<Line2> getThickLines(const Floorplan::FloorObstacleLine* line) {
|
||||
//const Line2 base(line->from*100, line->to*100);
|
||||
const float thickness_m = line->thickness_m;
|
||||
const Point2 dir = (line->to - line->from); // obstacle's direction
|
||||
const Point2 perp = dir.perpendicular().normalized(); // perpendicular direction (90 degree)
|
||||
const Point2 p1 = line->from + perp * thickness_m/2; // start-up
|
||||
const Point2 p2 = line->from - perp * thickness_m/2; // start-down
|
||||
const Point2 p3 = line->to + perp * thickness_m/2; // end-up
|
||||
const Point2 p4 = line->to - perp * thickness_m/2; // end-down
|
||||
return {
|
||||
Line2(p1, p2),
|
||||
Line2(p3, p4),
|
||||
Line2(p2, p4),
|
||||
Line2(p1, p3),
|
||||
};
|
||||
}
|
||||
|
||||
/** does the bbox intersect with any of the floor's walls? */
|
||||
static inline bool intersects(const GridNodeBBox& bbox, const Floorplan::Floor* floor) {
|
||||
@@ -427,18 +595,33 @@ private:
|
||||
// depends on the type of obstacle
|
||||
if (dynamic_cast<Floorplan::FloorObstacleLine*>(fo)) {
|
||||
const Floorplan::FloorObstacleLine* line = (Floorplan::FloorObstacleLine*) fo;
|
||||
const Line2 l2(line->from*100, line->to*100);
|
||||
if (bbox.intersects(l2)) {return true;}
|
||||
|
||||
// old method (does not support thickness)
|
||||
//const Line2 l2(line->from*100, line->to*100);
|
||||
//if (bbox.intersects(l2)) {return true;}
|
||||
const std::vector<Line2> lines = getThickLines(line);
|
||||
for (const Line2& l : lines) {
|
||||
if (bbox.intersects(l*100)) {return true;}
|
||||
}
|
||||
|
||||
|
||||
} else if (dynamic_cast<Floorplan::FloorObstacleCircle*>(fo)) {
|
||||
const Floorplan::FloorObstacleCircle* circle = (Floorplan::FloorObstacleCircle*) fo;
|
||||
const Point2 center = bbox.getCenter();
|
||||
const float dist = center.getDistance(circle->center*100);
|
||||
if (dist < circle->radius*100) {return true;}
|
||||
const float dist = std::min(
|
||||
bbox.getCorner1().getDistance(circle->center*100),
|
||||
bbox.getCorner2().getDistance(circle->center*100),
|
||||
bbox.getCorner3().getDistance(circle->center*100),
|
||||
bbox.getCorner4().getDistance(circle->center*100)
|
||||
);
|
||||
const float threshold = circle->radius * 100;
|
||||
if (dist < threshold) {return true;}
|
||||
|
||||
} else if (dynamic_cast<Floorplan::FloorObstacleDoor*>(fo)) {
|
||||
// DOORS ARE NOT AN OBSTACLE
|
||||
|
||||
} else if (dynamic_cast<Floorplan::FloorObstacleObject*>(fo)) {
|
||||
// ADDED EARLIER
|
||||
|
||||
} else {
|
||||
throw Exception("TODO: not yet implemented obstacle type");
|
||||
|
||||
@@ -472,11 +655,18 @@ private:
|
||||
if (lineObstacle.getSegmentIntersection(lineNodes)) {return true;}
|
||||
|
||||
} else if (dynamic_cast<Floorplan::FloorObstacleCircle*>(fo)) {
|
||||
throw Exception("should not happen");
|
||||
const Floorplan::FloorObstacleCircle* circle = (Floorplan::FloorObstacleCircle*) fo;
|
||||
Circle2 circ(circle->center, circle->radius);
|
||||
if (circ.intersects(lineNodes)) {return true;}
|
||||
//throw Exception("should not happen");
|
||||
|
||||
} else if (dynamic_cast<Floorplan::FloorObstacleDoor*>(fo)) {
|
||||
// DOORS ARE NOT AN OBSTACLE
|
||||
|
||||
} else if (dynamic_cast<Floorplan::FloorObstacleObject*>(fo)) {
|
||||
// removed earlier
|
||||
//std::cout << "GridFactory: TODO: Floorplan::FloorObstacleObject" << std::endl;
|
||||
|
||||
} else {
|
||||
throw Exception("TODO: not yet implemented obstacle type");
|
||||
|
||||
@@ -487,23 +677,30 @@ private:
|
||||
|
||||
}
|
||||
|
||||
/** does the bbox intersect with any of the floor's walls? */
|
||||
static inline int getType(const GridNodeBBox& bbox, const Floorplan::Floor* floor) {
|
||||
/** adjust the given gridNode's type if needed [e.g. from "floor" to "door"] */
|
||||
static inline void updateType(T& t, const PartOfOutline part, const GridNodeBBox& bbox, const Floorplan::Floor* floor) {
|
||||
|
||||
// process each obstacle
|
||||
// first, assume the type of the outline polygon
|
||||
if (part.outdoor) {
|
||||
t.setType(GridNode::TYPE_OUTDOOR);
|
||||
} else {
|
||||
//t.setType(GridNode::TYPE_FLOOR);
|
||||
}
|
||||
|
||||
// hereafter, process each obstacle and mark doors
|
||||
for (Floorplan::FloorObstacle* fo : floor->obstacles) {
|
||||
|
||||
if (dynamic_cast<Floorplan::FloorObstacleDoor*>(fo)) {
|
||||
const Floorplan::FloorObstacleDoor* door = (Floorplan::FloorObstacleDoor*) fo;
|
||||
const Line2 l2(door->from*100, door->to*100);
|
||||
if (bbox.intersects(l2)) {return GridNode::TYPE_DOOR;}
|
||||
|
||||
if (bbox.intersects(l2)) {
|
||||
t.setType(GridNode::TYPE_DOOR);
|
||||
return; // done
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return GridNode::TYPE_FLOOR;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef GRIDFACTORYLISTENER_H
|
||||
#define GRIDFACTORYLISTENER_H
|
||||
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef GRIDNODEIMPORTANCE_H
|
||||
#define GRIDNODEIMPORTANCE_H
|
||||
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef GRID_FACTORY_HELPER_H
|
||||
#define GRID_FACTORY_HELPER_H
|
||||
|
||||
@@ -44,6 +54,19 @@ struct HelperPoly {
|
||||
bbox_cm.add(p.xy());
|
||||
}
|
||||
|
||||
|
||||
void shrink(float cm) {
|
||||
Point2 center;
|
||||
for (const Point2 pt : points_cm) {center += pt;}
|
||||
center /= points_cm.size();
|
||||
for (Point2& pt : points_cm) {
|
||||
Point2 dir = pt - center;
|
||||
float len = dir.length();
|
||||
dir = dir.normalized();
|
||||
pt = center + dir * (len-cm);
|
||||
}
|
||||
}
|
||||
|
||||
/** does the polygon contain the given point (in cm)? */
|
||||
bool contains(const Point2 p_cm) const {
|
||||
|
||||
@@ -254,13 +277,13 @@ public:
|
||||
}
|
||||
|
||||
static bool bary(Point2 p, Point2 a, Point2 b, Point2 c, float &u, float &v, float &w) {
|
||||
Point2 v0 = b - a, v1 = c - a, v2 = p - a;
|
||||
float d00 = dot(v0, v0);
|
||||
float d01 = dot(v0, v1);
|
||||
float d11 = dot(v1, v1);
|
||||
float d20 = dot(v2, v0);
|
||||
float d21 = dot(v2, v1);
|
||||
float denom = d00 * d11 - d01 * d01;
|
||||
const Point2 v0 = b - a, v1 = c - a, v2 = p - a;
|
||||
double d00 = dot(v0, v0);
|
||||
double d01 = dot(v0, v1);
|
||||
double d11 = dot(v1, v1);
|
||||
double d20 = dot(v2, v0);
|
||||
double d21 = dot(v2, v1);
|
||||
double denom = d00 * d11 - d01 * d01;
|
||||
v = (d11 * d20 - d01 * d21) / denom;
|
||||
w = (d00 * d21 - d01 * d20) / denom;
|
||||
u = 1.0f - v - w;
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef IMPORTANCE_H
|
||||
#define IMPORTANCE_H
|
||||
|
||||
@@ -11,7 +21,8 @@
|
||||
#include "../../../misc/KNNArray.h"
|
||||
|
||||
#include "../../../math/MiniMat2.h"
|
||||
#include "../../../math/Distributions.h"
|
||||
#include "../../../math/distribution/Normal.h"
|
||||
#include "../../../math/distribution/Triangle.h"
|
||||
|
||||
|
||||
|
||||
@@ -153,6 +164,8 @@ public:
|
||||
|
||||
|
||||
// sanity check
|
||||
Assert::isNotNaN(n1.walkImportance, "detected NaN walk importance for " + n1.asString());
|
||||
Assert::isNotNaN(n1.navImportance, "detected NaN walk importance for " + n1.asString());
|
||||
Assert::isTrue(n1.walkImportance >= 0, "detected negative walk importance. does not make sense!");
|
||||
Assert::isTrue(n1.navImportance >= 0, "detected negative nav importance. does not make sense!");
|
||||
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef STAIRS_H
|
||||
#define STAIRS_H
|
||||
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef STAIRS_H
|
||||
#define STAIRS_H
|
||||
|
||||
@@ -31,19 +41,23 @@ private:
|
||||
// keep a list of all vertices below stairwells and remove them hereafter
|
||||
std::vector<T*> toDelete;
|
||||
|
||||
bool tryImproveStairConnections = true;
|
||||
bool abortOnError = true;
|
||||
|
||||
private:
|
||||
|
||||
/** helper struct */
|
||||
struct StairNode {
|
||||
|
||||
const int x_cm;
|
||||
const int y_cm;
|
||||
const int belongsToQuadIdx;
|
||||
int x_cm;
|
||||
int y_cm;
|
||||
int belongsToQuadIdx;
|
||||
|
||||
float z_cm; // interpolated
|
||||
int gridIdx = -1;
|
||||
|
||||
StairNode(const int x_cm, const int y_cm, const int quadIdx) : x_cm(x_cm), y_cm(y_cm), belongsToQuadIdx(quadIdx) {;}
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -58,11 +72,22 @@ public:
|
||||
finalize();
|
||||
}
|
||||
|
||||
/** whether to abort when errors are detected */
|
||||
void setAbortOnError(const bool abort) {
|
||||
this->abortOnError = abort;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void build(const Floorplan::IndoorMap* map, const Floorplan::Floor* floor, const Floorplan::Stair* stair) {
|
||||
|
||||
void build(const Floorplan::Floor* floor, const Floorplan::Stair* stair) {
|
||||
// starting-height of all available floors (in cm)
|
||||
// needed to ensure that stairs start and end at floors
|
||||
std::vector<int> floorsAtHeight_cm;
|
||||
for (const Floorplan::Floor* f : map->floors) {
|
||||
const int floorAtHeight_cm = std::round(f->atHeight*100);
|
||||
floorsAtHeight_cm.push_back(floorAtHeight_cm);
|
||||
}
|
||||
|
||||
const int gs_cm = grid.getGridSize_cm();
|
||||
|
||||
@@ -123,20 +148,66 @@ public:
|
||||
|
||||
}
|
||||
|
||||
const float stairAbsStart_m = floor->atHeight + stair->getParts().front().start.z;
|
||||
const float stairAbsEnd_m = floor->atHeight + stair->getParts().back().end.z;
|
||||
//const float stairHeight_m = stairAbsEnd_m - stairAbsStart_m;
|
||||
const float stairAbsStart_cm = std::round(stairAbsStart_m*100);
|
||||
const float stairAbsEnd_cm = std::round(stairAbsEnd_m*100);
|
||||
const float stairHeight_cm = stairAbsEnd_cm - stairAbsStart_cm;
|
||||
|
||||
// this might lead to stairs the start slightly above the starting-floor
|
||||
// or ending slightly below the ending floor. this would lead to DISCONNECTION!
|
||||
// therefore re-scale the z-values to ensure they start at floor1 and end at floor 2
|
||||
float minZ = +9999999;
|
||||
float maxZ = -9999999;
|
||||
for (const StairNode& sn : stairNodes) {
|
||||
if (sn.z_cm < minZ) {minZ = sn.z_cm;}
|
||||
if (sn.z_cm > maxZ) {maxZ = sn.z_cm;}
|
||||
}
|
||||
for (StairNode& sn : stairNodes) {
|
||||
const float zPercent = (sn.z_cm - minZ) / (maxZ - minZ); // current percentage from minZ to maxZ
|
||||
sn.z_cm = floor->getStartingZ()*100 + zPercent * floor->height*100; // apply percentage to floorStartZ <-> floorEndZ
|
||||
{
|
||||
|
||||
float minZ = +9999999;
|
||||
float maxZ = -9999999;
|
||||
for (const StairNode& sn : stairNodes) {
|
||||
if (sn.z_cm < minZ) {minZ = sn.z_cm;}
|
||||
if (sn.z_cm > maxZ) {maxZ = sn.z_cm;}
|
||||
}
|
||||
|
||||
for (StairNode& sn : stairNodes) {
|
||||
const float zPercent = (sn.z_cm - minZ) / (maxZ - minZ); // current percentage from minZ to maxZ WHICH ONE IS CORRECT?
|
||||
//const float zPercent = (sn.z_cm - stairAbsStart_cm) / (stairAbsEnd_cm - stairAbsStart_cm); // current percentage from minZ to maxZ WHICH ONE IS CORRECT?
|
||||
//sn.z_cm = floor->getStartingZ()*100 + zPercent * floor->height*100; // apply percentage to floorStartZ <-> floorEndZ
|
||||
sn.z_cm = std::round(stairAbsStart_cm + zPercent * stairHeight_cm); // apply percentage to floorStartZ <-> floorEndZ
|
||||
}
|
||||
|
||||
|
||||
|
||||
// // snap stair-nodes to nearby grid nodes, if possible
|
||||
// // we try to improve the number of connections between stair and starting/ending floor
|
||||
// if (tryImproveStairConnections) {
|
||||
// for (StairNode& sn : stairNodes) {
|
||||
// //if (std::abs(sn.z_cm-stairAbsStart_cm) < gs_cm*0.75 && grid.hasNodeFor(GridPoint(sn.x_cm, sn.y_cm, stairAbsStart_cm)) ) {sn.z_cm = stairAbsStart_cm;}
|
||||
// //if (std::abs(sn.z_cm-stairAbsEnd_cm) < gs_cm*0.75 && grid.hasNodeFor(GridPoint(sn.x_cm, sn.y_cm, stairAbsEnd_cm))) {sn.z_cm = stairAbsEnd_cm;}
|
||||
// //if (std::abs(sn.z_cm-stairAbsStart_cm) < gs_cm*1.5 && grid.hasNodeFor(GridPoint(sn.x_cm, sn.y_cm, stairAbsStart_cm)) ) {auxcon.push_back(AuxConnection(sn, GridPoint(sn.x_cm, sn.y_cm, stairAbsStart_cm)));}
|
||||
// //if (std::abs(sn.z_cm-stairAbsEnd_cm) < gs_cm*1.5 && grid.hasNodeFor(GridPoint(sn.x_cm, sn.y_cm, stairAbsEnd_cm))) {auxcon.push_back(AuxConnection(sn, GridPoint(sn.x_cm, sn.y_cm, stairAbsEnd_cm)));}
|
||||
// }
|
||||
// }
|
||||
|
||||
// stort all stair-nodes by z (ascending)
|
||||
const auto comp = [] (const StairNode& sn1, const StairNode& sn2) {return sn1.z_cm < sn2.z_cm;};
|
||||
std::sort(stairNodes.begin(), stairNodes.end(), comp);
|
||||
|
||||
// get first and last stair note
|
||||
const bool end1OK = std::find(floorsAtHeight_cm.begin(), floorsAtHeight_cm.end(), stairNodes.front().z_cm) != floorsAtHeight_cm.end();
|
||||
const bool end2OK = std::find(floorsAtHeight_cm.begin(), floorsAtHeight_cm.end(), stairNodes.back().z_cm) != floorsAtHeight_cm.end();
|
||||
|
||||
// be sure both are connected to a floor
|
||||
if (!end1OK || !end2OK) {
|
||||
if (abortOnError) {
|
||||
throw Exception("stair's start or end is not directly connectable to a floor");
|
||||
} else{
|
||||
std::cout << "stair's start or end is not directly connectable to a floor" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// track z-positions of already-existing grid nodes we connected the stair to
|
||||
// if this list contains 2 distinctive values, the stair is successfully connected to starting and ending floor!
|
||||
std::unordered_set<float> connectedWithHeights;
|
||||
@@ -157,19 +228,26 @@ public:
|
||||
// remember the z-position of the already-existing grid-node we connected the stair to
|
||||
connectedWithHeights.insert(grid[sn.gridIdx].z_cm);
|
||||
|
||||
// mark the node as stair-node
|
||||
//grid[sn.gridIdx].setType(GridNode::TYPE_STAIR);
|
||||
|
||||
} else {
|
||||
|
||||
sn.gridIdx = grid.add(T(gp.x_cm, gp.y_cm, gp.z_cm));
|
||||
|
||||
// check if there is a nearby floor-node to delete
|
||||
// -> remove nodes directly above/below the stair
|
||||
const int deleteDist_cm = 100;
|
||||
const float distToBelow = gp.z_cm - floor->getStartingZ()*100;
|
||||
const float distToAbove = floor->getEndingZ()*100 - gp.z_cm;
|
||||
if (distToBelow > gs_cm && distToBelow < deleteDist_cm) {
|
||||
T* n = (T*) grid.getNodePtrFor(GridPoint(gp.x_cm, gp.y_cm, floor->getStartingZ()*100));
|
||||
const float distToBelow = gp.z_cm - stairAbsStart_cm;//floor->getStartingZ()*100;
|
||||
const float distToAbove = stairAbsEnd_cm - gp.z_cm;//floor->getEndingZ()*100 - gp.z_cm;
|
||||
//if (distToBelow > gs_cm && distToBelow < deleteDist_cm) {
|
||||
if (distToBelow > 0 && distToBelow < deleteDist_cm) {
|
||||
T* n = (T*) grid.getNodePtrFor(GridPoint(gp.x_cm, gp.y_cm, stairAbsStart_cm));//floor->getStartingZ()*100));
|
||||
if (n) {toDelete.push_back(n);}
|
||||
} else if (distToAbove > gs_cm && distToAbove < deleteDist_cm) {
|
||||
T* n = (T*) grid.getNodePtrFor(GridPoint(gp.x_cm, gp.y_cm, floor->getEndingZ()*100));
|
||||
//} else if (distToAbove > gs_cm && distToAbove < deleteDist_cm) {
|
||||
}
|
||||
if (distToAbove > 0 && distToAbove < deleteDist_cm) {
|
||||
T* n = (T*) grid.getNodePtrFor(GridPoint(gp.x_cm, gp.y_cm, stairAbsEnd_cm));//floor->getEndingZ()*100));
|
||||
if (n) {toDelete.push_back(n);}
|
||||
}
|
||||
|
||||
@@ -180,7 +258,45 @@ public:
|
||||
|
||||
}
|
||||
|
||||
|
||||
// for larger grid-sizes stair connections to starting/ending floors are quite bad if the stairs are not 90degree aligned
|
||||
// to improve the number of connections between floor and stair, we brute-force search for connectable nodes (between stair and floor, given threshold)
|
||||
if (tryImproveStairConnections) {
|
||||
|
||||
// connect all stair-nodes to the floor if their distance is below this threshold
|
||||
const int maxDist_cm = gs_cm * 1.2;
|
||||
|
||||
for (StairNode& sn : stairNodes) {
|
||||
|
||||
const auto& gn1 = grid[sn.gridIdx];
|
||||
|
||||
// all stair-nodes that are near to a floor
|
||||
const bool lower = std::abs(sn.z_cm-stairAbsStart_cm) < maxDist_cm;
|
||||
const bool upper = std::abs(sn.z_cm-stairAbsEnd_cm) < maxDist_cm;
|
||||
|
||||
if (lower || upper) {
|
||||
|
||||
// cross-check with each grid node (slow...)
|
||||
for (const auto& gn2 : grid.getNodes()) {
|
||||
if (gn2.getDistanceInCM(gn1) > maxDist_cm) {continue;} // connect with a floor-node near the stair-node
|
||||
if (gn1.hasNeighbor(gn2.getIdx())) {continue;} // already connected?
|
||||
//if (gn2.hasNeighbor(gn1.getIdx())) {continue;}
|
||||
if (gn2.getIdx() == gn1.getIdx()) {continue;} // do not connect with myself
|
||||
if (gn2.fullyConnected()) {continue;} // skip full nodes
|
||||
if (gn1.fullyConnected()) {continue;} // skip full nodes
|
||||
grid.connectBiDir(gn1.getIdx(), gn2.getIdx()); // connect
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// sanity check
|
||||
// connectedWithHeights should contain 2 entries:
|
||||
// one for the starting floor
|
||||
// one for the ending floor
|
||||
// this mainly fails, when there is a REMOVING outline-area that removes to many nodes and the stair can not be connected
|
||||
Assert::isTrue(connectedWithHeights.size() == 2, "stair is not correctly connected to starting and ending floor!");
|
||||
|
||||
// now connect all new nodes with their neighbors
|
||||
@@ -215,16 +331,16 @@ public:
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void finalize() {
|
||||
|
||||
//std::cout << "stairs.finalize() crashes!" << std::endl;
|
||||
//return;
|
||||
|
||||
// delete all pending nodes and perform a cleanup
|
||||
if (!toDelete.empty()) {
|
||||
for (T* n : toDelete) {grid.remove(*n);}
|
||||
|
||||
502
grid/factory/v3/GridFactory3.h
Normal file
502
grid/factory/v3/GridFactory3.h
Normal file
@@ -0,0 +1,502 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef GRIDFACTORY3_H
|
||||
#define GRIDFACTORY3_H
|
||||
|
||||
#include "../../Grid.h"
|
||||
#include "../../../floorplan/v2/Floorplan.h"
|
||||
#include "HelperPoly3.h"
|
||||
#include <unordered_set>
|
||||
|
||||
#if (GRID_MODE == GM_BOX)
|
||||
|
||||
#define GF3_ITER_XY for (int y = y1; y <= y2; y += gs_cm) { for (int x = x1; x <= x2; x += gs_cm) {
|
||||
|
||||
#elif (GRID_MODE == GM_HOBEYCOMB)
|
||||
|
||||
#define GF3_ITER_XY\
|
||||
for (int y = y1; y <= y2; y += gs_cm) {\
|
||||
const int xx = (y / gs_cm % 2 == 0) ? (0) : (gs_cm/2);\
|
||||
for (int x = x1-xx; x <= x2; x += gs_cm) {
|
||||
|
||||
#endif
|
||||
|
||||
template <typename Node> class GridFactory3 {
|
||||
|
||||
private:
|
||||
|
||||
|
||||
Grid<Node>& grid;
|
||||
const int gs_cm;
|
||||
|
||||
struct NewNode {
|
||||
GridPoint pos;
|
||||
int type;
|
||||
NewNode(const GridPoint pos, const int type) : pos(pos), type(type) {;}
|
||||
bool operator == (const NewNode& o) const {return o.pos == pos;}
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
GridFactory3(Grid<Node>& grid) : grid(grid), gs_cm(grid.getGridSize_cm()) {
|
||||
|
||||
}
|
||||
|
||||
void build(const Floorplan::IndoorMap* map) {
|
||||
|
||||
std::vector<NewNode> add;
|
||||
std::vector<NewNode> rem;
|
||||
|
||||
for (const Floorplan::Floor* floor : map->floors) {
|
||||
|
||||
// for (const Floorplan::FloorOutlinePolygon* poly : floor->outline) {
|
||||
|
||||
// const std::vector<NewNode> pts = getPointsOn(floor, *poly);
|
||||
// if (poly->method == Floorplan::OutlineMethod::ADD) {
|
||||
// add.insert(add.end(), pts.begin(), pts.end());
|
||||
// } else {
|
||||
// rem.insert(rem.end(), pts.begin(), pts.end());
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
const std::vector<NewNode> pts = getPointsOn(floor);
|
||||
add.insert(add.end(), pts.begin(), pts.end());
|
||||
|
||||
for (const Floorplan::Stair* stair : floor->stairs) {
|
||||
std::vector<Floorplan::Quad3> quads = Floorplan::getQuads(stair->getParts(), floor);
|
||||
const std::vector<NewNode> pts = getPointsOn(floor, quads);
|
||||
add.insert(add.end(), pts.begin(), pts.end());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
for (const NewNode& nn : add) {
|
||||
auto it = std::find(rem.begin(), rem.end(), nn);
|
||||
if (it == rem.end()) {
|
||||
if (!grid.hasNodeFor(nn.pos)) {
|
||||
Node n(nn.pos.x_cm, nn.pos.y_cm, nn.pos.z_cm);
|
||||
n.setType(nn.type);
|
||||
grid.add(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
connect(map);
|
||||
removeIsolatedNodes();
|
||||
|
||||
}
|
||||
|
||||
bool isBlocked(const Floorplan::IndoorMap* map, const Node& n1, const Node& n2) {
|
||||
|
||||
Line2 lNodes(n1.inMeter().xy(), n2.inMeter().xy());
|
||||
|
||||
for (Floorplan::Floor* floor : map->floors) {
|
||||
|
||||
if (n1.inMeter().z != floor->atHeight) {continue;}
|
||||
if (n2.inMeter().z != floor->atHeight) {continue;}
|
||||
|
||||
for (Floorplan::FloorObstacle* obs : floor->obstacles) {
|
||||
Floorplan::FloorObstacleLine* line = dynamic_cast<Floorplan::FloorObstacleLine*>(obs);
|
||||
if (line) {
|
||||
const std::vector<Line2> lines = getThickLines(line);
|
||||
for (const Line2& lObs : lines) {
|
||||
if (lObs.getSegmentIntersection(lNodes)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/** as line-obstacles have a thickness, we need 4 lines for the intersection test! */
|
||||
static std::vector<Line2> getThickLines(const Floorplan::FloorObstacleLine* line) {
|
||||
//const Line2 base(line->from*100, line->to*100);
|
||||
const float thickness_m = line->thickness_m;
|
||||
const Point2 dir = (line->to - line->from); // obstacle's direction
|
||||
const Point2 perp = dir.perpendicular().normalized(); // perpendicular direction (90 degree)
|
||||
const Point2 p1 = line->from + perp * thickness_m/2; // start-up
|
||||
const Point2 p2 = line->from - perp * thickness_m/2; // start-down
|
||||
const Point2 p3 = line->to + perp * thickness_m/2; // end-up
|
||||
const Point2 p4 = line->to - perp * thickness_m/2; // end-down
|
||||
return {
|
||||
Line2(p1, p2),
|
||||
Line2(p3, p4),
|
||||
Line2(p2, p4),
|
||||
Line2(p1, p3),
|
||||
};
|
||||
}
|
||||
|
||||
void connect(const Floorplan::IndoorMap* map) {
|
||||
|
||||
for (Node& n1 : grid) {
|
||||
for (Node& n2 : grid) {
|
||||
|
||||
if (n1 == n2) {continue;}
|
||||
|
||||
|
||||
// stair with floor
|
||||
if (
|
||||
(n1.getType() == GridNode::TYPE_STAIR && n2.getType() == GridNode::TYPE_FLOOR) ||
|
||||
(n2.getType() == GridNode::TYPE_STAIR && n1.getType() == GridNode::TYPE_FLOOR)
|
||||
) {
|
||||
|
||||
const float distxy = n1.inMeter().xy().getDistance(n2.inMeter().xy());
|
||||
const float distz_cm = std::abs(n1.z_cm - n2.z_cm);
|
||||
if (distxy > 0 && distxy < gs_cm * 1.2 / 100.0f && distz_cm < gs_cm) { // [1.85]
|
||||
if (n1.fullyConnected()) {continue;}
|
||||
if (n2.fullyConnected()) {continue;}
|
||||
grid.connectUniDir(n1, n2);
|
||||
}
|
||||
|
||||
// floor with floor
|
||||
} else if (n1.getType() == GridNode::TYPE_FLOOR && n2.getType() == GridNode::TYPE_FLOOR) {
|
||||
if (n1.getDistanceInCM(n2) < gs_cm * 1.2 && !isBlocked(map, n1, n2)) { // [1.2 | 1.845]
|
||||
if (n1.fullyConnected()) {continue;}
|
||||
if (n2.fullyConnected()) {continue;}
|
||||
grid.connectUniDir(n1, n2);
|
||||
}
|
||||
|
||||
// stair with stair
|
||||
} else if (n1.getType() == GridNode::TYPE_STAIR && n2.getType() == GridNode::TYPE_STAIR) {
|
||||
|
||||
const float distxy = n1.inMeter().xy().getDistance(n2.inMeter().xy());
|
||||
const float distz_cm = std::abs(n1.z_cm - n2.z_cm);
|
||||
|
||||
// if (n1.getDistanceInCM(n2) < gs_cm * 1.45 && !isBlocked(map, n1, n2)) {
|
||||
if (distxy < gs_cm * 1.2 / 100.0f && distz_cm <= gs_cm) { // [1.845]
|
||||
if (n1.fullyConnected()) {continue;}
|
||||
if (n2.fullyConnected()) {continue;}
|
||||
grid.connectUniDir(n1, n2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// if (n1.getDistanceInCM(n2) < gs_cm * 1.7 && !isBlocked(map, n1, n2)) {
|
||||
// if (n1.fullyConnected()) {continue;}
|
||||
// if (n2.fullyConnected()) {continue;}
|
||||
// grid.connectUniDir(n1, n2);
|
||||
// }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** recursively get all connected nodes and add them to the set */
|
||||
void getConnected(Node& n1, std::unordered_set<int>& visited) {
|
||||
|
||||
std::unordered_set<int> toVisit;
|
||||
toVisit.insert(n1.getIdx());
|
||||
|
||||
// run while there are new nodes to visit
|
||||
while(!toVisit.empty()) {
|
||||
|
||||
// get the next node
|
||||
int nextIdx = *toVisit.begin();
|
||||
toVisit.erase(nextIdx);
|
||||
visited.insert(nextIdx);
|
||||
Node& next = grid[nextIdx];
|
||||
|
||||
// get all his (unprocessed) neighbors and add them to the region
|
||||
for (const Node& n2 : grid.neighbors(next)) {
|
||||
if (visited.find(n2.getIdx()) == visited.end()) {
|
||||
toVisit.insert(n2.getIdx());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void removeIsolatedNodes() {
|
||||
|
||||
//std::cout << "todo: remove" << std::endl;
|
||||
//return;
|
||||
|
||||
// try to start at the first stair
|
||||
for (Node& n : grid) {
|
||||
if (n.getType() == GridNode::TYPE_STAIR) {removeIsolatedNodes(n); return;}
|
||||
}
|
||||
|
||||
// no stair found? try to start at the first node
|
||||
removeIsolatedNodes(grid[0]);
|
||||
|
||||
}
|
||||
|
||||
/** remove all nodes not connected to n1 */
|
||||
void removeIsolatedNodes(Node& n1) {
|
||||
|
||||
// get the connected region around n1
|
||||
//Log::add(name, "getting set of all nodes connected to " + (std::string) n1, false);
|
||||
//Log::tick();
|
||||
std::unordered_set<int> set;
|
||||
getConnected(n1, set);
|
||||
//Log::tock();
|
||||
|
||||
//const int numToRemove = grid.getNumNodes() - set.size();
|
||||
//int numRemoved = 0;
|
||||
|
||||
// remove all other
|
||||
//Log::add(name, "removing all nodes NOT connected to " + (std::string) n1, false);
|
||||
//Log::tick();
|
||||
for (Node& n2 : grid) {
|
||||
if (set.find(n2.getIdx()) == set.end()) {
|
||||
|
||||
// sanity check
|
||||
// wouldn't make sense that a stair-node is removed..
|
||||
// maybe something went wrong elsewhere???
|
||||
Assert::notEqual(n2.getType(), GridNode::TYPE_STAIR, "detected an isolated stair?!");
|
||||
Assert::notEqual(n2.getType(), GridNode::TYPE_ELEVATOR, "detected an isolated elevator?!");
|
||||
//Assert::notEqual(n2.getType(), GridNode::TYPE_DOOR, "detected an isolated door?!");
|
||||
|
||||
// proceed ;)
|
||||
grid.remove(n2);
|
||||
|
||||
//++numRemoved;
|
||||
//std::cout << numRemoved << ":" << numToRemove << std::endl;
|
||||
|
||||
}
|
||||
}
|
||||
//Log::tock();
|
||||
|
||||
// clean the grid (physically delete the removed nodes)
|
||||
grid.cleanup();
|
||||
|
||||
}
|
||||
|
||||
|
||||
// std::vector<NewNode> getPointsOn(const Floorplan::Floor* floor, const Floorplan::FloorOutlinePolygon& poly) {
|
||||
|
||||
// std::vector<NewNode> res;
|
||||
|
||||
// BBox2 bbox;
|
||||
// for (Point2 pt : poly.poly.points) {bbox.add(pt);}
|
||||
|
||||
// int x1 = std::floor(bbox.getMin().x * 100 / gs_cm) * gs_cm;
|
||||
// int x2 = std::ceil(bbox.getMax().x * 100 / gs_cm) * gs_cm;
|
||||
|
||||
// int y1 = std::floor(bbox.getMin().y * 100 / gs_cm) * gs_cm;
|
||||
// int y2 = std::ceil(bbox.getMax().y * 100 / gs_cm) * gs_cm;
|
||||
|
||||
// int z = floor->atHeight * 100;
|
||||
|
||||
// for (int y = y1; y <= y2; y += gs_cm) {
|
||||
// for (int x = x1; x <= x2; x += gs_cm) {
|
||||
|
||||
// GridPoint gp(x, y, z);
|
||||
// int type = poly.outdoor ? GridNode::TYPE_OUTDOOR : GridNode::TYPE_FLOOR;
|
||||
// res.push_back(NewNode(gp, type));
|
||||
|
||||
// }
|
||||
// }
|
||||
|
||||
// return res;
|
||||
|
||||
// }
|
||||
|
||||
|
||||
|
||||
std::vector<NewNode> getPointsOn(const Floorplan::Floor* floor) {
|
||||
|
||||
std::vector<NewNode> res;
|
||||
|
||||
BBox2 bbox;
|
||||
for (const Floorplan::FloorOutlinePolygon* poly : floor->outline) {
|
||||
for (Point2 pt : poly->poly.points) {bbox.add(pt);}
|
||||
}
|
||||
|
||||
int x1 = std::floor(bbox.getMin().x * 100 / gs_cm) * gs_cm;
|
||||
int x2 = std::ceil(bbox.getMax().x * 100 / gs_cm) * gs_cm;
|
||||
|
||||
int y1 = std::floor(bbox.getMin().y * 100 / gs_cm) * gs_cm;
|
||||
int y2 = std::ceil(bbox.getMax().y * 100 / gs_cm) * gs_cm;
|
||||
|
||||
int z = floor->atHeight * 100;
|
||||
|
||||
struct Combo {
|
||||
HelperPoly3 poly;
|
||||
const Floorplan::FloorOutlinePolygon* orig;
|
||||
Combo(HelperPoly3 poly, const Floorplan::FloorOutlinePolygon* orig) : poly(poly), orig(orig) {;}
|
||||
};
|
||||
|
||||
std::vector<Combo> polygons;
|
||||
for (const Floorplan::FloorOutlinePolygon* poly : floor->outline) {
|
||||
HelperPoly3 pol(*poly);
|
||||
polygons.push_back(Combo(pol, poly));
|
||||
}
|
||||
|
||||
GF3_ITER_XY
|
||||
|
||||
int type = GridNode::TYPE_FLOOR;
|
||||
bool remove = false;
|
||||
bool add = false;
|
||||
|
||||
for (const Combo& c : polygons) {
|
||||
if (c.poly.contains(Point2(x,y))) {
|
||||
if (c.orig->method == Floorplan::OutlineMethod::ADD) {add = true;}
|
||||
if (c.orig->method == Floorplan::OutlineMethod::REMOVE) {remove = true; break;}
|
||||
if (c.orig->outdoor) {type = GridNode::TYPE_OUTDOOR;}
|
||||
}
|
||||
}
|
||||
|
||||
if (add && !remove) {
|
||||
GridPoint gp(x, y, z);
|
||||
res.push_back(NewNode(gp, type));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
|
||||
// const std::vector<NewNode> pts = getPointsOn(floor, *poly);
|
||||
// if (poly->method == Floorplan::OutlineMethod::ADD) {
|
||||
// add.insert(add.end(), pts.begin(), pts.end());
|
||||
// } else {
|
||||
// rem.insert(rem.end(), pts.begin(), pts.end());
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
static bool bary(Point2 p, Point2 a, Point2 b, Point2 c, float &u, float &v, float &w) {
|
||||
const Point2 v0 = b - a, v1 = c - a, v2 = p - a;
|
||||
double d00 = dot(v0, v0);
|
||||
double d01 = dot(v0, v1);
|
||||
double d11 = dot(v1, v1);
|
||||
double d20 = dot(v2, v0);
|
||||
double d21 = dot(v2, v1);
|
||||
double denom = d00 * d11 - d01 * d01;
|
||||
v = (d11 * d20 - d01 * d21) / denom;
|
||||
w = (d00 * d21 - d01 * d20) / denom;
|
||||
u = 1.0f - v - w;
|
||||
return (u <= 1 && v <= 1 && w <= 1) && (u >= 0 && v >= 0 && w >= 0);
|
||||
}
|
||||
|
||||
// void isBlocked(const GridPoint& gp) {
|
||||
// for (Floorplan::Floor* floor : map->floors) {
|
||||
// for (Floorplan::FloorObstacle* obs : floor->obstacles) {
|
||||
// Floorplan::FloorObstacleLine* line = dynamic_cast<Floorplan::FloorObstacleLine*>(obs);
|
||||
// if (line) {
|
||||
// line->
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
std::vector<NewNode> getPointsOn(const Floorplan::Floor* floor, const std::vector<Floorplan::Quad3>& quads) {
|
||||
|
||||
std::vector<NewNode> res;
|
||||
|
||||
// whole stair
|
||||
BBox3 bboxStair;
|
||||
for (const Floorplan::Quad3& quad : quads) {
|
||||
bboxStair.add(quad.p1);
|
||||
bboxStair.add(quad.p2);
|
||||
bboxStair.add(quad.p3);
|
||||
bboxStair.add(quad.p4);
|
||||
}
|
||||
|
||||
// stair's starting and ending z (must be connected to a floor)
|
||||
//int z1 = grid.snapZ( (floor->atHeight) * 100 );
|
||||
//
|
||||
int z2 = grid.snapZ( (floor->atHeight + bboxStair.getMax().z) * 100 );
|
||||
|
||||
// one quad
|
||||
for (const Floorplan::Quad3& quad : quads) {
|
||||
|
||||
BBox3 bbox;
|
||||
bbox.add(quad.p1);
|
||||
bbox.add(quad.p2);
|
||||
bbox.add(quad.p3);
|
||||
bbox.add(quad.p4);
|
||||
|
||||
|
||||
int x1 = std::floor(bbox.getMin().x * 100 / gs_cm) * gs_cm;
|
||||
int x2 = std::ceil(bbox.getMax().x * 100 / gs_cm) * gs_cm;
|
||||
|
||||
int y1 = std::floor(bbox.getMin().y * 100 / gs_cm) * gs_cm;
|
||||
int y2 = std::ceil(bbox.getMax().y * 100 / gs_cm) * gs_cm;
|
||||
|
||||
//int zFloor = floor->atHeight * 100;
|
||||
|
||||
// for (int y = y1; y <= y2; y += gs_cm) {
|
||||
// const int xx = (y / gs_cm % 2 == 0) ? (0) : (gs_cm/2);
|
||||
// for (int x = x1-xx; x <= x2; x += gs_cm) {
|
||||
|
||||
GF3_ITER_XY
|
||||
|
||||
int z = 0;
|
||||
Point2 p(x/100.0f, y/100.0f);
|
||||
|
||||
float u,v,w;
|
||||
if (bary(p, quad.p1.xy(), quad.p2.xy(), quad.p3.xy(), u, v, w)) {
|
||||
z = (quad.p1.z*u + quad.p2.z*v + quad.p3.z*w) * 100;
|
||||
} else if (bary(p, quad.p1.xy(), quad.p3.xy(), quad.p4.xy(), u, v, w)) {
|
||||
z = (quad.p1.z*u + quad.p3.z*v + quad.p4.z*w) * 100;
|
||||
} else {
|
||||
// outside of the quad -> skip
|
||||
//z = (quad.p1.z*u + quad.p3.z*v + quad.p4.z*w) * 100;
|
||||
continue;
|
||||
|
||||
//z = zFloor + (
|
||||
// (quad.p1.z*u + quad.p2.z*v + quad.p3.z*w)
|
||||
// ) * 100;
|
||||
|
||||
}
|
||||
|
||||
//z = grid.snapZ(z);
|
||||
|
||||
const GridPoint gp(x, y, z);
|
||||
const int type = GridNode::TYPE_STAIR;
|
||||
res.push_back(NewNode(gp, type));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// scale to ensure starting at floor, and ending at floor
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // GRIDFACTORY3_H
|
||||
111
grid/factory/v3/HelperPoly3.h
Normal file
111
grid/factory/v3/HelperPoly3.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef HELPERPOLY3_H
|
||||
#define HELPERPOLY3_H
|
||||
|
||||
#include "../../../geo/Point2.h"
|
||||
#include "../../../geo/Point3.h"
|
||||
#include "../../../geo/BBox2.h"
|
||||
#include "../../../geo/BBox3.h"
|
||||
#include "../../../floorplan/v2/Floorplan.h"
|
||||
#include "../../../grid/Grid.h"
|
||||
|
||||
/** helper class for polygon methods */
|
||||
struct HelperPoly3 {
|
||||
|
||||
BBox2 bbox_cm;
|
||||
std::vector<Point2> points_cm;
|
||||
|
||||
/** empty ctor */
|
||||
HelperPoly3() {
|
||||
;
|
||||
}
|
||||
|
||||
/** ctor from floorplan-polygon */
|
||||
HelperPoly3(const Floorplan::FloorOutlinePolygon& poly) {
|
||||
for (Point2 p : poly.poly.points) { add(p * 100); }
|
||||
}
|
||||
|
||||
/** ctor from floorplan-quad */
|
||||
HelperPoly3(const Floorplan::Quad3& quad) {
|
||||
add(quad.p1*100); add(quad.p2*100); add(quad.p3*100); add(quad.p4*100);
|
||||
}
|
||||
|
||||
/** ctor from floorplan-polygon */
|
||||
HelperPoly3(const Floorplan::Polygon2& poly) {
|
||||
for (Point2 p : poly.points) { add(p * 100); }
|
||||
}
|
||||
|
||||
void add(const Point2 p) {
|
||||
points_cm.push_back(p);
|
||||
bbox_cm.add(p);
|
||||
}
|
||||
|
||||
void add(const Point3& p) {
|
||||
points_cm.push_back(p.xy());
|
||||
bbox_cm.add(p.xy());
|
||||
}
|
||||
|
||||
/** does the polygon contain the given point (in cm)? */
|
||||
bool contains(const Point2 p_cm) const {
|
||||
|
||||
// not within bbox? -> not within polygon
|
||||
if (!bbox_cm.contains(p_cm)) {return false;}
|
||||
|
||||
// ensure the point is at least a bit outside of the polygon
|
||||
const float x1_cm = bbox_cm.getMin().x - 17.71920;
|
||||
const float y1_cm = bbox_cm.getMin().y - 23.10923891;
|
||||
|
||||
// construct line between point outside of the polygon and the point in question
|
||||
const Line2 l(x1_cm, y1_cm, p_cm.x, p_cm.y);
|
||||
|
||||
// determine the number of intersections
|
||||
int hits = 0;
|
||||
const int cnt = points_cm.size();
|
||||
for (int i = 0; i < cnt; ++i) {
|
||||
const Point2 p1 = points_cm[(i+0)%cnt];
|
||||
const Point2 p2 = points_cm[(i+1)%cnt];
|
||||
const Line2 l12(p1, p2);
|
||||
if (l12.getSegmentIntersection(l)) {++hits;}
|
||||
}
|
||||
|
||||
// inside or outside?
|
||||
return ((hits % 2) == 1);
|
||||
|
||||
}
|
||||
|
||||
/** call a user-function for each GRID-ALIGNED point within the polygon */
|
||||
void forEachGridPoint(const int gridSize_cm, std::function<void(int x_cm, int y_cm)> callback) const {
|
||||
|
||||
int x1 = std::floor(bbox_cm.getMin().x / gridSize_cm) * gridSize_cm;
|
||||
int x2 = std::ceil(bbox_cm.getMax().x / gridSize_cm) * gridSize_cm;
|
||||
|
||||
int y1 = std::floor(bbox_cm.getMin().y / gridSize_cm) * gridSize_cm;
|
||||
int y2 = std::ceil(bbox_cm.getMax().y / gridSize_cm) * gridSize_cm;
|
||||
|
||||
// process each point within the (aligned) bbox
|
||||
for (int y = y1; y <= y2; y += gridSize_cm) {
|
||||
for (int x = x1; x <= x2; x += gridSize_cm) {
|
||||
|
||||
// does this point belong to the polygon?
|
||||
if (!contains(Point2(x,y))) {continue;}
|
||||
|
||||
// call the callback
|
||||
callback(x,y);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // HELPERPOLY3_H
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef GRIDWALK_H
|
||||
#define GRIDWALK_H
|
||||
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef GRIDWALKHELPER_H
|
||||
#define GRIDWALKHELPER_H
|
||||
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef GRIDWALKLIGHTATTHEENDOFTHETUNNEL_H
|
||||
#define GRIDWALKLIGHTATTHEENDOFTHETUNNEL_H
|
||||
|
||||
@@ -5,7 +15,7 @@
|
||||
#include "../Grid.h"
|
||||
|
||||
#include "../../math/DrawList.h"
|
||||
#include "../../math/Distributions.h"
|
||||
#include "../../math/distribution/Normal.h"
|
||||
#include "../../math/DrawList.h"
|
||||
|
||||
#include "../../nav/dijkstra/Dijkstra.h"
|
||||
@@ -35,7 +45,7 @@ private:
|
||||
DrawList<T&> drawer;
|
||||
|
||||
/** fast random-number-generator */
|
||||
RandomGenerator gen;
|
||||
Random::RandomGenerator gen;
|
||||
|
||||
/** 0-mean normal distribution */
|
||||
std::normal_distribution<float> headingChangeDist = std::normal_distribution<float>(0.0, HEADING_CHANGE_SIGMA);
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef GRIDWALKPATHCONTROL_H
|
||||
#define GRIDWALKPATHCONTROL_H
|
||||
|
||||
@@ -23,7 +33,7 @@ private:
|
||||
static constexpr float HEADING_CHANGE_SIGMA = Angle::degToRad(10);
|
||||
|
||||
/** fast random-number-generator */
|
||||
RandomGenerator gen;
|
||||
Random::RandomGenerator gen;
|
||||
|
||||
/** 0-mean normal distribution */
|
||||
std::normal_distribution<float> headingChangeDist = std::normal_distribution<float>(0.0, HEADING_CHANGE_SIGMA);
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef GRIDWALKPUSHFORWARD_H
|
||||
#define GRIDWALKPUSHFORWARD_H
|
||||
|
||||
@@ -32,7 +42,7 @@ private:
|
||||
static constexpr float HEADING_ALLOWED_SIGMA = Angle::degToRad(20);
|
||||
|
||||
/** fast random-number-generator */
|
||||
RandomGenerator gen;
|
||||
Random::RandomGenerator gen;
|
||||
|
||||
/** 0-mean normal distribution */
|
||||
std::normal_distribution<float> headingChangeDist = std::normal_distribution<float>(0.0, HEADING_CHANGE_SIGMA);
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef GRIDWALKRANDOMHEADINGUPDATE_H
|
||||
#define GRIDWALKRANDOMHEADINGUPDATE_H
|
||||
|
||||
@@ -35,7 +45,7 @@ private:
|
||||
static constexpr float HEADING_CHANGE_SIGMA = Angle::degToRad(10);
|
||||
|
||||
/** fast random-number-generator */
|
||||
RandomGenerator gen;
|
||||
Random::RandomGenerator gen;
|
||||
|
||||
/** 0-mean normal distribution */
|
||||
std::normal_distribution<float> headingChangeDist = std::normal_distribution<float>(0.0, HEADING_CHANGE_SIGMA);
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef GRIDWALKRANDOMHEADINGUPDATEADV_H
|
||||
#define GRIDWALKRANDOMHEADINGUPDATEADV_H
|
||||
|
||||
@@ -35,7 +45,7 @@ private:
|
||||
static constexpr float HEADING_CHANGE_SIGMA = Angle::degToRad(10);
|
||||
|
||||
/** fast random-number-generator */
|
||||
RandomGenerator gen;
|
||||
Random::RandomGenerator gen;
|
||||
|
||||
/** 0-mean normal distribution */
|
||||
std::normal_distribution<float> headingChangeDist = std::normal_distribution<float>(0.0, HEADING_CHANGE_SIGMA);
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* © Copyright 2014 – Urheberrechtshinweis
|
||||
* Alle Rechte vorbehalten / All Rights Reserved
|
||||
*
|
||||
* Programmcode ist urheberrechtlich geschuetzt.
|
||||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||||
* Keine Verwendung ohne explizite Genehmigung.
|
||||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||||
*/
|
||||
|
||||
#ifndef GRIDWALKSHORTESTPATHCONTROL_H
|
||||
#define GRIDWALKSHORTESTPATHCONTROL_H
|
||||
|
||||
@@ -79,7 +89,7 @@ protected:
|
||||
static constexpr float HEADING_CHANGE_SIGMA = Angle::degToRad(10);
|
||||
|
||||
/** fast random-number-generator */
|
||||
RandomGenerator gen;
|
||||
Random::RandomGenerator gen;
|
||||
|
||||
/** 0-mean normal distribution */
|
||||
std::normal_distribution<float> headingChangeDist = std::normal_distribution<float>(0.0, HEADING_CHANGE_SIGMA);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user