203 Commits

Author SHA1 Message Date
MBulli
2e527797a3 FileReader adds ground truth points as entries 2020-04-08 15:29:46 +02:00
MBulli
6cda6beedf Median.h: Sort values on median retrieval instead live on adding 2020-04-08 15:29:22 +02:00
MBulli
2f1698c4cc Added default ctor for MovingMedianTS 2020-04-08 15:28:06 +02:00
Markus Bullmann
462ba1199c Fixed median class 2019-10-08 15:18:14 +02:00
Markus Bullmann
9a7ca32f37 FloorplanMesh: Added material names to mtl file 2019-10-01 14:57:39 +02:00
Markus Bullmann
d96af81109 Perform resampling as first update step to avoid plotting issue 2019-10-01 14:56:17 +02:00
Markus Bullmann
2668cfc600 Added option to read data with nanosecond timestamps 2019-10-01 14:55:19 +02:00
Markus Bullmann
8761dce3ce Added ftm attributes to WiFiMeasurement 2019-06-19 16:05:23 +02:00
Markus Bullmann
09e57292f6 Made Kahan Sum cross compile 2019-06-12 10:02:20 +02:00
mail@toni-fetzer.de
96c63ac3ec added measurement grouping for beacons
had to change the parameter boundaries of the wifi optimizer to be able to use it for bluetooth... this should be refactored to something more generic..
some minor changes in ble
2019-06-10 16:57:02 +02:00
mail@toni-fetzer.de
8d37e94647 added stuff for bluetooth
worked on resampling methods
2019-06-05 18:09:15 +02:00
toni
cb61e0fe68 tabbing... 2018-11-24 17:21:49 +01:00
toni
9de1e7142b added kde resampling with percentage 2018-11-24 17:11:28 +01:00
toni
7af6277da5 Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor 2018-11-24 17:09:55 +01:00
toni
1dac907004 added boxkde resamplin in 2D and 3D
read activity out of sensor offline file
2018-11-24 17:09:41 +01:00
63bc8131ac changes 2018-10-25 11:59:10 +02:00
kazu
ac91eeaa3d changes 2018-10-25 11:50:12 +02:00
857d7a1553 fixed some issues
added new pose/turn detections
new helper classes
define-flags for libEigen
2018-09-04 10:49:00 +02:00
f990485d44 worked on step-detection
adjusted iir biquad
added biquad-stacking
2018-08-07 19:36:07 +02:00
d6ac8a72ca worked on fir/iir filters 2018-08-06 18:33:17 +02:00
k-a-z-u
327b580b69 Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor 2018-08-01 18:07:23 +02:00
k-a-z-u
17f62e8788 minor changes 2018-08-01 18:07:10 +02:00
Markus Bullmann
9388e6e725 Workaround for stupid float errors in boxKDE 3D 2018-08-01 16:01:49 +02:00
Markus Bullmann
cbba812558 Replaced sleep 2018-08-01 15:50:37 +02:00
toni
0bb22b54e9 added KDE3DResampling Method.
However BoxKDE3D seems to be buggy
2018-08-01 11:54:10 +02:00
Markus Bullmann
d912a76246 Fixed encoding 2018-08-01 10:18:45 +02:00
Markus Bullmann
4aec7bae26 Added 3D boxKDE 2018-07-31 15:41:25 +02:00
Markus Bullmann
32870a62c6 Added some MSVC compiler switches 2018-07-31 15:38:59 +02:00
Markus Bullmann
726d5f8b90 Replaced gcc specific #warning with #pragma message 2018-07-31 10:50:50 +02:00
4d740d6236 changes to Floorplan 3D 2018-07-30 20:59:54 +02:00
kazu
e45cc1d3c6 fixed order issue 2018-07-26 08:43:46 +02:00
k-a-z-u
baeb8bef11 worked on windows 2018-07-25 18:12:48 +02:00
k-a-z-u
0d22d91470 started working on walling, dooring and windowing
refactoring
2018-07-25 16:21:47 +02:00
k-a-z-u
f7e4323d58 worked on 3D walls 2018-07-24 18:08:08 +02:00
3d79fd5af0 Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor 2018-07-24 08:13:44 +02:00
8dd1ba0be6 moved from ray3 to floorplan/3D
worked on new wall models
refactoring
2018-07-24 08:13:16 +02:00
toni
a9368ee0f0 Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor 2018-07-23 16:00:19 +02:00
toni
1408709efe added kde base resampling. doesn't work as expected atm.
it seems i have soom tabbing problems xD... we should talk about that
2018-07-23 16:00:05 +02:00
083a1c2cf2 new helper methods
adjusted wall intersection
2018-07-22 17:32:44 +02:00
8ea7b7f3b6 adjusted 3D modeling for walls
refactoring
new helper classes / methods
2018-07-22 13:47:07 +02:00
toni
f225788ac4 Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor 2018-07-19 10:29:56 +02:00
toni
db7c0e310e small changes so it works with yasmin 2018-07-19 10:29:51 +02:00
k-a-z-u
da06bacb6b worked on nav mesh walk 2018-07-18 16:52:16 +02:00
k-a-z-u
012abd5f80 worked on step-detection 2018-07-17 17:25:01 +02:00
18d237dcc8 Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor 2018-07-17 09:53:55 +02:00
2dee085131 added IIR stuff
worked on StepDetection
2018-07-17 09:53:30 +02:00
toni
74a424b555 merged 2018-07-12 11:13:37 +02:00
toni
d96f0588c2 changes in kld walker 2018-07-12 11:11:11 +02:00
k-a-z-u
94e53613ba adjusted distribution includes 2018-07-11 19:08:48 +02:00
k-a-z-u
6456e579cf std::experimental fix for android 2018-07-11 12:28:23 +02:00
k-a-z-u
dd9116086d added some sanity checks 2018-07-10 17:27:18 +02:00
k-a-z-u
19d9ce3961 added floorplan support for object-sizing
added floorplan support for different wall-heights
minor fixes/changes
2018-07-04 20:28:15 +02:00
fb8061125f minor change 2018-07-03 11:08:41 +02:00
k-a-z-u
039f9c4cee refactored FIR filters
- real and complex variant
adjusted StepDetection2
2018-07-03 10:56:30 +02:00
k-a-z-u
ae3b95cb0e added sanity check to csv
refactoring for wifi models
adjusted obj/mtl parsing
2018-06-26 11:58:36 +02:00
657e72b4c5 added csv parsing 2018-06-12 10:23:43 +02:00
kazu
b50995ffe6 Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor 2018-06-06 11:40:22 +02:00
kazu
2036469491 removed some obsolete includes 2018-06-06 11:40:04 +02:00
k-a-z-u
38b633b9be fixed some plotting issues
modified step detection
2018-06-06 11:21:00 +02:00
9e6d9f4ce7 added support for pillars
some new helper methods/classes
2018-05-22 11:45:35 +02:00
a22290415e fixed some issues with stats::variance
fixed umbrella header for stats
added error-feedback to wifi optimizers
improved logging for wifi optimizers
adjusted calling-API for wifi-optimizers
2018-05-20 18:56:49 +02:00
k-a-z-u
a8123d532d added wifi per-floor optimization
added plot to wifi-quality-analyzer
changes to per-floor wifi models
minor fixes
2018-05-16 13:02:06 +02:00
toni
5bec3a5c0d added sample impoverishment unblockable method 2018-05-15 10:27:41 +02:00
toni
d0b01b377d added code for advanced sample impoverishment using the mesh 2018-05-15 10:03:49 +02:00
k-a-z-u
628aafaecd worked on FIR-Convolution and LocalMaxima detection 2018-05-09 18:24:07 +02:00
k-a-z-u
0fcc3fb1e9 Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor 2018-05-09 10:03:28 +02:00
k-a-z-u
1af670a0a9 adjusted code and test-cases for fixed-freq-interpolater 2018-05-09 10:02:53 +02:00
Markus Bullmann
24f05a2846 Added new material 'metal' 2018-04-05 17:26:11 +02:00
k-a-z-u
caca1bf219 added lint for doors
small obj-pool fix
2018-04-04 13:00:51 +02:00
k-a-z-u
a4ed06c4a4 Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor 2018-04-04 10:14:40 +02:00
Markus Bullmann
2e6cf96f1d Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor 2018-04-04 09:45:49 +02:00
Markus Bullmann
3b76c09986 merged duplicated code regarding GPCPolygon 2018-04-04 09:37:05 +02:00
k-a-z-u
1b9084a9c0 Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor 2018-04-03 15:04:07 +02:00
k-a-z-u
5d089f4b86 added support for multiple obj-pool folders 2018-04-03 15:03:55 +02:00
bb31bc3b2d Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor 2018-04-03 14:56:38 +02:00
1c2081d406 worked on 3d models within map
adjusted grid factory
adjusted nav mesh factory
minoor changes/fixes
new helper classes
refactoring
2018-04-03 14:55:59 +02:00
Markus Bullmann
e6444ceeb7 VS fixes 2018-03-27 13:59:47 +02:00
k-a-z-u
705763f03c switched from linux-dir-api to c++17 dir-api 2018-03-27 12:39:48 +02:00
toni
f3b6155157 Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor 2018-02-20 10:08:37 +01:00
toni
e0da02d29d added estimation for BOXKDE
added resampling with simple random particle against sample impoverishment
2018-02-20 10:08:17 +01:00
8358f45674 added support for .obj objects within the floorplan
include objects within navmesh calculation
include objects within 3d mesh generation
minor changes/fixes
2018-02-17 17:20:43 +01:00
toni
1b22fdbc8e Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor 2018-02-12 17:00:47 +01:00
toni
77ffb76252 stuff dont know git... 2018-02-12 17:00:42 +01:00
42a3a47317 worked on 3D model stuff
minor changes
2018-02-12 16:57:08 +01:00
toni
632b1ca7a7 merged 2018-02-12 13:48:11 +01:00
toni
0fef66ac62 fixed some small issues regarding museum project 2018-02-12 13:47:23 +01:00
2a923dfabc fixed issues with random walks 2018-02-10 14:15:05 +01:00
5e749d4da8 worked on map->3d model generation 2018-02-08 21:27:24 +01:00
k-a-z-u
a35e043196 worked on 3D model creation 2018-02-06 17:34:29 +01:00
0bb1b707de started working on 3D building stuff 2018-02-05 20:20:48 +01:00
toni
4985d3bcdd Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor 2018-01-27 12:24:46 +01:00
d8b91cf144 added walker sanity checks 2018-01-27 12:23:37 +01:00
e44d3421de fixed issue within file-reader when reading empty data-parts 2018-01-27 12:07:06 +01:00
toni
4663c7d9e5 merged 2018-01-24 11:44:59 +01:00
toni
b207e5e0f2 added simple fft 2018-01-24 11:44:08 +01:00
k-a-z-u
1a1f249e9b some refactorings/fixes
worked on nav-mesh stuff
new tests
2018-01-24 11:27:11 +01:00
c9d02d440a added dijkstra support to navmesh 2018-01-21 13:41:17 +01:00
k-a-z-u
3c72bc814c worked on nav-mesh
added dijkstra support for nav mesh
some minor changes to distributions
minor fixes
2018-01-17 16:36:37 +01:00
k-a-z-u
e81e8c112d Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor 2018-01-17 16:30:04 +01:00
k-a-z-u
e589a40da6 added const member change 2018-01-17 10:33:52 +01:00
toni
3591106b38 added boxkde von bulli 2018-01-17 10:27:01 +01:00
toni
c02d07af51 merged 2018-01-17 10:26:16 +01:00
55061ef0da minor changes to floorplan
fixed some compile issues
worked on nav-meshes
added some tests
2018-01-16 12:41:05 +01:00
k-a-z-u
fee6cd3496 worked on navMesh stuff
- creation
- walking
- helper
2018-01-10 16:57:19 +01:00
3fc9688825 worked on nav-meshes 2018-01-10 08:31:14 +01:00
ca6fed5371 worked on grid-walking
worked on grid-generation
added helper library for nav-meshes
started working on nav meshes
2018-01-08 20:55:50 +01:00
k-a-z-u
c346b7f222 started testing a new grid-builder
minor fixes
worked on walkers
2017-12-20 17:12:30 +01:00
k-a-z-u
d48b0b8fd4 minor changes to grid walking 2017-12-13 16:37:51 +01:00
1114331fd2 worked on synthetic sensor data 2017-12-13 13:25:53 +01:00
toni
74981c6a45 ref #39 smoothing is refactored
KDE smoothing algorithmisch mal geschrieben, jetzt noch testen
2017-12-06 17:37:14 +01:00
k-a-z-u
ade2425fbd minor code changes 2017-12-06 17:09:54 +01:00
k-a-z-u
63bc2f3046 worked on grid-walker and synthetic steps/turns 2017-11-29 16:35:29 +01:00
k-a-z-u
55c061b344 minor chaqnges 2017-11-22 17:38:37 +01:00
d03372ad3d worked on grid-walking 2017-11-22 13:00:02 +01:00
toni
95a5c8f34f #39 #40 git add for last commit 2017-11-15 17:46:06 +01:00
toni
c8063bc862 ref #39 #40 moved all stuff left in KLib into Indoor. We are now able to perform localization without the need of KLib. Only K::Gnuplot is needed for drawing, but this will be separated into an own project in the future 2017-11-15 17:43:32 +01:00
k-a-z-u
89c6b07e68 Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor 2017-11-15 16:42:10 +01:00
k-a-z-u
7af5131ccf worked on grid walker 2017-11-15 16:41:57 +01:00
root
72932ad90f Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor 2017-11-08 18:11:01 +01:00
root
ce558d0c01 fixed issues and elevators 2017-11-08 18:10:40 +01:00
k-a-z-u
08d8292976 minor changes 2017-11-08 18:09:56 +01:00
toni
3f8d21e146 fixed two bugs in ActivityButterPressure:
1) when barometer produces false measurements, we needed some kind of upper boundary
2) coding error static.. while initializing this object over multiple test iterations isnt the best idea
2017-11-06 18:04:55 +01:00
284c6b11a6 many changes
added new helper class for 3x3 matrices and vec3
added magnetometer data
added compass detection
refactored pose-estimation (single class)
refactored debug plots (move to own class)
minor changes
2017-10-31 19:38:08 +01:00
k-a-z-u
d97b325650 Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor 2017-10-25 16:58:54 +02:00
k-a-z-u
ea351d6377 worked on grid-walker 2017-10-25 16:58:41 +02:00
toni
1c0e680b9d Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor 2017-10-24 18:42:33 +02:00
toni
5d6d62c2d2 fixed activity enum problems 2017-10-24 18:42:21 +02:00
k-a-z-u
feaa2ea12c Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor 2017-10-24 16:49:18 +02:00
k-a-z-u
81b6b2c96c worked on grid-walker v3 2017-10-24 16:48:56 +02:00
toni
0151a4f6e4 rebase activity rec 2017-10-24 12:44:16 +02:00
k-a-z-u
3e31f6da53 worked on synthetic sensors
worked on grid-walker
minor changes/fixes/improvements
2017-10-18 16:54:57 +02:00
k-a-z-u
72f083d32a worked on grid-walker
started adding code for synthetic sensor simulation based on given paths
2017-10-17 17:10:23 +02:00
3807c621c7 added new helper methods
worked on gridWalker v3
2017-10-17 13:01:26 +02:00
556bbe8829 added new math-stats
started working on a new grid walker
2017-10-12 10:55:47 +02:00
da477866c1 worked on wifi-scanner for linux
new time-grouping for vap grouper
adjusted test-cases
minor changes/fixes/improvements
2017-10-11 14:00:24 +02:00
628be72e1f addded netlink and iw based wifi scanner for linux 2017-10-10 17:00:12 +02:00
k-a-z-u
7eb3a16e48 worked on raytracing 2017-09-13 17:06:55 +02:00
686151b511 worked on 2D/3D raytracing
adjusted BVH
improved 2D/3D BVH
new bounding volumes
new test cases
renamed some test-cases for grouping reasons
made GPC header-only using slight adjustments
2017-09-13 08:08:00 +02:00
k-a-z-u
c19d18a3a6 modified lib GPC for header only
worked on 3d traytracing
2017-09-06 17:04:19 +02:00
845d89774d added lib GPC for triangulation 2017-09-06 08:37:21 +02:00
e4cd9c6b8d work on raytracing 2017-09-06 08:34:20 +02:00
k-a-z-u
c21925e86f worked on grid creation
fixed some issues with stairs
fixed/added LINT
2017-07-27 18:40:56 +02:00
3a92199777 fixed issue when two floor's outlines overlap 2017-06-08 12:49:57 +02:00
8ed4305d2a merge 2017-06-06 15:03:49 +02:00
ba9559e5df minor changes 2017-06-06 15:03:33 +02:00
9ea7da557b fixed some grid-factory issues
added some new attributes
minor changes
2017-06-01 15:58:58 +02:00
34271b5cb7 fixed grid factory issue with stairs
added some sanity checks
2017-05-24 17:51:29 +02:00
fdc47215ac Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor 2017-05-24 10:19:41 +02:00
04d8ae8c74 changes from the laptop
- some should be the same as previous commit (sorry!)
- some should be new: LINT checks, ...?
2017-05-24 10:03:39 +02:00
0864f55a54 added support for XML reading/writing
new serialization interfaces
new helper methods
new wifi models
2017-05-24 09:32:05 +02:00
1ef3e33f2e Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor 2017-05-24 09:29:52 +02:00
d40032ca74 interface changes
added new data-strcutures for new sensors
new helper methods
fixed some issues
2017-05-24 09:23:27 +02:00
toni
8e295e25af removed some hacks 2017-04-19 14:59:04 +02:00
toni
99f282180e commiting a hackgit diff! attention 2017-04-18 11:15:06 +02:00
toni
c42a592834 Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor 2017-04-17 16:53:05 +02:00
toni
200aa94ca8 fixed some bugs in jensen shannon and kullback leibler 2017-04-17 16:50:56 +02:00
f67f95d1ce added first draft of a wifi quality analyzer 2017-04-17 16:47:02 +02:00
toni
a9cd8d5e68 small bug change 2017-04-04 16:50:11 +02:00
8930be1e2c added new sanity-check assertions
fixed issue with angles [bad interface]
- adjusted other parts accordingly
- added corresponding test-cases
started working on absolute heading
2017-03-31 11:47:29 +02:00
2fdaa795b2 Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor 2017-03-28 21:03:50 +02:00
4439123e5b fixed some issues
some new helper methods
added listener-support to offline-reader
2017-03-28 21:03:17 +02:00
toni
d9788a71c0 fixed bugs in jensenshannon.h 2017-03-24 17:37:05 +01:00
toni
a25ffb2ba3 added jensen shannon, fixe kullback leibler when P=0 2017-03-24 17:28:57 +01:00
toni
749ee0b783 merged 2017-03-24 17:01:51 +01:00
e34e773e31 added new helper methods to work with ground-truth
made some assertions optional [i know what i am doing!]
2017-03-24 11:20:29 +01:00
c0cef979bb added helper methods for debug printing
fixed issue when reading wifi entries within old walk files
worked on earth-registration
added corresponding test-cases
other minor changes
2017-03-24 10:07:08 +01:00
toni
59502931e5 added kernel density wrapper
added general kld solution
fixed minor bugs
added tests
2017-03-23 19:52:06 +01:00
b03804d378 added earth-registration load/save 2017-03-21 21:11:58 +01:00
toni
b8b35d2a50 merged 2017-03-21 17:21:24 +01:00
toni
991f42060c added some asserts 2017-03-21 17:20:55 +01:00
bb43e7f0fe fixed some compiler warnings
added equality checks to sensor-data classes
more robust sensor reader [fixed some issues]
added support for gps
added support for compass
added sensor-data-writer
added test-cases
minor changes
2017-03-21 16:25:36 +01:00
18f48e23a8 fixed LINT issue 2017-03-20 19:24:29 +01:00
toni
3a83f5bdc9 Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor 2017-03-20 14:55:46 +01:00
4ac248d08e adjusted floorplan-lint for GUI integration 2017-03-20 14:44:17 +01:00
3868e42937 started adding floorplan LINT
new helper methods
2017-03-20 12:10:34 +01:00
toni
b6e396376e Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor 2017-03-20 11:42:56 +01:00
toni
485be83b44 fixed typo in normal, added gt point 3d 2017-03-20 11:42:46 +01:00
06e0e0a5aa fixed some potential issues with MAC addresses
added corresponding test-cases
switched to newer version of tinyxml due to some issues
adjusted affected code-parts accordingly
for better re-use, moved ceiling-calculation to a new class
some minor fixes
new helper methods
worked on wifi-opt
2017-03-20 11:19:57 +01:00
toni
d065015f7d added sampling function for NormalN and approximation of normalN using samples + testcases for both 2017-03-13 12:55:31 +01:00
7ec5fef697 added "outdoor" support to floorplan and grid-nodes 2017-03-12 16:47:29 +01:00
toni
e88f0b85a6 Merge branch 'master' into workingToni 2017-03-10 15:39:48 +01:00
toni
b18b1a795d merged with master 2017-03-10 15:35:44 +01:00
b99bb2e226 added support for adding fingerprint-locations with meta information 2017-03-10 15:12:27 +01:00
toni
7ff7334263 Merge branch 'master' into workingToni 2017-03-10 14:54:50 +01:00
toni
b1a9286d71 merged again? 2017-03-10 14:46:43 +01:00
toni
2eb9405950 merged 2017-03-10 14:45:26 +01:00
toni
e48d3bafcd added kullback leibler for gaussian cases 2017-03-10 14:43:46 +01:00
toni
62087fe072 added WalkModule for von Mises Heading 2017-03-10 14:43:46 +01:00
toni
54894a0c23 many small changes, added filereader with beacons, added motion detection stuff, testcases 2017-03-10 14:43:46 +01:00
toni
ef6f44969f added support for ground truth points \n small fixed in beaconprob 2017-03-10 14:43:46 +01:00
ae357ffd4b added new parameters to Floorplan-APs and -Beacons 2017-03-10 14:43:46 +01:00
toni
0bf8a9c25c small fix. added getter and setter! 2017-03-10 14:43:46 +01:00
toni
a35a22e676 added beacon stuff similiar architecture then wifi \n added activity percentage stuff \n added testcases 2017-03-10 14:43:46 +01:00
cad299cd7c started adding earth-mapping
some new helper methods
added support for floorplan metadata load/save
2017-03-10 13:47:12 +01:00
toni
d5bc1111f5 added kullback leibler for gaussian cases 2017-03-09 18:57:47 +01:00
toni
f7d0d88448 added WalkModule for von Mises Heading 2017-03-03 12:09:19 +01:00
toni
b6be58eebc many small changes, added filereader with beacons, added motion detection stuff, testcases 2017-03-02 18:08:02 +01:00
toni
5ec0b7e9ee Merge remote-tracking branch 'origin/master' into workingToni 2016-12-01 19:51:23 +01:00
toni
e10ba2cfd9 added support for ground truth points \n small fixed in beaconprob 2016-12-01 19:48:27 +01:00
2255904385 Merge branch 'master' of https://git.frank-ebner.de/FHWS/Indoor 2016-11-29 21:29:40 +01:00
272382ae3d added new parameters to Floorplan-APs and -Beacons 2016-11-29 21:29:10 +01:00
toni
41c1d90c54 small fix. added getter and setter! 2016-11-16 12:40:16 +01:00
toni
c083aae476 added beacon stuff similiar architecture then wifi \n added activity percentage stuff \n added testcases 2016-11-04 17:25:49 +01:00
454 changed files with 56982 additions and 2277 deletions

View File

@@ -22,7 +22,7 @@ namespace Assert {
template <typename STR> static inline void doThrow(const STR err) { template <typename STR> static inline void doThrow(const STR err) {
#ifdef WITH_ASSERTIONS #ifdef WITH_ASSERTIONS
std::string str = "in: "; std::string str = "in: ";
str += __PRETTY_FUNCTION__; str += __FUNCTION__;
str += " error: "; str += " error: ";
str += err; str += err;
throw Exception(err); throw Exception(err);
@@ -51,8 +51,8 @@ namespace Assert {
if (v != nullptr) {doThrow(err);} if (v != nullptr) {doThrow(err);}
} }
template <typename T, typename STR> static inline void isNotNull(const T v, const STR err) { template <typename T, typename STR> static inline void isNotNull(const T& v, const STR err) {
if (v == nullptr) {doThrow(err);} if (v == nullptr) {doThrow(err);}
} }
template <typename T, typename STR> static inline void isNotNaN(const T v, const STR 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) { 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) { if (v < min || v > max) {
std::stringstream ss; ss << "\n[" << min << ":" << max << "] but is " << v << "\n"; std::stringstream ss; ss << "\n[" << min << ":" << max << "] but is " << v << "\n";

View File

@@ -11,87 +11,115 @@ SET( CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" )
PROJECT(Indoor) PROJECT(Indoor)
IF(NOT CMAKE_BUILD_TYPE) IF(NOT CMAKE_BUILD_TYPE)
MESSAGE(STATUS "No build type selected. Default to Debug") MESSAGE(STATUS "No build type selected. Default to Debug")
SET(CMAKE_BUILD_TYPE "Debug") SET(CMAKE_BUILD_TYPE "Debug")
ENDIF() ENDIF()
INCLUDE_DIRECTORIES( INCLUDE_DIRECTORIES(
../ ../
/mnt/firma/kunden/HandyGames/
) )
FILE(GLOB HEADERS FILE(GLOB HEADERS
./*.h ./*.h
./*/*.h ./*/*.h
./*/*/*.h ./*/*/*.h
./*/*/*/*.h ./*/*/*/*.h
./*/*/*/*/*.h ./*/*/*/*/*.h
./*/*/*/*/*/*.h ./*/*/*/*/*/*.h
./tests/data/* ./*/*/*/*/*/*/*.h
./tests/data/*/* ./tests/data/*
./tests/data/*/*/* ./tests/data/*/*
./tests/data/*/*/*
) )
FILE(GLOB SOURCES 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") if(${CMAKE_GENERATOR} MATCHES "Visual Studio")
SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_X86_ /D_USE_MATH_DEFINES") add_definitions(
SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi /Oi /GL /Ot /Ox /D_X86_ /D_USE_MATH_DEFINES") -D_USE_MATH_DEFINES
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG") -DUNICODE
SET(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG /INCREMENTAL:NO") -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() else()
# system specific compiler flags # system specific compiler flags
ADD_DEFINITIONS( ADD_DEFINITIONS(
-std=gnu++11 -std=gnu++17
-Wall -Wall
-Werror=return-type -Werror=return-type
-Wextra -Wextra
-Wpedantic -Wpedantic
-fstack-protector-all -fstack-protector-all
-g3
-O0
-march=native
-DWITH_TESTS
-DWITH_ASSERTIONS
-DWITH_DEBUG_LOG
-g3
#-O0
#-O2
-march=native
-DWITH_TESTS
-DWITH_ASSERTIONS
-DWITH_DEBUG_LOG
-D_GLIBCXX_DEBUG
) )
endif() endif()
# build a binary file # build a binary file
ADD_EXECUTABLE( ADD_EXECUTABLE(
${PROJECT_NAME} ${PROJECT_NAME}
${HEADERS} ${HEADERS}
${SOURCES} ${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 # needed external libraries
TARGET_LINK_LIBRARIES( TARGET_LINK_LIBRARIES(
${PROJECT_NAME} ${PROJECT_NAME}
gtest gtest
pthread pthread
${EXTRA_LIBS}
) )
SET(CMAKE_C_COMPILER ${CMAKE_CXX_COMPILER}) SET(CMAKE_C_COMPILER ${CMAKE_CXX_COMPILER})

View File

@@ -5,7 +5,8 @@
#include <string> #include <string>
#ifdef ANDROID #ifdef ANDROID
#include <QMessageBox> //include <QMessageBox>
#include <QtDebug>
#endif #endif
class Exception : public std::exception { class Exception : public std::exception {
@@ -22,7 +23,10 @@ public:
// TODO better solution? // TODO better solution?
#ifdef ANDROID #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 #endif
} }

125
data/File.h Normal file
View 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
View 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

View File

@@ -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 #ifndef RINGBUFFER_H
#define RINGBUFFER_H #define RINGBUFFER_H

View File

@@ -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 #ifndef TIMESTAMP_H
#define TIMESTAMP_H #define TIMESTAMP_H
@@ -11,28 +21,51 @@ struct Timestamp {
private: private:
/** internal timestamp in milliseconds */ /** internal timestamp in milliseconds */
int64_t _ms; //int64_t _ms;
int64_t _us;
/** HIDDEN ctor. use factory methods */ /** hidden ctor. for internal methods only */
explicit Timestamp(const int64_t ms) : _ms(ms) {;} Timestamp(int64_t us) : _us(us) {;}
public: public:
/** empty ctor */ /** 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 */ /** 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 */ /** 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 */ /** get timestamp for the current unix-time */
static inline Timestamp fromUnixTime() { static inline Timestamp fromUnixTime() {
auto now = std::chrono::system_clock::now(); auto now = std::chrono::system_clock::now();
auto duration = now.time_since_epoch(); auto duration = now.time_since_epoch();
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count(); //auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
return Timestamp(millis); auto micros = std::chrono::duration_cast<std::chrono::microseconds>(duration).count();
return Timestamp::fromUS(micros);
} }
/** get timestamp for the current system-time */ /** get timestamp for the current system-time */
@@ -45,41 +78,50 @@ public:
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 */ /** get timestamp in milliseconds */
inline int64_t ms() const {return _ms;} inline int64_t ms() const {return _us/1000;}
/** get timestamp in seconds */ /** get timestamp in seconds */
inline float sec() const {return _ms/1000.0f;} inline float sec() const {return _us/1000.0f/1000.0f;}
public: public:
/** is this timestamp zero? */ /** is this timestamp zero? */
bool isZero() const {return _ms == 0;} bool isZero() const {return _us == 0;}
/** equal? */ /** equal? */
bool operator == (const Timestamp& o) const {return _ms == o._ms;} bool operator == (const Timestamp& o) const {return _us == o._us;}
/** not equal? */ /** 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? */ /** smaller than the given one? */
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 _ms <= o._ms;} bool operator <= (const Timestamp& o) const {return _us <= o._us;}
/** greater than the given one? */ /** greater than the given one? */
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 _ms >= o._ms;} 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 */ // /** cast to float */
// operator float () const {return sec();} // 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 #endif // TIMESTAMP_H

24
data/XMLload.h Normal file
View 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
View 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
View 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
View 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
View 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

View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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

View 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
View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View File

@@ -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 #ifndef FLOOR_H
#define FLOOR_H #define FLOOR_H

View File

@@ -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 #ifndef FLOORPLANFACTORYSVG_H
#define FLOORPLANFACTORYSVG_H #define FLOORPLANFACTORYSVG_H

View File

@@ -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 #ifndef PLATFORMSTAIR_H
#define PLATFORMSTAIR_H #define PLATFORMSTAIR_H

View File

@@ -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 #ifndef STAIR_H
#define STAIR_H #define STAIR_H

View File

@@ -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 #ifndef STAIRS_H
#define STAIRS_H #define STAIRS_H

View File

@@ -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 #ifndef FLOORPLAN2_H
#define FLOORPLAN2_H #define FLOORPLAN2_H
@@ -9,6 +20,7 @@
#include "../../geo/Point3.h" #include "../../geo/Point3.h"
#include "../../geo/Point2.h" #include "../../geo/Point2.h"
#include "../../geo/EarthPos.h"
namespace Floorplan { namespace Floorplan {
@@ -21,20 +33,86 @@ namespace Floorplan {
/** a free key-value meta element */ /** a free key-value meta element */
struct Meta { struct Meta {
struct KeyVal {
std::string key;
std::string val;
KeyVal() {;}
KeyVal(const std::string& key, const std::string& val) : key(key), val(val) {;}
};
const std::string EMPTY = ""; const std::string EMPTY = "";
std::unordered_map<std::string, std::string> params;
// std::unordered_map<std::string, std::string> params;
// const std::string& getVal(const std::string& key) const {
// const auto it = params.find(key);
// return (it == params.end()) ? (EMPTY) : (it->second);
// }
std::vector<KeyVal> params;
KeyVal* getKV(const std::string& key) {
for (KeyVal& kv : params) {
if (kv.key == key) {return &kv;}
}
return nullptr;
}
const KeyVal* getKV(const std::string& key) const {
for (const KeyVal& kv : params) {
if (kv.key == key) {return &kv;}
}
return nullptr;
}
const std::string& getVal(const std::string& key) const { const std::string& getVal(const std::string& key) const {
const auto it = params.find(key); static const std::string EMPTY = "";
return (it == params.end()) ? (EMPTY) : (it->second); const KeyVal* kv = getKV(key);
return (kv) ? (kv->val) : (EMPTY);
}
void setVal(const std::string& key, const std::string& val) {(*this)[key] = val;}
void add(const std::string& key, const std::string& val) {params.push_back(KeyVal(key,val));}
std::string& operator [] (const std::string& key) {
KeyVal* kv = getKV(key);
if (!kv) {params.push_back(KeyVal(key, ""));}
return getKV(key)->val;
} }
void setVal(const std::string& key, const std::string& val) {params[key] = val;}
float getFloat(const std::string& key) const { return std::stof(getVal(key)); } float getFloat(const std::string& key) const { return std::stof(getVal(key)); }
void setFloat(const std::string& key, const float val) { params[key] = std::to_string(val); } void setFloat(const std::string& key, const float val) { (*this)[key] = std::to_string(val); }
int getInt(const std::string& key) const { return std::stoi(getVal(key)); } int getInt(const std::string& key) const { return std::stoi(getVal(key)); }
void setInt(const std::string& key, const int val) { params[key] = std::to_string(val); } void setInt(const std::string& key, const int val) { (*this)[key] = std::to_string(val); }
size_t size() const {return params.size();}
const std::string& getKey(const int idx) const {return params[idx].key;}
const std::string& getVal(const int idx) const {return params[idx].val;}
void set(const int idx, const KeyVal& kv) {params[idx] = kv;}
void setKey(const int idx, const std::string& key) {params[idx].key = key;}
void setVal(const int idx, const std::string& val) {params[idx].val = val;}
/** delete the idx-th entry */
void deleteEntry(const int idx) {params.erase(params.begin()+idx);}
};
class HasMeta {
Meta* meta = nullptr;
public:
Meta* getMeta() const {return meta;}
void setMeta(Meta* meta) {
if (this->meta) {delete this->meta;}
this->meta = meta;
}
}; };
@@ -63,6 +141,10 @@ namespace Floorplan {
default: throw Exception("out of bounds"); 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 */ /** additional type-info for obstacles */
@@ -86,6 +168,12 @@ namespace Floorplan {
_END, _END,
}; };
/** available window types */
enum class WindowType {
UNKNOWN,
_END,
};
/** all supported material types */ /** all supported material types */
enum class Material { enum class Material {
UNKNOWN, UNKNOWN,
@@ -93,6 +181,8 @@ namespace Floorplan {
WOOD, WOOD,
DRYWALL, DRYWALL,
GLASS, GLASS,
METAL,
METALLIZED_GLAS,
_END, _END,
}; };
@@ -112,25 +202,54 @@ namespace Floorplan {
struct FloorObstacle; struct FloorObstacle;
struct AccessPoint; struct AccessPoint;
struct Beacon; struct Beacon;
struct FingerprintLocation;
struct FloorRegion; struct FloorRegion;
struct UnderlayImage; struct UnderlayImage;
struct POI; struct POI;
struct Stair; struct Stair;
struct Elevator; struct Elevator;
struct GroundTruthPoint;
struct FloorObstacleWallDoor;
struct FloorObstacleWallWindow;
using FloorOutline = std::vector<FloorOutlinePolygon*>; struct FloorOutline : public std::vector<FloorOutlinePolygon*> {
using FloorObstacles = std::vector<FloorObstacle*>; bool enabled = true;
using FloorAccessPoints = std::vector<AccessPoint*>; };
using FloorBeacons = std::vector<Beacon*>; struct FloorObstacles : public std::vector<FloorObstacle*> {
using FloorRegions = std::vector<FloorRegion*>; bool enabled = true;
using FloorUnderlays = std::vector<UnderlayImage*>; };
using FloorPOIs = std::vector<POI*>; struct FloorAccessPoints : public std::vector<AccessPoint*> {
using FloorStairs = std::vector<Stair*>; bool enabled = true;
using FloorElevators = std::vector<Elevator*>; };
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 */ /** describes one floor within the map, starting at a given height */
struct Floor { struct Floor {
bool enabled = true;
float atHeight; // the floor's starting height float atHeight; // the floor's starting height
float height; // the floor's total height (from start) float height; // the floor's total height (from start)
std::string name; // the floor's name std::string name; // the floor's name
@@ -139,10 +258,12 @@ namespace Floorplan {
FloorRegions regions; // all regions within the floor (rooms, ...) FloorRegions regions; // all regions within the floor (rooms, ...)
FloorAccessPoints accesspoints; FloorAccessPoints accesspoints;
FloorBeacons beacons; FloorBeacons beacons;
FloorFingerprintLocations fpLocations; // potential fingerprint locations
FloorUnderlays underlays; // underlay images (used for map-building) FloorUnderlays underlays; // underlay images (used for map-building)
FloorPOIs pois; // POIs within the floor FloorPOIs pois; // POIs within the floor
FloorStairs stairs; // all stairs within one floor FloorStairs stairs; // all stairs within one floor
FloorElevators elevators; // all elevators within one floor FloorElevators elevators; // all elevators within one floor
FloorGroundTruthPoints gtpoints; // all ground truth points within one floor
//FloorKeyValue other; // other, free elements //FloorKeyValue other; // other, free elements
Floor() {;} Floor() {;}
@@ -155,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 */ /** a POI located somewhere on a floor */
struct POI { struct POI {
@@ -167,11 +296,26 @@ namespace Floorplan {
bool operator == (const POI& o) const {return (o.type == type) && (o.name == name) && (o.pos == pos);} 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
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 */ /** an AccessPoint located somewhere on a floor */
struct AccessPoint { struct AccessPoint : public HasMeta {
std::string name; std::string name;
std::string mac; std::string mac;
Point3 pos; // z is relative to the floor's height Point3 pos; // z is relative to the floor's height
struct Model {
float txp = 0;
float exp = 0;
float waf = 0;
} model;
AccessPoint() : name(), mac(), pos() {;} AccessPoint() : name(), mac(), pos() {;}
AccessPoint(const std::string& name, const std::string& mac, const Point3& pos) : name(name), mac(toUpperCase(mac)), pos(pos) {;} AccessPoint(const std::string& name, const std::string& mac, const Point3& pos) : name(name), mac(toUpperCase(mac)), pos(pos) {;}
bool operator == (const AccessPoint& o) const {return (o.name == name) && (o.mac == mac) && (o.pos == pos);} bool operator == (const AccessPoint& o) const {return (o.name == name) && (o.mac == mac) && (o.pos == pos);}
@@ -182,10 +326,19 @@ namespace Floorplan {
struct Beacon { struct Beacon {
std::string name; std::string name;
std::string mac; std::string mac;
std::string major;
std::string minor;
std::string uuid;
Point3 pos; // z is relative to the floor's height Point3 pos; // z is relative to the floor's height
struct Model {
float txp;
float exp;
float waf;
} model;
Beacon() : name(), mac(), pos() {;} Beacon() : name(), mac(), pos() {;}
Beacon(const std::string& name, const std::string& mac, const Point3& pos) : name(name), mac(mac), pos(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);} 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
}; };
@@ -194,15 +347,13 @@ namespace Floorplan {
OutlineMethod method; OutlineMethod method;
std::string name; std::string name;
Polygon2 poly; Polygon2 poly;
FloorOutlinePolygon() : method(OutlineMethod::ADD), name(), poly() {;} bool outdoor; // special marker
FloorOutlinePolygon(const OutlineMethod method, const std::string& name, const Polygon2& poly) : method(method), name(name), poly(poly) {;} FloorOutlinePolygon() : method(OutlineMethod::ADD), name(), poly(), outdoor(false) {;}
bool operator == (const FloorOutlinePolygon& o) const {return (o.method == method) && (o.name == name) && (o.poly == poly);} 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 */ /** base-class for one obstacle (wall, door, window, pillar, ..) within a floor */
struct FloorObstacle { struct FloorObstacle {
Material material; Material material;
@@ -216,16 +367,20 @@ namespace Floorplan {
ObstacleType type; ObstacleType type;
Point2 from; Point2 from;
Point2 to; Point2 to;
FloorObstacleLine(const ObstacleType type, const Material material, const Point2 from, const Point2 to) : FloorObstacle(material), type(type), from(from), to(to) {;} float thickness_m;
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 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 */ /** circle obstacle */
struct FloorObstacleCircle : public FloorObstacle { struct FloorObstacleCircle : public FloorObstacle {
Point2 center; Point2 center;
float radius; float radius;
FloorObstacleCircle(const Material material, const Point2 center, const float radius) : FloorObstacle(material), center(center), radius(radius) {;} float height = 0; // 0 = floor's height
FloorObstacleCircle(const Material material, const float cx, const float cy, const float radius) : FloorObstacle(material), center(cx,cy), radius(radius) {;} 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 */ /** door obstacle */
@@ -241,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) {;}
};
@@ -308,7 +526,7 @@ namespace Floorplan {
/** snap quad-edges together if their distance is below the given maximum. is used to close minor gaps */ /** 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 (Floorplan::Quad3& quad1 : quads) {
for (int i1 = 0; i1 < 4; ++i1) { for (int i1 = 0; i1 < 4; ++i1) {
@@ -335,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 */ /** 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; std::vector<Quad3> vec;
@@ -375,8 +593,7 @@ namespace Floorplan {
/** base-class for stairs */ /** base-class for stairs */
struct Stair { struct Stair : public HasMeta {
Meta* meta = nullptr;
virtual std::vector<StairPart> getParts() const = 0; virtual std::vector<StairPart> getParts() const = 0;
}; };
@@ -405,6 +622,9 @@ namespace Floorplan {
/** the elevator's rotation (in radians) */ /** the elevator's rotation (in radians) */
float rotation; float rotation;
/** the elevator's height (from its starting position) */
float height_m;
/** get the 4 corner points for the elevator */ /** get the 4 corner points for the elevator */
Polygon2 getPoints() const { Polygon2 getPoints() const {
const Point2 p1 = Point2(+width/2, +depth/2).rotated(rotation) + center; const Point2 p1 = Point2(+width/2, +depth/2).rotated(rotation) + center;
@@ -427,7 +647,7 @@ namespace Floorplan {
/** an image file that can be added to the map */ /** an image file that can be added to the map */
struct UnderlayImage { struct UnderlayImage {
std::string name; std::string name;
std::string filename; std::string filename;
Point2 anchor; Point2 anchor;
@@ -437,16 +657,49 @@ namespace Floorplan {
/** one correspondence point: earth <-> map */
struct EarthPosMapPos {
EarthPos posOnEarth;
Point3 posOnMap_m;
/** empty ctor */
EarthPosMapPos() : posOnEarth(), posOnMap_m() {;}
/** ctor */
EarthPosMapPos(const EarthPos posOnEarth, const Point3 posOnMap_m) : posOnEarth(posOnEarth), posOnMap_m(posOnMap_m) {;}
};
/** describe the floorplan's location on earth */
struct EarthRegistration {
bool enabled = true;
/** all available correspondences: earth <-> map */
std::vector<EarthPosMapPos*> correspondences;
};
/** describes the whole indoor map */ /** describes the whole indoor map */
struct IndoorMap { struct IndoorMap {
float width; float width;
float depth; float depth;
std::vector<Floor*> floors; std::vector<Floor*> floors;
/** mapping: floorplan <-> earth */
EarthRegistration earthReg;
IndoorMap() {;} IndoorMap() {;}
/** no copy */
IndoorMap(const IndoorMap& o) = delete; IndoorMap(const IndoorMap& o) = delete;
/** no copy assign */
void operator = (const IndoorMap& o) = delete; void operator = (const IndoorMap& o) = delete;
}; };

View 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

View File

@@ -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 #ifndef FLOORPLANHELPER2_H
#define FLOORPLANHELPER2_H #define FLOORPLANHELPER2_H
@@ -7,11 +17,88 @@
#include "Floorplan.h" #include "Floorplan.h"
#include "../../sensors/MACAddress.h"
/** /**
* helper methods for the floorplan * helper methods for the floorplan
*/ */
class FloorplanHelper { 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: public:
/** align all floorplan values to the given grid size. needed for the grid factory */ /** align all floorplan values to the given grid size. needed for the grid factory */

View 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

View File

@@ -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 #ifndef FLOORPLANREADER_H
#define FLOORPLANREADER_H #define FLOORPLANREADER_H
#include <iostream> #include <iostream>
#include "Floorplan.h" #include "Floorplan.h"
#include "FloorplanLINT.h"
#include "../../misc/Debug.h" #include "../../misc/Debug.h"
#include "../../Assertions.h" #include "../../Assertions.h"
@@ -13,6 +25,7 @@ namespace Floorplan {
using XMLAttr = tinyxml2::XMLAttribute; using XMLAttr = tinyxml2::XMLAttribute;
using XMLElem = tinyxml2::XMLElement; using XMLElem = tinyxml2::XMLElement;
using XMLNode = tinyxml2::XMLNode;
/** /**
* read an IndoorMaps from XML-data * read an IndoorMaps from XML-data
@@ -31,8 +44,14 @@ namespace Floorplan {
setlocale(LC_NUMERIC, "C"); setlocale(LC_NUMERIC, "C");
tinyxml2::XMLDocument doc; tinyxml2::XMLDocument doc;
const tinyxml2::XMLError res = doc.LoadFile(file.c_str()); const tinyxml2::XMLError res = doc.LoadFile(file.c_str());
if (res != tinyxml2::XMLError::XML_SUCCESS) {throw Exception("error while loading XML " + file);} if (res != tinyxml2::XMLError::XML_SUCCESS) {
return parse(doc); throw Exception(
std::string() + "error while loading XML " + file + "\n" +
((doc.GetErrorStr1()) ? (doc.GetErrorStr1()) : ("")) + "\n" +
((doc.GetErrorStr2()) ? (doc.GetErrorStr2()) : ("")));
}
IndoorMap* map = parse(doc);
return map;
} }
/** read an IndoorMap from the given XMl-string */ /** read an IndoorMap from the given XMl-string */
@@ -41,13 +60,20 @@ namespace Floorplan {
setlocale(LC_NUMERIC, "C"); setlocale(LC_NUMERIC, "C");
tinyxml2::XMLDocument doc; tinyxml2::XMLDocument doc;
const tinyxml2::XMLError res = doc.Parse(str.c_str(), str.length()); const tinyxml2::XMLError res = doc.Parse(str.c_str(), str.length());
if (res != tinyxml2::XMLError::XML_SUCCESS) {throw Exception("error while parsing XML");} if (res != tinyxml2::XMLError::XML_SUCCESS) {
return parse(doc); throw Exception(
std::string() + "error while parsing XML\n" +
((doc.GetErrorStr1()) ? (doc.GetErrorStr1()) : ("")) + "\n" +
((doc.GetErrorStr2()) ? (doc.GetErrorStr2()) : (""))
);
}
IndoorMap* map = parse(doc);
return map;
} }
private: 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) { static void assertNode(const std::string& node, const XMLElem* el) {
std::string err = std::string("unexpected node '") + el->Name() + "' expected '" + node + "'"; std::string err = std::string("unexpected node '") + el->Name() + "' expected '" + node + "'";
@@ -56,7 +82,7 @@ namespace Floorplan {
/** parse the complete document */ /** parse the complete document */
static IndoorMap* parse(tinyxml2::XMLDocument& doc) { static IndoorMap* parse(tinyxml2::XMLDocument& doc) {
return parseMap((XMLElem*)doc.FirstChild()); return parseMap(doc.FirstChildElement());
} }
/** parse the <map> node */ /** parse the <map> node */
@@ -65,12 +91,36 @@ namespace Floorplan {
IndoorMap* map = new IndoorMap(); IndoorMap* map = new IndoorMap();
map->width = el->FloatAttribute("width"); map->width = el->FloatAttribute("width");
map->depth = el->FloatAttribute("depth"); map->depth = el->FloatAttribute("depth");
FOREACH_NODE(n, el) { 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; 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 */ /** parse the <floors> node */
static std::vector<Floor*> parseFloors(const XMLElem* el) { static std::vector<Floor*> parseFloors(const XMLElem* el) {
std::vector<Floor*> floors; std::vector<Floor*> floors;
@@ -92,19 +142,21 @@ namespace Floorplan {
if (std::string("obstacles") == n->Name()) {floor->obstacles = parseFloorObstacles(n);} if (std::string("obstacles") == n->Name()) {floor->obstacles = parseFloorObstacles(n);}
if (std::string("accesspoints") == n->Name()) {floor->accesspoints = parseFloorAccessPoints(n);} if (std::string("accesspoints") == n->Name()) {floor->accesspoints = parseFloorAccessPoints(n);}
if (std::string("beacons") == n->Name()) {floor->beacons = parseFloorBeacons(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("regions") == n->Name()) {floor->regions = parseFloorRegions(n);}
if (std::string("underlays") == n->Name()) {floor->underlays = parseFloorUnderlays(n);} if (std::string("underlays") == n->Name()) {floor->underlays = parseFloorUnderlays(n);}
if (std::string("pois") == n->Name()) {floor->pois = parseFloorPOIs(n);} if (std::string("pois") == n->Name()) {floor->pois = parseFloorPOIs(n);}
if (std::string("stairs") == n->Name()) {floor->stairs = parseFloorStairs(n);} if (std::string("stairs") == n->Name()) {floor->stairs = parseFloorStairs(n);}
if (std::string("elevators") == n->Name()) {floor->elevators = parseFloorElevators(n);} if (std::string("elevators") == n->Name()) {floor->elevators = parseFloorElevators(n);}
if (std::string("gtpoints") == n->Name()) {floor->gtpoints = parseFloorGroundTruthPoints(n);}
} }
return floor; return floor;
} }
/** parse the <elevators> tag */ /** parse the <elevators> tag */
static std::vector<Elevator*> parseFloorElevators(const XMLElem* el) { static FloorElevators parseFloorElevators(const XMLElem* el) {
std::vector<Elevator*> vec; FloorElevators vec;
FOREACH_NODE(n, el) { FOREACH_NODE(n, el) {
if (std::string("elevator") == n->Name()) { vec.push_back(parseFloorElevator(n)); } if (std::string("elevator") == n->Name()) { vec.push_back(parseFloorElevator(n)); }
} }
@@ -112,8 +164,8 @@ namespace Floorplan {
} }
/** parse the <stairs> tag */ /** parse the <stairs> tag */
static std::vector<Stair*> parseFloorStairs(const XMLElem* el) { static FloorStairs parseFloorStairs(const XMLElem* el) {
std::vector<Stair*> vec; FloorStairs vec;
FOREACH_NODE(n, el) { FOREACH_NODE(n, el) {
if (std::string("stair") == n->Name()) { vec.push_back(parseFloorStair(n)); } if (std::string("stair") == n->Name()) { vec.push_back(parseFloorStair(n)); }
} }
@@ -126,6 +178,7 @@ namespace Floorplan {
elev->center = Point2(el->FloatAttribute("cx"), el->FloatAttribute("cy")); elev->center = Point2(el->FloatAttribute("cx"), el->FloatAttribute("cy"));
elev->depth = el->FloatAttribute("depth"); elev->depth = el->FloatAttribute("depth");
elev->width = el->FloatAttribute("width"); elev->width = el->FloatAttribute("width");
elev->height_m = el->FloatAttribute("height");
elev->rotation = el->FloatAttribute("rotation"); elev->rotation = el->FloatAttribute("rotation");
return elev; return elev;
} }
@@ -157,9 +210,7 @@ namespace Floorplan {
// stair meta information? // stair meta information?
const XMLElem* meta = el->FirstChildElement("meta"); const XMLElem* meta = el->FirstChildElement("meta");
if (meta) { if (meta) {stair->setMeta(parseMetaElement(meta));}
stair->meta = parseMetaElement(meta);
}
return stair; return stair;
@@ -167,8 +218,8 @@ namespace Floorplan {
/** parse the <pois> tag */ /** parse the <pois> tag */
static std::vector<POI*> parseFloorPOIs(const XMLElem* el) { static FloorPOIs parseFloorPOIs(const XMLElem* el) {
std::vector<POI*> vec; FloorPOIs vec;
FOREACH_NODE(n, el) { FOREACH_NODE(n, el) {
if (std::string("poi") == n->Name()) { vec.push_back(parseFloorPOI(n)); } if (std::string("poi") == n->Name()) { vec.push_back(parseFloorPOI(n)); }
} }
@@ -185,9 +236,26 @@ namespace Floorplan {
} }
/** 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 */ /** parse the <accesspoints> tag */
static std::vector<AccessPoint*> parseFloorAccessPoints(const XMLElem* el) { static FloorAccessPoints parseFloorAccessPoints(const XMLElem* el) {
std::vector<AccessPoint*> vec; FloorAccessPoints vec;
FOREACH_NODE(n, el) { FOREACH_NODE(n, el) {
if (std::string("accesspoint") == n->Name()) { vec.push_back(parseAccessPoint(n)); } if (std::string("accesspoint") == n->Name()) { vec.push_back(parseAccessPoint(n)); }
} }
@@ -227,10 +295,18 @@ namespace Floorplan {
/** parse one <meta> element */ /** parse one <meta> element */
static Meta* parseMetaElement(const XMLElem* n) { static Meta* parseMetaElement(const XMLElem* n) {
Meta* elem = new Meta(); Meta* elem = new Meta();
const XMLAttr* attr = n->FirstAttribute(); // const XMLAttr* attr = n->FirstAttribute();
while (attr) { // while (attr) {
elem->params[attr->Name()] = attr->Value(); // elem->setVal(attr->Name(), attr->Value());
attr = attr->Next(); // attr = attr->Next();
// }
const XMLElem* sub = n->FirstChildElement();
while(sub) {
// <entry key="123">abc</entry>
const std::string key = sub->Attribute("key");
const std::string val = sub->GetText();
elem->add(key, val);
sub = sub->NextSiblingElement();
} }
return elem; return elem;
} }
@@ -241,13 +317,18 @@ namespace Floorplan {
ap->mac = n->Attribute("mac"); ap->mac = n->Attribute("mac");
ap->name = n->Attribute("name"); ap->name = n->Attribute("name");
ap->pos = parsePoint3(n); ap->pos = parsePoint3(n);
ap->model.txp = n->FloatAttribute("mdl_txp");
ap->model.exp = n->FloatAttribute("mdl_exp");
ap->model.waf = n->FloatAttribute("mdl_waf");
const XMLElem* meta = n->FirstChildElement("meta");
if (meta) {ap->setMeta(parseMetaElement(meta));}
return ap; return ap;
} }
/** parse the <beacons> tag */ /** parse the <beacons> tag */
static std::vector<Beacon*> parseFloorBeacons(const XMLElem* el) { static FloorBeacons parseFloorBeacons(const XMLElem* el) {
std::vector<Beacon*> vec; FloorBeacons vec;
FOREACH_NODE(n, el) { FOREACH_NODE(n, el) {
if (std::string("beacon") == n->Name()) { vec.push_back(parseBeacon(n)); } if (std::string("beacon") == n->Name()) { vec.push_back(parseBeacon(n)); }
} }
@@ -259,13 +340,41 @@ namespace Floorplan {
Beacon* b = new Beacon(); Beacon* b = new Beacon();
b->mac = n->Attribute("mac"); b->mac = n->Attribute("mac");
b->name = n->Attribute("name"); b->name = n->Attribute("name");
b->major = n->Attribute("major") ? n->Attribute("major") : "";
b->minor = n->Attribute("minor") ? n->Attribute("minor") : "";
b->uuid = n->Attribute("uuid") ? n->Attribute("uuid") : "";
b->model.txp = n->FloatAttribute("mdl_txp");
b->model.exp = n->FloatAttribute("mdl_exp");
b->model.waf = n->FloatAttribute("mdl_waf");
b->pos = parsePoint3(n); b->pos = parsePoint3(n);
return b; 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) { /** parse one fingerprint <location> */
std::vector<FloorRegion*> vec; 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) { FOREACH_NODE(n, el) {
if (std::string("region") == n->Name()) { vec.push_back(parseFloorRegion(n)); } if (std::string("region") == n->Name()) { vec.push_back(parseFloorRegion(n)); }
} }
@@ -281,29 +390,81 @@ namespace Floorplan {
} }
/** parse the <obstacles> tag */ /** parse the <obstacles> tag */
static std::vector<FloorObstacle*> parseFloorObstacles(const XMLElem* el) { static FloorObstacles parseFloorObstacles(const XMLElem* el) {
assertNode("obstacles", el); assertNode("obstacles", el);
std::vector<FloorObstacle*> obstacles; FloorObstacles obstacles;
FOREACH_NODE(n, el) { FOREACH_NODE(n, el) {
// if (std::string("wall") == n->Name()) {obstacles.push_back(parseFloorObstacleWall(n));} // 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("door") == n->Name()) {obstacles.push_back(parseFloorObstacleDoor(n));}
// if (std::string("window") == n->Name()) {obstacles.push_back(parseFloorObstacleWindow(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("pillar") == n->Name()) {obstacles.push_back(parseFloorObstaclePillar(n));}
//if (std::string("obstacle") == n->Name()) {obstacles.push_back(parseFloorObstacleLine(n));} // OLD //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("line") == n->Name()) {obstacles.push_back(parseFloorObstacleLine(n));}
if (std::string("circle") == n->Name()) {obstacles.push_back(parseFloorObstacleCircle(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("door") == n->Name()) {obstacles.push_back(parseFloorObstacleDoor(n));}
if (std::string("object") == n->Name()) {obstacles.push_back(parseFloorObstacleObject(n));}
} }
return obstacles; 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 */ /** parse one line */
static FloorObstacleLine* parseFloorObstacleLine(const XMLElem* el) { static FloorObstacleLine* parseFloorObstacleLine(const XMLElem* el) {
return new FloorObstacleLine( return new FloorObstacleLine(
parseObstacleType(el->Attribute("type")), parseObstacleType(el->Attribute("type")),
parseMaterial(el->Attribute("material")), parseMaterial(el->Attribute("material")),
el->FloatAttribute("x1"), el->FloatAttribute("y1"), 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")
); );
} }
@@ -312,7 +473,8 @@ namespace Floorplan {
return new FloorObstacleCircle( return new FloorObstacleCircle(
parseMaterial(el->Attribute("material")), parseMaterial(el->Attribute("material")),
el->FloatAttribute("cx"), el->FloatAttribute("cy"), el->FloatAttribute("cx"), el->FloatAttribute("cy"),
el->FloatAttribute("radius") el->FloatAttribute("radius"),
el->FloatAttribute("height")
); );
} }
@@ -327,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 */ /** parse a floor's <outline> tag */
static FloorOutline parseFloorOutline(const XMLElem* el) { static FloorOutline parseFloorOutline(const XMLElem* el) {
FloorOutline outline; FloorOutline outline;
FOREACH_NODE(n, el) { FOREACH_NODE(n, el) {
if (std::string("polygon") == n->Name()) { if (std::string("polygon") == n->Name()) {
outline.push_back(parseFloorPolygon(n)); // TODO outline.push_back(parseFloorPolygon(n));
} }
} }
return outline; return outline;
@@ -342,6 +514,7 @@ namespace Floorplan {
static FloorOutlinePolygon* parseFloorPolygon(const XMLElem* el) { static FloorOutlinePolygon* parseFloorPolygon(const XMLElem* el) {
FloorOutlinePolygon* poly = new FloorOutlinePolygon(); FloorOutlinePolygon* poly = new FloorOutlinePolygon();
poly->name = el->Attribute("name"); poly->name = el->Attribute("name");
poly->outdoor = el->BoolAttribute("outdoor");
poly->method = parseOutlineMethod(el->Attribute("method")); poly->method = parseOutlineMethod(el->Attribute("method"));
poly->poly = parsePoly2(el); poly->poly = parsePoly2(el);
return poly; return poly;
@@ -388,6 +561,10 @@ namespace Floorplan {
try { return (DoorType) std::stoi(type); } catch (...) { return DoorType::UNKNOWN; } 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) { static Material parseMaterial(const std::string material) {
try { try {
return (Material) std::stoi(material); return (Material) std::stoi(material);

View File

@@ -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 #ifndef FLOORPLANWRITER_H
#define FLOORPLANWRITER_H #define FLOORPLANWRITER_H
@@ -38,6 +49,7 @@ namespace Floorplan {
private: private:
static std::string toString(const ObstacleType t) { return std::to_string((int)t); } 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 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 Material m) { return std::to_string((int)m); }
static std::string toString(const OutlineMethod 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("width", map->width);
root->SetAttribute("depth", map->depth); root->SetAttribute("depth", map->depth);
// add earth registration to the map
addEarthReg(doc, root, map);
// add all floors to the map // add all floors to the map
addFloors(doc, root, 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 */ /** add all floors to the map */
static void addFloors(XMLDoc& doc, XMLElem* root, const IndoorMap* map) { static void addFloors(XMLDoc& doc, XMLElem* root, const IndoorMap* map) {
XMLElem* floors = doc.NewElement("floors"); XMLElem* floors = doc.NewElement("floors");
@@ -92,6 +127,7 @@ namespace Floorplan {
elem->SetAttribute("cy", elevator->center.y); elem->SetAttribute("cy", elevator->center.y);
elem->SetAttribute("width", elevator->width); elem->SetAttribute("width", elevator->width);
elem->SetAttribute("depth", elevator->depth); elem->SetAttribute("depth", elevator->depth);
elem->SetAttribute("height", elevator->height_m);
elem->SetAttribute("rotation", elevator->rotation); elem->SetAttribute("rotation", elevator->rotation);
elevators->InsertEndChild(elem); elevators->InsertEndChild(elem);
} }
@@ -105,7 +141,7 @@ namespace Floorplan {
XMLElem* stairs = doc.NewElement("stairs"); XMLElem* stairs = doc.NewElement("stairs");
for (const Stair* _stair : mf->stairs) { for (const Stair* _stair : mf->stairs) {
XMLElem* elem = doc.NewElement("stair"); XMLElem* elem = doc.NewElement("stair");
addMetaElement(doc, elem, _stair->meta); addMetaElement(doc, elem, _stair->getMeta());
if (dynamic_cast<const StairFreeform*>(_stair)) { if (dynamic_cast<const StairFreeform*>(_stair)) {
const StairFreeform* stair = (StairFreeform*) _stair; const StairFreeform* stair = (StairFreeform*) _stair;
elem->SetAttribute("type", 0); // TODO: other types? elem->SetAttribute("type", 0); // TODO: other types?
@@ -131,6 +167,7 @@ namespace Floorplan {
} }
/** add all sorts of POI to the floor */ /** add all sorts of POI to the floor */
static void addFloorPOI(XMLDoc& doc, XMLElem* floor, const Floor* mf) { static void addFloorPOI(XMLDoc& doc, XMLElem* floor, const Floor* mf) {
@@ -145,6 +182,17 @@ namespace Floorplan {
} }
floor->InsertEndChild(pois); 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);
elem->SetAttribute("z", gtp->pos.z);
gtpoints->InsertEndChild(elem);
}
floor->InsertEndChild(gtpoints);
XMLElem* accesspoints = doc.NewElement("accesspoints"); XMLElem* accesspoints = doc.NewElement("accesspoints");
for (const AccessPoint* ap : mf->accesspoints) { for (const AccessPoint* ap : mf->accesspoints) {
XMLElem* accesspoint = doc.NewElement("accesspoint"); XMLElem* accesspoint = doc.NewElement("accesspoint");
@@ -153,6 +201,10 @@ namespace Floorplan {
accesspoint->SetAttribute("x", ap->pos.x); accesspoint->SetAttribute("x", ap->pos.x);
accesspoint->SetAttribute("y", ap->pos.y); accesspoint->SetAttribute("y", ap->pos.y);
accesspoint->SetAttribute("z", ap->pos.z); accesspoint->SetAttribute("z", ap->pos.z);
accesspoint->SetAttribute("mdl_txp", ap->model.txp);
accesspoint->SetAttribute("mdl_exp", ap->model.exp);
accesspoint->SetAttribute("mdl_waf", ap->model.waf);
addMetaElement(doc, accesspoint, ap->getMeta());
accesspoints->InsertEndChild(accesspoint); accesspoints->InsertEndChild(accesspoint);
} }
floor->InsertEndChild(accesspoints); floor->InsertEndChild(accesspoints);
@@ -162,14 +214,32 @@ namespace Floorplan {
XMLElem* beacon = doc.NewElement("beacon"); XMLElem* beacon = doc.NewElement("beacon");
beacon->SetAttribute("name", b->name.c_str()); beacon->SetAttribute("name", b->name.c_str());
beacon->SetAttribute("mac", b->mac.c_str()); beacon->SetAttribute("mac", b->mac.c_str());
beacon->SetAttribute("major", b->major.c_str());
beacon->SetAttribute("minor", b->minor.c_str());
beacon->SetAttribute("uuid", b->uuid.c_str());
beacon->SetAttribute("x", b->pos.x); beacon->SetAttribute("x", b->pos.x);
beacon->SetAttribute("y", b->pos.y); beacon->SetAttribute("y", b->pos.y);
beacon->SetAttribute("z", b->pos.z); beacon->SetAttribute("z", b->pos.z);
beacon->SetAttribute("mdl_txp", b->model.txp);
beacon->SetAttribute("mdl_exp", b->model.exp);
beacon->SetAttribute("mdl_waf", b->model.waf);
beacons->InsertEndChild(beacon); beacons->InsertEndChild(beacon);
} }
floor->InsertEndChild(beacons); 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) { static void addFloorOutline(XMLDoc& doc, XMLElem* floor, const Floor* mf) {
@@ -194,6 +264,7 @@ namespace Floorplan {
XMLElem* polygon = doc.NewElement("polygon"); XMLElem* polygon = doc.NewElement("polygon");
polygon->SetAttribute("name", poly->name.c_str()); polygon->SetAttribute("name", poly->name.c_str());
polygon->SetAttribute("method", method.c_str()); polygon->SetAttribute("method", method.c_str());
polygon->SetAttribute("outdoor", poly->outdoor);
dst->InsertEndChild(polygon); dst->InsertEndChild(polygon);
for (Point2 p : poly->poly.points) { for (Point2 p : poly->poly.points) {
@@ -210,8 +281,15 @@ namespace Floorplan {
static void addMetaElement(XMLDoc& doc, XMLElem* other, const Meta* meta) { static void addMetaElement(XMLDoc& doc, XMLElem* other, const Meta* meta) {
if (meta == nullptr) {return;} // nothing to add if (meta == nullptr) {return;} // nothing to add
XMLElem* elem = doc.NewElement("meta"); XMLElem* elem = doc.NewElement("meta");
for (auto it : meta->params) { // for (auto it : meta->params) {
elem->Attribute(it.first.c_str(), it.second.c_str()); // elem->Attribute(it.first.c_str(), it.second.c_str());
// }
for (const Meta::KeyVal& kv : meta->params) {
// <entry key="123">abc</entry>
XMLElem* subElem = doc.NewElement("entry");
subElem->SetAttribute("key", kv.key.c_str());
subElem->SetText(kv.val.c_str());
elem->InsertEndChild(subElem);
} }
other->InsertEndChild(elem); other->InsertEndChild(elem);
} }
@@ -245,10 +323,14 @@ namespace Floorplan {
for (FloorObstacle* fo : mf->obstacles) { for (FloorObstacle* fo : mf->obstacles) {
if (dynamic_cast<FloorObstacleLine*>(fo)) { if (dynamic_cast<FloorObstacleLine*>(fo)) {
addFloorObstacleLine(doc, obstacles, (FloorObstacleLine*)fo); addFloorObstacleLine(doc, obstacles, (FloorObstacleLine*)fo);
} else if (dynamic_cast<FloorObstacleWall*>(fo)) {
addFloorObstacleWall(doc, obstacles, (FloorObstacleWall*)fo);
} else if (dynamic_cast<FloorObstacleCircle*>(fo)) { } else if (dynamic_cast<FloorObstacleCircle*>(fo)) {
addFloorObstacleCircle(doc, obstacles, (FloorObstacleCircle*)fo); addFloorObstacleCircle(doc, obstacles, (FloorObstacleCircle*)fo);
} else if (dynamic_cast<FloorObstacleDoor*>(fo)) { } else if (dynamic_cast<FloorObstacleDoor*>(fo)) {
addFloorObstacleDoor(doc, obstacles, (FloorObstacleDoor*)fo); addFloorObstacleDoor(doc, obstacles, (FloorObstacleDoor*)fo);
} else if (dynamic_cast<FloorObstacleObject*>(fo)) {
addFloorObstacleObject(doc, obstacles, (FloorObstacleObject*)fo);
} }
} }
@@ -256,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) { static void addFloorObstacleLine(XMLDoc& doc, XMLElem* obstacles, FloorObstacleLine* line) {
XMLElem* obstacle = doc.NewElement("line"); XMLElem* obstacle = doc.NewElement("line");
obstacle->SetAttribute("material", toString(line->material).c_str()); obstacle->SetAttribute("material", toString(line->material).c_str());
@@ -265,6 +396,8 @@ namespace Floorplan {
obstacle->SetAttribute("y1", line->from.y); obstacle->SetAttribute("y1", line->from.y);
obstacle->SetAttribute("x2", line->to.x); obstacle->SetAttribute("x2", line->to.x);
obstacle->SetAttribute("y2", line->to.y); 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); obstacles->InsertEndChild(obstacle);
} }
@@ -275,6 +408,7 @@ namespace Floorplan {
obstacle->SetAttribute("cx", circle->center.x); obstacle->SetAttribute("cx", circle->center.x);
obstacle->SetAttribute("cy", circle->center.y); obstacle->SetAttribute("cy", circle->center.y);
obstacle->SetAttribute("radius", circle->radius); obstacle->SetAttribute("radius", circle->radius);
if (circle->height != 0) {obstacle->SetAttribute("height", circle->height);}
obstacles->InsertEndChild(obstacle); obstacles->InsertEndChild(obstacle);
} }
@@ -292,6 +426,22 @@ namespace Floorplan {
obstacles->InsertEndChild(obstacle); 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);
}
}; };

View File

@@ -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 #ifndef ANGLE_H
#define ANGLE_H #define ANGLE_H
#include <cmath> #include <cmath>
#include "../Assertions.h" #include "../Assertions.h"
#include "Point2.h" #include "Point2.h"
#include "../math/speed.h"
#define PI ((float) M_PI) #define PI ((float) M_PI)
@@ -29,6 +40,13 @@ public:
return radToDeg(getRAD_2PI(x1,y1,x2,y2)); 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 * gets the angular difference between
@@ -44,13 +62,14 @@ public:
/** /**
* gets the angular difference between * gets the angular difference between
* "angular change from r1 to r2"
* - the given radians [0:2PI] * - the given radians [0:2PI]
* - as a change-in-direction between [-PI:+PI] * - as a change-in-direction between [-PI:+PI]
*/ */
static float getSignedDiffRAD_2PI(const float r1, const float r2) { 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(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 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);} if (diff > +PI) {diff = -(2*PI - diff);}
else 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 Assert::isBetween(diff, -PI, (float)(+PI), "result out of bounds"); // [-180:+180] deg

View File

@@ -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 "Point2.h"
#include "Line2.h" #include "Line2.h"
#include <vector>
class BBox2 { class BBox2 {
protected: protected:
static constexpr float MAX = +99999; static constexpr float MAX = +99999999;
static constexpr float MIN = -99999; static constexpr float MIN = -99999999;
/** minimum */ /** minimum */
Point2 p1; Point2 p1;
@@ -25,19 +37,30 @@ public:
/** ctor */ /** ctor */
BBox2(const Point2& p1, const Point2& p2) : p1(p1), p2(p2) {;} 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 */ /** adjust the bounding-box by adding this point */
void add(const Point2& p) { void add(const Point2& p) {
add(p.x, p.y);
}
if (p.x > p2.x) {p2.x = p.x;} /** adjust the bounding-box by adding this point */
if (p.y > p2.y) {p2.y = p.y;} void add(const float x, const float y) {
if (p.x < p1.x) {p1.x = p.x;} if (x > p2.x) {p2.x = x;}
if (p.y < p1.y) {p1.y = p.y;} 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 */ /** 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; return p1.x == MAX || p1.y == MAX || p2.x == MIN || p2.y == MIN;
} }
@@ -47,10 +70,19 @@ public:
/** get the bbox's maximum */ /** get the bbox's maximum */
const Point2& getMax() const {return p2;} 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 */ /** get the bbox's center point */
Point2 getCenter() const { return (p1+p2) / 2; } 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? */ /** equal? */
bool operator == (const BBox2& o) const { bool operator == (const BBox2& o) const {
return (p1.x == o.p1.x) && return (p1.x == o.p1.x) &&
@@ -59,6 +91,44 @@ public:
(p2.y == o.p2.y); (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? */ /** does the BBox intersect with the given line? */
bool intersects (const Line2& l) const { bool intersects (const Line2& l) const {
const Line2 l1(p1.x, p1.y, p2.x, p1.y); // upper const Line2 l1(p1.x, p1.y, p2.x, p1.y); // upper
@@ -83,10 +153,14 @@ public:
} }
bool contains(const Point2& p) const { bool contains(const Point2& p) const {
if (p.x < p1.x) {return false;} return contains(p.x, p.y);
if (p.x > p2.x) {return false;} }
if (p.y < p1.y) {return false;}
if (p.y > p2.y) {return false;} 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; return true;
} }
@@ -102,6 +176,21 @@ public:
p2 += Point2(val, val); // increase maximum 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

View File

@@ -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 #ifndef BBOX3_H
#define BBOX3_H #define BBOX3_H
#include "Point3.h" #include "Point3.h"
class BBox3 { template <typename Scalar>
class _BBox3 {
private: private:
static constexpr float MAX = +99999;
static constexpr float MIN = -99999;
/** minimum */ /** minimum */
Point3 p1; _Point3<Scalar> p1;
/** maximum */ /** maximum */
Point3 p2; _Point3<Scalar> p2;
public: public:
/** empty ctor */ /** empty ctor */
BBox3() : p1(MAX,MAX,MAX), p2(MIN,MIN,MIN) {;} _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 */ /** ctor with min and max */
BBox3(const Point3 min, const Point3 max) : p1(min), p2(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 */ /** 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.x > p2.x) {p2.x = p.x;}
if (p.y > p2.y) {p2.y = p.y;} if (p.y > p2.y) {p2.y = p.y;}
@@ -38,19 +56,25 @@ public:
} }
/** add the given bounding-box to this one */ /** add the given bounding-box to this one */
void add(const BBox3& bb) { void add(const _BBox3& bb) {
add(bb.getMin()); add(bb.getMin());
add(bb.getMax()); add(bb.getMax());
} }
/** get the bbox's minimum */ /** get the bbox's minimum */
const Point3& getMin() const {return p1;} const _Point3<Scalar>& getMin() const {return p1;}
/** get the bbox's maximum */ /** 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? */ /** equal? */
bool operator == (const BBox3& o) const { bool operator == (const _BBox3& o) const {
return (p1.x == o.p1.x) && return (p1.x == o.p1.x) &&
(p1.y == o.p1.y) && (p1.y == o.p1.y) &&
(p1.z == o.p1.z) && (p1.z == o.p1.z) &&
@@ -60,35 +84,38 @@ public:
} }
/** shrink the bbox in each dimension by the given amount */ /** shrink the bbox in each dimension by the given amount */
void shrink(const float v) { void shrink(const Scalar v) {
shrink(Point3(v,v,v)); shrink(_Point3<Scalar>(v,v,v));
} }
/** shrink the bbox by the amount given for each dimension */ /** 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 p1 += p; // increase minimum
p2 -= p; // decrease maximum p2 -= p; // decrease maximum
} }
/** grow the bbox by the amount given for each dimension */ /** grow the bbox by the amount given for each dimension */
void grow(const float v) { void grow(const Scalar v) {
grow(Point3(v,v,v)); grow(_Point3<Scalar>(v,v,v));
} }
/** grow the bbox by the amount given for each dimension */ /** 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 p1 -= p; // decrease minimum
p2 += p; // increase maximum p2 += p; // increase maximum
} }
/** set both, min/max z to the same value */ /** set both, min/max z to the same value */
void setZ(const float z) { void setZ(const Scalar z) {
p1.z = z; p1.z = z;
p2.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? */ /** 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 < p1.x) {return false;}
if (p.x > p2.x) {return false;} if (p.x > p2.x) {return false;}
if (p.y < p1.y) {return false;} if (p.y < p1.y) {return false;}
@@ -98,6 +125,15 @@ public:
return true; 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 #endif // BBOX3_H

55
geo/BBoxes3.h Normal file
View 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
View 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
View 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

195
geo/EarthMapping.h Normal file
View File

@@ -0,0 +1,195 @@
/*
* © 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
*/
class EarthMapping {
private:
/** one 3D position within the floorplan */
Point3 center_map_m;
/** corresponding 3D position on earth */
EarthPos center_earth;
/** rotation [in degrees] to ensure that the map's y-up-axis points towards the north */
float rotation_deg;
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 {
// move to (0,0,0)
Point3 pos = mapPos_m - center_map_m;
// undo the rotation
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 = 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);
}
/** convert from earth-coordinates to map-coordinates */
Point3 worldToMap(const EarthPos earthPos) const {
// 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 xy(x_m, y_m);
xy = xy.rotated(degToRad(-rotation_deg));
// append height
Point3 pos3(xy.x, xy.y, z_m);
// 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;
}
};
#endif // EARTHMAPPING_H

40
geo/EarthPos.h Normal file
View 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 EARTHPOS_H
#define EARTHPOS_H
/** describes the location on the earth's surface */
struct EarthPos {
double lat;
double lon;
/** height above sea level */
float 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
View 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

View File

@@ -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 #ifndef HEADING_H
#define HEADING_H #define HEADING_H
@@ -27,14 +37,22 @@ public:
Assert::isBetween(rad, 0.0f, (float)_2PI, "radians out of bounds"); 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] */ /** POSITIVE angular difference [0:PI] */
float getDiffHalfRAD(const Heading other) const { float getDiffHalfRAD(const Heading other) const {
return Angle::getDiffRAD_2PI_PI(rad, other.rad); return Angle::getDiffRAD_2PI_PI(rad, other.rad);
} }
/** signled angular difference [-PI:+PI] */ /** signled angular difference [-PI:+PI] */
float getSignedDiff(const Heading other) const { // float getSignedDiff(const Heading other) const {
return Angle::getSignedDiffRAD_2PI(rad, other.rad); // 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] */ /** update the angle but ensure we stay within [0:2PI] */
@@ -74,10 +92,10 @@ public:
} }
Heading& operator = (const float _rad) { Heading& operator = (const float _rad) {
rad = _rad; rad = _rad;
return *this; return *this;
} }
/** compare two headings */ /** compare two headings */
bool operator == (const Heading other) const {return rad == other.rad;} bool operator == (const Heading other) const {return rad == other.rad;}
@@ -92,6 +110,11 @@ public:
float getRAD() const {return rad;} 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 */ // /** get a random heading */
// static Heading rnd() { // static Heading rnd() {
// static std::minstd_rand gen(1234); // static std::minstd_rand gen(1234);

View File

@@ -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 #ifndef LENGTH_H
#define LENGTH_H #define LENGTH_H

View File

@@ -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 #ifndef LINE2D_H
#define LINE2D_H #define LINE2D_H
//#include <KLib/geo/Line.h> //#include <KLib/geo/Line.h>
#include "Point2.h" #include "Point2.h"
#include "Ray2.h"
class Line2 { class Line2 {
@@ -14,44 +25,71 @@ public:
public: public:
/** empty ctor */ /** empty ctor */
Line2() : p1(), p2() {;} Line2() : p1(), p2() {;}
/** value ctor */ /** value ctor */
Line2(const Point2 p1, const Point2 p2) : p1(p1), p2(p2) {;} 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(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 { // bool getSegmentIntersection(const Line& other) const {
// static K::Point p; // static K::Point p;
// return K::Line::getSegmentIntersection(other, p); // return K::Line::getSegmentIntersection(other, p);
// } // }
/** get intersection between these two lines (unlimited length!) */ float getAngle() const {
bool getLineIntersection(const Line2& other, Point2& result) const { return std::atan2(p2.y-p1.y, p2.x-p1.x);
}
double bx = p2.x - p1.x; void reverse() {
double by = p2.y - p1.y; std::swap(p1, p2);
}
double dx = other.p2.x - other.p1.x; float getLength() const {
double dy = other.p2.y - other.p1.y; 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; /** get intersection between these two lines (unlimited length!) */
double cy = other.p1.y - p1.y; bool getLineIntersection(const Line2& other, Point2& result) const {
double t = (cx*dy - cy*dx) / b_dot_d_perp;
result.x = p1.x + t * bx; double bx = p2.x - p1.x;
result.y = p1.y + t * by; 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 { 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 #endif // LINE2D_H

36
geo/Plane3.h Normal file
View 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

View File

@@ -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 #ifndef POINT2_H
#define POINT2_H #define POINT2_H
#include <cmath> #include <cmath>
#include <algorithm>
#include <string>
/** /**
* 2D Point * 2D Point
@@ -17,6 +29,8 @@ struct Point2 {
/** ctor */ /** ctor */
Point2(const float x, const float y) : x(x), y(y) {;} Point2(const float x, const float y) : x(x), y(y) {;}
Point2 operator - () const {return Point2(-x, -y);}
Point2 operator + (const Point2& o) const {return Point2(x+o.x, y+o.y);} Point2 operator + (const Point2& o) const {return Point2(x+o.x, y+o.y);}
@@ -40,6 +54,7 @@ struct Point2 {
bool operator != (const Point2& o) const {return x!=o.x || y!=o.y;} 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);} Point2 perpendicular() const {return Point2(-y, x);}
@@ -58,6 +73,40 @@ struct Point2 {
return std::sqrt(dx*dx + dy*dy); 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 #endif // POINT2_H

View File

@@ -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 "../Assertions.h"
#include <cmath> #include <cmath>
@@ -8,76 +18,77 @@
/** /**
* 3D Point * 3D Point
*/ */
struct Point3 { template <typename Scalar> struct _Point3 {
float x; Scalar x;
float y; Scalar y;
float z; Scalar z;
/** ctor */ /** ctor */
Point3() : x(0), y(0), z(0) {;} _Point3() : x(0), y(0), z(0) {;}
/** ctor */ /** 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 xy() const {return Point2(x,y);}
Point2 xz() const {return Point2(x,z);}
Point3 rotX(const float r) const { _Point3 rotX(const Scalar r) const {
return Point3(x, y*cos(r) - z*sin(r), y*sin(r) + z*cos(r)); return _Point3(x, y*cos(r) - z*sin(r), y*sin(r) + z*cos(r));
} }
Point3 rotY(const float r) const { _Point3 rotY(const Scalar r) const {
return Point3(z*sin(r) + x*cos(r), y, z*cos(r) - x*sin(r)); return _Point3(z*sin(r) + x*cos(r), y, z*cos(r) - x*sin(r));
} }
Point3 rotZ(const float r) const { _Point3 rotZ(const Scalar r) const {
return Point3(x*cos(r) - y*sin(r), x*sin(r) + y*cos(r), z); 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 rotX(rx).rotY(ry).rotZ(rz);
//return rotZ(rz).rotY(ry).rotX(rx); //return rotZ(rz).rotY(ry).rotX(rx);
} }
/** read-only array access */ /** 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"); Assert::isBetween(idx, 0, 2, "index out of bounds");
if (0 == idx) {return x;} if (0 == idx) {return x;}
if (1 == idx) {return y;} if (1 == idx) {return y;}
@@ -85,19 +96,19 @@ struct Point3 {
} }
/** get the distance between this point and the other one */ /** get the distance between this point and the other one */
float getDistance(const Point3& o) const { Scalar getDistance(const _Point3& o) const {
const float dx = x - o.x; const Scalar dx = x - o.x;
const float dy = y - o.y; const Scalar dy = y - o.y;
const float dz = z - o.z; const Scalar dz = z - o.z;
return std::sqrt(dx*dx + dy*dy + dz*dz); return std::sqrt(dx*dx + dy*dy + dz*dz);
} }
/** get a normalized copy */ /** 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( return std::pow(
(std::pow(std::abs(x),norm) + (std::pow(std::abs(x),norm) +
std::pow(std::abs(y),norm) + std::pow(std::abs(y),norm) +
@@ -111,16 +122,19 @@ struct Point3 {
private: private:
static inline bool eq(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 float a, const float b, const float 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) { //using Point3 = _Point3<double>;
return p1.x*p2.x + p1.y*p2.y + p1.z*p2.z; 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( return Point3(
a.y*b.z - a.z*b.y, a.y*b.z - a.z*b.y,
a.z*b.x - a.x*b.z, 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
View 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
View 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
View 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
View 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 rs 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
View 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
View 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

View File

@@ -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 #ifndef UNITS_H
#define UNITS_H #define UNITS_H

317
geo/volume/BVH.h Normal file
View 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
View 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

View 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

View 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

View 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

View 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

View 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

View File

@@ -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 #ifndef DEFAULTGRIDNODE_H
#define DEFAULTGRIDNODE_H #define DEFAULTGRIDNODE_H

View File

@@ -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 #ifndef GRID_H
#define GRID_H #define GRID_H
@@ -16,6 +26,11 @@
#include "../geo/BBox3.h" #include "../geo/BBox3.h"
#include "../misc/Debug.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 * grid of a given-size, storing some user-data-value which
* - extends GridPoint and GridNode * - extends GridPoint and GridNode
@@ -68,6 +83,11 @@ public:
hashes.clear(); hashes.clear();
} }
/** is the grid empty? */
bool isEmpty() const {
return nodes.empty();
}
/** no-assign */ /** no-assign */
void operator = (const Grid& o) = delete; void operator = (const Grid& o) = delete;
@@ -177,10 +197,11 @@ public:
} }
/** get the center-node the given Point belongs to */ /** get the center-node the given Point belongs to */
const T& getNodeFor(const GridPoint& p) { const T& getNodeFor(const GridPoint& p) const {
const UID uid = getUID(p); //const UID uid = getUID(p);
Assert::isTrue(hashes.find(uid) != hashes.end(), "element not found!"); auto it = hashes.find(getUID(p));
return nodes[hashes[uid]]; 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 */ /** get the center-node the given Point belongs to. or nullptr if not present */
@@ -213,6 +234,14 @@ public:
return GridNodeBBox(node, gridSize_cm); 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. * get an UID for the given point.
* this works only for aligned points. * this works only for aligned points.
@@ -231,14 +260,32 @@ public:
const uint64_t center = 1 << 19; const uint64_t center = 1 << 19;
// build // build
const uint64_t x = center + (int64_t) std::round((p.x_cm) / (float)gridSize_cm); #if (GRID_MODE == GM_HOBEYCOMB)
const uint64_t y = center + (int64_t) std::round((p.y_cm) / (float)gridSize_cm); const int xx = ((int)std::round(p.y_cm / gridSize_cm) % 2 == 0) ? (0) : (gridSize_cm/2);
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 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); 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 */ /** array access */
T& operator [] (const int idx) { T& operator [] (const int idx) {
Assert::isBetween(idx, 0, getNumNodes()-1, "index out of bounds"); 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!) */ /** 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) { for (int n = 0; n < n1._numNeighbors; ++n) {
if (n1._neighbors[n] == n2._idx) { if (n1._neighbors[n] == n2._idx) {
arrayRemove(n1._neighbors, n, n1._numNeighbors); 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 */ /** 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) { template <typename X> void arrayRemove(X* arr, const int idxToRemove, const int arrayLen) {
for (int i = idxToRemove+1; i < arrayLen; ++i) { for (int i = idxToRemove+1; i < arrayLen; ++i) {
@@ -301,12 +353,16 @@ public:
void remove(T& node) { void remove(T& node) {
// disconnect from all neighbors // disconnect from all neighbors
int cnt = 0;
while (node._numNeighbors) { while (node._numNeighbors) {
// by removing one neighbor, all others are shifted to close the gap within the array // by removing one neighbor, all others are shifted to close the gap within the array
// its therefor ok to always delete array index [0] // its therefor ok to always delete array index [0]
disconnectBiDir(node._idx, node._neighbors[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 // remove from hash-list
@@ -456,11 +512,11 @@ public:
NeighborForEach neighbors(const int idx) { NeighborForEach neighbors(const int idx) const {
return neighbors(nodes[idx]); return neighbors(nodes[idx]);
} }
NeighborForEach neighbors(const T& node) { NeighborForEach neighbors(const T& node) const {
return NeighborForEach(*this, node._idx); return NeighborForEach(*this, node._idx);
} }
@@ -500,9 +556,13 @@ private:
/** asssert that the given element is aligned to the grid */ /** asssert that the given element is aligned to the grid */
void assertAligned(const T& elem) { 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.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.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!");} //if (((int)elem.z_cm % gridSize_cm) != 0) {throw Exception("element's z is not aligned!");}
#endif
} }
}; };

View File

@@ -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 #ifndef GRIDNEIGHBORITERATOR_H
#define GRIDNEIGHBORITERATOR_H #define GRIDNEIGHBORITERATOR_H

View File

@@ -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 #ifndef GRIDNODE_H
#define GRIDNODE_H #define GRIDNODE_H
@@ -52,6 +62,9 @@ public:
static const uint8_t TYPE_ELEVATOR = 2; static const uint8_t TYPE_ELEVATOR = 2;
static const uint8_t TYPE_DOOR = 3; static const uint8_t TYPE_DOOR = 3;
static const uint8_t TYPE_OUTDOOR = 100;
public: public:
/** ctor */ /** ctor */
@@ -61,6 +74,9 @@ public:
/** get the node's index within its grid */ /** get the node's index within its grid */
int getIdx() const {return _idx;} 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 */ /** get the number of neighbors for this node */
int getNumNeighbors() const {return _numNeighbors;} int getNumNeighbors() const {return _numNeighbors;}
@@ -81,6 +97,7 @@ public:
/** set the node's semantic type */ /** set the node's semantic type */
void setType(const uint8_t type) {this->_type = type;} void setType(const uint8_t type) {this->_type = type;}
// /** get the n-th neighbor for this node */ // /** 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 { // template <int gridSize_cm, typename T> inline T& getNeighbor(const int nth, const Grid<gridSize_cm, T>& grid) const {
// return grid.getNeighbor(_idx, nth); // return grid.getNeighbor(_idx, nth);

View File

@@ -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 #ifndef GRIDNODEBBOX_H
#define GRIDNODEBBOX_H #define GRIDNODEBBOX_H

View File

@@ -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 #ifndef GRIDPOINT_H
#define GRIDPOINT_H #define GRIDPOINT_H

View File

@@ -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 #ifndef GRIDFACTORY_H
#define GRIDFACTORY_H #define GRIDFACTORY_H

View File

@@ -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 #ifndef GRIDIMPORTANCE_H
#define GRIDIMPORTANCE_H #define GRIDIMPORTANCE_H
@@ -11,7 +21,7 @@
#include "../../nav/dijkstra/Dijkstra.h" #include "../../nav/dijkstra/Dijkstra.h"
#include "../../nav/dijkstra/DijkstraPath.h" #include "../../nav/dijkstra/DijkstraPath.h"
#include "../../math/Distributions.h" #include "../../math/distribution/Normal.h"

View File

@@ -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 #ifndef GRID_ELEVATORS_H
#define GRID_ELEVATORS_H #define GRID_ELEVATORS_H
@@ -50,10 +60,15 @@ public:
std::vector<IntPos> nodesWithin; std::vector<IntPos> nodesWithin;
const HelperPoly poly(elevator->getPoints()); 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) { auto callback = [&] (const int x_cm, const int y_cm) {
const GridPoint gp1(x_cm, y_cm, floor->getStartingZ()*100); // starting floor const GridPoint gp1(x_cm, y_cm, f1_cm); // starting floor
const GridPoint gp2(x_cm, y_cm, floor->getEndingZ()*100); // the floor above 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) // ensure such a node is present in both floors (and thus a connection is possible)
if (grid.hasNodeFor(gp1) && grid.hasNodeFor(gp2)) { if (grid.hasNodeFor(gp1) && grid.hasNodeFor(gp2)) {
@@ -63,29 +78,85 @@ public:
}; };
poly.forEachGridPoint(gs_cm, callback); 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 // 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 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 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) { for (const IntPos nodePos : nodesWithin) {
// create nodes BETWEEN the two floors (skip the floors themselves! -> floor1+gridSize <-> floor2-gridSize // 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) { 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 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 //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 if (grid.hasNodeFor(gp1)) {
grid[idx].setType(GridNode::TYPE_ELEVATOR); // set the node-type 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) // connect each of the new nodes with the node below it
for (int z_cm = z1_cm; z_cm <= z2_cm + gs_cm; z_cm += gs_cm) { // also connect the elevator to the starting and ending floor
const GridPoint gpBelow(nodePos.x_cm, nodePos.y_cm, z_cm-gs_cm); for (int z_cm = z1_cm; z_cm <= z2_cm; z_cm += gs_cm) {
const GridPoint gp(nodePos.x_cm, nodePos.y_cm, z_cm);
Assert::isTrue(grid.hasNodeFor(gpBelow), "missing node"); // directly above starting floor
Assert::isTrue(grid.hasNodeFor(gp), "missing node"); if (z_cm == z1_cm) {
T& n1 = (T&) grid.getNodeFor(gpBelow);
T& n2 = (T&) grid.getNodeFor(gp); // connect with floor below
grid.connectBiDir(n1, n2); 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);
}
} }
} }

View File

@@ -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 #ifndef GRIDFACTORY_V2_H
#define GRIDFACTORY_V2_H #define GRIDFACTORY_V2_H
@@ -5,6 +15,8 @@
#include <unordered_set> #include <unordered_set>
#include <set> #include <set>
#include "../../../math/Math.h"
#include "../../../floorplan/v2/Floorplan.h" #include "../../../floorplan/v2/Floorplan.h"
#include "Helper.h" #include "Helper.h"
@@ -12,12 +24,15 @@
#include "Elevators.h" #include "Elevators.h"
#include "../../../geo/Units.h" #include "../../../geo/Units.h"
#include "../../../geo/Circle2.h"
#include "../../GridNodeBBox.h" #include "../../GridNodeBBox.h"
#include "../../Grid.h" #include "../../Grid.h"
#include "../../../misc/Debug.h" #include "../../../misc/Debug.h"
#include <functional> #include <functional>
#include "../../../floorplan/3D/objects/OBJPool.h"
#include "../../../geo/ConvexHull2.h"
#include "GridFactoryListener.h" #include "GridFactoryListener.h"
@@ -42,8 +57,12 @@ private:
Elevators<T> elevators; Elevators<T> elevators;
std::vector<GridPoint> withinRemovePolygon;
bool _buildStairs = true; bool _buildStairs = true;
bool _removeIsolated = true; bool _removeIsolated = true;
bool _addTightToObstacle = false;
bool _abortOnError = true;
public: 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 */ /** whether or not to build stairs */
void setBuildStairs(const bool build) {this->_buildStairs = build;} void setBuildStairs(const bool build) {this->_buildStairs = build;}
@@ -60,6 +82,14 @@ public:
void setRemoveIsolated(const bool remove) {this->_removeIsolated = remove;} 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 */ /** build using the given map */
void build(const Floorplan::IndoorMap* map, GridFactoryListener* listener = nullptr) { void build(const Floorplan::IndoorMap* map, GridFactoryListener* listener = nullptr) {
@@ -79,7 +109,7 @@ public:
if (listener) {listener->onGridBuildUpdateMajor("adding stairs");} if (listener) {listener->onGridBuildUpdateMajor("adding stairs");}
if (_buildStairs) { if (_buildStairs) {
for (Floorplan::Floor* f : map->floors) { for (Floorplan::Floor* f : map->floors) {
buildStairs(f, listener); buildStairs(map, f, listener);
if (listener) {listener->onGridBuildUpdateMajor(total, ++cur);} 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 // remove isolated nodes
if (_removeIsolated) { if (_removeIsolated) {
if (listener) {listener->onGridBuildUpdateMajor("removing isolated nodes");} if (listener) {listener->onGridBuildUpdateMajor("removing isolated nodes");}
@@ -114,21 +165,45 @@ public:
return bb; 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) { for (Floorplan::FloorOutlinePolygon* poly : outline) {
if (poly->method != Floorplan::OutlineMethod::ADD) {continue;}
HelperPoly pol(*poly); 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) { // done
if (poly->method != Floorplan::OutlineMethod::REMOVE) {continue;} return res;
HelperPoly pol(*poly);
if (pol.contains(Point2(x_cm, y_cm))) {add = false;} // TODO
}
return add;
} }
@@ -144,32 +219,79 @@ public:
const int x2 = helper.align(bbox.getMax().x); const int x2 = helper.align(bbox.getMax().x);
const int y1 = helper.align(bbox.getMin().y); const int y1 = helper.align(bbox.getMin().y);
const int y2 = helper.align(bbox.getMax().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(); const int total = (x2-x1) / helper.gridSize();
int cur = 0; int cur = 0;
int numNodes = 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 // build grid-points for floor-outline
for(int x_cm = x1; x_cm < x2; x_cm += helper.gridSize()) { for(int x_cm = x1; x_cm < x2; x_cm += helper.gridSize()) {
for (int y_cm = y1; y_cm < y2; y_cm += helper.gridSize()) { for (int y_cm = y1; y_cm < y2; y_cm += helper.gridSize()) {
// does the outline-polygon contain this position? // 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()); 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 // slightly grow the bbox to ensure even obstacles that are directly aligned to the bbox are hit
bbox.grow(0.42345); bbox.grow(0.1337);
if (intersects(bbox, floor)) {continue;} 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 t(x_cm, y_cm, z_cm);
t.setType(getType(bbox, floor));
if (grid.hasNodeFor(t)) {continue;} if (grid.hasNodeFor(t)) {continue;}
updateType(t, part, bbox, floor);
grid.add(t); grid.add(t);
// node part of remove-region?
// if so, remove >>AFTER<< stairs have been added
if (part.markedForRemove) {
withinRemovePolygon.push_back(t);
}
// debug // debug
++numNodes; ++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(); const int total = floor->stairs.size();
int cur = 0; int cur = 0;
@@ -194,7 +318,7 @@ public:
// process each stair within the floor // process each stair within the floor
for (const Floorplan::Stair* stair : floor->stairs) { for (const Floorplan::Stair* stair : floor->stairs) {
if (listener) {listener->onGridBuildUpdateMinor("adding " + floor->name + " stair " + std::to_string(cur+1));} 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);} if (listener) {listener->onGridBuildUpdateMinor(total, ++cur);}
} }
@@ -278,8 +402,15 @@ public:
// does the grid contain the potential neighbor? // does the grid contain the potential neighbor?
const T* n2 = grid.getNodePtrFor(p); const T* n2 = grid.getNodePtrFor(p);
if (n2 != nullptr) { if (n2 != nullptr) {
if (isBlocked(floor, n1, *n2)) {continue;} // is there a (e.g. small) obstacle between the two? 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() { void removeIsolatedNodes() {
//std::cout << "todo: remove" << std::endl;
//return;
// try to start at the first stair // try to start at the first stair
for (T& n : grid) { for (T& n : grid) {
if (n.getType() == GridNode::TYPE_STAIR) {removeIsolatedNodes(n); return;} if (n.getType() == GridNode::TYPE_STAIR) {removeIsolatedNodes(n); return;}
@@ -372,11 +506,29 @@ public:
getConnected(n1, set); getConnected(n1, set);
Log::tock(); Log::tock();
//const int numToRemove = grid.getNumNodes() - set.size();
//int numRemoved = 0;
// remove all other // remove all other
Log::add(name, "removing all nodes NOT connected to " + (std::string) n1, false); Log::add(name, "removing all nodes NOT connected to " + (std::string) n1, false);
Log::tick(); Log::tick();
for (T& n2 : grid) { 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(); Log::tock();
@@ -415,7 +567,23 @@ private:
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? */ /** does the bbox intersect with any of the floor's walls? */
static inline bool intersects(const GridNodeBBox& bbox, const Floorplan::Floor* floor) { static inline bool intersects(const GridNodeBBox& bbox, const Floorplan::Floor* floor) {
@@ -427,18 +595,33 @@ private:
// depends on the type of obstacle // depends on the type of obstacle
if (dynamic_cast<Floorplan::FloorObstacleLine*>(fo)) { if (dynamic_cast<Floorplan::FloorObstacleLine*>(fo)) {
const Floorplan::FloorObstacleLine* line = (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)) { } else if (dynamic_cast<Floorplan::FloorObstacleCircle*>(fo)) {
const Floorplan::FloorObstacleCircle* circle = (Floorplan::FloorObstacleCircle*) fo; const Floorplan::FloorObstacleCircle* circle = (Floorplan::FloorObstacleCircle*) fo;
const Point2 center = bbox.getCenter(); const float dist = std::min(
const float dist = center.getDistance(circle->center*100); bbox.getCorner1().getDistance(circle->center*100),
if (dist < circle->radius*100) {return true;} 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)) { } else if (dynamic_cast<Floorplan::FloorObstacleDoor*>(fo)) {
// DOORS ARE NOT AN OBSTACLE // DOORS ARE NOT AN OBSTACLE
} else if (dynamic_cast<Floorplan::FloorObstacleObject*>(fo)) {
// ADDED EARLIER
} else { } else {
throw Exception("TODO: not yet implemented obstacle type"); throw Exception("TODO: not yet implemented obstacle type");
@@ -472,11 +655,18 @@ private:
if (lineObstacle.getSegmentIntersection(lineNodes)) {return true;} if (lineObstacle.getSegmentIntersection(lineNodes)) {return true;}
} else if (dynamic_cast<Floorplan::FloorObstacleCircle*>(fo)) { } 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)) { } else if (dynamic_cast<Floorplan::FloorObstacleDoor*>(fo)) {
// DOORS ARE NOT AN OBSTACLE // DOORS ARE NOT AN OBSTACLE
} else if (dynamic_cast<Floorplan::FloorObstacleObject*>(fo)) {
// removed earlier
//std::cout << "GridFactory: TODO: Floorplan::FloorObstacleObject" << std::endl;
} else { } else {
throw Exception("TODO: not yet implemented obstacle type"); throw Exception("TODO: not yet implemented obstacle type");
@@ -487,23 +677,30 @@ private:
} }
/** does the bbox intersect with any of the floor's walls? */ /** adjust the given gridNode's type if needed [e.g. from "floor" to "door"] */
static inline int getType(const GridNodeBBox& bbox, const Floorplan::Floor* floor) { 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) { for (Floorplan::FloorObstacle* fo : floor->obstacles) {
if (dynamic_cast<Floorplan::FloorObstacleDoor*>(fo)) { if (dynamic_cast<Floorplan::FloorObstacleDoor*>(fo)) {
const Floorplan::FloorObstacleDoor* door = (Floorplan::FloorObstacleDoor*) fo; const Floorplan::FloorObstacleDoor* door = (Floorplan::FloorObstacleDoor*) fo;
const Line2 l2(door->from*100, door->to*100); 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;
} }

View File

@@ -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 #ifndef GRIDFACTORYLISTENER_H
#define GRIDFACTORYLISTENER_H #define GRIDFACTORYLISTENER_H

View File

@@ -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 #ifndef GRIDNODEIMPORTANCE_H
#define GRIDNODEIMPORTANCE_H #define GRIDNODEIMPORTANCE_H

View File

@@ -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 #ifndef GRID_FACTORY_HELPER_H
#define GRID_FACTORY_HELPER_H #define GRID_FACTORY_HELPER_H
@@ -44,6 +54,19 @@ struct HelperPoly {
bbox_cm.add(p.xy()); 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)? */ /** does the polygon contain the given point (in cm)? */
bool contains(const Point2 p_cm) const { 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) { 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; const Point2 v0 = b - a, v1 = c - a, v2 = p - a;
float d00 = dot(v0, v0); double d00 = dot(v0, v0);
float d01 = dot(v0, v1); double d01 = dot(v0, v1);
float d11 = dot(v1, v1); double d11 = dot(v1, v1);
float d20 = dot(v2, v0); double d20 = dot(v2, v0);
float d21 = dot(v2, v1); double d21 = dot(v2, v1);
float denom = d00 * d11 - d01 * d01; double denom = d00 * d11 - d01 * d01;
v = (d11 * d20 - d01 * d21) / denom; v = (d11 * d20 - d01 * d21) / denom;
w = (d00 * d21 - d01 * d20) / denom; w = (d00 * d21 - d01 * d20) / denom;
u = 1.0f - v - w; u = 1.0f - v - w;

View File

@@ -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 #ifndef IMPORTANCE_H
#define IMPORTANCE_H #define IMPORTANCE_H
@@ -11,7 +21,8 @@
#include "../../../misc/KNNArray.h" #include "../../../misc/KNNArray.h"
#include "../../../math/MiniMat2.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 // 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.walkImportance >= 0, "detected negative walk importance. does not make sense!");
Assert::isTrue(n1.navImportance >= 0, "detected negative nav importance. does not make sense!"); Assert::isTrue(n1.navImportance >= 0, "detected negative nav importance. does not make sense!");

View File

@@ -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 #ifndef STAIRS_H
#define STAIRS_H #define STAIRS_H

View File

@@ -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 #ifndef STAIRS_H
#define STAIRS_H #define STAIRS_H
@@ -31,19 +41,23 @@ private:
// keep a list of all vertices below stairwells and remove them hereafter // keep a list of all vertices below stairwells and remove them hereafter
std::vector<T*> toDelete; std::vector<T*> toDelete;
bool tryImproveStairConnections = true;
bool abortOnError = true;
private: private:
/** helper struct */ /** helper struct */
struct StairNode { struct StairNode {
const int x_cm; int x_cm;
const int y_cm; int y_cm;
const int belongsToQuadIdx; int belongsToQuadIdx;
float z_cm; // interpolated float z_cm; // interpolated
int gridIdx = -1; 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) {;} 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(); 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(); 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 // this might lead to stairs the start slightly above the starting-floor
// or ending slightly below the ending floor. this would lead to DISCONNECTION! // 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 // 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) { float minZ = +9999999;
if (sn.z_cm < minZ) {minZ = sn.z_cm;} float maxZ = -9999999;
if (sn.z_cm > maxZ) {maxZ = sn.z_cm;} for (const StairNode& sn : stairNodes) {
} if (sn.z_cm < minZ) {minZ = sn.z_cm;}
for (StairNode& sn : stairNodes) { if (sn.z_cm > maxZ) {maxZ = sn.z_cm;}
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
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 // 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! // if this list contains 2 distinctive values, the stair is successfully connected to starting and ending floor!
std::unordered_set<float> connectedWithHeights; 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 // remember the z-position of the already-existing grid-node we connected the stair to
connectedWithHeights.insert(grid[sn.gridIdx].z_cm); connectedWithHeights.insert(grid[sn.gridIdx].z_cm);
// mark the node as stair-node
//grid[sn.gridIdx].setType(GridNode::TYPE_STAIR);
} else { } else {
sn.gridIdx = grid.add(T(gp.x_cm, gp.y_cm, gp.z_cm)); sn.gridIdx = grid.add(T(gp.x_cm, gp.y_cm, gp.z_cm));
// check if there is a nearby floor-node to delete // check if there is a nearby floor-node to delete
// -> remove nodes directly above/below the stair
const int deleteDist_cm = 100; const int deleteDist_cm = 100;
const float distToBelow = gp.z_cm - floor->getStartingZ()*100; const float distToBelow = gp.z_cm - stairAbsStart_cm;//floor->getStartingZ()*100;
const float distToAbove = floor->getEndingZ()*100 - gp.z_cm; const float distToAbove = stairAbsEnd_cm - gp.z_cm;//floor->getEndingZ()*100 - gp.z_cm;
if (distToBelow > gs_cm && distToBelow < deleteDist_cm) { //if (distToBelow > gs_cm && distToBelow < deleteDist_cm) {
T* n = (T*) grid.getNodePtrFor(GridPoint(gp.x_cm, gp.y_cm, floor->getStartingZ()*100)); 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);} if (n) {toDelete.push_back(n);}
} else if (distToAbove > gs_cm && distToAbove < deleteDist_cm) { //} else if (distToAbove > gs_cm && distToAbove < deleteDist_cm) {
T* n = (T*) grid.getNodePtrFor(GridPoint(gp.x_cm, gp.y_cm, floor->getEndingZ()*100)); }
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);} 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 // 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!"); Assert::isTrue(connectedWithHeights.size() == 2, "stair is not correctly connected to starting and ending floor!");
// now connect all new nodes with their neighbors // now connect all new nodes with their neighbors
@@ -215,16 +331,16 @@ public:
} }
} }
} }
void finalize() { void finalize() {
//std::cout << "stairs.finalize() crashes!" << std::endl;
//return;
// delete all pending nodes and perform a cleanup // delete all pending nodes and perform a cleanup
if (!toDelete.empty()) { if (!toDelete.empty()) {
for (T* n : toDelete) {grid.remove(*n);} for (T* n : toDelete) {grid.remove(*n);}

View 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

View 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

View File

@@ -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 #ifndef GRIDWALK_H
#define GRIDWALK_H #define GRIDWALK_H

View File

@@ -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 #ifndef GRIDWALKHELPER_H
#define GRIDWALKHELPER_H #define GRIDWALKHELPER_H

View File

@@ -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 #ifndef GRIDWALKLIGHTATTHEENDOFTHETUNNEL_H
#define GRIDWALKLIGHTATTHEENDOFTHETUNNEL_H #define GRIDWALKLIGHTATTHEENDOFTHETUNNEL_H
@@ -5,7 +15,7 @@
#include "../Grid.h" #include "../Grid.h"
#include "../../math/DrawList.h" #include "../../math/DrawList.h"
#include "../../math/Distributions.h" #include "../../math/distribution/Normal.h"
#include "../../math/DrawList.h" #include "../../math/DrawList.h"
#include "../../nav/dijkstra/Dijkstra.h" #include "../../nav/dijkstra/Dijkstra.h"
@@ -35,7 +45,7 @@ private:
DrawList<T&> drawer; DrawList<T&> drawer;
/** fast random-number-generator */ /** fast random-number-generator */
RandomGenerator gen; Random::RandomGenerator gen;
/** 0-mean normal distribution */ /** 0-mean normal distribution */
std::normal_distribution<float> headingChangeDist = std::normal_distribution<float>(0.0, HEADING_CHANGE_SIGMA); std::normal_distribution<float> headingChangeDist = std::normal_distribution<float>(0.0, HEADING_CHANGE_SIGMA);

View File

@@ -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 #ifndef GRIDWALKPATHCONTROL_H
#define GRIDWALKPATHCONTROL_H #define GRIDWALKPATHCONTROL_H
@@ -23,7 +33,7 @@ private:
static constexpr float HEADING_CHANGE_SIGMA = Angle::degToRad(10); static constexpr float HEADING_CHANGE_SIGMA = Angle::degToRad(10);
/** fast random-number-generator */ /** fast random-number-generator */
RandomGenerator gen; Random::RandomGenerator gen;
/** 0-mean normal distribution */ /** 0-mean normal distribution */
std::normal_distribution<float> headingChangeDist = std::normal_distribution<float>(0.0, HEADING_CHANGE_SIGMA); std::normal_distribution<float> headingChangeDist = std::normal_distribution<float>(0.0, HEADING_CHANGE_SIGMA);

View File

@@ -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 #ifndef GRIDWALKPUSHFORWARD_H
#define GRIDWALKPUSHFORWARD_H #define GRIDWALKPUSHFORWARD_H
@@ -32,7 +42,7 @@ private:
static constexpr float HEADING_ALLOWED_SIGMA = Angle::degToRad(20); static constexpr float HEADING_ALLOWED_SIGMA = Angle::degToRad(20);
/** fast random-number-generator */ /** fast random-number-generator */
RandomGenerator gen; Random::RandomGenerator gen;
/** 0-mean normal distribution */ /** 0-mean normal distribution */
std::normal_distribution<float> headingChangeDist = std::normal_distribution<float>(0.0, HEADING_CHANGE_SIGMA); std::normal_distribution<float> headingChangeDist = std::normal_distribution<float>(0.0, HEADING_CHANGE_SIGMA);

View File

@@ -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 #ifndef GRIDWALKRANDOMHEADINGUPDATE_H
#define GRIDWALKRANDOMHEADINGUPDATE_H #define GRIDWALKRANDOMHEADINGUPDATE_H
@@ -35,7 +45,7 @@ private:
static constexpr float HEADING_CHANGE_SIGMA = Angle::degToRad(10); static constexpr float HEADING_CHANGE_SIGMA = Angle::degToRad(10);
/** fast random-number-generator */ /** fast random-number-generator */
RandomGenerator gen; Random::RandomGenerator gen;
/** 0-mean normal distribution */ /** 0-mean normal distribution */
std::normal_distribution<float> headingChangeDist = std::normal_distribution<float>(0.0, HEADING_CHANGE_SIGMA); std::normal_distribution<float> headingChangeDist = std::normal_distribution<float>(0.0, HEADING_CHANGE_SIGMA);

View File

@@ -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 #ifndef GRIDWALKRANDOMHEADINGUPDATEADV_H
#define GRIDWALKRANDOMHEADINGUPDATEADV_H #define GRIDWALKRANDOMHEADINGUPDATEADV_H
@@ -35,7 +45,7 @@ private:
static constexpr float HEADING_CHANGE_SIGMA = Angle::degToRad(10); static constexpr float HEADING_CHANGE_SIGMA = Angle::degToRad(10);
/** fast random-number-generator */ /** fast random-number-generator */
RandomGenerator gen; Random::RandomGenerator gen;
/** 0-mean normal distribution */ /** 0-mean normal distribution */
std::normal_distribution<float> headingChangeDist = std::normal_distribution<float>(0.0, HEADING_CHANGE_SIGMA); std::normal_distribution<float> headingChangeDist = std::normal_distribution<float>(0.0, HEADING_CHANGE_SIGMA);

View File

@@ -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 #ifndef GRIDWALKSHORTESTPATHCONTROL_H
#define GRIDWALKSHORTESTPATHCONTROL_H #define GRIDWALKSHORTESTPATHCONTROL_H
@@ -79,7 +89,7 @@ protected:
static constexpr float HEADING_CHANGE_SIGMA = Angle::degToRad(10); static constexpr float HEADING_CHANGE_SIGMA = Angle::degToRad(10);
/** fast random-number-generator */ /** fast random-number-generator */
RandomGenerator gen; Random::RandomGenerator gen;
/** 0-mean normal distribution */ /** 0-mean normal distribution */
std::normal_distribution<float> headingChangeDist = std::normal_distribution<float>(0.0, HEADING_CHANGE_SIGMA); 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