Compare commits
538 Commits
version/20
...
version/20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
489573329e | ||
|
|
843df4b334 | ||
|
|
ef1821c025 | ||
|
|
ed5372f439 | ||
|
|
bc63381fb0 | ||
|
|
c59f2a0749 | ||
|
|
5c30ca5dc6 | ||
|
|
37bc43c7aa | ||
|
|
4edd3f45b7 | ||
|
|
087ea4c5f3 | ||
|
|
40534d6316 | ||
|
|
8621925e75 | ||
|
|
5cb2360985 | ||
|
|
b831d2b64a | ||
|
|
e81597a176 | ||
|
|
7df4f32de6 | ||
|
|
db3725880e | ||
|
|
af5833cbc5 | ||
|
|
9078a085c3 | ||
|
|
396a7d7f09 | ||
|
|
8a128a57cd | ||
|
|
3dca9b8a68 | ||
|
|
5f8f5a1c33 | ||
|
|
5a72a7d9f4 | ||
|
|
51a91bfa8a | ||
|
|
4cedd0a346 | ||
|
|
b989f4085d | ||
|
|
19cc797c41 | ||
|
|
99ee3d9dec | ||
|
|
455753c774 | ||
|
|
da099d4312 | ||
|
|
a88b9181b6 | ||
|
|
7f7c632914 | ||
|
|
e7356223bb | ||
|
|
7547ad0391 | ||
|
|
ca30d6bb3d | ||
|
|
ebd2cdb7a7 | ||
|
|
52b8f60953 | ||
|
|
ab1e2d49ab | ||
|
|
401bd1c643 | ||
|
|
53e6bec425 | ||
|
|
f6ded69fa3 | ||
|
|
68053d64b5 | ||
|
|
1f12966628 | ||
|
|
8d3723c91f | ||
|
|
7dcc359110 | ||
|
|
854881bba2 | ||
|
|
ddb7958f40 | ||
|
|
fe0d3fd448 | ||
|
|
15d5c878f3 | ||
|
|
4f63c3f8a8 | ||
|
|
b766ce76ff | ||
|
|
0b4b09958f | ||
|
|
e12fc5a89d | ||
|
|
bd3c351f1f | ||
|
|
6064be33e5 | ||
|
|
1540d6f472 | ||
|
|
1310092c0c | ||
|
|
e485fac8ed | ||
|
|
55d75f18de | ||
|
|
e8648a3f71 | ||
|
|
0c7cabe46f | ||
|
|
a9ec3be2fd | ||
|
|
abaaee1af2 | ||
|
|
6283a515b9 | ||
|
|
ce7d463710 | ||
|
|
bc3404fcbe | ||
|
|
bd87d3963a | ||
|
|
fed449a801 | ||
|
|
19dd92d3e0 | ||
|
|
f22b9ba9f1 | ||
|
|
fedafb9352 | ||
|
|
18f0484249 | ||
|
|
10956056b3 | ||
|
|
e482f04123 | ||
|
|
db89f0d4d1 | ||
|
|
2ac97a9f1f | ||
|
|
c03359a189 | ||
|
|
84b636debc | ||
|
|
2642299d77 | ||
|
|
2a60e5e338 | ||
|
|
880c063d04 | ||
|
|
7a374c43dc | ||
|
|
7be1fcc32e | ||
|
|
b57dca66be | ||
|
|
d455f5f445 | ||
|
|
2200fad30e | ||
|
|
e5e112c3c2 | ||
|
|
dd3cdf63e6 | ||
|
|
a800189c25 | ||
|
|
b342245619 | ||
|
|
6cd5ac2d0d | ||
|
|
872a48dacb | ||
|
|
721aa544c9 | ||
|
|
7bfbda7188 | ||
|
|
602244979b | ||
|
|
8e29cae309 | ||
|
|
2a73e6c0d5 | ||
|
|
16b5dd5e78 | ||
|
|
1aba20d642 | ||
|
|
27786d709d | ||
|
|
e0e6a29150 | ||
|
|
dfd6076e19 | ||
|
|
e589ef8627 | ||
|
|
e8c1baa396 | ||
|
|
69ba2617d2 | ||
|
|
34c215ad83 | ||
|
|
2a3bb62001 | ||
|
|
fe46ae09ef | ||
|
|
46f67fce7a | ||
|
|
629e68428f | ||
|
|
100439aadd | ||
|
|
f6a348ba94 | ||
|
|
a54b3ffcee | ||
|
|
47842c9ea7 | ||
|
|
834e25521b | ||
|
|
f78597f010 | ||
|
|
c06e433e74 | ||
|
|
9971d517fd | ||
|
|
f8548029a2 | ||
|
|
3e337e3e97 | ||
|
|
e59f8eda74 | ||
|
|
31e70b205c | ||
|
|
5d02f1db5f | ||
|
|
b01bd93a20 | ||
|
|
05bbba5074 | ||
|
|
679d4e1dcd | ||
|
|
aaa6231f89 | ||
|
|
446c8cd70c | ||
|
|
11ff1d256c | ||
|
|
4acd047982 | ||
|
|
ee4b6621e7 | ||
|
|
77517b15fd | ||
|
|
19a86a6f0d | ||
|
|
a7dee1164d | ||
|
|
7512bc0fb2 | ||
|
|
d6b57d0937 | ||
|
|
1e99c4b2ec | ||
|
|
04ca3ad8f4 | ||
|
|
2b8915e35f | ||
|
|
2a0801da6b | ||
|
|
2935d78490 | ||
|
|
2404924bcb | ||
|
|
e4c4db5cf9 | ||
|
|
e1c655c570 | ||
|
|
2788da9c51 | ||
|
|
4860a70443 | ||
|
|
22d1433ab0 | ||
|
|
ef9eedf35a | ||
|
|
a962c90b30 | ||
|
|
36275f5cce | ||
|
|
60d1c87cef | ||
|
|
9223f30f08 | ||
|
|
fe87e7f60d | ||
|
|
f3e83cf020 | ||
|
|
9eee41d74a | ||
|
|
991d76b69e | ||
|
|
6f0c7da6ad | ||
|
|
c170f576b6 | ||
|
|
1446f559cc | ||
|
|
4467e68db1 | ||
|
|
cc1118b330 | ||
|
|
a1126bd42c | ||
|
|
370523d5bf | ||
|
|
96b4d2c03d | ||
|
|
c4898502bf | ||
|
|
f7a511d1b3 | ||
|
|
b66c51a6f8 | ||
|
|
a4cf38925b | ||
|
|
41059a24a7 | ||
|
|
ee4f5a5190 | ||
|
|
141e98564c | ||
|
|
72341a6de4 | ||
|
|
55ee59ac99 | ||
|
|
61525c555e | ||
|
|
dad30b0cc2 | ||
|
|
9c9e4e86e7 | ||
|
|
b88aa46e1c | ||
|
|
10fa8a471a | ||
|
|
4e875be0dd | ||
|
|
23cc940743 | ||
|
|
4b010cc416 | ||
|
|
46f39d5fbd | ||
|
|
03515151f0 | ||
|
|
8cd723d91b | ||
|
|
6f2943ed9a | ||
|
|
e509fc3f5d | ||
|
|
707d9e12cf | ||
|
|
9840302931 | ||
|
|
983047982f | ||
|
|
f977be5fe4 | ||
|
|
e21ad4b5c1 | ||
|
|
619055f544 | ||
|
|
c9611fc45b | ||
|
|
79f869a7f3 | ||
|
|
1b8dfb2bef | ||
|
|
143a47482b | ||
|
|
a28cf0f860 | ||
|
|
d9f4d7373f | ||
|
|
c3f48c7261 | ||
|
|
48b7b70e23 | ||
|
|
b93b362e2f | ||
|
|
2082b18e2e | ||
|
|
7f65e7f905 | ||
|
|
3417ca7e49 | ||
|
|
6334c30eb6 | ||
|
|
c87dff7e8f | ||
|
|
332f76f34d | ||
|
|
3387f3d084 | ||
|
|
bd421c381c | ||
|
|
0cce949837 | ||
|
|
ed3ba67925 | ||
|
|
866f85064a | ||
|
|
9215c530b3 | ||
|
|
a6437f4e96 | ||
|
|
5bd7be6ed1 | ||
|
|
d9cc3738b9 | ||
|
|
edec5bbc01 | ||
|
|
6a2d86c526 | ||
|
|
6c64e9b36c | ||
|
|
bcecee0f76 | ||
|
|
c5cdfa1a1d | ||
|
|
f9f2b4cbdb | ||
|
|
33feb9a416 | ||
|
|
9e1aaa8b56 | ||
|
|
ef2eb635af | ||
|
|
2db412a923 | ||
|
|
061fea48c8 | ||
|
|
c4ea62a899 | ||
|
|
789c09a402 | ||
|
|
de9b329115 | ||
|
|
1b793a127c | ||
|
|
3cb3084725 | ||
|
|
637f67888a | ||
|
|
fbc0986fd8 | ||
|
|
14ebe0b618 | ||
|
|
fd34cc30b8 | ||
|
|
7b0faed03a | ||
|
|
8d1dc30b07 | ||
|
|
03cff6abca | ||
|
|
d7821324b8 | ||
|
|
f3e066cce0 | ||
|
|
45ac758cc9 | ||
|
|
f0e6402fff | ||
|
|
41bf142e31 | ||
|
|
2e9efa98d7 | ||
|
|
2c2a57f368 | ||
|
|
4ea1326126 | ||
|
|
09b44ac68a | ||
|
|
a48ab434ab | ||
|
|
d39a56d4fb | ||
|
|
892579456d | ||
|
|
4dde1d365c | ||
|
|
2f21b582cd | ||
|
|
b29536f8b7 | ||
|
|
039f9920db | ||
|
|
7c254e9c04 | ||
|
|
35a115bfd4 | ||
|
|
70dd9d35b1 | ||
|
|
31e3cf06fb | ||
|
|
175eddd1fa | ||
|
|
fe73247b82 | ||
|
|
203db3d095 | ||
|
|
8c4695b991 | ||
|
|
a2b111bb09 | ||
|
|
2a1542d544 | ||
|
|
e5b51677c5 | ||
|
|
f9450d136d | ||
|
|
0586cb62c3 | ||
|
|
8fee04b32b | ||
|
|
04e16c95e2 | ||
|
|
e257dbe6ed | ||
|
|
abf78f8e31 | ||
|
|
da13bd9f04 | ||
|
|
86fb1ed00f | ||
|
|
56fb81dc03 | ||
|
|
343ce57468 | ||
|
|
3e52e37181 | ||
|
|
e768553a4a | ||
|
|
b74d1a8351 | ||
|
|
801d8c4af5 | ||
|
|
3a4693803b | ||
|
|
79f0d3356e | ||
|
|
6a1bf02ddb | ||
|
|
6662800deb | ||
|
|
c9bb6102c0 | ||
|
|
90479419cc | ||
|
|
b4178ae888 | ||
|
|
ab4814c916 | ||
|
|
6b16f96c8a | ||
|
|
e655d41817 | ||
|
|
b5c1902a2d | ||
|
|
d088259739 | ||
|
|
d8acf44a3a | ||
|
|
4664af12fa | ||
|
|
7a909d0c0b | ||
|
|
919c25769c | ||
|
|
5a0908d5bb | ||
|
|
1c39daec07 | ||
|
|
835ae941ce | ||
|
|
63edff078f | ||
|
|
0ea9786601 | ||
|
|
9e0bb33d58 | ||
|
|
64531c85e3 | ||
|
|
14cdae5102 | ||
|
|
2aaad212e8 | ||
|
|
bd88bf1126 | ||
|
|
ea9da65b7c | ||
|
|
e7f80cf5f3 | ||
|
|
16d62f93c8 | ||
|
|
e96834fcc6 | ||
|
|
22a74c63b4 | ||
|
|
5681fcbdc5 | ||
|
|
7755f8e094 | ||
|
|
e266e44f63 | ||
|
|
4dc66a385e | ||
|
|
aec29a3a37 | ||
|
|
0d213a1990 | ||
|
|
8162a49f6c | ||
|
|
d2e2603400 | ||
|
|
43a8277bdb | ||
|
|
61a8bd5cd3 | ||
|
|
ad6f3d2db2 | ||
|
|
9088f41352 | ||
|
|
73f57bbbd8 | ||
|
|
a8673356a2 | ||
|
|
5ecc1ab6f2 | ||
|
|
0702f85540 | ||
|
|
277dab0d55 | ||
|
|
321a3fdaba | ||
|
|
2eb17d0083 | ||
|
|
ff7e4597e7 | ||
|
|
4a4baf1b42 | ||
|
|
2672d5cd11 | ||
|
|
1bf3d7c9b1 | ||
|
|
f7c0a7f933 | ||
|
|
6bf864babb | ||
|
|
43bd1b15ee | ||
|
|
14c79d9ffb | ||
|
|
3009aadaa6 | ||
|
|
fca64343ae | ||
|
|
042a2659f6 | ||
|
|
fe54af405c | ||
|
|
ab70090a0a | ||
|
|
ee02750e95 | ||
|
|
f55007394e | ||
|
|
60a9e8fb7e | ||
|
|
7f8455f731 | ||
|
|
6ae86fc4ca | ||
|
|
e1fb13bed8 | ||
|
|
27fff3b72a | ||
|
|
6a9235223e | ||
|
|
c1e50e9a9c | ||
|
|
dcbf5b7c11 | ||
|
|
4b571f2a24 | ||
|
|
679b7b845c | ||
|
|
50d7127c51 | ||
|
|
0e09ee4bce | ||
|
|
76ebd569d5 | ||
|
|
edcd42bc2d | ||
|
|
f04e501472 | ||
|
|
32d152ba38 | ||
|
|
94c4c44d92 | ||
|
|
ab1d4e651e | ||
|
|
5cd250e452 | ||
|
|
2dcff4bb8e | ||
|
|
604a9ff614 | ||
|
|
a09630bcca | ||
|
|
b44c70b3f4 | ||
|
|
e4cddb100e | ||
|
|
e3a4144e6c | ||
|
|
51e7d95bf2 | ||
|
|
202571386b | ||
|
|
7837bd0e11 | ||
|
|
11c6e5bf04 | ||
|
|
8277857827 | ||
|
|
412111ba5a | ||
|
|
dd52b6af50 | ||
|
|
a97c145f56 | ||
|
|
b9deebb59d | ||
|
|
906813c90b | ||
|
|
1711592e64 | ||
|
|
7b2507cb19 | ||
|
|
2e19aaaff9 | ||
|
|
6854598b79 | ||
|
|
809ddb21c9 | ||
|
|
38bab59c1a | ||
|
|
87590cafb2 | ||
|
|
6e5cbd7fc5 | ||
|
|
4200572cad | ||
|
|
9b997ea1f7 | ||
|
|
c1ba974538 | ||
|
|
d7d59b08a2 | ||
|
|
6b82b78c7c | ||
|
|
8201301064 | ||
|
|
899778b354 | ||
|
|
a31d1342d5 | ||
|
|
8d266491c5 | ||
|
|
0acbe1f087 | ||
|
|
f8d5e58ccc | ||
|
|
fcd0f15ff2 | ||
|
|
f092f000fa | ||
|
|
69b127e2e6 | ||
|
|
65a3d9ed6c | ||
|
|
85c4e03823 | ||
|
|
7754f88be7 | ||
|
|
1e24245d6c | ||
|
|
5ea01039f9 | ||
|
|
8b8dbeb00d | ||
|
|
52ec6cee85 | ||
|
|
eb53d4ca78 | ||
|
|
f3c3b7ec1b | ||
|
|
f6e92ac9e5 | ||
|
|
48c5e5e43b | ||
|
|
7cc9a1753c | ||
|
|
068745617c | ||
|
|
935c3f901d | ||
|
|
0b60669075 | ||
|
|
488039d1de | ||
|
|
efe9648afa | ||
|
|
d12cd4945e | ||
|
|
968e0b4cd2 | ||
|
|
d902fffa46 | ||
|
|
3092274cac | ||
|
|
5f54388ed9 | ||
|
|
9b4f1b0ff8 | ||
|
|
c48a28beb9 | ||
|
|
96986c9377 | ||
|
|
33bd02f926 | ||
|
|
93226fc500 | ||
|
|
31ba9dfa70 | ||
|
|
19df18fefb | ||
|
|
a5a4bf6d41 | ||
|
|
9812315d96 | ||
|
|
c40044feeb | ||
|
|
a636da6959 | ||
|
|
ca84d2046a | ||
|
|
c037a0e461 | ||
|
|
5ab595b401 | ||
|
|
7e06e5382a | ||
|
|
d3c5c45262 | ||
|
|
efa1292b2d | ||
|
|
98de216878 | ||
|
|
82a9491de4 | ||
|
|
b23e9a3424 | ||
|
|
0e62c11fd0 | ||
|
|
5b54481555 | ||
|
|
f4344c5c6a | ||
|
|
372dead21a | ||
|
|
cf18d4eaaf | ||
|
|
32735428bb | ||
|
|
b862cf7e54 | ||
|
|
f21eac8473 | ||
|
|
38c8931950 | ||
|
|
cfe1c0933f | ||
|
|
a8d8158fac | ||
|
|
2321d9783d | ||
|
|
a3b3280123 | ||
|
|
8cfe5a2e08 | ||
|
|
bd896096cc | ||
|
|
855ff5a8b0 | ||
|
|
e695505e62 | ||
|
|
f824cf85a4 | ||
|
|
fb8b60b6fe | ||
|
|
516d76d41b | ||
|
|
d0e31c5cf5 | ||
|
|
daa10503e6 | ||
|
|
1c25d343a0 | ||
|
|
c762dbe864 | ||
|
|
37c551bae7 | ||
|
|
0ccf3e1629 | ||
|
|
da1aeece14 | ||
|
|
c722f90848 | ||
|
|
5ba9004853 | ||
|
|
dc1696dfd5 | ||
|
|
cb80af0ebe | ||
|
|
1492de4391 | ||
|
|
fad905e7e0 | ||
|
|
fb1b1b9c5e | ||
|
|
b7b304ecfb | ||
|
|
8690e4617f | ||
|
|
a6bed69d19 | ||
|
|
100e327684 | ||
|
|
729c9e3faa | ||
|
|
add3a934af | ||
|
|
e82e4f8c93 | ||
|
|
6582d041e6 | ||
|
|
92878f37f9 | ||
|
|
4224d2b86b | ||
|
|
c3db9b9d86 | ||
|
|
3223f16fe6 | ||
|
|
03f7f82856 | ||
|
|
be4ebddb60 | ||
|
|
34e804b784 | ||
|
|
75b2ef9372 | ||
|
|
f71e2e0e9f | ||
|
|
ce0cdcdcb0 | ||
|
|
426912173a | ||
|
|
976c85ff57 | ||
|
|
1ae9e74539 | ||
|
|
980ae3115c | ||
|
|
5ca9a06273 | ||
|
|
469097ed5b | ||
|
|
ae375f44f2 | ||
|
|
d1a808c630 | ||
|
|
733c283b1a | ||
|
|
f65a970d2e | ||
|
|
ef5b9ee66b | ||
|
|
c5a94e8899 | ||
|
|
b05488649a | ||
|
|
c654c82a3f | ||
|
|
4b7d577883 | ||
|
|
1b0289b11f | ||
|
|
00f4248137 | ||
|
|
8211f1c482 | ||
|
|
43ebde9914 | ||
|
|
7d59dd977f | ||
|
|
3e2f37418a | ||
|
|
9d2df12ab8 | ||
|
|
2b15b6b8ad | ||
|
|
49bd96c55d | ||
|
|
b3d95c0754 | ||
|
|
87c2427cfe | ||
|
|
857f7c9e61 | ||
|
|
91f184caff | ||
|
|
7cab98cf29 | ||
|
|
e9ea5e9036 | ||
|
|
03a8a2a3fb | ||
|
|
f1bffc7397 | ||
|
|
5fdc756a64 | ||
|
|
5b71d84a3a | ||
|
|
b1270376c9 | ||
|
|
871b418242 | ||
|
|
fd124e91de | ||
|
|
5b71ede2ea | ||
|
|
6285a409ed | ||
|
|
9818a123ca | ||
|
|
09ab029e2f |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -14,3 +14,4 @@ CTestTestfile.cmake
|
||||
install_manifest.txt
|
||||
build*
|
||||
Build
|
||||
CMakeLists.txt.user
|
||||
|
||||
4
3rdparty/CMakeLists.txt
vendored
4
3rdparty/CMakeLists.txt
vendored
@@ -2,8 +2,6 @@ if (NOT SYSTEM_EXPAT)
|
||||
add_subdirectory(expat)
|
||||
endif()
|
||||
|
||||
add_subdirectory(utf8)
|
||||
|
||||
if (ENABLE_DNS)
|
||||
if (ENABLE_DNS AND NOT SYSTEM_UDNS)
|
||||
add_subdirectory(udns)
|
||||
endif()
|
||||
|
||||
1
3rdparty/udns/udns.h
vendored
1
3rdparty/udns/udns.h
vendored
@@ -45,6 +45,7 @@
|
||||
#endif
|
||||
|
||||
#include <sys/types.h> /* for time_t */
|
||||
#include <time.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
||||
17
3rdparty/utf8/CMakeLists.txt
vendored
17
3rdparty/utf8/CMakeLists.txt
vendored
@@ -1,17 +0,0 @@
|
||||
include (SimGearComponent)
|
||||
|
||||
set(HEADERS
|
||||
source/utf8.h
|
||||
)
|
||||
|
||||
set(HEADERS_utf8
|
||||
source/utf8/checked.h
|
||||
source/utf8/core.h
|
||||
source/utf8/unchecked.h
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
)
|
||||
|
||||
simgear_component(utf8 3rdparty/utf8 "${SOURCES}" "${HEADERS}")
|
||||
simgear_component(utf8-internal 3rdparty/utf8/utf8 "" "${HEADERS_utf8}")
|
||||
12
3rdparty/utf8/doc/ReleaseNotes
vendored
12
3rdparty/utf8/doc/ReleaseNotes
vendored
@@ -1,12 +0,0 @@
|
||||
utf8 cpp library
|
||||
Release 2.3.4
|
||||
|
||||
A minor bug fix release. Thanks to all who reported bugs.
|
||||
|
||||
Note: Version 2.3.3 contained a regression, and therefore was removed.
|
||||
|
||||
Changes from version 2.3.2
|
||||
- Bug fix [39]: checked.h Line 273 and unchecked.h Line 182 have an extra ';'
|
||||
- Bug fix [36]: replace_invalid() only works with back_inserter
|
||||
|
||||
Files included in the release: utf8.h, core.h, checked.h, unchecked.h, utf8cpp.html, ReleaseNotes
|
||||
1789
3rdparty/utf8/doc/utf8cpp.html
vendored
1789
3rdparty/utf8/doc/utf8cpp.html
vendored
File diff suppressed because it is too large
Load Diff
34
3rdparty/utf8/source/utf8.h
vendored
34
3rdparty/utf8/source/utf8.h
vendored
@@ -1,34 +0,0 @@
|
||||
// Copyright 2006 Nemanja Trifunovic
|
||||
|
||||
/*
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
||||
#define UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
||||
|
||||
#include "utf8/checked.h"
|
||||
#include "utf8/unchecked.h"
|
||||
|
||||
#endif // header guard
|
||||
327
3rdparty/utf8/source/utf8/checked.h
vendored
327
3rdparty/utf8/source/utf8/checked.h
vendored
@@ -1,327 +0,0 @@
|
||||
// Copyright 2006 Nemanja Trifunovic
|
||||
|
||||
/*
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
||||
#define UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
||||
|
||||
#include "core.h"
|
||||
#include <stdexcept>
|
||||
|
||||
namespace utf8
|
||||
{
|
||||
// Base for the exceptions that may be thrown from the library
|
||||
class exception : public ::std::exception {
|
||||
};
|
||||
|
||||
// Exceptions that may be thrown from the library functions.
|
||||
class invalid_code_point : public exception {
|
||||
uint32_t cp;
|
||||
public:
|
||||
invalid_code_point(uint32_t cp) : cp(cp) {}
|
||||
virtual const char* what() const throw() { return "Invalid code point"; }
|
||||
uint32_t code_point() const {return cp;}
|
||||
};
|
||||
|
||||
class invalid_utf8 : public exception {
|
||||
uint8_t u8;
|
||||
public:
|
||||
invalid_utf8 (uint8_t u) : u8(u) {}
|
||||
virtual const char* what() const throw() { return "Invalid UTF-8"; }
|
||||
uint8_t utf8_octet() const {return u8;}
|
||||
};
|
||||
|
||||
class invalid_utf16 : public exception {
|
||||
uint16_t u16;
|
||||
public:
|
||||
invalid_utf16 (uint16_t u) : u16(u) {}
|
||||
virtual const char* what() const throw() { return "Invalid UTF-16"; }
|
||||
uint16_t utf16_word() const {return u16;}
|
||||
};
|
||||
|
||||
class not_enough_room : public exception {
|
||||
public:
|
||||
virtual const char* what() const throw() { return "Not enough space"; }
|
||||
};
|
||||
|
||||
/// The library API - functions intended to be called by the users
|
||||
|
||||
template <typename octet_iterator>
|
||||
octet_iterator append(uint32_t cp, octet_iterator result)
|
||||
{
|
||||
if (!utf8::internal::is_code_point_valid(cp))
|
||||
throw invalid_code_point(cp);
|
||||
|
||||
if (cp < 0x80) // one octet
|
||||
*(result++) = static_cast<uint8_t>(cp);
|
||||
else if (cp < 0x800) { // two octets
|
||||
*(result++) = static_cast<uint8_t>((cp >> 6) | 0xc0);
|
||||
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
|
||||
}
|
||||
else if (cp < 0x10000) { // three octets
|
||||
*(result++) = static_cast<uint8_t>((cp >> 12) | 0xe0);
|
||||
*(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
|
||||
}
|
||||
else { // four octets
|
||||
*(result++) = static_cast<uint8_t>((cp >> 18) | 0xf0);
|
||||
*(result++) = static_cast<uint8_t>(((cp >> 12) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename output_iterator>
|
||||
output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement)
|
||||
{
|
||||
while (start != end) {
|
||||
octet_iterator sequence_start = start;
|
||||
internal::utf_error err_code = utf8::internal::validate_next(start, end);
|
||||
switch (err_code) {
|
||||
case internal::UTF8_OK :
|
||||
for (octet_iterator it = sequence_start; it != start; ++it)
|
||||
*out++ = *it;
|
||||
break;
|
||||
case internal::NOT_ENOUGH_ROOM:
|
||||
throw not_enough_room();
|
||||
case internal::INVALID_LEAD:
|
||||
out = utf8::append (replacement, out);
|
||||
++start;
|
||||
break;
|
||||
case internal::INCOMPLETE_SEQUENCE:
|
||||
case internal::OVERLONG_SEQUENCE:
|
||||
case internal::INVALID_CODE_POINT:
|
||||
out = utf8::append (replacement, out);
|
||||
++start;
|
||||
// just one replacement mark for the sequence
|
||||
while (start != end && utf8::internal::is_trail(*start))
|
||||
++start;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename output_iterator>
|
||||
inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out)
|
||||
{
|
||||
static const uint32_t replacement_marker = utf8::internal::mask16(0xfffd);
|
||||
return utf8::replace_invalid(start, end, out, replacement_marker);
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
uint32_t next(octet_iterator& it, octet_iterator end)
|
||||
{
|
||||
uint32_t cp = 0;
|
||||
internal::utf_error err_code = utf8::internal::validate_next(it, end, cp);
|
||||
switch (err_code) {
|
||||
case internal::UTF8_OK :
|
||||
break;
|
||||
case internal::NOT_ENOUGH_ROOM :
|
||||
throw not_enough_room();
|
||||
case internal::INVALID_LEAD :
|
||||
case internal::INCOMPLETE_SEQUENCE :
|
||||
case internal::OVERLONG_SEQUENCE :
|
||||
throw invalid_utf8(*it);
|
||||
case internal::INVALID_CODE_POINT :
|
||||
throw invalid_code_point(cp);
|
||||
}
|
||||
return cp;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
uint32_t peek_next(octet_iterator it, octet_iterator end)
|
||||
{
|
||||
return utf8::next(it, end);
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
uint32_t prior(octet_iterator& it, octet_iterator start)
|
||||
{
|
||||
// can't do much if it == start
|
||||
if (it == start)
|
||||
throw not_enough_room();
|
||||
|
||||
octet_iterator end = it;
|
||||
// Go back until we hit either a lead octet or start
|
||||
while (utf8::internal::is_trail(*(--it)))
|
||||
if (it == start)
|
||||
throw invalid_utf8(*it); // error - no lead byte in the sequence
|
||||
return utf8::peek_next(it, end);
|
||||
}
|
||||
|
||||
/// Deprecated in versions that include "prior"
|
||||
template <typename octet_iterator>
|
||||
uint32_t previous(octet_iterator& it, octet_iterator pass_start)
|
||||
{
|
||||
octet_iterator end = it;
|
||||
while (utf8::internal::is_trail(*(--it)))
|
||||
if (it == pass_start)
|
||||
throw invalid_utf8(*it); // error - no lead byte in the sequence
|
||||
octet_iterator temp = it;
|
||||
return utf8::next(temp, end);
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename distance_type>
|
||||
void advance (octet_iterator& it, distance_type n, octet_iterator end)
|
||||
{
|
||||
for (distance_type i = 0; i < n; ++i)
|
||||
utf8::next(it, end);
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
typename std::iterator_traits<octet_iterator>::difference_type
|
||||
distance (octet_iterator first, octet_iterator last)
|
||||
{
|
||||
typename std::iterator_traits<octet_iterator>::difference_type dist;
|
||||
for (dist = 0; first < last; ++dist)
|
||||
utf8::next(first, last);
|
||||
return dist;
|
||||
}
|
||||
|
||||
template <typename u16bit_iterator, typename octet_iterator>
|
||||
octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result)
|
||||
{
|
||||
while (start != end) {
|
||||
uint32_t cp = utf8::internal::mask16(*start++);
|
||||
// Take care of surrogate pairs first
|
||||
if (utf8::internal::is_lead_surrogate(cp)) {
|
||||
if (start != end) {
|
||||
uint32_t trail_surrogate = utf8::internal::mask16(*start++);
|
||||
if (utf8::internal::is_trail_surrogate(trail_surrogate))
|
||||
cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET;
|
||||
else
|
||||
throw invalid_utf16(static_cast<uint16_t>(trail_surrogate));
|
||||
}
|
||||
else
|
||||
throw invalid_utf16(static_cast<uint16_t>(cp));
|
||||
|
||||
}
|
||||
// Lone trail surrogate
|
||||
else if (utf8::internal::is_trail_surrogate(cp))
|
||||
throw invalid_utf16(static_cast<uint16_t>(cp));
|
||||
|
||||
result = utf8::append(cp, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename u16bit_iterator, typename octet_iterator>
|
||||
u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result)
|
||||
{
|
||||
while (start != end) {
|
||||
uint32_t cp = utf8::next(start, end);
|
||||
if (cp > 0xffff) { //make a surrogate pair
|
||||
*result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET);
|
||||
*result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN);
|
||||
}
|
||||
else
|
||||
*result++ = static_cast<uint16_t>(cp);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename u32bit_iterator>
|
||||
octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result)
|
||||
{
|
||||
while (start != end)
|
||||
result = utf8::append(*(start++), result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename u32bit_iterator>
|
||||
u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result)
|
||||
{
|
||||
while (start != end)
|
||||
(*result++) = utf8::next(start, end);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// The iterator class
|
||||
template <typename octet_iterator>
|
||||
class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> {
|
||||
octet_iterator it;
|
||||
octet_iterator range_start;
|
||||
octet_iterator range_end;
|
||||
public:
|
||||
iterator () {}
|
||||
explicit iterator (const octet_iterator& octet_it,
|
||||
const octet_iterator& range_start,
|
||||
const octet_iterator& range_end) :
|
||||
it(octet_it), range_start(range_start), range_end(range_end)
|
||||
{
|
||||
if (it < range_start || it > range_end)
|
||||
throw std::out_of_range("Invalid utf-8 iterator position");
|
||||
}
|
||||
// the default "big three" are OK
|
||||
octet_iterator base () const { return it; }
|
||||
uint32_t operator * () const
|
||||
{
|
||||
octet_iterator temp = it;
|
||||
return utf8::next(temp, range_end);
|
||||
}
|
||||
bool operator == (const iterator& rhs) const
|
||||
{
|
||||
if (range_start != rhs.range_start || range_end != rhs.range_end)
|
||||
throw std::logic_error("Comparing utf-8 iterators defined with different ranges");
|
||||
return (it == rhs.it);
|
||||
}
|
||||
bool operator != (const iterator& rhs) const
|
||||
{
|
||||
return !(operator == (rhs));
|
||||
}
|
||||
iterator& operator ++ ()
|
||||
{
|
||||
utf8::next(it, range_end);
|
||||
return *this;
|
||||
}
|
||||
iterator operator ++ (int)
|
||||
{
|
||||
iterator temp = *this;
|
||||
utf8::next(it, range_end);
|
||||
return temp;
|
||||
}
|
||||
iterator& operator -- ()
|
||||
{
|
||||
utf8::prior(it, range_start);
|
||||
return *this;
|
||||
}
|
||||
iterator operator -- (int)
|
||||
{
|
||||
iterator temp = *this;
|
||||
utf8::prior(it, range_start);
|
||||
return temp;
|
||||
}
|
||||
}; // class iterator
|
||||
|
||||
} // namespace utf8
|
||||
|
||||
#endif //header guard
|
||||
|
||||
|
||||
329
3rdparty/utf8/source/utf8/core.h
vendored
329
3rdparty/utf8/source/utf8/core.h
vendored
@@ -1,329 +0,0 @@
|
||||
// Copyright 2006 Nemanja Trifunovic
|
||||
|
||||
/*
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
||||
#define UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
||||
|
||||
#include <iterator>
|
||||
|
||||
namespace utf8
|
||||
{
|
||||
// The typedefs for 8-bit, 16-bit and 32-bit unsigned integers
|
||||
// You may need to change them to match your system.
|
||||
// These typedefs have the same names as ones from cstdint, or boost/cstdint
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
|
||||
// Helper code - not intended to be directly called by the library users. May be changed at any time
|
||||
namespace internal
|
||||
{
|
||||
// Unicode constants
|
||||
// Leading (high) surrogates: 0xd800 - 0xdbff
|
||||
// Trailing (low) surrogates: 0xdc00 - 0xdfff
|
||||
const uint16_t LEAD_SURROGATE_MIN = 0xd800u;
|
||||
const uint16_t LEAD_SURROGATE_MAX = 0xdbffu;
|
||||
const uint16_t TRAIL_SURROGATE_MIN = 0xdc00u;
|
||||
const uint16_t TRAIL_SURROGATE_MAX = 0xdfffu;
|
||||
const uint16_t LEAD_OFFSET = LEAD_SURROGATE_MIN - (0x10000 >> 10);
|
||||
const uint32_t SURROGATE_OFFSET = 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN;
|
||||
|
||||
// Maximum valid value for a Unicode code point
|
||||
const uint32_t CODE_POINT_MAX = 0x0010ffffu;
|
||||
|
||||
template<typename octet_type>
|
||||
inline uint8_t mask8(octet_type oc)
|
||||
{
|
||||
return static_cast<uint8_t>(0xff & oc);
|
||||
}
|
||||
template<typename u16_type>
|
||||
inline uint16_t mask16(u16_type oc)
|
||||
{
|
||||
return static_cast<uint16_t>(0xffff & oc);
|
||||
}
|
||||
template<typename octet_type>
|
||||
inline bool is_trail(octet_type oc)
|
||||
{
|
||||
return ((utf8::internal::mask8(oc) >> 6) == 0x2);
|
||||
}
|
||||
|
||||
template <typename u16>
|
||||
inline bool is_lead_surrogate(u16 cp)
|
||||
{
|
||||
return (cp >= LEAD_SURROGATE_MIN && cp <= LEAD_SURROGATE_MAX);
|
||||
}
|
||||
|
||||
template <typename u16>
|
||||
inline bool is_trail_surrogate(u16 cp)
|
||||
{
|
||||
return (cp >= TRAIL_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX);
|
||||
}
|
||||
|
||||
template <typename u16>
|
||||
inline bool is_surrogate(u16 cp)
|
||||
{
|
||||
return (cp >= LEAD_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX);
|
||||
}
|
||||
|
||||
template <typename u32>
|
||||
inline bool is_code_point_valid(u32 cp)
|
||||
{
|
||||
return (cp <= CODE_POINT_MAX && !utf8::internal::is_surrogate(cp));
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
inline typename std::iterator_traits<octet_iterator>::difference_type
|
||||
sequence_length(octet_iterator lead_it)
|
||||
{
|
||||
uint8_t lead = utf8::internal::mask8(*lead_it);
|
||||
if (lead < 0x80)
|
||||
return 1;
|
||||
else if ((lead >> 5) == 0x6)
|
||||
return 2;
|
||||
else if ((lead >> 4) == 0xe)
|
||||
return 3;
|
||||
else if ((lead >> 3) == 0x1e)
|
||||
return 4;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename octet_difference_type>
|
||||
inline bool is_overlong_sequence(uint32_t cp, octet_difference_type length)
|
||||
{
|
||||
if (cp < 0x80) {
|
||||
if (length != 1)
|
||||
return true;
|
||||
}
|
||||
else if (cp < 0x800) {
|
||||
if (length != 2)
|
||||
return true;
|
||||
}
|
||||
else if (cp < 0x10000) {
|
||||
if (length != 3)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
enum utf_error {UTF8_OK, NOT_ENOUGH_ROOM, INVALID_LEAD, INCOMPLETE_SEQUENCE, OVERLONG_SEQUENCE, INVALID_CODE_POINT};
|
||||
|
||||
/// Helper for get_sequence_x
|
||||
template <typename octet_iterator>
|
||||
utf_error increase_safely(octet_iterator& it, octet_iterator end)
|
||||
{
|
||||
if (++it == end)
|
||||
return NOT_ENOUGH_ROOM;
|
||||
|
||||
if (!utf8::internal::is_trail(*it))
|
||||
return INCOMPLETE_SEQUENCE;
|
||||
|
||||
return UTF8_OK;
|
||||
}
|
||||
|
||||
#define UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(IT, END) {utf_error ret = increase_safely(IT, END); if (ret != UTF8_OK) return ret;}
|
||||
|
||||
/// get_sequence_x functions decode utf-8 sequences of the length x
|
||||
template <typename octet_iterator>
|
||||
utf_error get_sequence_1(octet_iterator& it, octet_iterator end, uint32_t& code_point)
|
||||
{
|
||||
if (it == end)
|
||||
return NOT_ENOUGH_ROOM;
|
||||
|
||||
code_point = utf8::internal::mask8(*it);
|
||||
|
||||
return UTF8_OK;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
utf_error get_sequence_2(octet_iterator& it, octet_iterator end, uint32_t& code_point)
|
||||
{
|
||||
if (it == end)
|
||||
return NOT_ENOUGH_ROOM;
|
||||
|
||||
code_point = utf8::internal::mask8(*it);
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point = ((code_point << 6) & 0x7ff) + ((*it) & 0x3f);
|
||||
|
||||
return UTF8_OK;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
utf_error get_sequence_3(octet_iterator& it, octet_iterator end, uint32_t& code_point)
|
||||
{
|
||||
if (it == end)
|
||||
return NOT_ENOUGH_ROOM;
|
||||
|
||||
code_point = utf8::internal::mask8(*it);
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point = ((code_point << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff);
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point += (*it) & 0x3f;
|
||||
|
||||
return UTF8_OK;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
utf_error get_sequence_4(octet_iterator& it, octet_iterator end, uint32_t& code_point)
|
||||
{
|
||||
if (it == end)
|
||||
return NOT_ENOUGH_ROOM;
|
||||
|
||||
code_point = utf8::internal::mask8(*it);
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point = ((code_point << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff);
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point += (utf8::internal::mask8(*it) << 6) & 0xfff;
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point += (*it) & 0x3f;
|
||||
|
||||
return UTF8_OK;
|
||||
}
|
||||
|
||||
#undef UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR
|
||||
|
||||
template <typename octet_iterator>
|
||||
utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t& code_point)
|
||||
{
|
||||
// Save the original value of it so we can go back in case of failure
|
||||
// Of course, it does not make much sense with i.e. stream iterators
|
||||
octet_iterator original_it = it;
|
||||
|
||||
uint32_t cp = 0;
|
||||
// Determine the sequence length based on the lead octet
|
||||
typedef typename std::iterator_traits<octet_iterator>::difference_type octet_difference_type;
|
||||
const octet_difference_type length = utf8::internal::sequence_length(it);
|
||||
|
||||
// Get trail octets and calculate the code point
|
||||
utf_error err = UTF8_OK;
|
||||
switch (length) {
|
||||
case 0:
|
||||
return INVALID_LEAD;
|
||||
case 1:
|
||||
err = utf8::internal::get_sequence_1(it, end, cp);
|
||||
break;
|
||||
case 2:
|
||||
err = utf8::internal::get_sequence_2(it, end, cp);
|
||||
break;
|
||||
case 3:
|
||||
err = utf8::internal::get_sequence_3(it, end, cp);
|
||||
break;
|
||||
case 4:
|
||||
err = utf8::internal::get_sequence_4(it, end, cp);
|
||||
break;
|
||||
}
|
||||
|
||||
if (err == UTF8_OK) {
|
||||
// Decoding succeeded. Now, security checks...
|
||||
if (utf8::internal::is_code_point_valid(cp)) {
|
||||
if (!utf8::internal::is_overlong_sequence(cp, length)){
|
||||
// Passed! Return here.
|
||||
code_point = cp;
|
||||
++it;
|
||||
return UTF8_OK;
|
||||
}
|
||||
else
|
||||
err = OVERLONG_SEQUENCE;
|
||||
}
|
||||
else
|
||||
err = INVALID_CODE_POINT;
|
||||
}
|
||||
|
||||
// Failure branch - restore the original value of the iterator
|
||||
it = original_it;
|
||||
return err;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
inline utf_error validate_next(octet_iterator& it, octet_iterator end) {
|
||||
uint32_t ignored;
|
||||
return utf8::internal::validate_next(it, end, ignored);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/// The library API - functions intended to be called by the users
|
||||
|
||||
// Byte order mark
|
||||
const uint8_t bom[] = {0xef, 0xbb, 0xbf};
|
||||
|
||||
template <typename octet_iterator>
|
||||
octet_iterator find_invalid(octet_iterator start, octet_iterator end)
|
||||
{
|
||||
octet_iterator result = start;
|
||||
while (result != end) {
|
||||
utf8::internal::utf_error err_code = utf8::internal::validate_next(result, end);
|
||||
if (err_code != internal::UTF8_OK)
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
inline bool is_valid(octet_iterator start, octet_iterator end)
|
||||
{
|
||||
return (utf8::find_invalid(start, end) == end);
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
inline bool starts_with_bom (octet_iterator it, octet_iterator end)
|
||||
{
|
||||
return (
|
||||
((it != end) && (utf8::internal::mask8(*it++)) == bom[0]) &&
|
||||
((it != end) && (utf8::internal::mask8(*it++)) == bom[1]) &&
|
||||
((it != end) && (utf8::internal::mask8(*it)) == bom[2])
|
||||
);
|
||||
}
|
||||
|
||||
//Deprecated in release 2.3
|
||||
template <typename octet_iterator>
|
||||
inline bool is_bom (octet_iterator it)
|
||||
{
|
||||
return (
|
||||
(utf8::internal::mask8(*it++)) == bom[0] &&
|
||||
(utf8::internal::mask8(*it++)) == bom[1] &&
|
||||
(utf8::internal::mask8(*it)) == bom[2]
|
||||
);
|
||||
}
|
||||
} // namespace utf8
|
||||
|
||||
#endif // header guard
|
||||
|
||||
|
||||
228
3rdparty/utf8/source/utf8/unchecked.h
vendored
228
3rdparty/utf8/source/utf8/unchecked.h
vendored
@@ -1,228 +0,0 @@
|
||||
// Copyright 2006 Nemanja Trifunovic
|
||||
|
||||
/*
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
||||
#define UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
||||
|
||||
#include "core.h"
|
||||
|
||||
namespace utf8
|
||||
{
|
||||
namespace unchecked
|
||||
{
|
||||
template <typename octet_iterator>
|
||||
octet_iterator append(uint32_t cp, octet_iterator result)
|
||||
{
|
||||
if (cp < 0x80) // one octet
|
||||
*(result++) = static_cast<uint8_t>(cp);
|
||||
else if (cp < 0x800) { // two octets
|
||||
*(result++) = static_cast<uint8_t>((cp >> 6) | 0xc0);
|
||||
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
|
||||
}
|
||||
else if (cp < 0x10000) { // three octets
|
||||
*(result++) = static_cast<uint8_t>((cp >> 12) | 0xe0);
|
||||
*(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
|
||||
}
|
||||
else { // four octets
|
||||
*(result++) = static_cast<uint8_t>((cp >> 18) | 0xf0);
|
||||
*(result++) = static_cast<uint8_t>(((cp >> 12) & 0x3f)| 0x80);
|
||||
*(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
uint32_t next(octet_iterator& it)
|
||||
{
|
||||
uint32_t cp = utf8::internal::mask8(*it);
|
||||
typename std::iterator_traits<octet_iterator>::difference_type length = utf8::internal::sequence_length(it);
|
||||
switch (length) {
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
it++;
|
||||
cp = ((cp << 6) & 0x7ff) + ((*it) & 0x3f);
|
||||
break;
|
||||
case 3:
|
||||
++it;
|
||||
cp = ((cp << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff);
|
||||
++it;
|
||||
cp += (*it) & 0x3f;
|
||||
break;
|
||||
case 4:
|
||||
++it;
|
||||
cp = ((cp << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff);
|
||||
++it;
|
||||
cp += (utf8::internal::mask8(*it) << 6) & 0xfff;
|
||||
++it;
|
||||
cp += (*it) & 0x3f;
|
||||
break;
|
||||
}
|
||||
++it;
|
||||
return cp;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
uint32_t peek_next(octet_iterator it)
|
||||
{
|
||||
return utf8::unchecked::next(it);
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
uint32_t prior(octet_iterator& it)
|
||||
{
|
||||
while (utf8::internal::is_trail(*(--it))) ;
|
||||
octet_iterator temp = it;
|
||||
return utf8::unchecked::next(temp);
|
||||
}
|
||||
|
||||
// Deprecated in versions that include prior, but only for the sake of consistency (see utf8::previous)
|
||||
template <typename octet_iterator>
|
||||
inline uint32_t previous(octet_iterator& it)
|
||||
{
|
||||
return utf8::unchecked::prior(it);
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename distance_type>
|
||||
void advance (octet_iterator& it, distance_type n)
|
||||
{
|
||||
for (distance_type i = 0; i < n; ++i)
|
||||
utf8::unchecked::next(it);
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
typename std::iterator_traits<octet_iterator>::difference_type
|
||||
distance (octet_iterator first, octet_iterator last)
|
||||
{
|
||||
typename std::iterator_traits<octet_iterator>::difference_type dist;
|
||||
for (dist = 0; first < last; ++dist)
|
||||
utf8::unchecked::next(first);
|
||||
return dist;
|
||||
}
|
||||
|
||||
template <typename u16bit_iterator, typename octet_iterator>
|
||||
octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result)
|
||||
{
|
||||
while (start != end) {
|
||||
uint32_t cp = utf8::internal::mask16(*start++);
|
||||
// Take care of surrogate pairs first
|
||||
if (utf8::internal::is_lead_surrogate(cp)) {
|
||||
uint32_t trail_surrogate = utf8::internal::mask16(*start++);
|
||||
cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET;
|
||||
}
|
||||
result = utf8::unchecked::append(cp, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename u16bit_iterator, typename octet_iterator>
|
||||
u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result)
|
||||
{
|
||||
while (start < end) {
|
||||
uint32_t cp = utf8::unchecked::next(start);
|
||||
if (cp > 0xffff) { //make a surrogate pair
|
||||
*result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET);
|
||||
*result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN);
|
||||
}
|
||||
else
|
||||
*result++ = static_cast<uint16_t>(cp);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename u32bit_iterator>
|
||||
octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result)
|
||||
{
|
||||
while (start != end)
|
||||
result = utf8::unchecked::append(*(start++), result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename u32bit_iterator>
|
||||
u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result)
|
||||
{
|
||||
while (start < end)
|
||||
(*result++) = utf8::unchecked::next(start);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// The iterator class
|
||||
template <typename octet_iterator>
|
||||
class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> {
|
||||
octet_iterator it;
|
||||
public:
|
||||
iterator () {}
|
||||
explicit iterator (const octet_iterator& octet_it): it(octet_it) {}
|
||||
// the default "big three" are OK
|
||||
octet_iterator base () const { return it; }
|
||||
uint32_t operator * () const
|
||||
{
|
||||
octet_iterator temp = it;
|
||||
return utf8::unchecked::next(temp);
|
||||
}
|
||||
bool operator == (const iterator& rhs) const
|
||||
{
|
||||
return (it == rhs.it);
|
||||
}
|
||||
bool operator != (const iterator& rhs) const
|
||||
{
|
||||
return !(operator == (rhs));
|
||||
}
|
||||
iterator& operator ++ ()
|
||||
{
|
||||
::std::advance(it, utf8::internal::sequence_length(it));
|
||||
return *this;
|
||||
}
|
||||
iterator operator ++ (int)
|
||||
{
|
||||
iterator temp = *this;
|
||||
::std::advance(it, utf8::internal::sequence_length(it));
|
||||
return temp;
|
||||
}
|
||||
iterator& operator -- ()
|
||||
{
|
||||
utf8::unchecked::prior(it);
|
||||
return *this;
|
||||
}
|
||||
iterator operator -- (int)
|
||||
{
|
||||
iterator temp = *this;
|
||||
utf8::unchecked::prior(it);
|
||||
return temp;
|
||||
}
|
||||
}; // class iterator
|
||||
|
||||
} // namespace utf8::unchecked
|
||||
} // namespace utf8
|
||||
|
||||
|
||||
#endif // header guard
|
||||
|
||||
336
CMakeLists.txt
336
CMakeLists.txt
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required (VERSION 2.8.11)
|
||||
cmake_minimum_required (VERSION 3.0)
|
||||
|
||||
if(COMMAND cmake_policy)
|
||||
if(POLICY CMP0054)
|
||||
@@ -7,6 +7,20 @@ if(COMMAND cmake_policy)
|
||||
if(POLICY CMP0042)
|
||||
cmake_policy(SET CMP0042 NEW)
|
||||
endif()
|
||||
if(POLICY CMP0067)
|
||||
cmake_policy(SET CMP0067 NEW)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
message(STATUS "CMAKE Build type: ${CMAKE_BUILD_TYPE}")
|
||||
# Set a default build type if none was specified
|
||||
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
message(STATUS "Setting build type to 'Debug' as none was specified.")
|
||||
set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE)
|
||||
# Set the possible values of build type for cmake-gui
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release"
|
||||
"MinSizeRel" "RelWithDebInfo")
|
||||
endif()
|
||||
|
||||
include (CheckFunctionExists)
|
||||
@@ -16,19 +30,29 @@ include (CheckCXXSourceCompiles)
|
||||
include (CheckCXXCompilerFlag)
|
||||
include (GenerateExportHeader)
|
||||
|
||||
# using 10.7 because boost requires libc++ and 10.6 doesn't include it
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.7)
|
||||
|
||||
# only relevant for building shared libs but let's set it regardless
|
||||
set(CMAKE_OSX_RPATH 1)
|
||||
|
||||
project(SimGear)
|
||||
# let's use & require C++11 - note these are only functional with CMake 3.1
|
||||
# we do manual fallbacks for CMake 3.0 in the compilers section
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED YES)
|
||||
|
||||
# read 'version' file into a variable (stripping any newlines or spaces)
|
||||
file(READ version versionFile)
|
||||
string(STRIP ${versionFile} SIMGEAR_VERSION)
|
||||
|
||||
set(FIND_LIBRARY_USE_LIB64_PATHS ON)
|
||||
project(SimGear VERSION ${SIMGEAR_VERSION} LANGUAGES C CXX)
|
||||
|
||||
# using 10.7 because boost requires libc++ and 10.6 doesn't include it
|
||||
# Cmake documentation says we must set this before calling project(), but
|
||||
# it only seems to be picked up setting it /after/ the call to project()
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.7")
|
||||
|
||||
# add a dependency on the versino file
|
||||
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS version)
|
||||
|
||||
set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS TRUE)
|
||||
|
||||
# use simgear version also as the SO version (if building SOs)
|
||||
SET(SIMGEAR_SOVERSION ${SIMGEAR_VERSION})
|
||||
@@ -63,8 +87,6 @@ set(CPACK_SOURCE_PACKAGE_FILE_NAME "simgear-${SIMGEAR_VERSION}" CACHE INTERNAL "
|
||||
set(CPACK_SOURCE_IGNORE_FILES
|
||||
"^${PROJECT_SOURCE_DIR}/.git;\\\\.gitignore;Makefile.am;~$;${CPACK_SOURCE_IGNORE_FILES}")
|
||||
|
||||
message(STATUS "ignoring: ${CPACK_SOURCE_IGNORE_FILES}")
|
||||
|
||||
include (CPack)
|
||||
|
||||
# We have some custom .cmake scripts not in the official distribution.
|
||||
@@ -87,37 +109,44 @@ message(STATUS "Library installation directory: ${CMAKE_INSTALL_LIBDIR}")
|
||||
# Configure library search paths
|
||||
#####################################################################################
|
||||
|
||||
if(NOT "${CMAKE_LIBRARY_ARCHITECTURE}" STREQUAL "")
|
||||
# Workaround for Ubuntu/Debian which introduced the "multiarch" library
|
||||
# directory structure, which is unsupported by CMake < 2.8.10, so we need to
|
||||
# add paths manually
|
||||
# see http://www.cmake.org/Bug/view.php?id=12049 and
|
||||
# http://www.cmake.org/Bug/view.php?id=12037
|
||||
list(APPEND ADDITIONAL_LIBRARY_PATHS
|
||||
/usr/local/lib/${CMAKE_LIBRARY_ARCHITECTURE}
|
||||
/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}
|
||||
/lib/${CMAKE_LIBRARY_ARCHITECTURE})
|
||||
message(STATUS "additional library directories: ${ADDITIONAL_LIBRARY_PATHS}")
|
||||
endif()
|
||||
|
||||
#####################################################################################
|
||||
|
||||
if (NOT MSVC)
|
||||
option(SIMGEAR_SHARED "Set to ON to build SimGear as a shared library/framework" OFF)
|
||||
option(SYSTEM_EXPAT "Set to ON to build SimGear using the system libExpat" OFF)
|
||||
option(SYSTEM_EXPAT "Set to ON to build SimGear using the system expat library" OFF)
|
||||
option(SYSTEM_UDNS "Set to ON to build SimGear using the system udns library" OFF)
|
||||
else()
|
||||
# Building SimGear DLLs is currently not supported for MSVC.
|
||||
set(SIMGEAR_SHARED OFF)
|
||||
# Using a system expat is currently not supported for MSVC - it would require shared simgear (DLL).
|
||||
# Using external 3rd party libraries is currently not supported for MSVC - it would require shared simgear (DLL).
|
||||
set(SYSTEM_EXPAT OFF)
|
||||
set(SYSTEM_UDNS OFF)
|
||||
endif()
|
||||
|
||||
option(SIMGEAR_HEADLESS "Set to ON to build SimGear without GUI/graphics support" OFF)
|
||||
option(ENABLE_RTI "Set to ON to build SimGear with RTI support" OFF)
|
||||
option(ENABLE_GDAL "Set to ON to build SimGear with GDAL support" OFF)
|
||||
option(ENABLE_TESTS "Set to OFF to disable building SimGear's test applications" ON)
|
||||
option(ENABLE_SOUND "Set to OFF to disable building SimGear's sound support" ON)
|
||||
option(USE_AEONWAVE "Set to ON to use AeonWave instead of OpenAL" OFF)
|
||||
option(ENABLE_PKGUTIL "Set to ON to build the sg_pkgutil application (default)" ON)
|
||||
option(ENABLE_DNS "Set to ON to use udns library and DNS service resolver" ON)
|
||||
option(ENABLE_SIMD "Enable SSE/SSE2 support for x86 compilers" ON)
|
||||
option(ENABLE_OPENMP "Enable OpenMP compiler support" OFF)
|
||||
|
||||
include (DetectArch)
|
||||
|
||||
# until the fstream fix is applied and generally available in OSG,
|
||||
# keep the compatability link option as the default
|
||||
option(OSG_FSTREAM_EXPORT_FIXED "Set to ON if the osgDB fstream export patch is applied" OFF)
|
||||
|
||||
if (CMAKE_COMPILER_IS_GNUCXX OR CLANG)
|
||||
if (CMAKE_VERSION VERSION_LESS 3.1)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++${CMAKE_CXX_STANDARD}")
|
||||
elseif (CMAKE_VERSION VERSION_LESS 3.8)
|
||||
# policy CMP0067 (try_compile does not honor CMAKE_CXX_STANDARD)
|
||||
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++${CMAKE_CXX_STANDARD}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
if (MSVC)
|
||||
GET_FILENAME_COMPONENT(PARENT_DIR ${PROJECT_BINARY_DIR} PATH)
|
||||
@@ -137,14 +166,15 @@ endif (MSVC)
|
||||
|
||||
if (MSVC AND MSVC_3RDPARTY_ROOT)
|
||||
message(STATUS "3rdparty files located in ${MSVC_3RDPARTY_ROOT}")
|
||||
|
||||
set( OSG_MSVC "msvc" )
|
||||
if (${MSVC_VERSION} EQUAL 1700)
|
||||
set( OSG_MSVC ${OSG_MSVC}110 )
|
||||
elseif (${MSVC_VERSION} EQUAL 1600)
|
||||
set( OSG_MSVC ${OSG_MSVC}100 )
|
||||
else (${MSVC_VERSION} EQUAL 1700)
|
||||
set( OSG_MSVC ${OSG_MSVC}90 )
|
||||
endif (${MSVC_VERSION} EQUAL 1700)
|
||||
if (${MSVC_VERSION} EQUAL 1900)
|
||||
set( OSG_MSVC ${OSG_MSVC}140 )
|
||||
elseif (${MSVC_VERSION} EQUAL 1800)
|
||||
set( OSG_MSVC ${OSG_MSVC}120 )
|
||||
else ()
|
||||
message(FATAL_ERROR "Visual Studio 2013/2015 is required now")
|
||||
endif ()
|
||||
if (CMAKE_CL_64)
|
||||
set( OSG_MSVC ${OSG_MSVC}-64 )
|
||||
set( MSVC_3RDPARTY_DIR 3rdParty.x64 )
|
||||
@@ -154,44 +184,34 @@ if (MSVC AND MSVC_3RDPARTY_ROOT)
|
||||
|
||||
set (CMAKE_LIBRARY_PATH ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenScenegraph/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenRTI/lib )
|
||||
set (CMAKE_INCLUDE_PATH ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenScenegraph/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenRTI/include)
|
||||
find_path(BOOST_ROOT boost/version.hpp
|
||||
${MSVC_3RDPARTY_ROOT}/boost
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_52_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_51_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_50_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_49_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_48_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_47_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_46_1
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_46_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_45_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_44_0
|
||||
)
|
||||
# set (BOOST_ROOT ${MSVC_3RDPARTY_ROOT}/boost_1_44_0)
|
||||
message(STATUS "BOOST_ROOT is ${BOOST_ROOT}")
|
||||
set (OPENAL_INCLUDE_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include)
|
||||
set (OPENAL_LIBRARY_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib)
|
||||
|
||||
if(NOT BOOST_INCLUDEDIR)
|
||||
# if this variable was not set by the user, set it to 3rdparty root's
|
||||
# parent dir, which is the normal location for people using our
|
||||
# windows-3rd-party repo
|
||||
GET_FILENAME_COMPONENT(MSVC_ROOT_PARENT_DIR ${MSVC_3RDPARTY_ROOT} PATH)
|
||||
set(BOOST_INCLUDEDIR ${MSVC_ROOT_PARENT_DIR})
|
||||
message(STATUS "BOOST_INCLUDEDIR is ${BOOST_INCLUDEDIR}")
|
||||
endif()
|
||||
|
||||
if (NOT USE_AEONWAVE)
|
||||
set (OPENAL_INCLUDE_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include)
|
||||
set (OPENAL_LIBRARY_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib)
|
||||
message(STATUS "OPENAL_INCLUDE_DIR is ${OPENAL_INCLUDE_DIR}")
|
||||
endif()
|
||||
endif (MSVC AND MSVC_3RDPARTY_ROOT)
|
||||
|
||||
if(APPLE)
|
||||
find_library(COCOA_LIBRARY Cocoa)
|
||||
|
||||
# this should be handled by setting CMAKE_OSX_DEPLOYMENT_TARGET
|
||||
# but it's not working reliably, so forcing it for now
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.7")
|
||||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=10.7")
|
||||
endif()
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR
|
||||
${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
|
||||
find_package(Threads REQUIRED)
|
||||
endif()
|
||||
|
||||
# Somehow this only works if included before searching for Boost...
|
||||
include(BoostTestTargets)
|
||||
|
||||
find_package(Boost REQUIRED)
|
||||
set (BOOST_CXX_FLAGS "-DBOOST_BIMAP_DISABLE_SERIALIZATION")
|
||||
include(BoostTestTargets)
|
||||
|
||||
if(SIMGEAR_HEADLESS)
|
||||
message(STATUS "SimGear mode: HEADLESS")
|
||||
@@ -201,14 +221,34 @@ else()
|
||||
find_package(OpenGL REQUIRED)
|
||||
|
||||
if (ENABLE_SOUND)
|
||||
find_package(OpenAL REQUIRED)
|
||||
if (USE_AEONWAVE)
|
||||
find_package(AAX COMPONENTS aax REQUIRED)
|
||||
else()
|
||||
find_package(OpenAL REQUIRED)
|
||||
endif()
|
||||
|
||||
message(STATUS "Sound support: ENABLED")
|
||||
endif(ENABLE_SOUND)
|
||||
|
||||
find_package(OpenSceneGraph 3.2.0 REQUIRED osgText osgSim osgDB osgParticle osgGA osgViewer osgUtil)
|
||||
|
||||
if (MSVC)
|
||||
set(CMAKE_REQUIRED_INCLUDES ${OPENSCENEGRAPH_INCLUDE_DIRS})
|
||||
# ensure OSG was compiled with OSG_USE_UTF8_FILENAME set
|
||||
check_cxx_source_compiles(
|
||||
"#include <osg/Config>
|
||||
#if !defined(OSG_USE_UTF8_FILENAME)
|
||||
#error OSG UTF8 support not enabled
|
||||
#endif
|
||||
int main() { return 0; }"
|
||||
SIMGEAR_OSG_USE_UTF8_FILENAME)
|
||||
if (NOT SIMGEAR_OSG_USE_UTF8_FILENAME)
|
||||
message(FATAL_ERROR "Please rebuild OSG with OSG_USE_UTF8_FILENAME set to ON")
|
||||
endif()
|
||||
endif()
|
||||
endif(SIMGEAR_HEADLESS)
|
||||
|
||||
find_package(ZLIB REQUIRED)
|
||||
find_package(ZLIB 1.2.4 REQUIRED)
|
||||
find_package(CURL REQUIRED)
|
||||
|
||||
if (SYSTEM_EXPAT)
|
||||
@@ -222,15 +262,12 @@ else()
|
||||
# declaring symbols as declspec(import)
|
||||
add_definitions(-DHAVE_EXPAT_CONFIG_H -DXML_STATIC)
|
||||
set(EXPAT_INCLUDE_DIRS
|
||||
${PROJECT_SOURCE_DIR}/3rdparty/expat
|
||||
${PROJECT_BINARY_DIR}/3rdparty/expat)
|
||||
${PROJECT_SOURCE_DIR}/3rdparty/expat
|
||||
${PROJECT_BINARY_DIR}/3rdparty/expat)
|
||||
endif(SYSTEM_EXPAT)
|
||||
|
||||
include_directories(${EXPAT_INCLUDE_DIRS})
|
||||
|
||||
check_include_file(inttypes.h HAVE_INTTYPES_H)
|
||||
check_include_file(sys/time.h HAVE_SYS_TIME_H)
|
||||
check_include_file(sys/timeb.h HAVE_SYS_TIMEB_H)
|
||||
check_include_file(unistd.h HAVE_UNISTD_H)
|
||||
check_include_file(windows.h HAVE_WINDOWS_H)
|
||||
|
||||
@@ -240,21 +277,41 @@ if(HAVE_INTTYPES_H)
|
||||
endif()
|
||||
|
||||
if(ENABLE_RTI)
|
||||
# See if we have any rti library variant installed
|
||||
message(STATUS "RTI: ENABLED")
|
||||
find_package(RTI)
|
||||
find_package(PkgConfig)
|
||||
if(PKG_CONFIG_FOUND)
|
||||
SET(ENV{PKG_CONFIG_PATH} "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig:$ENV{PKG_CONFIG_PATH}")
|
||||
pkg_check_modules(RTI hla-rti13)
|
||||
endif(PKG_CONFIG_FOUND)
|
||||
if(RTI_FOUND)
|
||||
SET(RTI_INCLUDE_DIR "${RTI_INCLUDE_DIRS}")
|
||||
message(STATUS "RTI: ENABLED")
|
||||
else()
|
||||
message(STATUS "RTI: DISABLED")
|
||||
endif(RTI_FOUND)
|
||||
else()
|
||||
message(STATUS "RTI: DISABLED")
|
||||
endif(ENABLE_RTI)
|
||||
|
||||
if(ENABLE_GDAL)
|
||||
find_package(GDAL 2.0.0 REQUIRED)
|
||||
if (GDAL_FOUND)
|
||||
include_directories(${GDAL_INCLUDE_DIR})
|
||||
endif(GDAL_FOUND)
|
||||
endif(ENABLE_GDAL)
|
||||
|
||||
check_function_exists(gettimeofday HAVE_GETTIMEOFDAY)
|
||||
check_function_exists(ftime HAVE_FTIME)
|
||||
check_function_exists(timegm HAVE_TIMEGM)
|
||||
check_function_exists(rint HAVE_RINT)
|
||||
check_function_exists(mkdtemp HAVE_MKDTEMP)
|
||||
check_function_exists(bcopy HAVE_BCOPY)
|
||||
check_function_exists(mmap HAVE_MMAP)
|
||||
|
||||
if (NOT MSVC)
|
||||
check_function_exists(timegm HAVE_TIMEGM)
|
||||
if (NOT HAVE_TIMEGM)
|
||||
message(FATAL_ERROR "Non-Windows platforms must support timegm()")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(HAVE_UNISTD_H)
|
||||
set(CMAKE_REQUIRED_INCLUDES ${CMAKE_INCLUDE_PATH})
|
||||
check_cxx_source_compiles(
|
||||
@@ -298,19 +355,54 @@ SET(CMAKE_RELWITHDEBINFO_POSTFIX "" CACHE STRING "add a postfix, usually empty o
|
||||
SET(CMAKE_MINSIZEREL_POSTFIX "" CACHE STRING "add a postfix, usually empty on windows")
|
||||
|
||||
# isnan might not be real symbol, so can't check using function_exists
|
||||
check_cxx_source_compiles(
|
||||
"#include <cmath>
|
||||
int main() { return isnan(0.0);} "
|
||||
HAVE_ISNAN)
|
||||
|
||||
check_cxx_source_compiles(
|
||||
"#include <cmath>
|
||||
int main() { return std::isnan(0.0);} "
|
||||
HAVE_STD_ISNAN)
|
||||
|
||||
if (NOT ${HAVE_STD_ISNAN})
|
||||
message(FATAL_ERROR "Your compiler lacks C++11 std::isnan, please update it")
|
||||
endif()
|
||||
|
||||
# Check if the <regex> implementation in the C++ standard library is usable.
|
||||
# This is necessary because g++ 4.8 lies about its C++11 compliance: its
|
||||
# <regex> is utterly unusable, cf. [1].
|
||||
# The big preprocessor test essentially comes from [2], and gcc upstream devs
|
||||
# appear to back it (see comments following [2], as well as [3]).
|
||||
#
|
||||
# [1] https://stackoverflow.com/a/12665408/4756009
|
||||
# [2] https://stackoverflow.com/a/41186162/4756009
|
||||
# [3] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78905
|
||||
check_cxx_source_compiles(
|
||||
"#include <regex>
|
||||
|
||||
int main() {
|
||||
#if __cplusplus >= 201103L && \
|
||||
(!defined(__GLIBCXX__) || \
|
||||
(__cplusplus >= 201402L) || \
|
||||
defined(_GLIBCXX_REGEX_DFS_QUANTIFIERS_LIMIT) || \
|
||||
defined(_GLIBCXX_REGEX_STATE_LIMIT) || \
|
||||
(defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE > 4))
|
||||
#else
|
||||
nullptr = void; // intentionally trigger a compilation error
|
||||
#endif
|
||||
}"
|
||||
HAVE_WORKING_STD_REGEX)
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(WARNING_FLAGS_CXX "-Wall")
|
||||
set(WARNING_FLAGS_C "-Wall")
|
||||
set(WARNING_FLAGS_CXX "-Wall -fPIC")
|
||||
set(WARNING_FLAGS_C "-Wall -fPIC")
|
||||
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.4)
|
||||
message(WARNING "GCC 4.4 will be required soon, please upgrade")
|
||||
endif()
|
||||
|
||||
if(ENABLE_SIMD)
|
||||
if (X86 OR X86_64)
|
||||
set(CMAKE_C_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# certain GCC versions don't provide the atomic builds, and hence
|
||||
# require is to provide them in SGAtomic.cxx
|
||||
@@ -320,13 +412,33 @@ if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
GCC_ATOMIC_BUILTINS_FOUND)
|
||||
endif(CMAKE_COMPILER_IS_GNUCXX)
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
if (CLANG)
|
||||
# Boost redeclares class members
|
||||
set(WARNING_FLAGS_CXX "-Wall -Wno-overloaded-virtual -Wno-redeclared-class-member")
|
||||
set(WARNING_FLAGS_C "-Wall")
|
||||
set(WARNING_FLAGS_CXX "-Wall -fPIC -Wno-overloaded-virtual -Wno-redeclared-class-member")
|
||||
set(WARNING_FLAGS_C "-Wall -fPIC")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++11")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++")
|
||||
# fix Boost compilation :(
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||
|
||||
if(ENABLE_SIMD)
|
||||
if (X86 OR X86_64)
|
||||
set(CMAKE_C_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (ENABLE_OPENMP)
|
||||
find_package(OpenMP)
|
||||
if(OPENMP_FOUND)
|
||||
message(STATUS "OpenMP: ENABLED")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
|
||||
else()
|
||||
message(STATUS "OpenMP: NOT FOUND")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "OpenMP: DISABLED")
|
||||
endif()
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
@@ -344,21 +456,35 @@ if(WIN32)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
# turn off various warnings
|
||||
# foreach(warning 4244 4251 4267 4275 4290 4786 4305 4996)
|
||||
# SET(WARNING_FLAGS "${WARNING_FLAGS} /wd${warning}")
|
||||
# endforeach(warning)
|
||||
set(MSVC_FLAGS "-DWIN32 -DNOMINMAX -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS -D__CRT_NONSTDC_NO_WARNINGS /MP")
|
||||
if(ENABLE_SIMD)
|
||||
if (X86)
|
||||
SET(CMAKE_C_FLAGS_RELEASE "/O2 /arch:SSE /arch:SSE2")
|
||||
SET(CMAKE_CXX_FLAGS_RELEASE "/O2 /arch:SSE /arch:SSE2")
|
||||
else()
|
||||
SET(CMAKE_C_FLAGS_RELEASE "/O2")
|
||||
SET(CMAKE_CXX_FLAGS_RELEASE "/O2")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(MSVC_FLAGS "-DWIN32 -DNOMINMAX -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS -D__CRT_NONSTDC_NO_WARNINGS /wd4996 /wd4250 -Dstrdup=_strdup")
|
||||
if (${MSVC_VERSION} GREATER 1599)
|
||||
if (NOT OSG_FSTREAM_EXPORT_FIXED)
|
||||
message(STATUS "For better linking performance, use OSG with patched fstream header")
|
||||
# needed to avoid link errors on multiply-defined standard C++
|
||||
# symbols. Suspect this may be an OSG-DB export bug
|
||||
set( MSVC_LD_FLAGS "/FORCE:MULTIPLE" )
|
||||
endif (${MSVC_VERSION} GREATER 1599)
|
||||
endif ()
|
||||
|
||||
if (${MSVC_VERSION} GREATER 1899)
|
||||
# needed for debug builds with VS2015
|
||||
set( MSVC_FLAGS "${MSVC_FLAGS} /bigobj" )
|
||||
endif()
|
||||
endif(MSVC)
|
||||
|
||||
# assumed on Windows
|
||||
set(HAVE_GETLOCALTIME 1)
|
||||
|
||||
set( WINSOCK_LIBRARY "ws2_32.lib" )
|
||||
set( SHLWAPI_LIBRARY "Shlwapi.lib" )
|
||||
set( RT_LIBRARY "winmm" )
|
||||
endif(WIN32)
|
||||
|
||||
@@ -366,19 +492,12 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS_C} ${MSVC_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS_CXX} ${MSVC_FLAGS} ${BOOST_CXX_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MSVC_LD_FLAGS}")
|
||||
|
||||
include(CheckCXXFeatures)
|
||||
|
||||
# use BEFORE to ensure local directories are used first,
|
||||
# ahead of system-installed libs
|
||||
include_directories(BEFORE ${PROJECT_SOURCE_DIR})
|
||||
include_directories(BEFORE ${PROJECT_SOURCE_DIR}/simgear/canvas/ShivaVG/include)
|
||||
include_directories(BEFORE ${PROJECT_BINARY_DIR}/simgear)
|
||||
|
||||
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}
|
||||
${Boost_INCLUDE_DIRS}
|
||||
${ZLIB_INCLUDE_DIR}
|
||||
${OPENAL_INCLUDE_DIR}
|
||||
${CURL_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
add_definitions(-DHAVE_CONFIG_H)
|
||||
|
||||
# configure a header file to pass some of the CMake settings
|
||||
@@ -403,10 +522,12 @@ set(TEST_LIBS_INTERNAL_CORE
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${ZLIB_LIBRARY}
|
||||
${WINSOCK_LIBRARY}
|
||||
${SHLWAPI_LIBRARY}
|
||||
${RT_LIBRARY}
|
||||
${DL_LIBRARY}
|
||||
${COCOA_LIBRARY}
|
||||
${CURL_LIBRARIES})
|
||||
${CURL_LIBRARIES}
|
||||
${GDAL_LIBRARY})
|
||||
set(TEST_LIBS SimGearCore ${TEST_LIBS_INTERNAL_CORE})
|
||||
|
||||
if(NOT SIMGEAR_HEADLESS)
|
||||
@@ -415,10 +536,15 @@ endif()
|
||||
|
||||
install (FILES ${PROJECT_BINARY_DIR}/simgear/simgear_config.h DESTINATION include/simgear/)
|
||||
|
||||
include_directories(3rdparty/utf8/source)
|
||||
if (ENABLE_DNS)
|
||||
message(STATUS "DNS resolver: ENABLED")
|
||||
include_directories(3rdparty/udns)
|
||||
if(ENABLE_DNS)
|
||||
if(SYSTEM_UDNS)
|
||||
message(STATUS "Requested to use system udns library, forcing SIMGEAR_SHARED to true")
|
||||
set(SIMGEAR_SHARED ON)
|
||||
find_package(Udns REQUIRED)
|
||||
else()
|
||||
message(STATUS "DNS resolver: ENABLED")
|
||||
include_directories(3rdparty/udns)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "DNS resolver: DISABLED")
|
||||
endif()
|
||||
@@ -449,7 +575,7 @@ configure_file(SimGearConfig.cmake.in
|
||||
@ONLY
|
||||
)
|
||||
|
||||
set(ConfigPackageLocation lib/cmake/SimGear)
|
||||
set(ConfigPackageLocation ${CMAKE_INSTALL_LIBDIR}/cmake/SimGear)
|
||||
install(EXPORT SimGearTargets
|
||||
DESTINATION ${ConfigPackageLocation}
|
||||
)
|
||||
|
||||
@@ -157,7 +157,7 @@ function(add_boost_test _name)
|
||||
endforeach()
|
||||
|
||||
if(NOT _boostTestTargetsNagged${_name} STREQUAL "${includeType}")
|
||||
if("includeType" STREQUAL "CONFIGURED")
|
||||
if("${includeType}" STREQUAL "CONFIGURED")
|
||||
message(STATUS
|
||||
"Test '${_name}' uses the CMake-configurable form of the boost test framework - congrats! (Including File: ${includeFileLoc})")
|
||||
elseif("${includeType}" STREQUAL "INCLUDED")
|
||||
|
||||
30
CMakeModules/CheckCXXFeatures.cmake
Normal file
30
CMakeModules/CheckCXXFeatures.cmake
Normal file
@@ -0,0 +1,30 @@
|
||||
check_cxx_source_compiles("
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
std::make_index_sequence<0> t;
|
||||
int main() {}" HAVE_STD_INDEX_SEQUENCE
|
||||
)
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <type_traits>
|
||||
std::remove_cv_t<const int> t;
|
||||
int main() {}" HAVE_STD_REMOVE_CV_T
|
||||
)
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <type_traits>
|
||||
std::remove_cvref_t<const int&> t;
|
||||
int main() {}" HAVE_STD_REMOVE_CVREF_T
|
||||
)
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <type_traits>
|
||||
std::enable_if_t<true, int> t;
|
||||
int main() {}" HAVE_STD_ENABLE_IF_T
|
||||
)
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <type_traits>
|
||||
std::bool_constant<true> t;
|
||||
int main() {}" HAVE_STD_BOOL_CONSTANT
|
||||
)
|
||||
37
CMakeModules/DetectArch.cmake
Normal file
37
CMakeModules/DetectArch.cmake
Normal file
@@ -0,0 +1,37 @@
|
||||
IF(CMAKE_SYSTEM_PROCESSOR MATCHES amd64.*|x86_64.* OR CMAKE_GENERATOR MATCHES "Visual Studio.*Win64")
|
||||
IF(CMAKE_C_FLAGS MATCHES -m32 OR CMAKE_CXX_FLAGS MATCHES -m32)
|
||||
SET(X86 1)
|
||||
ELSE(CMAKE_C_FLAGS MATCHES -m32 OR CMAKE_CXX_FLAGS MATCHES -m32)
|
||||
SET(X86_64 1)
|
||||
ENDIF(CMAKE_C_FLAGS MATCHES -m32 OR CMAKE_CXX_FLAGS MATCHES -m32)
|
||||
ELSEIF(CMAKE_SYSTEM_PROCESSOR MATCHES i686.*|i386.*|x86.* OR WIN32)
|
||||
IF(CMAKE_C_FLAGS MATCHES -m64 OR CMAKE_CXX_FLAGS MATCHES -m64)
|
||||
SET(X86_64 1)
|
||||
ELSE(CMAKE_C_FLAGS MATCHES -m64 OR CMAKE_CXX_FLAGS MATCHES -m64)
|
||||
SET(X86 1)
|
||||
ENDIF(CMAKE_C_FLAGS MATCHES -m64 OR CMAKE_CXX_FLAGS MATCHES -m64)
|
||||
ELSEIF(CMAKE_SYSTEM_PROCESSOR MATCHES arm.* AND CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
SET(ARM 1)
|
||||
ELSEIF(CMAKE_SYSTEM_PROCESSOR MATCHES mips)
|
||||
SET(MIPS 1)
|
||||
ENDIF()
|
||||
|
||||
IF ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang")
|
||||
# using Clang
|
||||
SET(CLANG 1)
|
||||
ELSEIF ("${CMAKE_C_COMPILER_ID}" STREQUAL "TinyCC")
|
||||
# using TinyCC
|
||||
SET(TINYCC 1)
|
||||
ELSEIF ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
|
||||
# using GCC
|
||||
SET(GCC 1)
|
||||
ELSEIF ("${CMAKE_C_COMPILER_ID}" STREQUAL "Intel")
|
||||
# using Intel C++
|
||||
SET(INTELCC 1)
|
||||
ELSEIF ("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
|
||||
# using Visual Studio C++
|
||||
SET(MSVC 1)
|
||||
ELSEIF ("${CMAKE_C_COMPILER_ID}" STREQUAL "MIPSpro")
|
||||
# using SGI MIPSpro
|
||||
SET(MIPSPRO 1)
|
||||
ENDIF()
|
||||
61
CMakeModules/FindAAX.cmake
Normal file
61
CMakeModules/FindAAX.cmake
Normal file
@@ -0,0 +1,61 @@
|
||||
# Locate AAX
|
||||
# This module defines
|
||||
# AAX_LIBRARIES
|
||||
# AAX_FOUND, if false, do not try to link to AAX
|
||||
# AAX_INCLUDE_DIR, where to find the headers
|
||||
#
|
||||
# $AAXDIR is an environment variable that would
|
||||
# correspond to the ./configure --prefix=$AAXDIR
|
||||
# used in building AAX.
|
||||
#
|
||||
# Created by Erik Hofman.
|
||||
|
||||
FIND_PATH(AAX_INCLUDE_DIR aax/aax.h
|
||||
HINTS
|
||||
$ENV{AAXDIR}
|
||||
$ENV{ProgramFiles}/aax
|
||||
$ENV{ProgramFiles}/AeonWave
|
||||
$ENV{ProgramFiles}/Adalin/AeonWave
|
||||
${CMAKE_SOURCE_DIR}/aax
|
||||
PATH_SUFFIXES include
|
||||
PATHS
|
||||
~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/usr/local
|
||||
/usr
|
||||
/opt
|
||||
)
|
||||
|
||||
FIND_LIBRARY(AAX_LIBRARY
|
||||
NAMES AAX aax AAX32
|
||||
HINTS
|
||||
$ENV{AAXDIR}
|
||||
$ENV{ProgramFiles}/AAX
|
||||
$ENV{ProgramFiles}/AeonWave
|
||||
$ENV{ProgramFiles}/Adalin/AeonWave
|
||||
${CMAKE_BUILD_DIR}/aax
|
||||
PATH_SUFFIXES bin lib lib/${CMAKE_LIBRARY_ARCHITECTURE} lib64 libs64 libs libs/Win32 libs/Win64
|
||||
PATHS
|
||||
~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/usr/local
|
||||
/usr
|
||||
/opt
|
||||
)
|
||||
|
||||
IF(AAX_LIBRARY AND AAX_INCLUDE_DIR)
|
||||
SET(AAX_FOUND "YES")
|
||||
ELSE(AAX_LIBRARY AND AAX_INCLUDE_DIR)
|
||||
IF(NOT AAX_INCLUDE_DIR)
|
||||
MESSAGE(FATAL_ERROR "Unable to find the AAX library development files.")
|
||||
SET(AAX_FOUND "NO")
|
||||
ENDIF(NOT AAX_INCLUDE_DIR)
|
||||
IF(NOT AAX_LIBRARY)
|
||||
IF(SINGLE_PACKAGE)
|
||||
SET(AAX_LIBRARY "${aax_BUILD_DIR}/aax/AAX32.dll")
|
||||
SET(AAX_FOUND "YES")
|
||||
ELSE(SINGLE_PACKAGE)
|
||||
ENDIF(SINGLE_PACKAGE)
|
||||
ENDIF(NOT AAX_LIBRARY)
|
||||
ENDIF(AAX_LIBRARY AND AAX_INCLUDE_DIR)
|
||||
|
||||
42
CMakeModules/FindUdns.cmake
Normal file
42
CMakeModules/FindUdns.cmake
Normal file
@@ -0,0 +1,42 @@
|
||||
# - Try to find UDNS library
|
||||
# Once done this will define
|
||||
#
|
||||
# UDNS_FOUND - system has UDNS
|
||||
# UDNS_INCLUDE_DIRS - the UDNS include directory
|
||||
# UDNS_LIBRARIES - Link these to use UDNS
|
||||
# UDNS_DEFINITIONS - Compiler switches required for using UDNS
|
||||
#
|
||||
# Copyright (c) 2016 Maciej Mrozowski <reavertm@gmail.com>
|
||||
#
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
|
||||
if (UDNS_LIBRARIES AND UDNS_INCLUDE_DIRS)
|
||||
# in cache already
|
||||
set(UDNS_FOUND TRUE)
|
||||
else ()
|
||||
set(UDNS_DEFINITIONS "")
|
||||
|
||||
find_path(UDNS_INCLUDE_DIRS NAMES udns.h)
|
||||
find_library(UDNS_LIBRARIES NAMES udns)
|
||||
|
||||
if (UDNS_INCLUDE_DIRS AND UDNS_LIBRARIES)
|
||||
set(UDNS_FOUND TRUE)
|
||||
endif ()
|
||||
|
||||
if (UDNS_FOUND)
|
||||
if (NOT Udns_FIND_QUIETLY)
|
||||
message(STATUS "Found UDNS: ${UDNS_LIBRARIES}")
|
||||
endif ()
|
||||
else ()
|
||||
if (Udns_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "Could not find UDNS")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# show the UDNS_INCLUDE_DIRS and UDNS_LIBRARIES variables only in the advanced view
|
||||
mark_as_advanced(UDNS_INCLUDE_DIRS UDNS_LIBRARIES)
|
||||
|
||||
endif ()
|
||||
@@ -2,6 +2,10 @@
|
||||
documentation. It has a .cxx extension so that emacs will happily
|
||||
autoindent correctly. */
|
||||
|
||||
/**
|
||||
* \namespace simgear
|
||||
* \brief \ref index "SimGear" main namespace.
|
||||
*/
|
||||
/** \mainpage SimGear
|
||||
* Simulation, Visualization, and Game development libraries.
|
||||
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
%define ver @VERSION@
|
||||
%define rel 1
|
||||
%define prefix /usr
|
||||
|
||||
Summary: Simulator Construction Gear.
|
||||
Name: @PACKAGE@
|
||||
Version: %ver
|
||||
Release: %rel
|
||||
Copyright: LGPL
|
||||
Group: Libraries/Graphics
|
||||
Source: %{name}-%{version}.tar.gz
|
||||
#URL:
|
||||
BuildRoot: /tmp/%{name}-%{version}-%{rel}-root
|
||||
Packager: Fill In As You Wish
|
||||
Docdir: %{prefix}/doc
|
||||
|
||||
%description
|
||||
This package contains a tools and libraries useful for constructing
|
||||
simulation and visualization applications such as FlightGear or TerraGear.
|
||||
|
||||
Authors:
|
||||
N/A
|
||||
|
||||
%prep
|
||||
%setup -n %{name}-%{version}
|
||||
|
||||
|
||||
%build
|
||||
# Needed for snapshot releases.
|
||||
if [ ! -f configure ]; then
|
||||
CFLAGS="$RPM_OPT_FLAGS" ./autogen.sh --prefix=%prefix
|
||||
else
|
||||
CFLAGS="$RPM_OPT_FLAGS" ./configure --prefix=%prefix
|
||||
fi
|
||||
|
||||
if [ "$SMP" != "" ]; then
|
||||
JSMP = '"MAKE=make -k -j $SMP"'
|
||||
fi
|
||||
|
||||
make ${JSMP};
|
||||
|
||||
|
||||
%install
|
||||
[ -d ${RPM_BUILD_ROOT} ] && rm -rf ${RPM_BUILD_ROOT}
|
||||
|
||||
make prefix=${RPM_BUILD_ROOT}%{prefix} install
|
||||
|
||||
#
|
||||
# Generating file lists and store them in file-lists
|
||||
# Starting with the directory listings
|
||||
#
|
||||
find ${RPM_BUILD_ROOT}%{prefix}/{bin,include,lib} -type d | sed "s#^${RPM_BUILD_ROOT}#\%attr (-\,root\,root) \%dir #" > file-lists
|
||||
%{?ETCDR:find ${RPM_BUILD_ROOT}%{!?SYSCF:%{prefix}}/etc -type d | sed "s#^${RPM_BUILD_ROOT}#\%attr (-\,root\,root) \%dir #" >> file-lists}
|
||||
|
||||
#
|
||||
# Then, the file listings
|
||||
#
|
||||
echo "%defattr (-, root, root)" >> file-lists
|
||||
%{?ETCDR:find ${RPM_BUILD_ROOT}%{!?SYSCF:%{prefix}}/etc/%{name}.conf -type f | sed -e "s#^${RPM_BUILD_ROOT}#%config #g" >> file-lists}
|
||||
find ${RPM_BUILD_ROOT}%{prefix} -type f | sed -e "s#^${RPM_BUILD_ROOT}##g" >> file-lists
|
||||
|
||||
|
||||
%clean
|
||||
(cd ..; rm -rf %{name}-%{version} ${RPM_BUILD_ROOT})
|
||||
|
||||
|
||||
%files -f file-lists
|
||||
%defattr (-, root, root)
|
||||
%doc AUTHORS
|
||||
%doc COPYING
|
||||
%doc ChangeLog
|
||||
%doc INSTALL
|
||||
%doc NEWS
|
||||
%doc README
|
||||
%doc %{name}.spec.in
|
||||
|
||||
@@ -7,10 +7,20 @@ find_dependency(Threads)
|
||||
|
||||
set(SIMGEAR_HEADLESS @SIMGEAR_HEADLESS@)
|
||||
set(SIMGEAR_SOUND @ENABLE_SOUND@)
|
||||
set(USE_AEONWAVE @USE_AEONWAVE@)
|
||||
|
||||
# OpenAL isn't a public dependency, so maybe not needed
|
||||
#if (SIMGEAR_SOUND)
|
||||
# find_dependency(OpenAL)
|
||||
#endif()
|
||||
|
||||
# SSE/SSE2 support
|
||||
|
||||
set(ENABLE_SIMD @ENABLE_SIMD@)
|
||||
|
||||
# Alternative terrain engine based on pagedLOD
|
||||
|
||||
set(ENABLE_GDAL @ENABLE_GDAL@)
|
||||
set(ENABLE_OPENMP @ENABLE_OPENMP@)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/SimGearTargets.cmake")
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
Building a SimGear RPM package for Red Hat
|
||||
|
||||
Please see the "package/openSUSE" directory for an
|
||||
example how to build a SimGear RPM package with
|
||||
shared SimGear libraries.
|
||||
|
||||
You may need to adapt the names (exact spelling) of some
|
||||
of the package dependencies in the openSUSE RPM spec,
|
||||
since these may slightly differ for Red Hat.
|
||||
|
||||
(If you have a working and tested Red Hat RPM spec,
|
||||
you're welcome to contribute it to this project.)
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
Building a SimGear RPM package for openSUSE
|
||||
|
||||
(Last tested with openSUSE 11.4+12.1)
|
||||
|
||||
This directory contains the files which, along with
|
||||
the source code tar files, can be used to build
|
||||
an RPM package targeted at an openSUSE Linux system.
|
||||
|
||||
To build SimGear from source do the following:
|
||||
|
||||
1. obtain simgear-2.8.0.tar.bz2 (adapt version if
|
||||
necessary) and copy it into ~/rpmbuild/SOURCES
|
||||
|
||||
2. look in the BuildRequires section of SimGear.spec
|
||||
and check that all the packages referred to are
|
||||
installed (note, some of these packages may be part
|
||||
of openSUSE's "games" repository).
|
||||
|
||||
3. run 'rpmbuild -ba simgear.spec' and find the RPM
|
||||
build result in ~/rpmbuild/RPMS
|
||||
|
||||
That's all!
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
Summary: Simulator Construction Gear
|
||||
Name: SimGear
|
||||
Version: 2.8.0
|
||||
Release: 1
|
||||
License: LGPL
|
||||
URL: http://www.flightgear.org
|
||||
Group: Amusements/Games/3D/Simulation
|
||||
Source: http://mirrors.ibiblio.org/pub/mirrors/flightgear/ftp/Source/simgear-%{version}.tar.bz2
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-build
|
||||
|
||||
BuildRequires: gcc, gcc-c++, cmake
|
||||
BuildRequires: libopenal1-soft, openal-soft
|
||||
BuildRequires: libOpenSceneGraph-devel >= 3.0
|
||||
BuildRequires: zlib, zlib-devel
|
||||
BuildRequires: libjpeg62, libjpeg62-devel
|
||||
BuildRequires: boost-devel >= 1.37
|
||||
BuildRequires: subversion-devel, libapr1-devel
|
||||
Requires: OpenSceneGraph-plugins >= 3.0
|
||||
|
||||
%description
|
||||
This package contains a tools and libraries useful for constructing
|
||||
simulation and visualization applications such as FlightGear or TerraGear.
|
||||
|
||||
%package devel
|
||||
Group: Development/Libraries/Other
|
||||
Summary: Development header files for SimGear
|
||||
Requires: SimGear = %{version}
|
||||
|
||||
%description devel
|
||||
Development headers and libraries for building applications against SimGear.
|
||||
|
||||
%prep
|
||||
%setup -T -q -n simgear-%{version} -b 0
|
||||
|
||||
%build
|
||||
export CFLAGS="$RPM_OPT_FLAGS"
|
||||
export CXXFLAGS="$RPM_OPT_FLAGS"
|
||||
# build SHARED simgear libraries
|
||||
cmake -DCMAKE_INSTALL_PREFIX=%{_prefix} -DSIMGEAR_SHARED:BOOL=ON -DENABLE_TESTS:BOOL=OFF -DJPEG_FACTORY:BOOL=ON
|
||||
make %{?_smp_mflags}
|
||||
|
||||
%install
|
||||
make DESTDIR=%{buildroot} install
|
||||
|
||||
%post -p /sbin/ldconfig
|
||||
|
||||
%postun -p /sbin/ldconfig
|
||||
|
||||
%files
|
||||
%defattr (-, root, root, -)
|
||||
%doc AUTHORS COPYING ChangeLog NEWS README
|
||||
%{_libdir}/libSimGear*.so.*
|
||||
|
||||
%files devel
|
||||
%defattr(-,root,root,-)
|
||||
%dir %{_includedir}/simgear
|
||||
%{_includedir}/simgear/*
|
||||
%{_libdir}/libSimGear*.so
|
||||
|
||||
%changelog
|
||||
* Mon Jul 02 2012 thorstenb@flightgear.org
|
||||
- Initial version
|
||||
|
||||
@@ -5,6 +5,7 @@ foreach( mylibfolder
|
||||
bucket
|
||||
bvh
|
||||
debug
|
||||
embedded_resources
|
||||
ephemeris
|
||||
io
|
||||
magvar
|
||||
@@ -14,6 +15,7 @@ foreach( mylibfolder
|
||||
nasal/cppbind
|
||||
props
|
||||
serial
|
||||
std
|
||||
structure
|
||||
threads
|
||||
timing
|
||||
@@ -54,24 +56,14 @@ if(SIMGEAR_SHARED)
|
||||
set_property(TARGET SimGearCore PROPERTY LINKER_LANGUAGE CXX)
|
||||
set_property(TARGET SimGearCore PROPERTY VERSION ${SIMGEAR_VERSION})
|
||||
set_property(TARGET SimGearCore PROPERTY SOVERSION ${SIMGEAR_SOVERSION})
|
||||
install(TARGETS SimGearCore
|
||||
EXPORT SimGearTargets
|
||||
LIBRARY DESTINATION
|
||||
${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
|
||||
if(NOT SIMGEAR_HEADLESS)
|
||||
add_library(SimGearScene SHARED ${sceneSources})
|
||||
set_property(TARGET SimGearScene PROPERTY LINKER_LANGUAGE CXX)
|
||||
set_property(TARGET SimGearScene PROPERTY VERSION ${SIMGEAR_VERSION})
|
||||
set_property(TARGET SimGearScene PROPERTY SOVERSION ${SIMGEAR_SOVERSION})
|
||||
|
||||
# EXPORT SimGearSceneConfig
|
||||
install(TARGETS SimGearScene
|
||||
EXPORT SimGearTargets
|
||||
LIBRARY
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR} )
|
||||
endif()
|
||||
|
||||
else()
|
||||
message(STATUS "Library building mode: STATIC LIBRARIES")
|
||||
|
||||
@@ -94,9 +86,6 @@ else()
|
||||
endforeach()
|
||||
|
||||
add_library(SimGearCore STATIC ${coreSources} ${localExpatSources})
|
||||
install(TARGETS SimGearCore
|
||||
EXPORT SimGearTargets
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
if(NOT SIMGEAR_HEADLESS)
|
||||
get_property(FG_GROUPS_SCENE_SOURCES_C GLOBAL PROPERTY FG_GROUPS_SCENE_SOURCES_C)
|
||||
@@ -118,22 +107,65 @@ else()
|
||||
endforeach()
|
||||
|
||||
add_library(SimGearScene STATIC ${sceneSources})
|
||||
install(TARGETS SimGearScene
|
||||
EXPORT SimGearTargets
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
endif(NOT SIMGEAR_HEADLESS)
|
||||
endif(SIMGEAR_SHARED)
|
||||
|
||||
target_include_directories(SimGearCore BEFORE PUBLIC
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
|
||||
$<INSTALL_INTERFACE:include>)
|
||||
|
||||
target_include_directories(SimGearCore PUBLIC
|
||||
${Boost_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIR})
|
||||
target_include_directories(SimGearCore PRIVATE
|
||||
${EXPAT_INCLUDE_DIRS} ${CURL_INCLUDE_DIRS})
|
||||
|
||||
install(TARGETS SimGearCore
|
||||
EXPORT SimGearTargets
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
if (NOT SIMGEAR_HEADLESS)
|
||||
install(TARGETS SimGearScene
|
||||
EXPORT SimGearTargets
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
target_include_directories(SimGearScene BEFORE PUBLIC
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
|
||||
$<INSTALL_INTERFACE:include>)
|
||||
|
||||
target_include_directories(SimGearScene PUBLIC ${OPENSCENEGRAPH_INCLUDE_DIRS})
|
||||
|
||||
if (USE_AEONWAVE)
|
||||
target_include_directories(SimGearScene PRIVATE ${AAX_INCLUDE_DIR} )
|
||||
else()
|
||||
target_include_directories(SimGearScene PRIVATE ${OPENAL_INCLUDE_DIR} )
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
target_link_libraries(SimGearCore
|
||||
${ZLIB_LIBRARY}
|
||||
${RT_LIBRARY}
|
||||
${DL_LIBRARY}
|
||||
${EXPAT_LIBRARIES}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${COCOA_LIBRARY}
|
||||
${CURL_LIBRARIES})
|
||||
${CURL_LIBRARIES}
|
||||
${WINSOCK_LIBRARY})
|
||||
|
||||
if(SYSTEM_EXPAT)
|
||||
target_link_libraries(SimGearCore
|
||||
${EXPAT_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(ENABLE_DNS AND SYSTEM_UDNS)
|
||||
target_link_libraries(SimGearCore
|
||||
${UDNS_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(NOT SIMGEAR_HEADLESS)
|
||||
target_include_directories(SimGearScene PRIVATE ${PROJECT_SOURCE_DIR}/simgear/canvas/ShivaVG/include)
|
||||
|
||||
target_link_libraries(SimGearScene
|
||||
SimGearCore
|
||||
${ZLIB_LIBRARY}
|
||||
@@ -141,6 +173,14 @@ if(NOT SIMGEAR_HEADLESS)
|
||||
${OPENAL_LIBRARY}
|
||||
${OPENGL_LIBRARY}
|
||||
${JPEG_LIBRARY})
|
||||
|
||||
if(ENABLE_GDAL)
|
||||
target_link_libraries(SimGearScene
|
||||
${GDAL_LIBRARIES})
|
||||
endif()
|
||||
|
||||
# only actually needed by canvas/KeyboardEvent.cxx
|
||||
target_include_directories(SimGearScene PRIVATE ${PROJECT_SOURCE_DIR}/3rdparty/utf8/source)
|
||||
endif()
|
||||
|
||||
if(ENABLE_RTI)
|
||||
|
||||
@@ -227,9 +227,7 @@ std::string SGBucket::gen_base_path() const {
|
||||
hem, top_lon, pole, top_lat,
|
||||
hem, main_lon, pole, main_lat);
|
||||
|
||||
SGPath path( raw_path );
|
||||
|
||||
return path.str();
|
||||
return raw_path;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -35,124 +35,152 @@ using std::endl;
|
||||
|
||||
void testBucketSpans()
|
||||
{
|
||||
COMPARE(sg_bucket_span(0.0), 0.125);
|
||||
COMPARE(sg_bucket_span(-20), 0.125);
|
||||
COMPARE(sg_bucket_span(-40), 0.25);
|
||||
COMPARE(sg_bucket_span(89.9), 12.0);
|
||||
COMPARE(sg_bucket_span(88.1), 4.0);
|
||||
COMPARE(sg_bucket_span(-89.9), 12.0);
|
||||
SG_CHECK_EQUAL(sg_bucket_span(0.0), 0.125);
|
||||
SG_CHECK_EQUAL(sg_bucket_span(-20), 0.125);
|
||||
SG_CHECK_EQUAL(sg_bucket_span(-40), 0.25);
|
||||
SG_CHECK_EQUAL(sg_bucket_span(89.9), 12.0);
|
||||
SG_CHECK_EQUAL(sg_bucket_span(88.1), 4.0);
|
||||
SG_CHECK_EQUAL(sg_bucket_span(-89.9), 12.0);
|
||||
}
|
||||
|
||||
void testBasic()
|
||||
{
|
||||
SGBucket b1(5.1, 55.05);
|
||||
COMPARE(b1.get_chunk_lon(), 5);
|
||||
COMPARE(b1.get_chunk_lat(), 55);
|
||||
COMPARE(b1.get_x(), 0);
|
||||
COMPARE(b1.get_y(), 0);
|
||||
COMPARE(b1.gen_index(), 3040320);
|
||||
COMPARE(b1.gen_base_path(), "e000n50/e005n55");
|
||||
VERIFY(b1.isValid());
|
||||
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lon(), 5);
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lat(), 55);
|
||||
SG_CHECK_EQUAL(b1.get_x(), 0);
|
||||
SG_CHECK_EQUAL(b1.get_y(), 0);
|
||||
SG_CHECK_EQUAL(b1.gen_index(), 3040320);
|
||||
SG_CHECK_EQUAL(b1.gen_base_path(), "e000n50/e005n55");
|
||||
SG_VERIFY(b1.isValid());
|
||||
|
||||
SGBucket b2(-10.1, -43.8);
|
||||
COMPARE(b2.get_chunk_lon(), -11);
|
||||
COMPARE(b2.get_chunk_lat(), -44);
|
||||
COMPARE(b2.get_x(), 3);
|
||||
COMPARE(b2.get_y(), 1); // latitude chunks numbered bottom to top, it seems
|
||||
COMPARE(b2.gen_base_path(), "w020s50/w011s44");
|
||||
VERIFY(b2.isValid());
|
||||
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lon(), -11);
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lat(), -44);
|
||||
SG_CHECK_EQUAL(b2.get_x(), 3);
|
||||
// Latitude chunks numbered bottom to top, it seems
|
||||
SG_CHECK_EQUAL(b2.get_y(), 1);
|
||||
SG_CHECK_EQUAL(b2.gen_base_path(), "w020s50/w011s44");
|
||||
SG_VERIFY(b2.isValid());
|
||||
|
||||
SGBucket b3(123.48, 9.01);
|
||||
COMPARE(b3.get_chunk_lon(), 123);
|
||||
COMPARE(b3.get_chunk_lat(), 9);
|
||||
COMPARE(b3.get_x(), 3);
|
||||
COMPARE(b3.get_y(), 0);
|
||||
COMPARE(b3.gen_base_path(), "e120n00/e123n09");
|
||||
VERIFY(b3.isValid());
|
||||
|
||||
SG_CHECK_EQUAL(b3.get_chunk_lon(), 123);
|
||||
SG_CHECK_EQUAL(b3.get_chunk_lat(), 9);
|
||||
SG_CHECK_EQUAL(b3.get_x(), 3);
|
||||
SG_CHECK_EQUAL(b3.get_y(), 0);
|
||||
SG_CHECK_EQUAL(b3.gen_base_path(), "e120n00/e123n09");
|
||||
SG_VERIFY(b3.isValid());
|
||||
|
||||
SGBucket defBuck;
|
||||
VERIFY(!defBuck.isValid());
|
||||
|
||||
SG_VERIFY(!defBuck.isValid());
|
||||
|
||||
b3.make_bad();
|
||||
VERIFY(!b3.isValid());
|
||||
SG_VERIFY(!b3.isValid());
|
||||
|
||||
SGBucket atAntiMeridian(180.0, 12.3);
|
||||
VERIFY(atAntiMeridian.isValid());
|
||||
COMPARE(atAntiMeridian.get_chunk_lon(), -180);
|
||||
COMPARE(atAntiMeridian.get_x(), 0);
|
||||
|
||||
SG_VERIFY(atAntiMeridian.isValid());
|
||||
SG_CHECK_EQUAL(atAntiMeridian.get_chunk_lon(), -180);
|
||||
SG_CHECK_EQUAL(atAntiMeridian.get_x(), 0);
|
||||
|
||||
SGBucket atAntiMeridian2(-180.0, -78.1);
|
||||
VERIFY(atAntiMeridian2.isValid());
|
||||
COMPARE(atAntiMeridian2.get_chunk_lon(), -180);
|
||||
COMPARE(atAntiMeridian2.get_x(), 0);
|
||||
|
||||
// check comparisom operator overload
|
||||
SG_VERIFY(atAntiMeridian2.isValid());
|
||||
SG_CHECK_EQUAL(atAntiMeridian2.get_chunk_lon(), -180);
|
||||
SG_CHECK_EQUAL(atAntiMeridian2.get_x(), 0);
|
||||
|
||||
// check comparison operator overload
|
||||
SGBucket b4(5.11, 55.1);
|
||||
VERIFY(b1 == b4); // should be equal
|
||||
VERIFY(b1 == b1);
|
||||
VERIFY(b1 != defBuck);
|
||||
VERIFY(b1 != b2);
|
||||
|
||||
// check wrapping/clipping of inputs
|
||||
SG_VERIFY(b1 == b4); // should be equal
|
||||
SG_VERIFY(b1 == b1);
|
||||
SG_VERIFY(b1 != defBuck);
|
||||
SG_VERIFY(b1 != b2);
|
||||
|
||||
// check wrapping/clipping of inputs
|
||||
SGBucket wrapMeridian(-200.0, 45.0);
|
||||
COMPARE(wrapMeridian.get_chunk_lon(), 160);
|
||||
|
||||
SG_CHECK_EQUAL(wrapMeridian.get_chunk_lon(), 160);
|
||||
|
||||
SGBucket clipPole(48.9, 91);
|
||||
COMPARE(clipPole.get_chunk_lat(), 89);
|
||||
SG_CHECK_EQUAL(clipPole.get_chunk_lat(), 89);
|
||||
|
||||
// test override of a bucket's geod
|
||||
auto geod = SGGeod::fromDegFt(-86.678, 36.1248, 599.0);
|
||||
#ifndef NO_DEPRECATED_API
|
||||
SGBucket bna_airport;
|
||||
bna_airport.set_bucket(geod);
|
||||
#else
|
||||
SGBucket bna_airport(geod);
|
||||
#endif
|
||||
SG_VERIFY(bna_airport.isValid());
|
||||
SG_CHECK_EQUAL(bna_airport.get_chunk_lon(), -87); // left origin of the 1-degree chunk
|
||||
SG_CHECK_EQUAL(bna_airport.get_chunk_lat(), 36); // bottom origin of the 1-degree chunk
|
||||
SG_CHECK_EQUAL(bna_airport.get_x(), 1); // buckets are 0.25 deg wide at the W87 parallel
|
||||
// we're 0.322 deg from the origin (second bucket)
|
||||
SG_CHECK_EQUAL(bna_airport.get_y(), 0); // buckets are always 0.125 deg tall
|
||||
// we're 0.1248 deg from the origin (first bucket)
|
||||
SG_CHECK_EQUAL(bna_airport.gen_base_path(), "w090n30/w087n36");
|
||||
SG_CHECK_EQUAL_EP2(bna_airport.get_width_m(), 22479.1, 0.1);
|
||||
SG_CHECK_EQUAL_EP2(bna_airport.get_height_m(), 13914.9, 0.1);
|
||||
SG_CHECK_EQUAL(bna_airport.gen_index_str(), "1531777"); // 0x175F81 = b01011101|01111110|000|001
|
||||
// = 93-180 | 126-90 | 0 | 1
|
||||
// = -87 | 36 | 0 | 1
|
||||
|
||||
// test stream output
|
||||
cout << "[TEST] BNA Airport: " << bna_airport << endl;
|
||||
auto center = bna_airport.get_center();
|
||||
cout << "[TEST] BNA lon: " << center.getLongitudeDeg() << endl;
|
||||
cout << "[TEST] BNA lat: " << center.getLatitudeDeg() << endl;
|
||||
}
|
||||
|
||||
void testPolar()
|
||||
{
|
||||
SGBucket b1(0.0, 89.92);
|
||||
SGBucket b2(10.0, 89.96);
|
||||
COMPARE(b1.get_chunk_lat(), 89);
|
||||
COMPARE(b1.get_chunk_lon(), 0);
|
||||
COMPARE(b1.get_x(), 0);
|
||||
COMPARE(b1.get_y(), 7);
|
||||
|
||||
COMPARE_EP(b1.get_highest_lat(), 90.0);
|
||||
COMPARE_EP(b1.get_width_m(), 10.0);
|
||||
|
||||
COMPARE(b2.get_chunk_lat(), 89);
|
||||
COMPARE(b2.get_chunk_lon(), 0);
|
||||
COMPARE(b2.get_x(), 0);
|
||||
COMPARE(b2.get_y(), 7);
|
||||
|
||||
COMPARE(b1.gen_index(), b2.gen_index());
|
||||
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lat(), 89);
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lon(), 0);
|
||||
SG_CHECK_EQUAL(b1.get_x(), 0);
|
||||
SG_CHECK_EQUAL(b1.get_y(), 7);
|
||||
|
||||
SG_CHECK_EQUAL_EP(b1.get_highest_lat(), 90.0);
|
||||
SG_CHECK_EQUAL_EP(b1.get_width_m(), 10.0);
|
||||
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lat(), 89);
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lon(), 0);
|
||||
SG_CHECK_EQUAL(b2.get_x(), 0);
|
||||
SG_CHECK_EQUAL(b2.get_y(), 7);
|
||||
|
||||
SG_CHECK_EQUAL(b1.gen_index(), b2.gen_index());
|
||||
|
||||
SGGeod actualNorthPole1 = b1.get_corner(2);
|
||||
SGGeod actualNorthPole2 = b1.get_corner(3);
|
||||
COMPARE_EP(actualNorthPole1.getLatitudeDeg(), 90.0);
|
||||
COMPARE_EP(actualNorthPole1.getLongitudeDeg(), 12.0);
|
||||
COMPARE_EP(actualNorthPole2.getLatitudeDeg(), 90.0);
|
||||
COMPARE_EP(actualNorthPole2.getLongitudeDeg(), 0.0);
|
||||
SG_CHECK_EQUAL_EP(actualNorthPole1.getLatitudeDeg(), 90.0);
|
||||
SG_CHECK_EQUAL_EP(actualNorthPole1.getLongitudeDeg(), 12.0);
|
||||
SG_CHECK_EQUAL_EP(actualNorthPole2.getLatitudeDeg(), 90.0);
|
||||
SG_CHECK_EQUAL_EP(actualNorthPole2.getLongitudeDeg(), 0.0);
|
||||
|
||||
SGBucket b3(-2, 89.88);
|
||||
SGBucket b4(-7, 89.88);
|
||||
COMPARE(b3.gen_index(), b4.gen_index());
|
||||
|
||||
SG_CHECK_EQUAL(b3.gen_index(), b4.gen_index());
|
||||
|
||||
// south pole
|
||||
SGBucket b5(-170, -89.88);
|
||||
SGBucket b6(-179, -89.88);
|
||||
|
||||
COMPARE(b5.get_chunk_lat(), -90);
|
||||
COMPARE(b5.get_chunk_lon(), -180);
|
||||
COMPARE(b5.get_x(), 0);
|
||||
COMPARE(b5.get_y(), 0);
|
||||
COMPARE(b5.gen_index(), b6.gen_index());
|
||||
COMPARE_EP(b5.get_highest_lat(), -90.0);
|
||||
COMPARE_EP(b5.get_width_m(), 10.0);
|
||||
|
||||
|
||||
SG_CHECK_EQUAL(b5.get_chunk_lat(), -90);
|
||||
SG_CHECK_EQUAL(b5.get_chunk_lon(), -180);
|
||||
SG_CHECK_EQUAL(b5.get_x(), 0);
|
||||
SG_CHECK_EQUAL(b5.get_y(), 0);
|
||||
SG_CHECK_EQUAL(b5.gen_index(), b6.gen_index());
|
||||
SG_CHECK_EQUAL_EP(b5.get_highest_lat(), -90.0);
|
||||
SG_CHECK_EQUAL_EP(b5.get_width_m(), 10.0);
|
||||
|
||||
SGGeod actualSouthPole1 = b5.get_corner(0);
|
||||
SGGeod actualSouthPole2 = b5.get_corner(1);
|
||||
COMPARE_EP(actualSouthPole1.getLatitudeDeg(), -90.0);
|
||||
COMPARE_EP(actualSouthPole1.getLongitudeDeg(), -180);
|
||||
COMPARE_EP(actualSouthPole2.getLatitudeDeg(), -90.0);
|
||||
COMPARE_EP(actualSouthPole2.getLongitudeDeg(), -168);
|
||||
|
||||
SGBucket b7(200, 89.88);
|
||||
COMPARE(b7.get_chunk_lon(), -168);
|
||||
SG_CHECK_EQUAL_EP(actualSouthPole1.getLatitudeDeg(), -90.0);
|
||||
SG_CHECK_EQUAL_EP(actualSouthPole1.getLongitudeDeg(), -180);
|
||||
SG_CHECK_EQUAL_EP(actualSouthPole2.getLatitudeDeg(), -90.0);
|
||||
SG_CHECK_EQUAL_EP(actualSouthPole2.getLongitudeDeg(), -168);
|
||||
|
||||
SGBucket b7(200, 89.88);
|
||||
SG_CHECK_EQUAL(b7.get_chunk_lon(), -168);
|
||||
}
|
||||
|
||||
// test the tiles just below the pole (between 86 & 89 degrees N/S)
|
||||
@@ -160,15 +188,15 @@ void testNearPolar()
|
||||
{
|
||||
SGBucket b1(1, 88.5);
|
||||
SGBucket b2(-1, 88.8);
|
||||
COMPARE(b1.get_chunk_lon(), 0);
|
||||
COMPARE(b1.get_chunk_lat(), 88);
|
||||
VERIFY(b1.gen_index() != b2.gen_index());
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lon(), 0);
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lat(), 88);
|
||||
SG_VERIFY(b1.gen_index() != b2.gen_index());
|
||||
|
||||
SGBucket b3(176.1, 88.5);
|
||||
COMPARE(b3.get_chunk_lon(), 176);
|
||||
|
||||
SG_CHECK_EQUAL(b3.get_chunk_lon(), 176);
|
||||
|
||||
SGBucket b4(-178, 88.5);
|
||||
COMPARE(b4.get_chunk_lon(), -180);
|
||||
SG_CHECK_EQUAL(b4.get_chunk_lon(), -180);
|
||||
}
|
||||
|
||||
void testOffset()
|
||||
@@ -176,74 +204,74 @@ void testOffset()
|
||||
// bucket just below the 22 degree cutoff, so the next tile north
|
||||
// is twice the width
|
||||
SGBucket b1(-59.8, 21.9);
|
||||
COMPARE(b1.get_chunk_lat(), 21);
|
||||
COMPARE(b1.get_chunk_lon(), -60);
|
||||
COMPARE(b1.get_x(), 1);
|
||||
COMPARE(b1.get_y(), 7);
|
||||
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lat(), 21);
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lon(), -60);
|
||||
SG_CHECK_EQUAL(b1.get_x(), 1);
|
||||
SG_CHECK_EQUAL(b1.get_y(), 7);
|
||||
|
||||
// offset vertically
|
||||
SGBucket b2(b1.sibling(0, 1));
|
||||
COMPARE(b2.get_chunk_lat(), 22);
|
||||
COMPARE(b2.get_chunk_lon(), -60);
|
||||
COMPARE(b2.get_x(), 0);
|
||||
COMPARE(b2.get_y(), 0);
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lat(), 22);
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lon(), -60);
|
||||
SG_CHECK_EQUAL(b2.get_x(), 0);
|
||||
SG_CHECK_EQUAL(b2.get_y(), 0);
|
||||
|
||||
SG_CHECK_EQUAL(b2.gen_index(), sgBucketOffset(-59.8, 21.9, 0, 1));
|
||||
|
||||
COMPARE(b2.gen_index(), sgBucketOffset(-59.8, 21.9, 0, 1));
|
||||
|
||||
// offset vertically and horizontally. We compute horizontal (x)
|
||||
// movement at the target latitude, so this should move 0.25 * -3 degrees,
|
||||
// NOT 0.125 * -3 degrees.
|
||||
SGBucket b3(b1.sibling(-3, 1));
|
||||
COMPARE(b3.get_chunk_lat(), 22);
|
||||
COMPARE(b3.get_chunk_lon(), -61);
|
||||
COMPARE(b3.get_x(), 1);
|
||||
COMPARE(b3.get_y(), 0);
|
||||
|
||||
COMPARE(b3.gen_index(), sgBucketOffset(-59.8, 21.9, -3, 1));
|
||||
SG_CHECK_EQUAL(b3.get_chunk_lat(), 22);
|
||||
SG_CHECK_EQUAL(b3.get_chunk_lon(), -61);
|
||||
SG_CHECK_EQUAL(b3.get_x(), 1);
|
||||
SG_CHECK_EQUAL(b3.get_y(), 0);
|
||||
|
||||
SG_CHECK_EQUAL(b3.gen_index(), sgBucketOffset(-59.8, 21.9, -3, 1));
|
||||
}
|
||||
|
||||
void testPolarOffset()
|
||||
{
|
||||
SGBucket b1(-11.7, -89.6);
|
||||
COMPARE(b1.get_chunk_lat(), -90);
|
||||
COMPARE(b1.get_chunk_lon(), -12);
|
||||
COMPARE(b1.get_x(), 0);
|
||||
COMPARE(b1.get_y(), 3);
|
||||
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lat(), -90);
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lon(), -12);
|
||||
SG_CHECK_EQUAL(b1.get_x(), 0);
|
||||
SG_CHECK_EQUAL(b1.get_y(), 3);
|
||||
|
||||
// offset horizontally
|
||||
SGBucket b2(b1.sibling(-2, 0));
|
||||
COMPARE(b2.get_chunk_lat(), -90);
|
||||
COMPARE(b2.get_chunk_lon(), -36);
|
||||
COMPARE(b2.get_x(), 0);
|
||||
COMPARE(b2.get_y(), 3);
|
||||
|
||||
COMPARE(b2.gen_index(), sgBucketOffset(-11.7, -89.6, -2, 0));
|
||||
|
||||
// offset and wrap
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lat(), -90);
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lon(), -36);
|
||||
SG_CHECK_EQUAL(b2.get_x(), 0);
|
||||
SG_CHECK_EQUAL(b2.get_y(), 3);
|
||||
|
||||
SG_CHECK_EQUAL(b2.gen_index(), sgBucketOffset(-11.7, -89.6, -2, 0));
|
||||
|
||||
// offset and wrap
|
||||
SGBucket b3(-170, 89.1);
|
||||
SGBucket b4(b3.sibling(-1, 0));
|
||||
COMPARE(b4.get_chunk_lat(), 89);
|
||||
COMPARE(b4.get_chunk_lon(), 168);
|
||||
COMPARE(b4.get_x(), 0);
|
||||
COMPARE(b4.get_y(), 0);
|
||||
|
||||
COMPARE(b4.gen_index(), sgBucketOffset(-170, 89.1, -1, 0));
|
||||
SG_CHECK_EQUAL(b4.get_chunk_lat(), 89);
|
||||
SG_CHECK_EQUAL(b4.get_chunk_lon(), 168);
|
||||
SG_CHECK_EQUAL(b4.get_x(), 0);
|
||||
SG_CHECK_EQUAL(b4.get_y(), 0);
|
||||
|
||||
SG_CHECK_EQUAL(b4.gen_index(), sgBucketOffset(-170, 89.1, -1, 0));
|
||||
|
||||
|
||||
|
||||
SGBucket b5(177, 87.3);
|
||||
SGBucket b6(b5.sibling(1, 1));
|
||||
COMPARE(b6.get_chunk_lat(), 87);
|
||||
COMPARE(b6.get_chunk_lon(), -180);
|
||||
COMPARE(b6.get_x(), 0);
|
||||
COMPARE(b6.get_y(), 3);
|
||||
|
||||
COMPARE(b6.gen_index(), sgBucketOffset(177, 87.3, 1, 1));
|
||||
SG_CHECK_EQUAL(b6.get_chunk_lat(), 87);
|
||||
SG_CHECK_EQUAL(b6.get_chunk_lon(), -180);
|
||||
SG_CHECK_EQUAL(b6.get_x(), 0);
|
||||
SG_CHECK_EQUAL(b6.get_y(), 3);
|
||||
|
||||
SG_CHECK_EQUAL(b6.gen_index(), sgBucketOffset(177, 87.3, 1, 1));
|
||||
|
||||
// offset vertically towards the pole
|
||||
SGBucket b7(b1.sibling(0, -5));
|
||||
VERIFY(!b7.isValid());
|
||||
|
||||
VERIFY(!SGBucket(0, 90).sibling(0, 1).isValid());
|
||||
SG_VERIFY(!b7.isValid());
|
||||
|
||||
SG_VERIFY(!SGBucket(0, 90).sibling(0, 1).isValid());
|
||||
}
|
||||
|
||||
// test behaviour of bucket-offset near the anti-meridian (180-meridian)
|
||||
@@ -251,33 +279,84 @@ void testOffsetWrap()
|
||||
{
|
||||
// near the equator
|
||||
SGBucket b1(-179.8, 16.8);
|
||||
COMPARE(b1.get_chunk_lat(), 16);
|
||||
COMPARE(b1.get_chunk_lon(), -180);
|
||||
COMPARE(b1.get_x(), 1);
|
||||
COMPARE(b1.get_y(), 6);
|
||||
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lat(), 16);
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lon(), -180);
|
||||
SG_CHECK_EQUAL(b1.get_x(), 1);
|
||||
SG_CHECK_EQUAL(b1.get_y(), 6);
|
||||
|
||||
SGBucket b2(b1.sibling(-2, 0));
|
||||
COMPARE(b2.get_chunk_lat(), 16);
|
||||
COMPARE(b2.get_chunk_lon(), 179);
|
||||
COMPARE(b2.get_x(), 7);
|
||||
COMPARE(b2.get_y(), 6);
|
||||
COMPARE(b2.gen_index(), sgBucketOffset(-179.8, 16.8, -2, 0));
|
||||
|
||||
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lat(), 16);
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lon(), 179);
|
||||
SG_CHECK_EQUAL(b2.get_x(), 7);
|
||||
SG_CHECK_EQUAL(b2.get_y(), 6);
|
||||
SG_CHECK_EQUAL(b2.gen_index(), sgBucketOffset(-179.8, 16.8, -2, 0));
|
||||
}
|
||||
|
||||
void testSiblings()
|
||||
{
|
||||
SGBucket bna_airport(-86.678, 36.1248);
|
||||
SG_VERIFY(bna_airport.isValid());
|
||||
|
||||
// retrieve the sibling two positions north-east of my position
|
||||
auto sib1 = bna_airport.sibling(2, 2);
|
||||
SG_CHECK_EQUAL(sib1.get_chunk_lon(), bna_airport.get_chunk_lon());
|
||||
SG_CHECK_EQUAL(sib1.get_chunk_lat(), bna_airport.get_chunk_lat());
|
||||
SG_CHECK_EQUAL(sib1.get_x(), 3); // my x-pos (1) + 2 = 3
|
||||
SG_CHECK_EQUAL(sib1.get_y(), 2); // my y-pos (0) + 2 = 2
|
||||
SG_CHECK_EQUAL(sib1.gen_base_path(), bna_airport.gen_base_path());
|
||||
|
||||
// retrieve the one sibling two positions to the north-east
|
||||
std::vector<SGBucket> siblings;
|
||||
bna_airport.siblings(2, 2, siblings);
|
||||
SG_CHECK_EQUAL(siblings.size(), static_cast<std::vector<SGBucket>::size_type>(1));
|
||||
siblings.clear();
|
||||
|
||||
// retrieve the one sibling at the chunk origin of sib1
|
||||
sib1.siblings(-2, -2, siblings);
|
||||
SG_CHECK_EQUAL(siblings.size(), static_cast<std::vector<SGBucket>::size_type>(1));
|
||||
siblings.clear();
|
||||
|
||||
// calculate delta between two buckets
|
||||
int dx = 0;
|
||||
int dy = 0;
|
||||
sgBucketDiff(bna_airport, sib1, &dx, &dy);
|
||||
SG_CHECK_EQUAL(dx, 2);
|
||||
SG_CHECK_EQUAL(dy, 2);
|
||||
|
||||
// retrieve all siblings between two geodetic locations
|
||||
auto geod_bna = SGGeod::fromDegFt(-86.678, 36.1248, 599.0);
|
||||
auto geod_m54 = SGGeod::fromDegFt(-86.317, 36.1908, 122.0);
|
||||
sgGetBuckets(geod_bna, geod_m54, siblings);
|
||||
SG_CHECK_EQUAL(siblings.size(), static_cast<std::vector<SGBucket>::size_type>(4));
|
||||
siblings.clear();
|
||||
|
||||
// edge cases
|
||||
|
||||
// ensure you cannot retrieve the sibling of an invalid bucket
|
||||
SGBucket bad;
|
||||
auto bad_sib = bad.sibling(1, 1);
|
||||
SG_CHECK_EQUAL(bad_sib.get_chunk_lon(), -1000);
|
||||
SG_CHECK_EQUAL(bad.siblings(2, 2, siblings), 0);
|
||||
|
||||
// if we drop below the 22nd parallel, the bucket widths are half the size
|
||||
// expect this to retrieve two buckets
|
||||
bna_airport.siblings(0, -160, siblings);
|
||||
SG_CHECK_EQUAL(siblings.size(), static_cast<std::vector<SGBucket>::size_type>(2));
|
||||
siblings.clear();
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
testBucketSpans();
|
||||
|
||||
|
||||
testBasic();
|
||||
testPolar();
|
||||
testNearPolar();
|
||||
testOffset();
|
||||
testOffsetWrap();
|
||||
testPolarOffset();
|
||||
|
||||
testSiblings();
|
||||
|
||||
cout << "all tests passed OK" << endl;
|
||||
return 0; // passed
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "BVHMaterial.hxx"
|
||||
|
||||
namespace simgear {
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "BVHPageNode.hxx"
|
||||
|
||||
#include "BVHPager.hxx"
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "BVHPageRequest.hxx"
|
||||
|
||||
namespace simgear {
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "BVHPager.hxx"
|
||||
|
||||
#include <list>
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "BVHStaticNode.hxx"
|
||||
|
||||
namespace simgear {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include <iostream>
|
||||
#include <simgear/structure/SGSharedPtr.hxx>
|
||||
|
||||
|
||||
@@ -16,12 +16,16 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "Canvas.hxx"
|
||||
#include "CanvasEventManager.hxx"
|
||||
#include "CanvasEventVisitor.hxx"
|
||||
#include "CanvasPlacement.hxx"
|
||||
|
||||
#include <simgear/canvas/events/KeyboardEvent.hxx>
|
||||
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
#include <simgear/scene/util/parse_color.hxx>
|
||||
#include <simgear/scene/util/RenderConstants.hxx>
|
||||
|
||||
@@ -30,9 +34,6 @@
|
||||
#include <osgText/Text>
|
||||
#include <osgViewer/Viewer>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
@@ -62,18 +63,9 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
Canvas::Canvas(SGPropertyNode* node):
|
||||
PropertyBasedElement(node),
|
||||
_canvas_mgr(0),
|
||||
_event_manager(new EventManager),
|
||||
_size_x(-1),
|
||||
_size_y(-1),
|
||||
_view_width(-1),
|
||||
_view_height(-1),
|
||||
_status(node, "status"),
|
||||
_status_msg(node, "status-msg"),
|
||||
_sampling_dirty(false),
|
||||
_render_dirty(true),
|
||||
_visible(true),
|
||||
_render_always(false)
|
||||
_status_msg(node, "status-msg")
|
||||
{
|
||||
_status = 0;
|
||||
setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
|
||||
@@ -234,6 +226,10 @@ namespace canvas
|
||||
|
||||
if( _status & STATUS_DIRTY )
|
||||
{
|
||||
// Retrieve reference here, to ensure the scene group is not deleted while
|
||||
// creating the new texture and camera
|
||||
osg::ref_ptr<osg::Group> root_scene_group = _root_group->getSceneGroup();
|
||||
|
||||
_texture.setSize(_size_x, _size_y);
|
||||
|
||||
if( !_texture.serviceable() )
|
||||
@@ -259,7 +255,7 @@ namespace canvas
|
||||
parseColor(_node->getStringValue("background"), clear_color);
|
||||
camera->setClearColor(clear_color);
|
||||
|
||||
camera->addChild(_root_group->getMatrixTransform());
|
||||
camera->addChild(root_scene_group);
|
||||
|
||||
if( _texture.serviceable() )
|
||||
{
|
||||
@@ -279,7 +275,7 @@ namespace canvas
|
||||
|
||||
if( _visible || _render_always )
|
||||
{
|
||||
BOOST_FOREACH(CanvasWeakPtr canvas_weak, _child_canvases)
|
||||
for(auto& canvas_weak: _child_canvases)
|
||||
{
|
||||
// TODO should we check if the image the child canvas is displayed
|
||||
// within is really visible?
|
||||
@@ -291,7 +287,7 @@ namespace canvas
|
||||
if( _render_dirty )
|
||||
{
|
||||
// Also mark all canvases this canvas is displayed within as dirty
|
||||
BOOST_FOREACH(CanvasWeakPtr canvas_weak, _parent_canvases)
|
||||
for(auto& canvas_weak: _parent_canvases)
|
||||
{
|
||||
CanvasPtr canvas = canvas_weak.lock();
|
||||
if( canvas )
|
||||
@@ -520,8 +516,8 @@ namespace canvas
|
||||
{
|
||||
const std::string& name = node->getNameString();
|
||||
|
||||
if( boost::starts_with(name, "status")
|
||||
|| boost::starts_with(name, "data-") )
|
||||
if( strutils::starts_with(name, "status")
|
||||
|| strutils::starts_with(name, "data-") )
|
||||
return;
|
||||
_render_dirty = true;
|
||||
|
||||
@@ -536,7 +532,7 @@ namespace canvas
|
||||
if( !placements.empty() )
|
||||
{
|
||||
bool placement_dirty = false;
|
||||
BOOST_FOREACH(PlacementPtr& placement, placements)
|
||||
for(auto& placement: placements)
|
||||
{
|
||||
// check if change can be directly handled by placement
|
||||
if( placement->getProps() == node->getParent()
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
#include <osg/NodeCallback>
|
||||
#include <osg/observer_ptr>
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace simgear
|
||||
@@ -211,21 +211,21 @@ namespace canvas
|
||||
|
||||
protected:
|
||||
|
||||
CanvasMgr *_canvas_mgr;
|
||||
CanvasMgr *_canvas_mgr {nullptr};
|
||||
|
||||
boost::scoped_ptr<EventManager> _event_manager;
|
||||
std::unique_ptr<EventManager> _event_manager;
|
||||
|
||||
int _size_x,
|
||||
_size_y,
|
||||
_view_width,
|
||||
_view_height;
|
||||
int _size_x {-1},
|
||||
_size_y {-1},
|
||||
_view_width {-1},
|
||||
_view_height {-1};
|
||||
|
||||
PropertyObject<int> _status;
|
||||
PropertyObject<std::string> _status_msg;
|
||||
|
||||
bool _sampling_dirty,
|
||||
_render_dirty,
|
||||
_visible;
|
||||
bool _sampling_dirty {false},
|
||||
_render_dirty {true},
|
||||
_visible {true};
|
||||
|
||||
ODGauge _texture;
|
||||
|
||||
@@ -235,7 +235,9 @@ namespace canvas
|
||||
ElementWeakPtr _focus_element;
|
||||
|
||||
CullCallbackPtr _cull_callback;
|
||||
bool _render_always; //!< Used to disable automatic lazy rendering (culling)
|
||||
|
||||
/** Used to disable automatic lazy rendering (culling) */
|
||||
bool _render_always {false};
|
||||
|
||||
std::vector<SGPropertyNode*> _dirty_placements;
|
||||
std::vector<Placements> _placements;
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "CanvasEvent.hxx"
|
||||
|
||||
namespace simgear
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "CanvasEventManager.hxx"
|
||||
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||
#include <simgear/canvas/elements/CanvasElement.hxx>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "CanvasEvent.hxx"
|
||||
#include "CanvasEventVisitor.hxx"
|
||||
#include <simgear/canvas/elements/CanvasElement.hxx>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "CanvasMgr.hxx"
|
||||
#include "Canvas.hxx"
|
||||
#include "CanvasEventManager.hxx"
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "Canvas.hxx"
|
||||
#include "CanvasObjectPlacement.hxx"
|
||||
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "CanvasPlacement.hxx"
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
|
||||
@@ -16,18 +16,17 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "CanvasMgr.hxx"
|
||||
#include "CanvasSystemAdapter.hxx"
|
||||
#include "CanvasWindow.hxx"
|
||||
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
#include <simgear/scene/util/OsgMath.hxx>
|
||||
|
||||
#include <osgGA/GUIEventHandler>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
@@ -42,9 +41,6 @@ namespace canvas
|
||||
const Style& parent_style,
|
||||
Element* parent ):
|
||||
Image(canvas, node, parent_style, parent),
|
||||
_attributes_dirty(0),
|
||||
_resizable(false),
|
||||
_capture_events(true),
|
||||
_resize_top(node, "resize-top"),
|
||||
_resize_right(node, "resize-right"),
|
||||
_resize_bottom(node, "resize-bottom"),
|
||||
@@ -91,7 +87,7 @@ namespace canvas
|
||||
_capture_events = node->getBoolValue();
|
||||
else if( name == "decoration-border" )
|
||||
parseDecorationBorder(node->getStringValue());
|
||||
else if( boost::starts_with(name, "shadow-")
|
||||
else if( strutils::starts_with(name, "shadow-")
|
||||
|| name == "content-size" )
|
||||
_attributes_dirty |= DECORATION;
|
||||
else
|
||||
@@ -102,16 +98,10 @@ namespace canvas
|
||||
Image::valueChanged(node);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::Group* Window::getGroup()
|
||||
{
|
||||
return getMatrixTransform();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
const SGVec2<float> Window::getPosition() const
|
||||
{
|
||||
const osg::Matrix& m = getMatrixTransform()->getMatrix();
|
||||
auto const& m = getMatrix();
|
||||
return SGVec2<float>( m(3, 0), m(3, 1) );
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,6 @@ namespace canvas
|
||||
virtual void update(double delta_time_sec);
|
||||
virtual void valueChanged(SGPropertyNode* node);
|
||||
|
||||
osg::Group* getGroup();
|
||||
const SGVec2<float> getPosition() const;
|
||||
const SGRect<float> getScreenRegion() const;
|
||||
|
||||
@@ -104,7 +103,7 @@ namespace canvas
|
||||
DECORATION = 1
|
||||
};
|
||||
|
||||
uint32_t _attributes_dirty;
|
||||
uint32_t _attributes_dirty {0};
|
||||
|
||||
CanvasPtr _canvas_decoration;
|
||||
CanvasWeakPtr _canvas_content;
|
||||
@@ -113,8 +112,8 @@ namespace canvas
|
||||
ImagePtr _image_content,
|
||||
_image_shadow;
|
||||
|
||||
bool _resizable,
|
||||
_capture_events;
|
||||
bool _resizable {false},
|
||||
_capture_events {true};
|
||||
|
||||
PropertyObject<int> _resize_top,
|
||||
_resize_right,
|
||||
|
||||
@@ -44,6 +44,11 @@
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(disable:4311)
|
||||
# pragma warning(disable:4312)
|
||||
# define ALIGN16 __declspec(align(16))
|
||||
# define ALIGN16C
|
||||
#elif defined(__GNUC__)
|
||||
# define ALIGN16
|
||||
# define ALIGN16C __attribute__((aligned(16)))
|
||||
#endif
|
||||
|
||||
/* Type definitions */
|
||||
|
||||
@@ -80,8 +80,12 @@ void updateBlendingStateGL(VGContext *c, int alphaIsOne)
|
||||
|
||||
case VG_BLEND_SRC_OVER: default:
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
if (alphaIsOne) glDisable(GL_BLEND);
|
||||
else glEnable(GL_BLEND); break;
|
||||
if (alphaIsOne) {
|
||||
glDisable(GL_BLEND);
|
||||
} else {
|
||||
glEnable(GL_BLEND);
|
||||
}
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -35,21 +35,33 @@ void SHVector2_dtor(SHVector2 *v) {
|
||||
}
|
||||
|
||||
void SHVector3_ctor(SHVector3 *v) {
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
v->vec = _mm_setzero_ps();
|
||||
#else
|
||||
v->x=0.0f; v->y=0.0f; v->z=0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SHVector3_dtor(SHVector3 *v) {
|
||||
}
|
||||
|
||||
void SHVector4_ctor(SHVector4 *v) {
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
v->vec = _mm_setzero_ps();
|
||||
#else
|
||||
v->x=0.0f; v->y=0.0f; v->z=0.0f; v->w=0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SHVector4_dtor(SHVector4 *v) {
|
||||
}
|
||||
|
||||
void SHRectangle_ctor(SHRectangle *r) {
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
r->vec = _mm_setzero_ps();
|
||||
#else
|
||||
r->x=0.0f; r->y=0.0f; r->w=0.0f; r->h=0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SHRectangle_dtor(SHRectangle *r) {
|
||||
@@ -135,3 +147,24 @@ int shLineLineXsection(SHVector2 *o1, SHVector2 *v1,
|
||||
xsection->y = o1->y + t1*v1->y;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# ifdef __SSE3__
|
||||
# include <pmmintrin.h>
|
||||
inline float hsum_ps_sse(__m128 v) {
|
||||
__m128 shuf = _mm_movehdup_ps(v);
|
||||
__m128 sums = _mm_add_ps(v, shuf);
|
||||
shuf = _mm_movehl_ps(shuf, sums);
|
||||
sums = _mm_add_ss(sums, shuf);
|
||||
return _mm_cvtss_f32(sums);
|
||||
}
|
||||
# else
|
||||
inline float hsum_ps_sse(__m128 v) {
|
||||
__m128 shuf = _mm_shuffle_ps(v, v, _MM_SHUFFLE(2, 3, 0, 1));
|
||||
__m128 sums = _mm_add_ps(v, shuf);
|
||||
shuf = _mm_movehl_ps(shuf, sums);
|
||||
sums = _mm_add_ss(sums, shuf);
|
||||
return _mm_cvtss_f32(sums);
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
|
||||
@@ -21,6 +21,21 @@
|
||||
#ifndef __SHVECTORS_H
|
||||
#define __SHVECTORS_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <simgear/simgear_config.h>
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SIMD
|
||||
# ifdef __SSE__
|
||||
// # define SHIVA_USE_SIMD
|
||||
# endif
|
||||
# endif
|
||||
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# include <xmmintrin.h>
|
||||
float hsum_ps_sse(__m128 v);
|
||||
#endif
|
||||
|
||||
#include "shDefs.h"
|
||||
|
||||
/* Vector structures
|
||||
@@ -33,9 +48,17 @@ typedef struct
|
||||
void SHVector2_ctor(SHVector2 *v);
|
||||
void SHVector2_dtor(SHVector2 *v);
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
union ALIGN16 {
|
||||
__m128 vec;
|
||||
struct { SHfloat x,y,z,w; };
|
||||
} ALIGN16C;
|
||||
#else
|
||||
SHfloat x,y,z;
|
||||
#endif
|
||||
} SHVector3;
|
||||
|
||||
void SHVector3_ctor(SHVector3 *v);
|
||||
@@ -43,7 +66,14 @@ void SHVector3_dtor(SHVector3 *v);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
union ALIGN16 {
|
||||
__m128 vec;
|
||||
struct { SHfloat x,y,z,w; };
|
||||
} ALIGN16C;
|
||||
#else
|
||||
SHfloat x,y,z,w;
|
||||
#endif
|
||||
} SHVector4;
|
||||
|
||||
void SHVector4_ctor(SHVector4 *v);
|
||||
@@ -51,7 +81,14 @@ void SHVector4_dtor(SHVector4 *v);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
union ALIGN16 {
|
||||
__m128 vec;
|
||||
struct { SHfloat x,y,w,h; };
|
||||
} ALIGN16C;
|
||||
#else
|
||||
SHfloat x,y,w,h;
|
||||
#endif
|
||||
} SHRectangle;
|
||||
|
||||
void SHRectangle_ctor(SHRectangle *r);
|
||||
@@ -61,7 +98,14 @@ void shRectangleSet(SHRectangle *r, SHfloat x,
|
||||
|
||||
typedef struct
|
||||
{
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
union ALIGN16 {
|
||||
__m128 mtx[4];
|
||||
SHfloat m[4][4];
|
||||
} ALIGN16C;
|
||||
#else
|
||||
SHfloat m[3][3];
|
||||
#endif
|
||||
} SHMatrix3x3;
|
||||
|
||||
void SHMatrix3x3_ctor(SHMatrix3x3 *m);
|
||||
@@ -83,12 +127,22 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
|
||||
*--------------------------------------------------------- */
|
||||
|
||||
#define SET2(v,xs,ys) { v.x=xs; v.y=ys; }
|
||||
#define SET3(v,xs,ys,zs) { v.x=xs; v.y=ys; v.z=zs; }
|
||||
#define SET4(v,xs,ys,zs,ws) { v.x=xs; v.y=ys; v.z=zs; v.w=ws; }
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define SET3(v,xs,ys,zs,ws) { v.vec=_mm_set_ps(0,zs,ys,xs); }
|
||||
# define SET4(v,xs,ys,zs,ws) { v.vec=_mm_set_ps(ws,zs,ys,xs); }
|
||||
#else
|
||||
# define SET3(v,xs,ys,zs) { v.x=xs; v.y=ys; v.z=zs; }
|
||||
# define SET4(v,xs,ys,zs,ws) { v.x=xs; v.y=ys; v.z=zs; v.w=ws; }
|
||||
#endif
|
||||
|
||||
#define SET2V(v1,v2) { v1.x=v2.x; v1.y=v2.y; }
|
||||
#define SET3V(v1,v2) { v1.x=v2.x; v1.y=v2.y; v1.z=v2.z; }
|
||||
#define SET4V(v1,v2) { v1.x=v2.x; v1.y=v2.y; v1.z=v2.z; v1.w=v2.w; }
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define SET3V(v1,v2) { v1.vec=v2.vec; }
|
||||
# define SET4V(v1,v2) { v1.vec=v2.vec; }
|
||||
#else
|
||||
# define SET3V(v1,v2) { v1.x=v2.x; v1.y=v2.y; v1.z=v2.z; }
|
||||
# define SET4V(v1,v2) { v1.x=v2.x; v1.y=v2.y; v1.z=v2.z; v1.w=v2.w; }
|
||||
#endif
|
||||
|
||||
#define EQ2(v,xx,yy) ( v.x==xx && v.y==yy )
|
||||
#define EQ3(v,xx,yy,zz) ( v.x==xx && v.y==yy && v.z==zz )
|
||||
@@ -103,48 +157,89 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
|
||||
#define EQ4V(v1,v2) ( v1.x==v2.x && v1.y==v2.y && v1.z==v2.z && v1.w==v2.w )
|
||||
|
||||
#define ADD2(v,xx,yy) { v.x+=xx; v.y+=yy; }
|
||||
#define ADD3(v,xx,yy,zz) { v.x+=xx; v.y+=yy; v.z+=zz; }
|
||||
#define ADD4(v,xx,yy,zz,ww) { v.x+=xx; v.y+=yy; v.z+=zz; v.w+=ww; }
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define ADD3(v,xx,yy,zz,ww) { v.vec=_mm_add_ps(v.vec,_mm_set_ps(0,zz,yy,xx)); }
|
||||
# define ADD4(v,xx,yy,zz,ww) { v.vec=_mm_add_ps(v.vec,_mm_set_ps(ww,zz,yy,xx)); }
|
||||
#else
|
||||
# define ADD3(v,xx,yy,zz) { v.x+=xx; v.y+=yy; v.z+=zz; }
|
||||
# define ADD4(v,xx,yy,zz,ww) { v.x+=xx; v.y+=yy; v.z+=zz; v.w+=ww; }
|
||||
#endif
|
||||
|
||||
#define ADD2V(v1,v2) { v1.x+=v2.x; v1.y+=v2.y; }
|
||||
#define ADD3V(v1,v2) { v1.x+=v2.x; v1.y+=v2.y; v1.z+=v2.z; }
|
||||
#define ADD4V(v1,v2) { v1.x+=v2.x; v1.y+=v2.y; v1.z+=v2.z; v1.w+=v2.w; }
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define ADD3V(v1,v2) { v1.vec=_mm_add_ps(v1.vec,v2.vec); }
|
||||
# define ADD4V(v1,v2) { v1.vec=_mm_add_ps(v1.vec,v2.vec); }
|
||||
#else
|
||||
# define ADD3V(v1,v2) { v1.x+=v2.x; v1.y+=v2.y; v1.z+=v2.z; }
|
||||
# define ADD4V(v1,v2) { v1.x+=v2.x; v1.y+=v2.y; v1.z+=v2.z; v1.w+=v2.w; }
|
||||
#endif
|
||||
|
||||
#define SUB2(v,xx,yy) { v.x-=xx; v.y-=yy; }
|
||||
#define SUB3(v,xx,yy,zz) { v.x-=xx; v.y-=yy; v.z-=zz; }
|
||||
#define SUB4(v,xx,yy,zz,ww) { v.x-=xx; v.y-=yy; v.z-=zz; v.w-=v2.w; }
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define SUB3(v,xx,yy,zz,ww) { v.vec=_mm_sub_ps(v.vec,_mm_set_ps(0,zz,yy,xx)); }
|
||||
# define SUB4(v,xx,yy,zz,ww) { v.vec=_mm_sub_ps(v.vec,_mm_set_ps(ww,zz,yy,xx)); }
|
||||
#else
|
||||
# define SUB3(v,xx,yy,zz) { v.x-=xx; v.y-=yy; v.z-=zz; }
|
||||
# define SUB4(v,xx,yy,zz,ww) { v.x-=xx; v.y-=yy; v.z-=zz; v.w-=v2.w; }
|
||||
#endif
|
||||
|
||||
#define SUB2V(v1,v2) { v1.x-=v2.x; v1.y-=v2.y; }
|
||||
#define SUB3V(v1,v2) { v1.x-=v2.x; v1.y-=v2.y; v1.z-=v2.z; }
|
||||
#define SUB4V(v1,v2) { v1.x-=v2.x; v1.y-=v2.y; v1.z-=v2.z; v1.w-=v2.w; }
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define SUB3V(v1,v2) { v1.vec=_mm_sub_ps(v1.vec,v2.vec); }
|
||||
# define SUB4V(v1,v2) { v1.vec=_mm_sub_ps(v1.vec,v2.vec); }
|
||||
#else
|
||||
# define SUB3V(v1,v2) { v1.x-=v2.x; v1.y-=v2.y; v1.z-=v2.z; }
|
||||
# define SUB4V(v1,v2) { v1.x-=v2.x; v1.y-=v2.y; v1.z-=v2.z; v1.w-=v2.w; }
|
||||
#endif
|
||||
|
||||
#define MUL2(v,f) { v.x*=f; v.y*=f; }
|
||||
#define MUL3(v,f) { v.x*=f; v.y*=f; v.z*=z; }
|
||||
#define MUL4(v,f) { v.x*=f; v.y*=f; v.z*=z; v.w*=w; }
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define MUL3(v,f) { v.vec=_mm_mul_ps(v.vec,_mm_set1_ps(f)); }
|
||||
# define MUL4(v,f) { v.vec=_mm_mul_ps(v.vec,_mm_set1_ps(f)); }
|
||||
#else
|
||||
# define MUL3(v,f) { v.x*=f; v.y*=f; v.z*=z; }
|
||||
# define MUL4(v,f) { v.x*=f; v.y*=f; v.z*=z; v.w*=w; }
|
||||
#endif
|
||||
|
||||
#define DIV2(v,f) { v.x/=f; v.y/=f; }
|
||||
#define DIV3(v,f) { v.x/=f; v.y/=f; v.z/=z; }
|
||||
#define DIV4(v,f) { v.x/=f; v.y/=f; v.z/=z; v.w/=w; }
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define DIV3(v,f) { v.vec=_mm_div_ps(v.vec,_mm_set1_ps(f)); }
|
||||
# define DIV4(v,f) { v.vec=_mm_div_ps(v.vec,_mm_set1_ps(f)); }
|
||||
#else
|
||||
# define DIV3(v,f) { v.x/=f; v.y/=f; v.z/=z; }
|
||||
# define DIV4(v,f) { v.x/=f; v.y/=f; v.z/=z; v.w/=w; }
|
||||
#endif
|
||||
|
||||
#define ABS2(v) { v.x=SH_ABS(v.x); v.y=SH_ABS(v.y); }
|
||||
#define ABS3(v) { v.x=SH_ABS(v.x); v.y=SH_ABS(v.y); v.z=SH_ABS(v.z); }
|
||||
#define ABS4(v) { v.x=SH_ABS(v.x); v.y=SH_ABS(v.y); v.z=SH_ABS(v.z); v.w=SH_ABS(v.w); }
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define ABS_MASK _mm_set1_ps(-0.f)
|
||||
# define ABS3(v) { v.vec=_mm_andnot_ps(ABS_MASK, v.vec); }
|
||||
# define ABS4(v) { v.vec=_mm_andnot_ps(ABS_MASK, v.vec); }
|
||||
#else
|
||||
# define ABS3(v) { v.x=SH_ABS(v.x); v.y=SH_ABS(v.y); v.z=SH_ABS(v.z); }
|
||||
# define ABS4(v) { v.x=SH_ABS(v.x); v.y=SH_ABS(v.y); v.z=SH_ABS(v.z); v.w=SH_ABS(v.w); }
|
||||
#endif
|
||||
|
||||
#define NORMSQ2(v) (v.x*v.x + v.y*v.y)
|
||||
#define NORMSQ3(v) (v.x*v.x + v.y*v.y + v.z*v.z)
|
||||
#define NORMSQ4(v) (v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w)
|
||||
#define NORMSQ2(v) DOT2(v,v)
|
||||
#define NORMSQ3(v) DOT3(v,v)
|
||||
#define NORMSQ4(v) DOT4(v,v)
|
||||
|
||||
#define NORM2(v) SH_SQRT(NORMSQ2(v))
|
||||
#define NORM3(v) SH_SQRT(NORMSQ3(v))
|
||||
#define NORM4(v) SH_SQRT(NORMSQ4(v))
|
||||
|
||||
#define NORMALIZE2(v) { SHfloat n=NORM2(v); v.x/=n; v.y/=n; }
|
||||
#define NORMALIZE3(v) { SHfloat n=NORM3(v); v.x/=n; v.y/=n; v.z/=n; }
|
||||
#define NORMALIZE4(v) { SHfloat n=NORM4(v); v.x/=n; v.y/=n; v.z/=n; v.w/=w; }
|
||||
#define NORMALIZE2(v) { SHfloat n=NORM2(v); DIV2(v,n); }
|
||||
#define NORMALIZE3(v) { SHfloat n=NORM3(v); DIV3(v,n); }
|
||||
#define NORMALIZE4(v) { SHfloat n=NORM4(v); DIV4(v,n); }
|
||||
|
||||
#define DOT2(v1,v2) (v1.x*v2.x + v1.y*v2.y)
|
||||
#define DOT3(v1,v2) (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z)
|
||||
#define DOT4(v1,v2) (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z + v1.w*v2.w)
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define DOT4(v1,v2) hsum_ps_sse(_mm_mul_ps(v1.vec,v2.vec))
|
||||
# define DOT4(v1,v2) hsum_ps_sse(_mm_mul_ps(v1.vec,v2.vec))
|
||||
#else
|
||||
# define DOT3(v1,v2) (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z)
|
||||
# define DOT4(v1,v2) (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z + v1.w*v2.w)
|
||||
#endif
|
||||
|
||||
#define CROSS2(v1,v2) (v1.x*v2.y - v2.x*v1.y)
|
||||
|
||||
@@ -152,37 +247,84 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
|
||||
#define ANGLE2N(v1,v2) (SH_ACOS( DOT2(v1,v2) ))
|
||||
|
||||
#define OFFSET2V(v, o, s) { v.x += o.x*s; v.y += o.y*s; }
|
||||
#define OFFSET3V(v, o, s) { v.x += o.x*s; v.y += o.y*s; v.z += o.z*s; }
|
||||
#define OFFSET4V(v, o, s) { v.x += o.x*s; v.y += o.y*s; v.z += o.z*s; v.w += o.w*s; }
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define OFFSET4V(v, o, s) { v.vec=_mm_add_ps(v.vec,_mm_mul_ps(o.vec,_mm_set1_ps(s))); }
|
||||
# define OFFSET4V(v, o, s) { v.vec=_mm_add_ps(v.vec,_mm_mul_ps(o.vec,_mm_set1_ps(s))); }
|
||||
#else
|
||||
# define OFFSET3V(v, o, s) { v.x += o.x*s; v.y += o.y*s; v.z += o.z*s; }
|
||||
# define OFFSET4V(v, o, s) { v.x += o.x*s; v.y += o.y*s; v.z += o.z*s; v.w += o.w*s; }
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------
|
||||
* Macros for matrix operations
|
||||
*-----------------------------------------------------*/
|
||||
|
||||
#define SETMAT(mat, m00, m01, m02, m10, m11, m12, m20, m21, m22) { \
|
||||
mat.m[0][0] = m00; mat.m[0][1] = m01; mat.m[0][2] = m02; \
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define SETMAT(mat, m00, m01, m02, m10, m11, m12, m20, m21, m22) { \
|
||||
mat.mtx[0] = _mm_set_ps(0,m02,m01,m00); \
|
||||
mat.mtx[1] = _mm_set_ps(0,m12,m11,m10); \
|
||||
mat.mtx[2] = _mm_set_ps(0,m22,m21,m20); \
|
||||
mat.mtx[3] = _mm_setzero_ps(); }
|
||||
#else
|
||||
# define SETMAT(mat, m00, m01, m02, m10, m11, m12, m20, m21, m22) { \
|
||||
mat.m[0][0] = m00; mat.m[0][1] = m01; mat.m[0][2] = m02; \
|
||||
mat.m[1][0] = m10; mat.m[1][1] = m11; mat.m[1][2] = m12; \
|
||||
mat.m[2][0] = m20; mat.m[2][1] = m21; mat.m[2][2] = m22; }
|
||||
#endif
|
||||
|
||||
#define SETMATMAT(m1, m2) { \
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define SETMATMAT(m1, m2) { \
|
||||
m1.mtx[0] = m2.mtx[0]; \
|
||||
m1.mtx[1] = m2.mtx[1]; \
|
||||
m1.mtx[2] = m2.mtx[2]; }
|
||||
#else
|
||||
# define SETMATMAT(m1, m2) { \
|
||||
int i,j; \
|
||||
for(i=0;i<3;i++) \
|
||||
for(j=0;j<3;j++) \
|
||||
m1.m[i][j] = m2.m[i][j]; }
|
||||
#endif
|
||||
|
||||
#define MULMATS(mat, s) { \
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define MULMATS(mat, s) { \
|
||||
mat.mtx[0] = _mm_mul_ps(mat.mtx[0],_mm_set1_ps(s)); \
|
||||
mat.mtx[1] = _mm_mul_ps(mat.mtx[1],_mm_set1_ps(s)); \
|
||||
mat.mtx[2] = _mm_mul_ps(mat.mtx[2],_mm_set1_ps(s)); }
|
||||
#else
|
||||
# define MULMATS(mat, s) { \
|
||||
int i,j; \
|
||||
for(i=0;i<3;i++) \
|
||||
for(j=0;j<3;j++) \
|
||||
mat.m[i][j] *= s; }
|
||||
#endif
|
||||
|
||||
#define DIVMATS(mat, s) { \
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define DIVMATS(mat, s) { \
|
||||
mat.mtx[0] = _mm_mul_ps(mat.mtx[0],_mm_set1_ps(1/s)); \
|
||||
mat.mtx[1] = _mm_mul_ps(mat.mtx[1],_mm_set1_ps(1/s)); \
|
||||
mat.mtx[2] = _mm_mul_ps(mat.mtx[2],_mm_set1_ps(1/s)); }
|
||||
#else
|
||||
# define DIVMATS(mat, s) { \
|
||||
int i,j; \
|
||||
for(i=0;i<3;i++) \
|
||||
for(j=0;j<3;j++) \
|
||||
mat.m[i][j] /= s; }
|
||||
#endif
|
||||
|
||||
#define MULMATMAT(m1, m2, mout) { \
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define MULMATMAT(m2, m1, mout) { \
|
||||
int i,j; \
|
||||
for (i=0;i<4;i++) { \
|
||||
__m128 a = m1.mtx[0]; \
|
||||
__m128 b = _mm_set1_ps(m2.m[i][0]); \
|
||||
mout.mtx[i] = a*b; \
|
||||
for (j=1;j<4;j++) { \
|
||||
a = m1.mtx[j]; \
|
||||
b = _mm_set1_ps(m2.m[i][j]); \
|
||||
mout.mtx[i] += a*b; } } }
|
||||
|
||||
#else
|
||||
# define MULMATMAT(m1, m2, mout) { \
|
||||
int i,j; \
|
||||
for(i=0;i<3;i++) \
|
||||
for(j=0;j<3;j++) \
|
||||
@@ -190,6 +332,7 @@ int i,j; \
|
||||
m1.m[i][0] * m2.m[0][j] + \
|
||||
m1.m[i][1] * m2.m[1][j] + \
|
||||
m1.m[i][2] * m2.m[2][j]; }
|
||||
#endif
|
||||
|
||||
#define IDMAT(mat) SETMAT(mat, 1,0,0, 0,1,0, 0,0,1)
|
||||
|
||||
|
||||
@@ -9,10 +9,6 @@ set(HEADERS
|
||||
CanvasText.hxx
|
||||
)
|
||||
|
||||
set(DETAIL_HEADERS
|
||||
detail/add_segment_variadic.hxx
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
CanvasElement.cxx
|
||||
CanvasGroup.cxx
|
||||
@@ -23,7 +19,6 @@ set(SOURCES
|
||||
)
|
||||
|
||||
simgear_scene_component(canvas-elements canvas/elements "${SOURCES}" "${HEADERS}")
|
||||
simgear_component(canvas-elements/detail canvas/elements/detail "" "${DETAIL_HEADERS}")
|
||||
|
||||
add_boost_test(canvas_element
|
||||
SOURCES canvas_element_test.cpp
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "CanvasElement.hxx"
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
#include <simgear/canvas/CanvasEventVisitor.hxx>
|
||||
@@ -29,10 +31,6 @@
|
||||
#include <osg/StateAttribute>
|
||||
#include <osg/Version>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
@@ -212,22 +210,22 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::onDestroy()
|
||||
{
|
||||
if( !_transform.valid() )
|
||||
if( !_scene_group.valid() )
|
||||
return;
|
||||
|
||||
// The transform node keeps a reference on this element, so ensure it is
|
||||
// deleted.
|
||||
BOOST_FOREACH(osg::Group* parent, _transform->getParents())
|
||||
for(osg::Group* parent: _scene_group->getParents())
|
||||
{
|
||||
parent->removeChild(_transform.get());
|
||||
parent->removeChild(_scene_group.get());
|
||||
}
|
||||
|
||||
// Hide in case someone still holds a reference
|
||||
setVisible(false);
|
||||
removeListener();
|
||||
|
||||
_parent = 0;
|
||||
_transform = 0;
|
||||
_parent = nullptr;
|
||||
_scene_group = nullptr;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -245,29 +243,8 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::update(double dt)
|
||||
{
|
||||
if( !isVisible() )
|
||||
return;
|
||||
|
||||
// Trigger matrix update
|
||||
getMatrix();
|
||||
|
||||
// Update bounding box on manual update (manual updates pass zero dt)
|
||||
if( dt == 0 && _drawable )
|
||||
_drawable->getBound();
|
||||
|
||||
if( (_attributes_dirty & BLEND_FUNC) && _transform.valid() )
|
||||
{
|
||||
parseBlendFunc(
|
||||
_transform->getOrCreateStateSet(),
|
||||
_node->getChild("blend-source"),
|
||||
_node->getChild("blend-destination"),
|
||||
_node->getChild("blend-source-rgb"),
|
||||
_node->getChild("blend-destination-rgb"),
|
||||
_node->getChild("blend-source-alpha"),
|
||||
_node->getChild("blend-destination-alpha")
|
||||
);
|
||||
_attributes_dirty &= ~BLEND_FUNC;
|
||||
}
|
||||
if( isVisible() )
|
||||
updateImpl(dt);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -339,7 +316,8 @@ namespace canvas
|
||||
if( listeners == _listener.end() )
|
||||
return false;
|
||||
|
||||
BOOST_FOREACH(EventListener const& listener, listeners->second)
|
||||
for(auto const& listener: listeners->second)
|
||||
{
|
||||
try
|
||||
{
|
||||
listener(event);
|
||||
@@ -352,6 +330,7 @@ namespace canvas
|
||||
"canvas::Element: event handler error: '" << ex.what() << "'"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -393,9 +372,9 @@ namespace canvas
|
||||
getBoundingBox()
|
||||
#endif
|
||||
.contains(osg::Vec3f(local_pos, 0));
|
||||
else if( _transform.valid() )
|
||||
else if( _scene_group.valid() )
|
||||
// ... for other elements, i.e. groups only a bounding sphere is available
|
||||
return _transform->getBound().contains(osg::Vec3f(parent_pos, 0));
|
||||
return _scene_group->getBound().contains(osg::Vec3f(parent_pos, 0));
|
||||
else
|
||||
return false;
|
||||
}
|
||||
@@ -404,34 +383,32 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::setVisible(bool visible)
|
||||
{
|
||||
if( _transform.valid() )
|
||||
if( _scene_group.valid() )
|
||||
// TODO check if we need another nodemask
|
||||
_transform->setNodeMask(visible ? 0xffffffff : 0);
|
||||
_scene_group->setNodeMask(visible ? 0xffffffff : 0);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Element::isVisible() const
|
||||
{
|
||||
return _transform.valid() && _transform->getNodeMask() != 0;
|
||||
return _scene_group.valid() && _scene_group->getNodeMask() != 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::MatrixTransform* Element::getMatrixTransform()
|
||||
osg::MatrixTransform* Element::getSceneGroup() const
|
||||
{
|
||||
return _transform.get();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::MatrixTransform const* Element::getMatrixTransform() const
|
||||
{
|
||||
return _transform.get();
|
||||
return _scene_group.get();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::Vec2f Element::posToLocal(const osg::Vec2f& pos) const
|
||||
{
|
||||
getMatrix();
|
||||
const osg::Matrix& m = _transform->getInverseMatrix();
|
||||
if( !_scene_group )
|
||||
// TODO log warning?
|
||||
return pos;
|
||||
|
||||
updateMatrix();
|
||||
const osg::Matrix& m = _scene_group->getInverseMatrix();
|
||||
return osg::Vec2f
|
||||
(
|
||||
m(0, 0) * pos[0] + m(1, 0) * pos[1] + m(3, 0),
|
||||
@@ -484,9 +461,6 @@ namespace canvas
|
||||
{
|
||||
if( child->getNameString() == NAME_TRANSFORM )
|
||||
{
|
||||
if( !_transform.valid() )
|
||||
return;
|
||||
|
||||
if( child->getIndex() >= static_cast<int>(_transform_types.size()) )
|
||||
{
|
||||
SG_LOG
|
||||
@@ -523,7 +497,7 @@ namespace canvas
|
||||
if( parent == _node )
|
||||
{
|
||||
const std::string& name = child->getNameString();
|
||||
if( boost::starts_with(name, "data-") )
|
||||
if( strutils::starts_with(name, "data-") )
|
||||
return;
|
||||
else if( StyleInfo const* style_info = getStyleInfo(name) )
|
||||
{
|
||||
@@ -538,7 +512,7 @@ namespace canvas
|
||||
}
|
||||
else if( name == "update" )
|
||||
return update(0);
|
||||
else if( boost::starts_with(name, "blend-") )
|
||||
else if( strutils::starts_with(name, "blend-") )
|
||||
return (void)(_attributes_dirty |= BLEND_FUNC);
|
||||
}
|
||||
else if( parent
|
||||
@@ -562,6 +536,10 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::setClip(const std::string& clip)
|
||||
{
|
||||
if( !_scene_group )
|
||||
// TODO warn?
|
||||
return;
|
||||
|
||||
osg::StateSet* ss = getOrCreateStateSet();
|
||||
if( !ss )
|
||||
return;
|
||||
@@ -575,8 +553,8 @@ namespace canvas
|
||||
|
||||
// TODO generalize CSS property parsing
|
||||
const std::string RECT("rect(");
|
||||
if( !boost::ends_with(clip, ")")
|
||||
|| !boost::starts_with(clip, RECT) )
|
||||
if( !strutils::ends_with(clip, ")")
|
||||
|| !strutils::starts_with(clip, RECT) )
|
||||
{
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "Canvas: invalid clip: " << clip);
|
||||
return;
|
||||
@@ -616,7 +594,7 @@ namespace canvas
|
||||
}
|
||||
|
||||
if( !_scissor )
|
||||
_scissor = new RelativeScissor(_transform.get());
|
||||
_scissor = new RelativeScissor(_scene_group.get());
|
||||
|
||||
// <top>, <right>, <bottom>, <left>
|
||||
_scissor->x() = values[3];
|
||||
@@ -640,6 +618,27 @@ namespace canvas
|
||||
_scissor->_coord_reference = rf;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::setRotation(unsigned int index, double r)
|
||||
{
|
||||
_node->getChild(NAME_TRANSFORM, index, true)->setDoubleValue("rot", r);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::setTranslation(unsigned int index, double x, double y)
|
||||
{
|
||||
SGPropertyNode* tf = _node->getChild(NAME_TRANSFORM, index, true);
|
||||
tf->getChild("t", 0, true)->setDoubleValue(x);
|
||||
tf->getChild("t", 1, true)->setDoubleValue(y);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::setTransformEnabled(unsigned int index, bool enabled)
|
||||
{
|
||||
SGPropertyNode* tf = _node->getChild(NAME_TRANSFORM, index, true);
|
||||
tf->setBoolValue("enabled", enabled);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::BoundingBox Element::getBoundingBox() const
|
||||
{
|
||||
@@ -652,8 +651,8 @@ namespace canvas
|
||||
|
||||
osg::BoundingBox bb;
|
||||
|
||||
if( _transform.valid() )
|
||||
bb.expandBy(_transform->getBound());
|
||||
if( _scene_group.valid() )
|
||||
bb.expandBy( _scene_group->getBound() );
|
||||
|
||||
return bb;
|
||||
}
|
||||
@@ -687,70 +686,11 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
osg::Matrix Element::getMatrix() const
|
||||
{
|
||||
if( !_transform )
|
||||
if( !_scene_group )
|
||||
return osg::Matrix::identity();
|
||||
|
||||
if( !(_attributes_dirty & TRANSFORM) )
|
||||
return _transform->getMatrix();
|
||||
|
||||
osg::Matrix m;
|
||||
for( size_t i = 0; i < _transform_types.size(); ++i )
|
||||
{
|
||||
// Skip unused indizes...
|
||||
if( _transform_types[i] == TT_NONE )
|
||||
continue;
|
||||
|
||||
SGPropertyNode* tf_node = _node->getChild("tf", i, true);
|
||||
|
||||
// Build up the matrix representation of the current transform node
|
||||
osg::Matrix tf;
|
||||
switch( _transform_types[i] )
|
||||
{
|
||||
case TT_MATRIX:
|
||||
tf = osg::Matrix( tf_node->getDoubleValue("m[0]", 1),
|
||||
tf_node->getDoubleValue("m[1]", 0),
|
||||
0,
|
||||
tf_node->getDoubleValue("m[6]", 0),
|
||||
|
||||
tf_node->getDoubleValue("m[2]", 0),
|
||||
tf_node->getDoubleValue("m[3]", 1),
|
||||
0,
|
||||
tf_node->getDoubleValue("m[7]", 0),
|
||||
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
|
||||
tf_node->getDoubleValue("m[4]", 0),
|
||||
tf_node->getDoubleValue("m[5]", 0),
|
||||
0,
|
||||
tf_node->getDoubleValue("m[8]", 1) );
|
||||
break;
|
||||
case TT_TRANSLATE:
|
||||
tf.makeTranslate( osg::Vec3f( tf_node->getDoubleValue("t[0]", 0),
|
||||
tf_node->getDoubleValue("t[1]", 0),
|
||||
0 ) );
|
||||
break;
|
||||
case TT_ROTATE:
|
||||
tf.makeRotate( tf_node->getDoubleValue("rot", 0), 0, 0, 1 );
|
||||
break;
|
||||
case TT_SCALE:
|
||||
{
|
||||
float sx = tf_node->getDoubleValue("s[0]", 1);
|
||||
// sy defaults to sx...
|
||||
tf.makeScale( sx, tf_node->getDoubleValue("s[1]", sx), 1 );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
m.postMult( tf );
|
||||
}
|
||||
_transform->setMatrix(m);
|
||||
_attributes_dirty &= ~TRANSFORM;
|
||||
|
||||
return m;
|
||||
updateMatrix();
|
||||
return _scene_group->getMatrix();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -764,11 +704,8 @@ namespace canvas
|
||||
PropertyBasedElement(node),
|
||||
_canvas( canvas ),
|
||||
_parent( parent ),
|
||||
_attributes_dirty( 0 ),
|
||||
_transform( new osg::MatrixTransform ),
|
||||
_style( parent_style ),
|
||||
_scissor( 0 ),
|
||||
_drawable( 0 )
|
||||
_scene_group( new osg::MatrixTransform ),
|
||||
_style( parent_style )
|
||||
{
|
||||
staticInit();
|
||||
|
||||
@@ -780,15 +717,15 @@ namespace canvas
|
||||
);
|
||||
|
||||
// Ensure elements are drawn in order they appear in the element tree
|
||||
_transform->getOrCreateStateSet()
|
||||
->setRenderBinDetails
|
||||
(
|
||||
0,
|
||||
"PreOrderBin",
|
||||
osg::StateSet::OVERRIDE_RENDERBIN_DETAILS
|
||||
);
|
||||
_scene_group
|
||||
->getOrCreateStateSet()
|
||||
->setRenderBinDetails(
|
||||
0,
|
||||
"PreOrderBin",
|
||||
osg::StateSet::OVERRIDE_RENDERBIN_DETAILS
|
||||
);
|
||||
|
||||
_transform->setUserData( new OSGUserData(this) );
|
||||
_scene_group->setUserData( new OSGUserData(this) );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -876,11 +813,21 @@ namespace canvas
|
||||
void Element::setDrawable( osg::Drawable* drawable )
|
||||
{
|
||||
_drawable = drawable;
|
||||
assert( _drawable );
|
||||
|
||||
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
|
||||
if( !_drawable )
|
||||
{
|
||||
SG_LOG(SG_GL, SG_WARN, "canvas::Element::setDrawable: NULL drawable");
|
||||
return;
|
||||
}
|
||||
if( !_scene_group )
|
||||
{
|
||||
SG_LOG(SG_GL, SG_WARN, "canvas::Element::setDrawable: NULL scenegroup");
|
||||
return;
|
||||
}
|
||||
|
||||
auto geode = new osg::Geode;
|
||||
geode->addDrawable(_drawable);
|
||||
_transform->addChild(geode);
|
||||
_scene_group->addChild(geode);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -888,18 +835,109 @@ namespace canvas
|
||||
{
|
||||
if( _drawable.valid() )
|
||||
return _drawable->getOrCreateStateSet();
|
||||
if( _transform.valid() )
|
||||
return _transform->getOrCreateStateSet();
|
||||
|
||||
return 0;
|
||||
else if( _scene_group.valid() )
|
||||
return _scene_group->getOrCreateStateSet();
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::setupStyle()
|
||||
{
|
||||
BOOST_FOREACH( Style::value_type style, _style )
|
||||
for(auto const& style: _style)
|
||||
setStyle(style.second);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::updateMatrix() const
|
||||
{
|
||||
if( !(_attributes_dirty & TRANSFORM) || !_scene_group )
|
||||
return;
|
||||
|
||||
osg::Matrix m;
|
||||
for( size_t i = 0; i < _transform_types.size(); ++i )
|
||||
{
|
||||
// Skip unused indizes...
|
||||
if( _transform_types[i] == TT_NONE )
|
||||
continue;
|
||||
|
||||
SGPropertyNode* tf_node = _node->getChild("tf", i, true);
|
||||
if (!tf_node->getBoolValue("enabled", true)) {
|
||||
continue; // skip disabled transforms
|
||||
}
|
||||
|
||||
// Build up the matrix representation of the current transform node
|
||||
osg::Matrix tf;
|
||||
switch( _transform_types[i] )
|
||||
{
|
||||
case TT_MATRIX:
|
||||
tf = osg::Matrix( tf_node->getDoubleValue("m[0]", 1),
|
||||
tf_node->getDoubleValue("m[1]", 0),
|
||||
0,
|
||||
tf_node->getDoubleValue("m[6]", 0),
|
||||
|
||||
tf_node->getDoubleValue("m[2]", 0),
|
||||
tf_node->getDoubleValue("m[3]", 1),
|
||||
0,
|
||||
tf_node->getDoubleValue("m[7]", 0),
|
||||
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
|
||||
tf_node->getDoubleValue("m[4]", 0),
|
||||
tf_node->getDoubleValue("m[5]", 0),
|
||||
0,
|
||||
tf_node->getDoubleValue("m[8]", 1) );
|
||||
break;
|
||||
case TT_TRANSLATE:
|
||||
tf.makeTranslate( osg::Vec3f( tf_node->getDoubleValue("t[0]", 0),
|
||||
tf_node->getDoubleValue("t[1]", 0),
|
||||
0 ) );
|
||||
break;
|
||||
case TT_ROTATE:
|
||||
tf.makeRotate( tf_node->getDoubleValue("rot", 0), 0, 0, 1 );
|
||||
break;
|
||||
case TT_SCALE:
|
||||
{
|
||||
float sx = tf_node->getDoubleValue("s[0]", 1);
|
||||
// sy defaults to sx...
|
||||
tf.makeScale( sx, tf_node->getDoubleValue("s[1]", sx), 1 );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
m.postMult( tf );
|
||||
}
|
||||
_scene_group->setMatrix(m);
|
||||
_attributes_dirty &= ~TRANSFORM;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::updateImpl(double dt)
|
||||
{
|
||||
updateMatrix();
|
||||
|
||||
// Update bounding box on manual update (manual updates pass zero dt)
|
||||
if( dt == 0 && _drawable )
|
||||
_drawable->getBound();
|
||||
|
||||
if( (_attributes_dirty & BLEND_FUNC) )
|
||||
{
|
||||
parseBlendFunc(
|
||||
_scene_group->getOrCreateStateSet(),
|
||||
_node->getChild("blend-source"),
|
||||
_node->getChild("blend-destination"),
|
||||
_node->getChild("blend-source-rgb"),
|
||||
_node->getChild("blend-destination-rgb"),
|
||||
_node->getChild("blend-source-alpha"),
|
||||
_node->getChild("blend-destination-alpha")
|
||||
);
|
||||
_attributes_dirty &= ~BLEND_FUNC;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
@@ -24,13 +24,13 @@
|
||||
#include <simgear/canvas/CanvasEvent.hxx>
|
||||
#include <simgear/props/PropertyBasedElement.hxx>
|
||||
#include <simgear/misc/stdint.hxx> // for uint32_t
|
||||
#include <simgear/std/type_traits.hxx>
|
||||
|
||||
#include <osg/BoundingBox>
|
||||
#include <osg/MatrixTransform>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/type_traits/is_base_of.hpp>
|
||||
|
||||
namespace osg
|
||||
{
|
||||
@@ -49,6 +49,7 @@ namespace canvas
|
||||
public PropertyBasedElement
|
||||
{
|
||||
public:
|
||||
using SceneGroupWeakPtr = osg::observer_ptr<osg::MatrixTransform>;
|
||||
|
||||
/**
|
||||
* Store pointer to window as user data
|
||||
@@ -142,8 +143,11 @@ namespace canvas
|
||||
*/
|
||||
virtual bool isVisible() const;
|
||||
|
||||
osg::MatrixTransform* getMatrixTransform();
|
||||
osg::MatrixTransform const* getMatrixTransform() const;
|
||||
/**
|
||||
* Get the according group in the OSG scene graph
|
||||
*/
|
||||
// TODO ref_ptr
|
||||
osg::MatrixTransform* getSceneGroup() const;
|
||||
|
||||
/**
|
||||
* Transform position to local coordinages.
|
||||
@@ -172,6 +176,22 @@ namespace canvas
|
||||
*/
|
||||
void setClipFrame(ReferenceFrame rf);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void setRotation(unsigned int index, double r);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void setTranslation(unsigned int index, double x, double y);
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
void setTransformEnabled(unsigned int index, bool enabled);
|
||||
|
||||
/**
|
||||
* Get bounding box (may not be as tight as bounding box returned by
|
||||
* #getTightBoundingBox)
|
||||
@@ -201,13 +221,14 @@ namespace canvas
|
||||
*/
|
||||
template<typename Derived>
|
||||
static
|
||||
typename boost::enable_if<
|
||||
boost::is_base_of<Element, Derived>,
|
||||
std::enable_if_t<
|
||||
std::is_base_of<Element, Derived>::value,
|
||||
ElementPtr
|
||||
>::type create( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& style = Style(),
|
||||
Element* parent = NULL )
|
||||
>
|
||||
create( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& style = Style(),
|
||||
Element* parent = NULL )
|
||||
{
|
||||
return ElementPtr( new Derived(canvas, node, style, parent) );
|
||||
}
|
||||
@@ -235,13 +256,13 @@ namespace canvas
|
||||
CanvasWeakPtr _canvas;
|
||||
ElementWeakPtr _parent;
|
||||
|
||||
mutable uint32_t _attributes_dirty;
|
||||
mutable uint32_t _attributes_dirty = 0;
|
||||
|
||||
osg::observer_ptr<osg::MatrixTransform> _transform;
|
||||
std::vector<TransformType> _transform_types;
|
||||
SceneGroupWeakPtr _scene_group;
|
||||
std::vector<TransformType> _transform_types;
|
||||
|
||||
Style _style;
|
||||
RelativeScissor *_scissor;
|
||||
RelativeScissor *_scissor = nullptr;
|
||||
|
||||
typedef std::vector<EventListener> Listener;
|
||||
typedef std::map<int, Listener> ListenerMap;
|
||||
@@ -568,6 +589,10 @@ namespace canvas
|
||||
|
||||
void setupStyle();
|
||||
|
||||
void updateMatrix() const;
|
||||
|
||||
virtual void updateImpl(double dt);
|
||||
|
||||
private:
|
||||
|
||||
osg::ref_ptr<osg::Drawable> _drawable;
|
||||
|
||||
@@ -16,18 +16,17 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "CanvasGroup.hxx"
|
||||
#include "CanvasImage.hxx"
|
||||
#include "CanvasMap.hxx"
|
||||
#include "CanvasPath.hxx"
|
||||
#include "CanvasText.hxx"
|
||||
|
||||
#include <simgear/canvas/CanvasEventVisitor.hxx>
|
||||
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/lambda/core.hpp>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
@@ -46,7 +45,7 @@ namespace canvas
|
||||
ElementFactories Group::_child_factories;
|
||||
const std::string Group::TYPE_NAME = "group";
|
||||
|
||||
void warnTransformExpired(const char* member_name)
|
||||
void warnSceneGroupExpired(const char* member_name)
|
||||
{
|
||||
SG_LOG( SG_GENERAL,
|
||||
SG_WARN,
|
||||
@@ -133,63 +132,57 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
ElementPtr Group::getElementById(const std::string& id)
|
||||
{
|
||||
if( !_transform.valid() )
|
||||
if( !_scene_group.valid() )
|
||||
{
|
||||
warnTransformExpired("getElementById");
|
||||
return ElementPtr();
|
||||
warnSceneGroupExpired("getElementById");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<GroupPtr> groups;
|
||||
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
||||
// TODO check search algorithm. Not completely breadth-first and might be
|
||||
// possible with using less dynamic memory
|
||||
std::vector<GroupPtr> child_groups;
|
||||
for(size_t i = 0; i < _scene_group->getNumChildren(); ++i)
|
||||
{
|
||||
const ElementPtr& el = getChildByIndex(i);
|
||||
if( el->get<std::string>("id") == id )
|
||||
return el;
|
||||
|
||||
Group* group = dynamic_cast<Group*>(el.get());
|
||||
if( group )
|
||||
groups.push_back(group);
|
||||
if( Group* child_group = dynamic_cast<Group*>(el.get()) )
|
||||
child_groups.push_back(child_group);
|
||||
}
|
||||
|
||||
BOOST_FOREACH( GroupPtr group, groups )
|
||||
for(auto group: child_groups)
|
||||
{
|
||||
ElementPtr el = group->getElementById(id);
|
||||
if( el )
|
||||
if( ElementPtr el = group->getElementById(id) )
|
||||
return el;
|
||||
}
|
||||
|
||||
return ElementPtr();
|
||||
return {};
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Group::clearEventListener()
|
||||
{
|
||||
if( !_transform.valid() )
|
||||
return warnTransformExpired("clearEventListener");
|
||||
|
||||
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
||||
getChildByIndex(i)->clearEventListener();
|
||||
|
||||
Element::clearEventListener();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Group::update(double dt)
|
||||
{
|
||||
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
||||
getChildByIndex(i)->update(dt);
|
||||
if( !_scene_group.valid() )
|
||||
return warnSceneGroupExpired("clearEventListener");
|
||||
|
||||
Element::update(dt);
|
||||
for(size_t i = 0; i < _scene_group->getNumChildren(); ++i)
|
||||
getChildByIndex(i)->clearEventListener();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Group::traverse(EventVisitor& visitor)
|
||||
{
|
||||
// Iterate in reverse order as last child is displayed on top
|
||||
for(size_t i = _transform->getNumChildren(); i --> 0;)
|
||||
if( _scene_group.valid() )
|
||||
{
|
||||
if( getChildByIndex(i)->accept(visitor) )
|
||||
return true;
|
||||
// Iterate in reverse order as last child is displayed on top
|
||||
for(size_t i = _scene_group->getNumChildren(); i --> 0;)
|
||||
{
|
||||
if( getChildByIndex(i)->accept(visitor) )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -204,13 +197,13 @@ namespace canvas
|
||||
bool handled = setStyleImpl(style, style_info);
|
||||
if( style_info->inheritable )
|
||||
{
|
||||
if( !_transform.valid() )
|
||||
if( !_scene_group.valid() )
|
||||
{
|
||||
warnTransformExpired("setStyle");
|
||||
warnSceneGroupExpired("setStyle");
|
||||
return false;
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
||||
for(size_t i = 0; i < _scene_group->getNumChildren(); ++i)
|
||||
handled |= getChildByIndex(i)->setStyle(style, style_info);
|
||||
}
|
||||
|
||||
@@ -220,26 +213,20 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
osg::BoundingBox Group::getTransformedBounds(const osg::Matrix& m) const
|
||||
{
|
||||
osg::BoundingBox bb;
|
||||
if( !_transform.valid() )
|
||||
if( !_scene_group.valid() )
|
||||
{
|
||||
warnTransformExpired("getTransformedBounds");
|
||||
return bb;
|
||||
warnSceneGroupExpired("getTransformedBounds");
|
||||
return {};
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
||||
osg::BoundingBox bb;
|
||||
for(size_t i = 0; i < _scene_group->getNumChildren(); ++i)
|
||||
{
|
||||
const ElementPtr& child = getChildByIndex(i);
|
||||
if( !child->getMatrixTransform()->getNodeMask() )
|
||||
auto child = getChildByIndex(i);
|
||||
if( !child || !child->isVisible() )
|
||||
continue;
|
||||
|
||||
bb.expandBy
|
||||
(
|
||||
child->getTransformedBounds
|
||||
(
|
||||
child->getMatrixTransform()->getMatrix() * m
|
||||
)
|
||||
);
|
||||
bb.expandBy( child->getTransformedBounds(child->getMatrix() * m) );
|
||||
}
|
||||
|
||||
return bb;
|
||||
@@ -255,6 +242,15 @@ namespace canvas
|
||||
return ElementFactory();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Group::updateImpl(double dt)
|
||||
{
|
||||
Element::updateImpl(dt);
|
||||
|
||||
for(size_t i = 0; i < _scene_group->getNumChildren(); ++i)
|
||||
getChildByIndex(i)->update(dt);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Group::childAdded(SGPropertyNode* child)
|
||||
{
|
||||
@@ -264,13 +260,13 @@ namespace canvas
|
||||
ElementFactory child_factory = getChildFactory( child->getNameString() );
|
||||
if( child_factory )
|
||||
{
|
||||
if( !_transform.valid() )
|
||||
return warnTransformExpired("childAdded");
|
||||
if( !_scene_group.valid() )
|
||||
return warnSceneGroupExpired("childAdded");
|
||||
|
||||
ElementPtr element = child_factory(_canvas, child, _style, this);
|
||||
|
||||
// Add to osg scene graph...
|
||||
_transform->addChild( element->getMatrixTransform() );
|
||||
_scene_group->addChild(element->getSceneGroup());
|
||||
|
||||
// ...and ensure correct ordering
|
||||
handleZIndexChanged(element);
|
||||
@@ -291,7 +287,7 @@ namespace canvas
|
||||
|
||||
if( getChildFactory(node->getNameString()) )
|
||||
{
|
||||
if( !_transform.valid() )
|
||||
if( !_scene_group.valid() )
|
||||
// If transform is destroyed also all children are destroyed, so we can
|
||||
// not do anything here.
|
||||
return;
|
||||
@@ -321,7 +317,7 @@ namespace canvas
|
||||
void Group::childChanged(SGPropertyNode* node)
|
||||
{
|
||||
SGPropertyNode* parent = node->getParent();
|
||||
SGPropertyNode* grand_parent = parent ? parent->getParent() : NULL;
|
||||
SGPropertyNode* grand_parent = parent ? parent->getParent() : nullptr;
|
||||
|
||||
if( grand_parent == _node
|
||||
&& node->getNameString() == "z-index" )
|
||||
@@ -331,16 +327,18 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
void Group::handleZIndexChanged(ElementPtr child, int z_index)
|
||||
{
|
||||
if( !child || !_transform.valid() )
|
||||
if( !child || !_scene_group.valid() )
|
||||
return;
|
||||
|
||||
osg::ref_ptr<osg::MatrixTransform> tf = child->getMatrixTransform();
|
||||
size_t index = _transform->getChildIndex(tf),
|
||||
// Keep reference to prevent deleting while removing and re-inserting later
|
||||
osg::ref_ptr<osg::MatrixTransform> tf = child->getSceneGroup();
|
||||
|
||||
size_t index = _scene_group->getChildIndex(tf),
|
||||
index_new = index;
|
||||
|
||||
for(;; ++index_new)
|
||||
{
|
||||
if( index_new + 1 == _transform->getNumChildren() )
|
||||
if( index_new + 1 == _scene_group->getNumChildren() )
|
||||
break;
|
||||
|
||||
// Move to end of block with same index (= move upwards until the next
|
||||
@@ -367,13 +365,13 @@ namespace canvas
|
||||
return;
|
||||
}
|
||||
|
||||
_transform->removeChild(index);
|
||||
_transform->insertChild(index_new, tf);
|
||||
_scene_group->removeChild(index);
|
||||
_scene_group->insertChild(index_new, tf);
|
||||
|
||||
SG_LOG
|
||||
(
|
||||
SG_GENERAL,
|
||||
SG_INFO,
|
||||
SG_DEBUG,
|
||||
"canvas::Group: Moved element " << index << " to position " << index_new
|
||||
);
|
||||
}
|
||||
@@ -381,24 +379,27 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
ElementPtr Group::getChildByIndex(size_t index) const
|
||||
{
|
||||
assert(_transform.valid());
|
||||
OSGUserData* ud =
|
||||
static_cast<OSGUserData*>(_transform->getChild(index)->getUserData());
|
||||
assert(ud);
|
||||
return ud->element;
|
||||
assert( _scene_group.valid() );
|
||||
|
||||
auto child = _scene_group->getChild(index);
|
||||
if( !child )
|
||||
return {};
|
||||
|
||||
auto ud = static_cast<OSGUserData*>(child->getUserData());
|
||||
return ud ? ud->element : ElementPtr();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
ElementPtr Group::findChild( const SGPropertyNode* node,
|
||||
const std::string& id ) const
|
||||
{
|
||||
if( !_transform.valid() )
|
||||
if( !_scene_group.valid() )
|
||||
{
|
||||
warnTransformExpired("findChild");
|
||||
return ElementPtr();
|
||||
warnSceneGroupExpired("findChild");
|
||||
return {};
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
||||
for(size_t i = 0; i < _scene_group->getNumChildren(); ++i)
|
||||
{
|
||||
ElementPtr el = getChildByIndex(i);
|
||||
|
||||
@@ -414,7 +415,7 @@ namespace canvas
|
||||
}
|
||||
}
|
||||
|
||||
return ElementPtr();
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
|
||||
@@ -88,8 +88,6 @@ namespace canvas
|
||||
|
||||
virtual void clearEventListener();
|
||||
|
||||
virtual void update(double dt);
|
||||
|
||||
virtual bool traverse(EventVisitor& visitor);
|
||||
|
||||
virtual bool setStyle( const SGPropertyNode* child,
|
||||
@@ -107,6 +105,8 @@ namespace canvas
|
||||
*/
|
||||
virtual ElementFactory getChildFactory(const std::string& type) const;
|
||||
|
||||
virtual void updateImpl(double dt);
|
||||
|
||||
virtual void childAdded(SGPropertyNode * child);
|
||||
virtual void childRemoved(SGPropertyNode * child);
|
||||
virtual void childChanged(SGPropertyNode * child);
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "CanvasImage.hxx"
|
||||
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
@@ -115,9 +117,7 @@ namespace canvas
|
||||
ElementWeakPtr parent ):
|
||||
Element(canvas, node, parent_style, parent),
|
||||
_texture(new osg::Texture2D),
|
||||
_node_src_rect( node->getNode("source", 0, true) ),
|
||||
_src_rect(0,0),
|
||||
_region(0,0)
|
||||
_node_src_rect( node->getNode("source", 0, true) )
|
||||
{
|
||||
staticInit();
|
||||
|
||||
@@ -155,22 +155,207 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
Image::~Image()
|
||||
{
|
||||
if( _http_request ) {
|
||||
Canvas::getSystemAdapter()->getHTTPClient()->cancelRequest(_http_request, "image destroyed");
|
||||
}
|
||||
if( _http_request )
|
||||
{
|
||||
Canvas::getSystemAdapter()
|
||||
->getHTTPClient()
|
||||
->cancelRequest(_http_request, "image destroyed");
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::update(double dt)
|
||||
void Image::valueChanged(SGPropertyNode* child)
|
||||
{
|
||||
Element::update(dt);
|
||||
// If the image is switched from invisible to visible, and it shows a
|
||||
// canvas, we need to delay showing it by one frame to ensure the canvas is
|
||||
// updated before the image is displayed.
|
||||
//
|
||||
// As canvas::Element handles and filters changes to the "visible" property
|
||||
// we can not check this in Image::childChanged but instead have to override
|
||||
// Element::valueChanged.
|
||||
if( !isVisible()
|
||||
&& child->getParent() == _node
|
||||
&& child->getNameString() == "visible"
|
||||
&& child->getBoolValue() )
|
||||
{
|
||||
CullCallback* cb =
|
||||
#if OSG_VERSION_LESS_THAN(3,3,2)
|
||||
static_cast<CullCallback*>
|
||||
#else
|
||||
dynamic_cast<CullCallback*>
|
||||
#endif
|
||||
( _geom->getCullCallback() );
|
||||
|
||||
if( cb )
|
||||
cb->cullNextFrame();
|
||||
}
|
||||
|
||||
Element::valueChanged(child);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setSrcCanvas(CanvasPtr canvas)
|
||||
{
|
||||
CanvasPtr src_canvas = _src_canvas.lock(),
|
||||
self_canvas = _canvas.lock();
|
||||
|
||||
if( src_canvas )
|
||||
src_canvas->removeParentCanvas(self_canvas);
|
||||
if( self_canvas )
|
||||
self_canvas->removeChildCanvas(src_canvas);
|
||||
|
||||
_src_canvas = src_canvas = canvas;
|
||||
_attributes_dirty |= SRC_CANVAS;
|
||||
_geom->setCullCallback(canvas ? new CullCallback(canvas) : 0);
|
||||
|
||||
if( src_canvas )
|
||||
{
|
||||
setupDefaultDimensions();
|
||||
|
||||
if( self_canvas )
|
||||
{
|
||||
self_canvas->addChildCanvas(src_canvas);
|
||||
src_canvas->addParentCanvas(self_canvas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
CanvasWeakPtr Image::getSrcCanvas() const
|
||||
{
|
||||
return _src_canvas;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setImage(osg::ref_ptr<osg::Image> img)
|
||||
{
|
||||
// remove canvas...
|
||||
setSrcCanvas( CanvasPtr() );
|
||||
|
||||
_texture->setResizeNonPowerOfTwoHint(false);
|
||||
_texture->setImage(img);
|
||||
_texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
|
||||
_texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
|
||||
_geom->getOrCreateStateSet()
|
||||
->setTextureAttributeAndModes(0, _texture);
|
||||
|
||||
if( img )
|
||||
setupDefaultDimensions();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setFill(const std::string& fill)
|
||||
{
|
||||
osg::Vec4 color(1,1,1,1);
|
||||
if( !fill.empty() // If no color is given default to white
|
||||
&& !parseColor(fill, color) )
|
||||
return;
|
||||
setFill(color);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setFill(const osg::Vec4& color)
|
||||
{
|
||||
_colors->front() = color;
|
||||
_colors->dirty();
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setOutset(const std::string& outset)
|
||||
{
|
||||
_outset = CSSBorder::parse(outset);
|
||||
_attributes_dirty |= DEST_SIZE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setPreserveAspectRatio(const std::string& scale)
|
||||
{
|
||||
_preserve_aspect_ratio = SVGpreserveAspectRatio::parse(scale);
|
||||
_attributes_dirty |= SRC_RECT;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setSourceRect(const SGRect<float>& sourceRect)
|
||||
{
|
||||
_attributes_dirty |= SRC_RECT;
|
||||
_src_rect = sourceRect;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setSlice(const std::string& slice)
|
||||
{
|
||||
_slice = CSSBorder::parse(slice);
|
||||
_attributes_dirty |= SRC_RECT | DEST_SIZE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setSliceWidth(const std::string& width)
|
||||
{
|
||||
_slice_width = CSSBorder::parse(width);
|
||||
_attributes_dirty |= DEST_SIZE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
const SGRect<float>& Image::getRegion() const
|
||||
{
|
||||
return _region;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Image::handleEvent(const EventPtr& event)
|
||||
{
|
||||
bool handled = Element::handleEvent(event);
|
||||
|
||||
CanvasPtr src_canvas = _src_canvas.lock();
|
||||
if( !src_canvas )
|
||||
return handled;
|
||||
|
||||
if( MouseEventPtr mouse_event = dynamic_cast<MouseEvent*>(event.get()) )
|
||||
{
|
||||
mouse_event.reset( new MouseEvent(*mouse_event) );
|
||||
|
||||
mouse_event->client_pos = mouse_event->local_pos
|
||||
- toOsg(_region.getMin());
|
||||
|
||||
osg::Vec2f size(_region.width(), _region.height());
|
||||
if( _outset.isValid() )
|
||||
{
|
||||
CSSBorder::Offsets outset =
|
||||
_outset.getAbsOffsets(getTextureDimensions());
|
||||
|
||||
mouse_event->client_pos += osg::Vec2f(outset.l, outset.t);
|
||||
size.x() += outset.l + outset.r;
|
||||
size.y() += outset.t + outset.b;
|
||||
}
|
||||
|
||||
// Scale event pos according to canvas view size vs. displayed/screen size
|
||||
mouse_event->client_pos.x() *= src_canvas->getViewWidth() / size.x();
|
||||
mouse_event->client_pos.y() *= src_canvas->getViewHeight()/ size.y();
|
||||
mouse_event->local_pos = mouse_event->client_pos;
|
||||
|
||||
handled |= src_canvas->handleMouseEvent(mouse_event);
|
||||
}
|
||||
else if( KeyboardEventPtr keyboard_event =
|
||||
dynamic_cast<KeyboardEvent*>(event.get()) )
|
||||
{
|
||||
handled |= src_canvas->handleKeyboardEvent(keyboard_event);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::updateImpl(double dt)
|
||||
{
|
||||
Element::updateImpl(dt);
|
||||
|
||||
osg::Texture2D* texture = dynamic_cast<osg::Texture2D*>
|
||||
(
|
||||
_geom->getOrCreateStateSet()
|
||||
->getTextureAttribute(0, osg::StateAttribute::TEXTURE)
|
||||
);
|
||||
simgear::canvas::CanvasPtr canvas = _src_canvas.lock();
|
||||
auto canvas = _src_canvas.lock();
|
||||
|
||||
if( (_attributes_dirty & SRC_CANVAS)
|
||||
// check if texture has changed (eg. due to resizing)
|
||||
@@ -400,175 +585,6 @@ namespace canvas
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::valueChanged(SGPropertyNode* child)
|
||||
{
|
||||
// If the image is switched from invisible to visible, and it shows a
|
||||
// canvas, we need to delay showing it by one frame to ensure the canvas is
|
||||
// updated before the image is displayed.
|
||||
//
|
||||
// As canvas::Element handles and filters changes to the "visible" property
|
||||
// we can not check this in Image::childChanged but instead have to override
|
||||
// Element::valueChanged.
|
||||
if( !isVisible()
|
||||
&& child->getParent() == _node
|
||||
&& child->getNameString() == "visible"
|
||||
&& child->getBoolValue() )
|
||||
{
|
||||
CullCallback* cb =
|
||||
#if OSG_VERSION_LESS_THAN(3,3,2)
|
||||
static_cast<CullCallback*>
|
||||
#else
|
||||
dynamic_cast<CullCallback*>
|
||||
#endif
|
||||
( _geom->getCullCallback() );
|
||||
|
||||
if( cb )
|
||||
cb->cullNextFrame();
|
||||
}
|
||||
|
||||
Element::valueChanged(child);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setSrcCanvas(CanvasPtr canvas)
|
||||
{
|
||||
CanvasPtr src_canvas = _src_canvas.lock(),
|
||||
self_canvas = _canvas.lock();
|
||||
|
||||
if( src_canvas )
|
||||
src_canvas->removeParentCanvas(self_canvas);
|
||||
if( self_canvas )
|
||||
self_canvas->removeChildCanvas(src_canvas);
|
||||
|
||||
_src_canvas = src_canvas = canvas;
|
||||
_attributes_dirty |= SRC_CANVAS;
|
||||
_geom->setCullCallback(canvas ? new CullCallback(canvas) : 0);
|
||||
|
||||
if( src_canvas )
|
||||
{
|
||||
setupDefaultDimensions();
|
||||
|
||||
if( self_canvas )
|
||||
{
|
||||
self_canvas->addChildCanvas(src_canvas);
|
||||
src_canvas->addParentCanvas(self_canvas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
CanvasWeakPtr Image::getSrcCanvas() const
|
||||
{
|
||||
return _src_canvas;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setImage(osg::ref_ptr<osg::Image> img)
|
||||
{
|
||||
// remove canvas...
|
||||
setSrcCanvas( CanvasPtr() );
|
||||
|
||||
_texture->setResizeNonPowerOfTwoHint(false);
|
||||
_texture->setImage(img);
|
||||
_texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
|
||||
_texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
|
||||
_geom->getOrCreateStateSet()
|
||||
->setTextureAttributeAndModes(0, _texture);
|
||||
|
||||
if( img )
|
||||
setupDefaultDimensions();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setFill(const std::string& fill)
|
||||
{
|
||||
osg::Vec4 color(1,1,1,1);
|
||||
if( !fill.empty() // If no color is given default to white
|
||||
&& !parseColor(fill, color) )
|
||||
return;
|
||||
|
||||
_colors->front() = color;
|
||||
_colors->dirty();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setOutset(const std::string& outset)
|
||||
{
|
||||
_outset = CSSBorder::parse(outset);
|
||||
_attributes_dirty |= DEST_SIZE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setPreserveAspectRatio(const std::string& scale)
|
||||
{
|
||||
_preserve_aspect_ratio = SVGpreserveAspectRatio::parse(scale);
|
||||
_attributes_dirty |= SRC_RECT;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setSlice(const std::string& slice)
|
||||
{
|
||||
_slice = CSSBorder::parse(slice);
|
||||
_attributes_dirty |= SRC_RECT | DEST_SIZE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setSliceWidth(const std::string& width)
|
||||
{
|
||||
_slice_width = CSSBorder::parse(width);
|
||||
_attributes_dirty |= DEST_SIZE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
const SGRect<float>& Image::getRegion() const
|
||||
{
|
||||
return _region;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Image::handleEvent(const EventPtr& event)
|
||||
{
|
||||
bool handled = Element::handleEvent(event);
|
||||
|
||||
CanvasPtr src_canvas = _src_canvas.lock();
|
||||
if( !src_canvas )
|
||||
return handled;
|
||||
|
||||
if( MouseEventPtr mouse_event = dynamic_cast<MouseEvent*>(event.get()) )
|
||||
{
|
||||
mouse_event.reset( new MouseEvent(*mouse_event) );
|
||||
|
||||
mouse_event->client_pos = mouse_event->local_pos
|
||||
- toOsg(_region.getMin());
|
||||
|
||||
osg::Vec2f size(_region.width(), _region.height());
|
||||
if( _outset.isValid() )
|
||||
{
|
||||
CSSBorder::Offsets outset =
|
||||
_outset.getAbsOffsets(getTextureDimensions());
|
||||
|
||||
mouse_event->client_pos += osg::Vec2f(outset.l, outset.t);
|
||||
size.x() += outset.l + outset.r;
|
||||
size.y() += outset.t + outset.b;
|
||||
}
|
||||
|
||||
// Scale event pos according to canvas view size vs. displayed/screen size
|
||||
mouse_event->client_pos.x() *= src_canvas->getViewWidth() / size.x();
|
||||
mouse_event->client_pos.y() *= src_canvas->getViewHeight()/ size.y();
|
||||
mouse_event->local_pos = mouse_event->client_pos;
|
||||
|
||||
handled |= src_canvas->handleMouseEvent(mouse_event);
|
||||
}
|
||||
else if( KeyboardEventPtr keyboard_event =
|
||||
dynamic_cast<KeyboardEvent*>(event.get()) )
|
||||
{
|
||||
handled |= src_canvas->handleKeyboardEvent(keyboard_event);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::childChanged(SGPropertyNode* child)
|
||||
{
|
||||
@@ -619,7 +635,9 @@ namespace canvas
|
||||
// Abort pending request
|
||||
if( _http_request )
|
||||
{
|
||||
Canvas::getSystemAdapter()->getHTTPClient()->cancelRequest(_http_request, "setting new image");
|
||||
Canvas::getSystemAdapter()
|
||||
->getHTTPClient()
|
||||
->cancelRequest(_http_request, "setting new image");
|
||||
_http_request.reset();
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,6 @@ namespace canvas
|
||||
ElementWeakPtr parent = 0 );
|
||||
virtual ~Image();
|
||||
|
||||
virtual void update(double dt);
|
||||
virtual void valueChanged(SGPropertyNode* child);
|
||||
|
||||
void setSrcCanvas(CanvasPtr canvas);
|
||||
@@ -61,6 +60,7 @@ namespace canvas
|
||||
|
||||
void setImage(osg::ref_ptr<osg::Image> img);
|
||||
void setFill(const std::string& fill);
|
||||
void setFill(const osg::Vec4& color);
|
||||
|
||||
/**
|
||||
* @see http://www.w3.org/TR/css3-background/#border-image-outset
|
||||
@@ -95,8 +95,12 @@ namespace canvas
|
||||
|
||||
bool handleEvent(const EventPtr& event);
|
||||
|
||||
protected:
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void setSourceRect(const SGRect<float>& sourceRect);
|
||||
|
||||
protected:
|
||||
enum ImageAttributes
|
||||
{
|
||||
SRC_RECT = LAST_ATTRIBUTE << 1, // Source image rectangle
|
||||
@@ -104,6 +108,8 @@ namespace canvas
|
||||
SRC_CANVAS = DEST_SIZE << 1
|
||||
};
|
||||
|
||||
virtual void updateImpl(double dt);
|
||||
|
||||
virtual void childChanged(SGPropertyNode * child);
|
||||
|
||||
void setupDefaultDimensions();
|
||||
@@ -129,9 +135,9 @@ namespace canvas
|
||||
osg::ref_ptr<osg::Vec2Array> _texCoords;
|
||||
osg::ref_ptr<osg::Vec4Array> _colors;
|
||||
|
||||
SGPropertyNode *_node_src_rect;
|
||||
SGRect<float> _src_rect,
|
||||
_region;
|
||||
SGPropertyNode *_node_src_rect = nullptr;
|
||||
SGRect<float> _src_rect {0, 0},
|
||||
_region {0, 0};
|
||||
|
||||
SVGpreserveAspectRatio _preserve_aspect_ratio;
|
||||
|
||||
|
||||
@@ -17,13 +17,15 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "CanvasMap.hxx"
|
||||
#include "map/geo_node_pair.hxx"
|
||||
#include "map/projection.hxx"
|
||||
|
||||
#include <cmath>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <cmath>
|
||||
|
||||
#define LOG_GEO_RET(msg) \
|
||||
{\
|
||||
@@ -46,6 +48,12 @@ namespace canvas
|
||||
const std::string GEO = "-geo";
|
||||
const std::string HDG = "hdg";
|
||||
const std::string Map::TYPE_NAME = "map";
|
||||
const std::string WEB_MERCATOR = "webmercator";
|
||||
const std::string REF_LAT = "ref-lat";
|
||||
const std::string REF_LON = "ref-lon";
|
||||
const std::string SCREEN_RANGE = "screen-range";
|
||||
const std::string RANGE = "range";
|
||||
const std::string PROJECTION = "projection";
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Map::staticInit()
|
||||
@@ -63,12 +71,11 @@ namespace canvas
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style,
|
||||
ElementWeakPtr parent ):
|
||||
Group(canvas, node, parent_style, parent),
|
||||
// TODO make projection configurable
|
||||
_projection(new SansonFlamsteedProjection),
|
||||
_projection_dirty(true)
|
||||
Group(canvas, node, parent_style, parent)
|
||||
{
|
||||
staticInit();
|
||||
|
||||
projectionNodeChanged(node->getChild(PROJECTION));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -78,13 +85,13 @@ namespace canvas
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Map::update(double dt)
|
||||
void Map::updateImpl(double dt)
|
||||
{
|
||||
for( GeoNodes::iterator it = _geo_nodes.begin();
|
||||
it != _geo_nodes.end();
|
||||
++it )
|
||||
Group::updateImpl(dt);
|
||||
|
||||
for(auto& it: _geo_nodes)
|
||||
{
|
||||
GeoNodePair* geo_node = it->second.get();
|
||||
GeoNodePair* geo_node = it.second.get();
|
||||
if( !geo_node->isComplete()
|
||||
|| (!geo_node->isDirty() && !_projection_dirty) )
|
||||
continue;
|
||||
@@ -106,14 +113,12 @@ namespace canvas
|
||||
geo_node->setDirty(false);
|
||||
}
|
||||
_projection_dirty = false;
|
||||
|
||||
Group::update(dt);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Map::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
|
||||
{
|
||||
if( boost::ends_with(child->getNameString(), GEO) )
|
||||
if( strutils::ends_with(child->getNameString(), GEO) )
|
||||
_geo_nodes[child].reset(new GeoNodePair());
|
||||
else if( parent != _node && child->getNameString() == HDG )
|
||||
_hdg_nodes.insert(child);
|
||||
@@ -124,7 +129,7 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
void Map::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
|
||||
{
|
||||
if( boost::ends_with(child->getNameString(), GEO) )
|
||||
if( strutils::ends_with(child->getNameString(), GEO) )
|
||||
// TODO remove from other node
|
||||
_geo_nodes.erase(child);
|
||||
else if( parent != _node && child->getName() == HDG )
|
||||
@@ -146,7 +151,7 @@ namespace canvas
|
||||
{
|
||||
const std::string& name = child->getNameString();
|
||||
|
||||
if( boost::ends_with(name, GEO) )
|
||||
if( strutils::ends_with(name, GEO) )
|
||||
return geoNodeChanged(child);
|
||||
else if( name == HDG )
|
||||
return hdgNodeChanged(child);
|
||||
@@ -161,10 +166,10 @@ namespace canvas
|
||||
if( child->getParent() != _node )
|
||||
return Group::childChanged(child);
|
||||
|
||||
if( child->getNameString() == "ref-lat"
|
||||
|| child->getNameString() == "ref-lon" )
|
||||
_projection->setWorldPosition( _node->getDoubleValue("ref-lat"),
|
||||
_node->getDoubleValue("ref-lon") );
|
||||
if( child->getNameString() == REF_LAT
|
||||
|| child->getNameString() == REF_LON )
|
||||
_projection->setWorldPosition( _node->getDoubleValue(REF_LAT),
|
||||
_node->getDoubleValue(REF_LON) );
|
||||
else if( child->getNameString() == HDG )
|
||||
{
|
||||
_projection->setOrientation(child->getFloatValue());
|
||||
@@ -173,16 +178,35 @@ namespace canvas
|
||||
++it )
|
||||
hdgNodeChanged(*it);
|
||||
}
|
||||
else if( child->getNameString() == "range" )
|
||||
else if( child->getNameString() == RANGE )
|
||||
_projection->setRange(child->getDoubleValue());
|
||||
else if( child->getNameString() == "screen-range" )
|
||||
else if( child->getNameString() == SCREEN_RANGE )
|
||||
_projection->setScreenRange(child->getDoubleValue());
|
||||
else if( child->getNameString() == PROJECTION )
|
||||
projectionNodeChanged(child);
|
||||
else
|
||||
return Group::childChanged(child);
|
||||
|
||||
_projection_dirty = true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Map::projectionNodeChanged(SGPropertyNode* child)
|
||||
{
|
||||
if(child && child->getStringValue() == WEB_MERCATOR)
|
||||
_projection = std::make_shared<WebMercatorProjection>();
|
||||
else
|
||||
_projection = std::make_shared<SansonFlamsteedProjection>();
|
||||
|
||||
_projection->setWorldPosition(_node->getDoubleValue(REF_LAT),
|
||||
_node->getDoubleValue(REF_LON) );
|
||||
_projection->setOrientation(_node->getFloatValue(HDG));
|
||||
_projection->setScreenRange(_node->getDoubleValue(SCREEN_RANGE));
|
||||
_projection->setRange(_node->getDoubleValue(RANGE));
|
||||
|
||||
_projection_dirty = true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Map::geoNodeChanged(SGPropertyNode* child)
|
||||
{
|
||||
|
||||
@@ -22,9 +22,9 @@
|
||||
|
||||
#include "CanvasGroup.hxx"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/unordered_set.hpp>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
@@ -45,45 +45,41 @@ namespace canvas
|
||||
ElementWeakPtr parent = 0 );
|
||||
virtual ~Map();
|
||||
|
||||
virtual void update(double dt);
|
||||
|
||||
virtual void childAdded( SGPropertyNode * parent,
|
||||
SGPropertyNode * child );
|
||||
virtual void childRemoved( SGPropertyNode * parent,
|
||||
SGPropertyNode * child );
|
||||
virtual void valueChanged(SGPropertyNode * child);
|
||||
|
||||
protected:
|
||||
virtual void updateImpl(double dt);
|
||||
|
||||
virtual void childChanged(SGPropertyNode * child);
|
||||
void updateProjection(SGPropertyNode* type_node);
|
||||
|
||||
typedef boost::unordered_map< SGPropertyNode*,
|
||||
boost::shared_ptr<GeoNodePair>
|
||||
> GeoNodes;
|
||||
typedef boost::unordered_set<SGPropertyNode*> NodeSet;
|
||||
virtual void childAdded( SGPropertyNode* parent,
|
||||
SGPropertyNode* child );
|
||||
virtual void childRemoved( SGPropertyNode* parent,
|
||||
SGPropertyNode* child );
|
||||
virtual void valueChanged(SGPropertyNode* child);
|
||||
virtual void childChanged(SGPropertyNode* child);
|
||||
|
||||
using GeoNodes =
|
||||
std::unordered_map<SGPropertyNode*, std::shared_ptr<GeoNodePair>>;
|
||||
using NodeSet = std::unordered_set<SGPropertyNode*>;
|
||||
|
||||
GeoNodes _geo_nodes;
|
||||
NodeSet _hdg_nodes;
|
||||
boost::shared_ptr<HorizontalProjection> _projection;
|
||||
bool _projection_dirty;
|
||||
std::shared_ptr<HorizontalProjection> _projection;
|
||||
bool _projection_dirty = false;
|
||||
|
||||
struct GeoCoord
|
||||
{
|
||||
GeoCoord():
|
||||
type(INVALID),
|
||||
value(0)
|
||||
{}
|
||||
enum
|
||||
{
|
||||
INVALID,
|
||||
LATITUDE,
|
||||
LONGITUDE
|
||||
} type;
|
||||
double value;
|
||||
} type = INVALID;
|
||||
double value = 0;
|
||||
};
|
||||
|
||||
void geoNodeChanged(SGPropertyNode * child);
|
||||
void hdgNodeChanged(SGPropertyNode * child);
|
||||
void projectionNodeChanged(SGPropertyNode* child);
|
||||
void geoNodeChanged(SGPropertyNode* child);
|
||||
void hdgNodeChanged(SGPropertyNode* child);
|
||||
|
||||
GeoCoord parseGeoCoord(const std::string& val) const;
|
||||
};
|
||||
|
||||
@@ -16,14 +16,18 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "CanvasPath.hxx"
|
||||
#include <simgear/scene/util/parse_color.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
|
||||
#include <osg/Drawable>
|
||||
#include <osg/Version>
|
||||
|
||||
#include <vg/openvg.h>
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
@@ -57,21 +61,158 @@ namespace canvas
|
||||
*/
|
||||
std::vector<float> splitAndConvert(const char del[], const std::string& str);
|
||||
|
||||
static float parseCSSNumber(const std::string& s)
|
||||
{
|
||||
if (strutils::ends_with(s, "px")) {
|
||||
return std::stof(s.substr(0, s.length() - 2));
|
||||
} else if (s.back() == '%') {
|
||||
float f = std::stof(s.substr(0, s.length() - 1));
|
||||
return f / 100.0f;
|
||||
}
|
||||
return std::stof(s);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
static bool parseSVGPathToVGPath(const std::string& svgPath, CmdList& commands, CoordList& coords)
|
||||
{
|
||||
const string_list& tokens = simgear::strutils::split_on_any_of(svgPath, "\t \n\r,");
|
||||
char activeSVGCommand = 0;
|
||||
bool isRelative = false;
|
||||
int tokensNeeded = 0;
|
||||
|
||||
for (auto it = tokens.begin(); it != tokens.end(); ) {
|
||||
// set up the new command data
|
||||
if ((it->size() == 1) && std::isalpha(it->at(0))) {
|
||||
const char svgCommand = std::toupper(it->at(0));
|
||||
isRelative = std::islower(it->at(0));
|
||||
switch (svgCommand) {
|
||||
case 'Z':
|
||||
tokensNeeded = 0;
|
||||
break;
|
||||
case 'M':
|
||||
case 'L':
|
||||
case 'T':
|
||||
tokensNeeded = 2;
|
||||
break;
|
||||
case 'H':
|
||||
case 'V':
|
||||
tokensNeeded = 1;
|
||||
break;
|
||||
case 'C':
|
||||
tokensNeeded = 6;
|
||||
break;
|
||||
case 'S':
|
||||
case 'Q':
|
||||
tokensNeeded = 4;
|
||||
break;
|
||||
case 'A':
|
||||
tokensNeeded = 7;
|
||||
break;
|
||||
default:
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "unrecognized SVG path command: "
|
||||
<< *it << " at token " << std::distance(tokens.begin(), it));
|
||||
return false;
|
||||
}
|
||||
|
||||
activeSVGCommand = svgCommand;
|
||||
++it; // advance to first coordinate token
|
||||
}
|
||||
|
||||
const int numTokensRemaining = std::distance(it, tokens.end());
|
||||
if (numTokensRemaining < tokensNeeded) {
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "insufficent SVG path tokens");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool pushTokensDirectly = true;
|
||||
if (activeSVGCommand == 'Z') {
|
||||
commands.push_back(VG_CLOSE_PATH);
|
||||
activeSVGCommand = 0;
|
||||
} else if (activeSVGCommand == 'M') {
|
||||
commands.push_back(VG_MOVE_TO | isRelative);
|
||||
activeSVGCommand = 'L';
|
||||
} else if (activeSVGCommand == 'L') {
|
||||
commands.push_back(VG_LINE_TO | isRelative);
|
||||
} else if (activeSVGCommand == 'H') {
|
||||
commands.push_back(VG_HLINE_TO | isRelative);
|
||||
} else if (activeSVGCommand == 'V') {
|
||||
commands.push_back(VG_HLINE_TO | isRelative);
|
||||
} else if (activeSVGCommand == 'C') {
|
||||
commands.push_back(VG_CUBIC_TO | isRelative);
|
||||
} else if (activeSVGCommand == 'S') {
|
||||
commands.push_back(VG_SCUBIC_TO | isRelative);
|
||||
} else if (activeSVGCommand == 'Q') {
|
||||
commands.push_back(VG_SCUBIC_TO | isRelative);
|
||||
} else if (activeSVGCommand == 'T') {
|
||||
commands.push_back(VG_SCUBIC_TO | isRelative);
|
||||
} else if (activeSVGCommand == 'A') {
|
||||
pushTokensDirectly = false; // deal with tokens manually
|
||||
coords.push_back(parseCSSNumber(*it++)); // rx
|
||||
coords.push_back(parseCSSNumber(*it++)); // ry
|
||||
coords.push_back(parseCSSNumber(*it++)); // x-axis rotation
|
||||
|
||||
const bool isLargeArc = std::stoi(*it++); // large-angle
|
||||
const bool isCCW = std::stoi(*it++); // sweep-flag
|
||||
|
||||
int vgCmd = isLargeArc ? (isCCW ? VG_LCCWARC_TO : VG_LCWARC_TO) :
|
||||
(isCCW ? VG_SCCWARC_TO : VG_SCWARC_TO);
|
||||
|
||||
coords.push_back(parseCSSNumber(*it++));
|
||||
coords.push_back(parseCSSNumber(*it++));
|
||||
commands.push_back(vgCmd | isRelative);
|
||||
} else {
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "malformed SVG path string: expected a command at token:"
|
||||
<< std::distance(tokens.begin(), it) << " :" << *it);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pushTokensDirectly) {
|
||||
for (int i=0; i<tokensNeeded;++i) {
|
||||
coords.push_back(parseCSSNumber(*it++));
|
||||
}
|
||||
}
|
||||
} // of tokens iteration
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static SGVec2f parseRectCornerRadius(SGPropertyNode* node, const std::string& xDir, const std::string& yDir, bool& haveCorner)
|
||||
{
|
||||
haveCorner = false;
|
||||
std::string propName = "border-" + yDir + "-" + xDir + "-radius";
|
||||
if (!node->hasChild(propName)) {
|
||||
propName = "border-" + yDir + "-radius";
|
||||
if (!node->hasChild(propName)) {
|
||||
propName = "border-radius";
|
||||
}
|
||||
}
|
||||
|
||||
PropertyList props = node->getChildren(propName);
|
||||
if (props.size() == 1) {
|
||||
double r = props.at(0)->getDoubleValue(propName);
|
||||
haveCorner = true;
|
||||
return SGVec2f(r, r);
|
||||
}
|
||||
|
||||
if (props.size() >= 2 ) {
|
||||
haveCorner = true;
|
||||
return SGVec2f(props.at(0)->getDoubleValue(),
|
||||
props.at(1)->getDoubleValue());
|
||||
}
|
||||
|
||||
return SGVec2f(-1.0f, -1.0f);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
class Path::PathDrawable:
|
||||
public osg::Drawable
|
||||
{
|
||||
public:
|
||||
PathDrawable(Path* path):
|
||||
_path_element(path),
|
||||
_path(VG_INVALID_HANDLE),
|
||||
_paint(VG_INVALID_HANDLE),
|
||||
_paint_fill(VG_INVALID_HANDLE),
|
||||
_attributes_dirty(~0),
|
||||
_mode(0),
|
||||
_fill_rule(VG_EVEN_ODD),
|
||||
_stroke_width(1),
|
||||
_stroke_linecap(VG_CAP_BUTT),
|
||||
_stroke_linejoin(VG_JOIN_MITER)
|
||||
_path_element(path)
|
||||
{
|
||||
setSupportsDisplayList(false);
|
||||
setDataVariance(Object::DYNAMIC);
|
||||
@@ -132,6 +273,16 @@ namespace canvas
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set path fill opacity (Only used if fill is not "none")
|
||||
*/
|
||||
void setFillOpacity(float opacity)
|
||||
{
|
||||
_fill_opacity =
|
||||
static_cast<uint8_t>(SGMiscf::clip(opacity, 0.f, 1.f) * 255);
|
||||
_attributes_dirty |= FILL_COLOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set path fill rule ("pseudo-nonzero" or "evenodd")
|
||||
*
|
||||
@@ -159,7 +310,7 @@ namespace canvas
|
||||
else if( parseColor(stroke, _stroke_color) )
|
||||
{
|
||||
_mode |= VG_STROKE_PATH;
|
||||
_attributes_dirty |= STROKE_COLOR;
|
||||
_attributes_dirty |= STROKE_COLOR;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -172,6 +323,16 @@ namespace canvas
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set path stroke opacity (only used if stroke is not "none")
|
||||
*/
|
||||
void setStrokeOpacity(float opacity)
|
||||
{
|
||||
_stroke_opacity =
|
||||
static_cast<uint8_t>(SGMiscf::clip(opacity, 0.f, 1.f) * 255);
|
||||
_attributes_dirty |= STROKE_COLOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set stroke width
|
||||
*/
|
||||
@@ -239,31 +400,22 @@ namespace canvas
|
||||
osg::StateAttribute const* blend_func =
|
||||
state->getLastAppliedAttribute(osg::StateAttribute::BLENDFUNC);
|
||||
|
||||
// Initialize/Update the paint
|
||||
if( _attributes_dirty & STROKE_COLOR )
|
||||
{
|
||||
if( _paint == VG_INVALID_HANDLE )
|
||||
_paint = vgCreatePaint();
|
||||
|
||||
vgSetParameterfv(_paint, VG_PAINT_COLOR, 4, _stroke_color._v);
|
||||
|
||||
_attributes_dirty &= ~STROKE_COLOR;
|
||||
}
|
||||
|
||||
// Initialize/update fill paint
|
||||
if( _attributes_dirty & FILL_COLOR )
|
||||
{
|
||||
if( _paint_fill == VG_INVALID_HANDLE )
|
||||
_paint_fill = vgCreatePaint();
|
||||
|
||||
vgSetParameterfv(_paint_fill, VG_PAINT_COLOR, 4, _fill_color._v);
|
||||
|
||||
_attributes_dirty &= ~FILL_COLOR;
|
||||
}
|
||||
|
||||
// Setup paint
|
||||
if( _mode & VG_STROKE_PATH )
|
||||
{
|
||||
// Initialize/Update the paint
|
||||
if( _attributes_dirty & STROKE_COLOR )
|
||||
{
|
||||
if( _paint == VG_INVALID_HANDLE )
|
||||
_paint = vgCreatePaint();
|
||||
|
||||
auto color = _stroke_color;
|
||||
color.a() *= _stroke_opacity / 255.f;
|
||||
vgSetParameterfv(_paint, VG_PAINT_COLOR, 4, color._v);
|
||||
|
||||
_attributes_dirty &= ~STROKE_COLOR;
|
||||
}
|
||||
|
||||
vgSetPaint(_paint, VG_STROKE_PATH);
|
||||
|
||||
vgSetf(VG_STROKE_LINE_WIDTH, _stroke_width);
|
||||
@@ -275,6 +427,19 @@ namespace canvas
|
||||
}
|
||||
if( _mode & VG_FILL_PATH )
|
||||
{
|
||||
// Initialize/update fill paint
|
||||
if( _attributes_dirty & FILL_COLOR )
|
||||
{
|
||||
if( _paint_fill == VG_INVALID_HANDLE )
|
||||
_paint_fill = vgCreatePaint();
|
||||
|
||||
auto color = _fill_color;
|
||||
color.a() *= _fill_opacity / 255.f;
|
||||
vgSetParameterfv(_paint_fill, VG_PAINT_COLOR, 4, color._v);
|
||||
|
||||
_attributes_dirty &= ~FILL_COLOR;
|
||||
}
|
||||
|
||||
vgSetPaint(_paint_fill, VG_FILL_PATH);
|
||||
|
||||
vgSeti(VG_FILL_RULE, _fill_rule);
|
||||
@@ -428,22 +593,24 @@ namespace canvas
|
||||
|
||||
Path *_path_element;
|
||||
|
||||
mutable VGPath _path;
|
||||
mutable VGPaint _paint;
|
||||
mutable VGPaint _paint_fill;
|
||||
mutable uint32_t _attributes_dirty;
|
||||
mutable VGPath _path {VG_INVALID_HANDLE};
|
||||
mutable VGPaint _paint {VG_INVALID_HANDLE};
|
||||
mutable VGPaint _paint_fill {VG_INVALID_HANDLE};
|
||||
mutable uint32_t _attributes_dirty {~0u};
|
||||
|
||||
CmdList _cmds;
|
||||
CoordList _coords;
|
||||
|
||||
VGbitfield _mode;
|
||||
VGbitfield _mode {0};
|
||||
osg::Vec4f _fill_color;
|
||||
VGFillRule _fill_rule;
|
||||
uint8_t _fill_opacity {255};
|
||||
VGFillRule _fill_rule {VG_EVEN_ODD};
|
||||
osg::Vec4f _stroke_color;
|
||||
VGfloat _stroke_width;
|
||||
uint8_t _stroke_opacity {255};
|
||||
VGfloat _stroke_width {1};
|
||||
std::vector<VGfloat> _stroke_dash;
|
||||
VGCapStyle _stroke_linecap;
|
||||
VGJoinStyle _stroke_linejoin;
|
||||
VGCapStyle _stroke_linecap {VG_CAP_BUTT};
|
||||
VGJoinStyle _stroke_linejoin {VG_JOIN_MITER};
|
||||
|
||||
osg::Vec3f transformPoint( const osg::Matrix& m,
|
||||
osg::Vec2f pos ) const
|
||||
@@ -519,8 +686,10 @@ namespace canvas
|
||||
PathDrawableRef Path::*path = &Path::_path;
|
||||
|
||||
addStyle("fill", "color", &PathDrawable::setFill, path);
|
||||
addStyle("fill-opacity", "numeric", &PathDrawable::setFillOpacity, path);
|
||||
addStyle("fill-rule", "", &PathDrawable::setFillRule, path);
|
||||
addStyle("stroke", "color", &PathDrawable::setStroke, path);
|
||||
addStyle("stroke-opacity", "numeric", &PathDrawable::setStrokeOpacity, path);
|
||||
addStyle("stroke-width", "numeric", &PathDrawable::setStrokeWidth, path);
|
||||
addStyle("stroke-dasharray", "", &PathDrawable::setStrokeDashArray, path);
|
||||
addStyle("stroke-linecap", "", &PathDrawable::setStrokeLinecap, path);
|
||||
@@ -533,7 +702,9 @@ namespace canvas
|
||||
const Style& parent_style,
|
||||
ElementWeakPtr parent ):
|
||||
Element(canvas, node, parent_style, parent),
|
||||
_path( new PathDrawable(this) )
|
||||
_path( new PathDrawable(this) ),
|
||||
_hasSVG(false),
|
||||
_hasRect(false)
|
||||
{
|
||||
staticInit();
|
||||
|
||||
@@ -547,75 +718,69 @@ namespace canvas
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Path::update(double dt)
|
||||
{
|
||||
if( _attributes_dirty & (CMDS | COORDS) )
|
||||
{
|
||||
_path->setSegments
|
||||
(
|
||||
_node->getChildValues<VGubyte, int>("cmd"),
|
||||
_node->getChildValues<VGfloat, float>("coord")
|
||||
);
|
||||
|
||||
_attributes_dirty &= ~(CMDS | COORDS);
|
||||
}
|
||||
|
||||
Element::update(dt);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::BoundingBox Path::getTransformedBounds(const osg::Matrix& m) const
|
||||
{
|
||||
return _path->getTransformedBounds(m);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path& Path::addSegment(uint8_t cmd, std::initializer_list<float> coords)
|
||||
{
|
||||
_node->addChild("cmd")->setIntValue(cmd);
|
||||
|
||||
for(float coord: coords)
|
||||
_node->addChild("coord")->setFloatValue(coord);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path& Path::moveTo(float x_abs, float y_abs)
|
||||
{
|
||||
return addSegment(VG_MOVE_TO_ABS, x_abs, y_abs);
|
||||
return addSegment(VG_MOVE_TO_ABS, {x_abs, y_abs});
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path& Path::move(float x_rel, float y_rel)
|
||||
{
|
||||
return addSegment(VG_MOVE_TO_REL, x_rel, y_rel);
|
||||
return addSegment(VG_MOVE_TO_REL, {x_rel, y_rel});
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path& Path::lineTo(float x_abs, float y_abs)
|
||||
{
|
||||
return addSegment(VG_LINE_TO_ABS, x_abs, y_abs);
|
||||
return addSegment(VG_LINE_TO_ABS, {x_abs, y_abs});
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path& Path::line(float x_rel, float y_rel)
|
||||
{
|
||||
return addSegment(VG_LINE_TO_REL, x_rel, y_rel);
|
||||
return addSegment(VG_LINE_TO_REL, {x_rel, y_rel});
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path& Path::horizTo(float x_abs)
|
||||
{
|
||||
return addSegment(VG_HLINE_TO_ABS, x_abs);
|
||||
return addSegment(VG_HLINE_TO_ABS, {x_abs});
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path& Path::horiz(float x_rel)
|
||||
{
|
||||
return addSegment(VG_HLINE_TO_REL, x_rel);
|
||||
return addSegment(VG_HLINE_TO_REL, {x_rel});
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path& Path::vertTo(float y_abs)
|
||||
{
|
||||
return addSegment(VG_VLINE_TO_ABS, y_abs);
|
||||
return addSegment(VG_VLINE_TO_ABS, {y_abs});
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path& Path::vert(float y_rel)
|
||||
{
|
||||
return addSegment(VG_VLINE_TO_REL, y_rel);
|
||||
return addSegment(VG_VLINE_TO_REL, {y_rel});
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -630,16 +795,109 @@ namespace canvas
|
||||
childChanged(child);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Path::setSVGPath(const std::string& svgPath)
|
||||
{
|
||||
_node->setStringValue("svg", svgPath);
|
||||
_hasSVG = true;
|
||||
_attributes_dirty |= SVG;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Path::setRect(const SGRect<float> &r)
|
||||
{
|
||||
_rect = r;
|
||||
_hasRect = true;
|
||||
_attributes_dirty |= RECT;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Path::setRoundRect(const SGRect<float> &r, float radiusX, float radiusY)
|
||||
{
|
||||
if (radiusY < 0.0) {
|
||||
radiusY = radiusX;
|
||||
}
|
||||
|
||||
setRect(r);
|
||||
_node->getChild("border-radius", 0, true)->setDoubleValue(radiusX);
|
||||
_node->getChild("border-radius", 1, true)->setDoubleValue(radiusY);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Path::updateImpl(double dt)
|
||||
{
|
||||
Element::updateImpl(dt);
|
||||
|
||||
if( _attributes_dirty & (CMDS | COORDS) )
|
||||
{
|
||||
_path->setSegments
|
||||
(
|
||||
_node->getChildValues<VGubyte, int>("cmd"),
|
||||
_node->getChildValues<VGfloat, float>("coord")
|
||||
);
|
||||
|
||||
_attributes_dirty &= ~(CMDS | COORDS);
|
||||
}
|
||||
|
||||
// SVG path overrides manual cmd/coord specification
|
||||
if( _hasSVG && (_attributes_dirty & SVG) )
|
||||
{
|
||||
CmdList cmds;
|
||||
CoordList coords;
|
||||
parseSVGPathToVGPath(_node->getStringValue("svg"), cmds, coords);
|
||||
_path->setSegments(cmds, coords);
|
||||
_attributes_dirty &= ~SVG;
|
||||
}
|
||||
|
||||
if( _hasRect &&(_attributes_dirty & RECT) )
|
||||
{
|
||||
parseRectToVGPath();
|
||||
_attributes_dirty &= ~RECT;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Path::childChanged(SGPropertyNode* child)
|
||||
{
|
||||
const std::string& name = child->getNameString();
|
||||
const std::string &prName = child->getParent()->getNameString();
|
||||
|
||||
if( strutils::starts_with(name, "border-") )
|
||||
{
|
||||
_attributes_dirty |= RECT;
|
||||
return;
|
||||
}
|
||||
|
||||
if (prName == "rect")
|
||||
{
|
||||
_hasRect = true;
|
||||
if (name == "left") {
|
||||
_rect.setLeft(child->getDoubleValue());
|
||||
} else if (name == "top") {
|
||||
_rect.setTop(child->getDoubleValue());
|
||||
} else if (name == "right") {
|
||||
_rect.setRight(child->getDoubleValue());
|
||||
} else if (name == "bottom") {
|
||||
_rect.setBottom(child->getDoubleValue());
|
||||
} else if (name == "width") {
|
||||
_rect.setWidth(child->getDoubleValue());
|
||||
} else if (name == "height") {
|
||||
_rect.setHeight(child->getDoubleValue());
|
||||
}
|
||||
_attributes_dirty |= RECT;
|
||||
return;
|
||||
}
|
||||
|
||||
if( child->getParent() != _node )
|
||||
return;
|
||||
|
||||
if( child->getNameString() == "cmd" )
|
||||
if( name == "cmd" )
|
||||
_attributes_dirty |= CMDS;
|
||||
else if( child->getNameString() == "coord" )
|
||||
else if( name == "coord" )
|
||||
_attributes_dirty |= COORDS;
|
||||
else if ( name == "svg")
|
||||
_hasSVG = true;
|
||||
_attributes_dirty |= SVG;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -664,5 +922,68 @@ namespace canvas
|
||||
return values;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void operator+=(CoordList& base, const std::initializer_list<VGfloat>& other)
|
||||
{
|
||||
base.insert(base.end(), other.begin(), other.end());
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Path::parseRectToVGPath()
|
||||
{
|
||||
CmdList commands;
|
||||
CoordList coords;
|
||||
commands.reserve(4);
|
||||
coords.reserve(8);
|
||||
|
||||
bool haveCorner = false;
|
||||
SGVec2f topLeft = parseRectCornerRadius(_node, "left", "top", haveCorner);
|
||||
if (haveCorner) {
|
||||
commands.push_back(VG_MOVE_TO_ABS);
|
||||
coords += {_rect.l(), _rect.t() + topLeft.y()};
|
||||
commands.push_back(VG_SCCWARC_TO_REL);
|
||||
coords += {topLeft.x(), topLeft.y(), 0.0, topLeft.x(), -topLeft.y()};
|
||||
} else {
|
||||
commands.push_back(VG_MOVE_TO_ABS);
|
||||
coords += {_rect.l(), _rect.t()};
|
||||
}
|
||||
|
||||
SGVec2f topRight = parseRectCornerRadius(_node, "right", "top", haveCorner);
|
||||
if (haveCorner) {
|
||||
commands.push_back(VG_HLINE_TO_ABS);
|
||||
coords += {_rect.r() - topRight.x()};
|
||||
commands.push_back(VG_SCCWARC_TO_REL);
|
||||
coords += {topRight.x(), topRight.y(), 0.0, topRight.x(), topRight.y()};
|
||||
} else {
|
||||
commands.push_back(VG_HLINE_TO_ABS);
|
||||
coords += {_rect.r()};
|
||||
}
|
||||
|
||||
SGVec2f bottomRight = parseRectCornerRadius(_node, "right", "bottom", haveCorner);
|
||||
if (haveCorner) {
|
||||
commands.push_back(VG_VLINE_TO_ABS);
|
||||
coords += {_rect.b() - bottomRight.y()};
|
||||
commands.push_back(VG_SCCWARC_TO_REL);
|
||||
coords += {bottomRight.x(), bottomRight.y(), 0.0, -bottomRight.x(), bottomRight.y()};
|
||||
} else {
|
||||
commands.push_back(VG_VLINE_TO_ABS);
|
||||
coords += {_rect.b()};
|
||||
}
|
||||
|
||||
SGVec2f bottomLeft = parseRectCornerRadius(_node, "left", "bottom", haveCorner);
|
||||
if (haveCorner) {
|
||||
commands.push_back(VG_HLINE_TO_ABS);
|
||||
coords += {_rect.l() + bottomLeft.x()};
|
||||
commands.push_back(VG_SCCWARC_TO_REL);
|
||||
coords += {bottomLeft.x(), bottomLeft.y(), 0.0, -bottomLeft.x(), -bottomLeft.y()};
|
||||
} else {
|
||||
commands.push_back(VG_HLINE_TO_ABS);
|
||||
coords += {_rect.l()};
|
||||
}
|
||||
|
||||
commands.push_back(VG_CLOSE_PATH);
|
||||
_path->setSegments(commands, coords);
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
#define CANVAS_PATH_HXX_
|
||||
|
||||
#include "CanvasElement.hxx"
|
||||
#include <boost/preprocessor/iteration/iterate.hpp>
|
||||
#include <simgear/math/SGRect.hxx>
|
||||
#include <initializer_list>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
@@ -39,14 +40,10 @@ namespace canvas
|
||||
ElementWeakPtr parent = 0 );
|
||||
virtual ~Path();
|
||||
|
||||
virtual void update(double dt);
|
||||
|
||||
virtual osg::BoundingBox getTransformedBounds(const osg::Matrix& m) const;
|
||||
|
||||
#define BOOST_PP_ITERATION_LIMITS (0, 6)
|
||||
#define BOOST_PP_FILENAME_1 \
|
||||
<simgear/canvas/elements/detail/add_segment_variadic.hxx>
|
||||
#include BOOST_PP_ITERATE()
|
||||
/** Add a segment with the given command and coordinates */
|
||||
Path& addSegment(uint8_t cmd, std::initializer_list<float> coords = {});
|
||||
|
||||
/** Move path cursor */
|
||||
Path& moveTo(float x_abs, float y_abs);
|
||||
@@ -67,20 +64,34 @@ namespace canvas
|
||||
/** Close the path (implicit lineTo to first point of path) */
|
||||
Path& close();
|
||||
|
||||
protected:
|
||||
void setSVGPath(const std::string& svgPath);
|
||||
|
||||
void setRect(const SGRectf& r);
|
||||
void setRoundRect(const SGRectf& r, float radiusX, float radiusY = -1.0);
|
||||
|
||||
protected:
|
||||
enum PathAttributes
|
||||
{
|
||||
CMDS = LAST_ATTRIBUTE << 1,
|
||||
COORDS = CMDS << 1
|
||||
COORDS = CMDS << 1,
|
||||
SVG = COORDS << 1,
|
||||
RECT = SVG << 1
|
||||
};
|
||||
|
||||
class PathDrawable;
|
||||
typedef osg::ref_ptr<PathDrawable> PathDrawableRef;
|
||||
PathDrawableRef _path;
|
||||
|
||||
bool _hasSVG : 1;
|
||||
bool _hasRect : 1;
|
||||
SGRectf _rect;
|
||||
|
||||
virtual void updateImpl(double dt);
|
||||
|
||||
virtual void childRemoved(SGPropertyNode * child);
|
||||
virtual void childChanged(SGPropertyNode * child);
|
||||
|
||||
void parseRectToVGPath();
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "CanvasText.hxx"
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
#include <simgear/canvas/CanvasSystemAdapter.hxx>
|
||||
@@ -496,18 +497,31 @@ namespace canvas
|
||||
{
|
||||
case LEFT_TO_RIGHT:
|
||||
{
|
||||
osg::Vec2 delta( activefont->getKerning( previous_charcode,
|
||||
charcode,
|
||||
_kerningType ) );
|
||||
#if OSG_VERSION_LESS_THAN(3,5,2)
|
||||
osg::Vec2 delta(activefont->getKerning(previous_charcode,
|
||||
charcode,
|
||||
_kerningType));
|
||||
#else
|
||||
osg::Vec2 delta(activefont->getKerning(_fontSize,
|
||||
previous_charcode,
|
||||
charcode,
|
||||
_kerningType));
|
||||
#endif
|
||||
cursor.x() += delta.x() * wr;
|
||||
cursor.y() += delta.y() * hr;
|
||||
break;
|
||||
}
|
||||
case RIGHT_TO_LEFT:
|
||||
{
|
||||
osg::Vec2 delta( activefont->getKerning( charcode,
|
||||
previous_charcode,
|
||||
_kerningType ) );
|
||||
#if OSG_VERSION_LESS_THAN(3,5,2)
|
||||
osg::Vec2 delta(activefont->getKerning(charcode,
|
||||
previous_charcode,
|
||||
_kerningType));
|
||||
#else
|
||||
osg::Vec2 delta(activefont->getKerning(_fontSize, charcode,
|
||||
previous_charcode,
|
||||
_kerningType));
|
||||
#endif
|
||||
cursor.x() -= delta.x() * wr;
|
||||
cursor.y() -= delta.y() * hr;
|
||||
break;
|
||||
@@ -851,12 +865,12 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
osg::StateSet* Text::getOrCreateStateSet()
|
||||
{
|
||||
if( !_transform.valid() )
|
||||
return 0;
|
||||
if( !_scene_group.valid() )
|
||||
return nullptr;
|
||||
|
||||
// Only check for StateSet on Transform, as the text stateset is shared
|
||||
// between all text instances using the same font (texture).
|
||||
return _transform->getOrCreateStateSet();
|
||||
return _scene_group->getOrCreateStateSet();
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
#ifndef CANVAS_PATH_HXX_
|
||||
# error Canvas - do not include this file!
|
||||
#endif
|
||||
|
||||
#define n BOOST_PP_ITERATION()
|
||||
|
||||
Path& addSegment( uint8_t cmd
|
||||
BOOST_PP_COMMA_IF(n)
|
||||
BOOST_PP_ENUM_PARAMS(n, float coord) )
|
||||
{
|
||||
_node->addChild("cmd")->setIntValue(cmd);
|
||||
|
||||
#define SG_CANVAS_PATH_SET_COORD(z, n, dummy)\
|
||||
_node->addChild("coord")->setFloatValue(coord##n);
|
||||
|
||||
BOOST_PP_REPEAT(n, SG_CANVAS_PATH_SET_COORD, 0)
|
||||
#undef SG_CANVAS_PATH_SET_COORD
|
||||
return *this;
|
||||
}
|
||||
|
||||
#undef n
|
||||
@@ -190,6 +190,32 @@ namespace canvas
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* WebMercator projection, relative to the projection center.
|
||||
* Required for Slippy Maps - i.e. openstreetmap
|
||||
*/
|
||||
class WebMercatorProjection:
|
||||
public HorizontalProjection
|
||||
{
|
||||
protected:
|
||||
|
||||
virtual ScreenPosition project(double lat, double lon) const
|
||||
{
|
||||
double d_lat = lat - _ref_lat,
|
||||
d_lon = lon - _ref_lon;
|
||||
double r = 6378137.f / 1852; // Equatorial radius divided by ?
|
||||
|
||||
ScreenPosition pos;
|
||||
|
||||
pos.x = r * d_lon;
|
||||
pos.y = r * (log(tan(d_lat) + 1.0 / cos(d_lat)));
|
||||
//pos.x = lon;
|
||||
//pos.y = log(tan(lat) + 1.0 / cos(lat));
|
||||
return pos;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "CustomEvent.hxx"
|
||||
|
||||
namespace simgear
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "DeviceEvent.hxx"
|
||||
#include <osgGA/GUIEventAdapter>
|
||||
|
||||
|
||||
@@ -16,8 +16,9 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "KeyboardEvent.hxx"
|
||||
#include "utf8.h"
|
||||
|
||||
#include <osgGA/GUIEventAdapter>
|
||||
|
||||
@@ -40,6 +41,7 @@ namespace canvas
|
||||
|
||||
// TODO check Win/Mac keycode for altgr/ISO Level3 Shift
|
||||
const uint32_t KEY_AltGraph = 0xfe03;
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
KeyboardEvent::KeyboardEvent():
|
||||
@@ -267,10 +269,24 @@ namespace canvas
|
||||
// Empty or no mapping -> convert UTF-32 key value to UTF-8
|
||||
if( _name.empty() )
|
||||
{
|
||||
if( !utf8::internal::is_code_point_valid(_key) )
|
||||
if (( _key >= 0xd800u && _key <= 0xdfffu ) || _key > 0x10ffffu )
|
||||
_name = "Unidentified";
|
||||
else
|
||||
utf8::unchecked::append(_key, std::back_inserter(_name));
|
||||
if ( _key <= 0x7f ) {
|
||||
_name.push_back(static_cast<uint8_t>(_key));
|
||||
} else if ( _key <= 0x7ff ) {
|
||||
_name.push_back(static_cast<uint8_t>((_key >> 6) | 0xc0));
|
||||
_name.push_back(static_cast<uint8_t>((_key & 0x3f) | 0x80));
|
||||
} else if ( _key <= 0xffff ) {
|
||||
_name.push_back(static_cast<uint8_t>((_key >> 12) | 0xe0));
|
||||
_name.push_back(static_cast<uint8_t>(((_key >> 6) & 0x3f) | 0x80));
|
||||
_name.push_back(static_cast<uint8_t>((_key & 0x3f) | 0x80));
|
||||
} else {
|
||||
_name.push_back(static_cast<uint8_t>((_key >> 18) | 0xf0));
|
||||
_name.push_back(static_cast<uint8_t>(((_key >> 12) & 0x3f) | 0x80));
|
||||
_name.push_back(static_cast<uint8_t>(((_key >> 6) & 0x3f) | 0x80));
|
||||
_name.push_back(static_cast<uint8_t>((_key & 0x3f) | 0x80));
|
||||
}
|
||||
}
|
||||
|
||||
// Keys on the numpad with NumLock enabled are reported just like their
|
||||
@@ -305,11 +321,30 @@ namespace canvas
|
||||
if( key_name.empty() )
|
||||
return false;
|
||||
|
||||
std::string::const_iterator it = key_name.begin();
|
||||
uint32_t cp = utf8::next(it, key_name.end());
|
||||
// Convert the key name to the corresponding code point by checking the
|
||||
// sequence length (the first bits of the first byte) and performing the
|
||||
// conversion accordingly.
|
||||
uint32_t cp = key_name[0] & 0xff;
|
||||
size_t len;
|
||||
if (cp < 0x80) {
|
||||
len = 1;
|
||||
} else if ((cp >> 5) == 0x6) {
|
||||
cp = ((cp << 6) & 0x7ff) + (key_name[1] & 0x3f);
|
||||
len = 2;
|
||||
} else if ((cp >> 4) == 0xe) {
|
||||
cp = ((cp << 12) & 0xffff) + (((key_name[1] & 0xff) << 6) & 0xfff)
|
||||
+ (key_name[2] & 0x3f);
|
||||
len = 3;
|
||||
} else if ((cp >> 3) == 0x1e) {
|
||||
cp = ((cp << 18) & 0x1fffff) + (((key_name[1] & 0xff) << 12) & 0x3ffff)
|
||||
+ (((key_name[2] & 0xff) << 6) & 0xfff) + (key_name[3] & 0x3f);
|
||||
len = 4;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if _name contains exactly one (UTF-8 encoded) character.
|
||||
if( it != key_name.end() )
|
||||
if (key_name.length() > len)
|
||||
return false;
|
||||
|
||||
// C0 and C1 control characters are not printable.
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "MouseEvent.hxx"
|
||||
#include <osgGA/GUIEventAdapter>
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "KeyboardEvent.hxx"
|
||||
|
||||
#include <osgViewer/Viewer>
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "BoxLayout.hxx"
|
||||
#include "SpacerItem.hxx"
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "Layout.hxx"
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "LayoutItem.hxx"
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "NasalWidget.hxx"
|
||||
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
@@ -43,7 +45,7 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
NasalWidget::~NasalWidget()
|
||||
{
|
||||
onRemove();
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -187,10 +189,10 @@ namespace canvas
|
||||
if( hfw.empty() )
|
||||
return -1;
|
||||
|
||||
naContext c = naNewContext();
|
||||
try
|
||||
{
|
||||
return hfw(nasal::to_nasal(c, const_cast<NasalWidget*>(this)), w);
|
||||
nasal::Context ctx;
|
||||
return hfw(ctx.to_me(const_cast<NasalWidget*>(this)), w);
|
||||
}
|
||||
catch( std::exception const& ex )
|
||||
{
|
||||
@@ -200,7 +202,6 @@ namespace canvas
|
||||
"NasalWidget.heightForWidth: callback error: '" << ex.what() << "'"
|
||||
);
|
||||
}
|
||||
naFreeContext(c);
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -260,8 +261,8 @@ namespace canvas
|
||||
|
||||
try
|
||||
{
|
||||
nasal::Context c;
|
||||
_set_geometry(nasal::to_nasal(c, this), rect);
|
||||
nasal::Context ctx;
|
||||
_set_geometry(ctx.to_me(this), rect);
|
||||
_flags &= ~LAYOUT_DIRTY;
|
||||
}
|
||||
catch( std::exception const& ex )
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "SpacerItem.hxx"
|
||||
|
||||
namespace simgear
|
||||
|
||||
@@ -40,96 +40,40 @@
|
||||
#define SG_DO_STRINGIZE(X) #X
|
||||
|
||||
#ifdef __GNUC__
|
||||
# if __GNUC__ < 3
|
||||
# error Time to upgrade. GNU compilers < 3.0 not supported
|
||||
# elif (__GNUC__ == 3) && (__GNUC_MINOR__ < 4)
|
||||
# warning GCC compilers prior to 3.4 are suspect
|
||||
# endif
|
||||
|
||||
# define SG_GCC_VERSION (__GNUC__ * 10000 \
|
||||
+ __GNUC_MINOR__ * 100 \
|
||||
+ __GNUC_PATCHLEVEL__)
|
||||
# define SG_COMPILER_STR "GNU C++ version " SG_STRINGIZE(__GNUC__) "." SG_STRINGIZE(__GNUC_MINOR__)
|
||||
#endif // __GNUC__
|
||||
|
||||
/* KAI C++ */
|
||||
#if defined(__KCC)
|
||||
# define SG_COMPILER_STR "Kai C++ version " SG_STRINGIZE(__KCC_VERSION)
|
||||
#endif // __KCC
|
||||
|
||||
//
|
||||
// Microsoft compilers.
|
||||
//
|
||||
#ifdef _MSC_VER
|
||||
# define bcopy(from, to, n) memcpy(to, from, n)
|
||||
# define strcasecmp stricmp
|
||||
|
||||
# if _MSC_VER >= 1200 // msvc++ 6.0 or greater
|
||||
# define isnan _isnan
|
||||
# define snprintf _snprintf
|
||||
# if _MSC_VER < 1500
|
||||
# define vsnprintf _vsnprintf
|
||||
# if _MSC_VER >= 1200 // msvc++ 6.0 up to MSVC2013
|
||||
# if _MSC_VER < 1900
|
||||
# define bcopy(from, to, n) memcpy(to, from, n)
|
||||
# define snprintf _snprintf
|
||||
# define strdup _strdup
|
||||
# define copysign _copysign
|
||||
# endif
|
||||
# define copysign _copysign
|
||||
# define strcasecmp stricmp
|
||||
|
||||
# undef min
|
||||
# undef max
|
||||
|
||||
# pragma warning(disable: 4786) // identifier was truncated to '255' characters
|
||||
# pragma warning(disable: 4244) // conversion from double to float
|
||||
# pragma warning(disable: 4305) //
|
||||
# pragma warning(disable: 4305) // truncation from larger type to smaller
|
||||
# pragma warning(disable: 4267) // conversion from size_t to int / 32-bit type
|
||||
# pragma warning(disable: 4996) // don't require _ prefix for standard library functions
|
||||
# pragma warning(disable: 4800) // don't warn about int -> bool performance
|
||||
|
||||
# else
|
||||
# error What version of MSVC++ is this?
|
||||
# endif
|
||||
|
||||
# define SG_COMPILER_STR "Microsoft Visual C++ version " SG_STRINGIZE(_MSC_VER)
|
||||
# define SG_COMPILER_STR "Microsoft Visual C++ version " SG_STRINGIZE(_MSC_VER)
|
||||
|
||||
#endif // _MSC_VER
|
||||
|
||||
//
|
||||
// Native SGI compilers
|
||||
//
|
||||
|
||||
#if defined ( sgi ) && !defined( __GNUC__ )
|
||||
# if (_COMPILER_VERSION < 740)
|
||||
# error Need MipsPro 7.4.0 or higher now
|
||||
# endif
|
||||
|
||||
#define SG_HAVE_NATIVE_SGI_COMPILERS
|
||||
|
||||
#pragma set woff 1001,1012,1014,1116,1155,1172,1174
|
||||
#pragma set woff 1401,1460,1551,1552,1681
|
||||
|
||||
#ifdef __cplusplus
|
||||
# pragma set woff 1682,3303
|
||||
# pragma set woff 3624
|
||||
#endif
|
||||
|
||||
# define SG_COMPILER_STR "SGI MipsPro compiler version " SG_STRINGIZE(_COMPILER_VERSION)
|
||||
|
||||
#endif // Native SGI compilers
|
||||
|
||||
|
||||
#if defined (__sun)
|
||||
# define SG_UNIX
|
||||
# include <strings.h>
|
||||
# include <memory.h>
|
||||
# if defined ( __cplusplus )
|
||||
// typedef unsigned int size_t;
|
||||
extern "C" {
|
||||
extern void *memmove(void *, const void *, size_t);
|
||||
}
|
||||
# else
|
||||
extern void *memmove(void *, const void *, size_t);
|
||||
# endif // __cplusplus
|
||||
|
||||
# if !defined( __GNUC__ )
|
||||
# define SG_COMPILER_STR "Sun compiler version " SG_STRINGIZE(__SUNPRO_CC)
|
||||
# endif
|
||||
|
||||
#endif // sun
|
||||
|
||||
//
|
||||
// Intel C++ Compiler
|
||||
//
|
||||
@@ -144,29 +88,10 @@
|
||||
#ifdef __APPLE__
|
||||
# define SG_MAC
|
||||
# define SG_UNIX
|
||||
# ifdef __GNUC__
|
||||
# if ( __GNUC__ > 3 ) || ( __GNUC__ == 3 && __GNUC_MINOR__ >= 3 )
|
||||
inline int (isnan)(double r) { return !(r <= 0 || r >= 0); }
|
||||
# else
|
||||
// any C++ header file undefines isinf and isnan
|
||||
// so this should be included before <iostream>
|
||||
// the functions are STILL in libm (libSystem on mac os x)
|
||||
extern "C" int (isnan)(double);
|
||||
extern "C" int (isinf)(double);
|
||||
# endif
|
||||
# else
|
||||
inline int (isnan)(double r) { return !(r <= 0 || r >= 0); }
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined (__FreeBSD__)
|
||||
# define SG_UNIX
|
||||
#include <sys/param.h>
|
||||
# if __FreeBSD_version < 500000
|
||||
extern "C" {
|
||||
inline int isnan(double r) { return !(r <= 0 || r >= 0); }
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined (__CYGWIN__)
|
||||
@@ -184,10 +109,13 @@ inline int (isnan)(double r) { return !(r <= 0 || r >= 0); }
|
||||
# define SG_UNIX
|
||||
#endif
|
||||
|
||||
#if defined( __GNUC__ )
|
||||
# define DEPRECATED __attribute__ ((deprecated))
|
||||
#ifdef __GNUC__
|
||||
#define SG_DEPRECATED(func) func __attribute__ ((deprecated))
|
||||
#elif defined(_MSC_VER)
|
||||
#define SG_DEPRECATED(func) __declspec(deprecated) func
|
||||
#else
|
||||
# define DEPRECATED
|
||||
#pragma message("WARNING: You need to implement SG_DEPRECATED for this compiler")
|
||||
#define SG_DEPRECATED(func) func
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
@@ -201,4 +129,3 @@ inline int (isnan)(double r) { return !(r <= 0 || r >= 0); }
|
||||
//
|
||||
|
||||
#endif // _SG_COMPILER_H
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ const float SG_RADIANS_TO_DEGREES = 180.0f / SG_PI;
|
||||
/** Value of earth radius from LaRCsim (ft) */
|
||||
#define SG_EQUATORIAL_RADIUS_FT 20925650.
|
||||
|
||||
/** Value of earth radius from LaRCsim (meter) */
|
||||
/** Value of equatorial earth radius from LaRCsim (meter) */
|
||||
#define SG_EQUATORIAL_RADIUS_M 6378138.12
|
||||
|
||||
/** Radius squared (ft) */
|
||||
@@ -104,6 +104,8 @@ const float SG_RADIANS_TO_DEGREES = 180.0f / SG_PI;
|
||||
/** Radius squared (meter) */
|
||||
#define SG_EQ_RAD_SQUARE_M 40680645877797.1344
|
||||
|
||||
/** Value of WGS84 polar earth radius (meter) */
|
||||
#define SG_POLAR_RADIUS_M 6356752.3142451794975639668
|
||||
|
||||
// Physical Constants, SI
|
||||
|
||||
@@ -211,8 +213,10 @@ const float SG_RADIANS_TO_DEGREES = 180.0f / SG_PI;
|
||||
/** for backwards compatibility */
|
||||
#define SG_SCENERY_FILE_FORMAT "0.4"
|
||||
|
||||
/** Default range in m at which all objects are displayed. Overridden by /sim/rendering/static-lod/rough **/
|
||||
#define SG_OBJECT_RANGE 9000.0
|
||||
/** Default object ranges. Overridden by /sim/rendering/static-lod/[bare|rough|detailed] **/
|
||||
#define SG_OBJECT_RANGE_BARE 30000.0
|
||||
#define SG_OBJECT_RANGE_ROUGH 9000.0
|
||||
#define SG_OBJECT_RANGE_DETAILED 1500.0
|
||||
|
||||
/** Radius of scenery tiles in m **/
|
||||
#define SG_TILE_RADIUS 14000.0
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include <simgear/debug/BufferedLogCallback.hxx>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#define SG_DEBUG_BUFFEREDLOGCALLBACK_HXX
|
||||
|
||||
#include <vector>
|
||||
#include <memory> // for std::auto_ptr
|
||||
#include <memory> // for std::unique_ptr
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
@@ -70,10 +70,10 @@ public:
|
||||
unsigned int threadsafeCopy(vector_cstring& aOutput);
|
||||
private:
|
||||
class BufferedLogCallbackPrivate;
|
||||
std::auto_ptr<BufferedLogCallbackPrivate> d;
|
||||
std::unique_ptr<BufferedLogCallbackPrivate> d;
|
||||
};
|
||||
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
#endif // of SG_DEBUG_BUFFEREDLOGCALLBACK_HXX
|
||||
#endif // of SG_DEBUG_BUFFEREDLOGCALLBACK_HXX
|
||||
|
||||
@@ -42,14 +42,22 @@ typedef enum {
|
||||
|
||||
/**
|
||||
* Define the possible logging priorities (and their order).
|
||||
*
|
||||
* Caution - unfortunately, this enum is exposed to Nasal via the logprint()
|
||||
* function as an integer parameter. Therefore, new values should only be
|
||||
* appended, or the priority Nasal reports to compiled code will change.
|
||||
*/
|
||||
typedef enum {
|
||||
SG_BULK = 1, // For frequent messages
|
||||
SG_DEBUG, // Less frequent debug type messages
|
||||
SG_INFO, // Informatory messages
|
||||
SG_WARN, // Possible impending problem
|
||||
SG_ALERT // Very possible impending problem
|
||||
SG_ALERT, // Very possible impending problem
|
||||
SG_POPUP, // Severe enough to alert using a pop-up window
|
||||
// SG_EXIT, // Problem (no core)
|
||||
// SG_ABORT // Abandon ship (core)
|
||||
|
||||
SG_DEV_WARN, // Warning for developers, translated to other priority
|
||||
SG_DEV_ALERT // Alert for developers, translated
|
||||
} sgDebugPriority;
|
||||
|
||||
|
||||
@@ -36,46 +36,17 @@
|
||||
#include <simgear/threads/SGQueue.hxx>
|
||||
#include <simgear/threads/SGGuard.hxx>
|
||||
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
#ifdef SG_WINDOWS
|
||||
#if defined (SG_WINDOWS)
|
||||
// for AllocConsole, OutputDebugString
|
||||
#include "windows.h"
|
||||
#include <windows.h>
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
const char* debugClassToString(sgDebugClass c)
|
||||
{
|
||||
switch (c) {
|
||||
case SG_NONE: return "none";
|
||||
case SG_TERRAIN: return "terrain";
|
||||
case SG_ASTRO: return "astro";
|
||||
case SG_FLIGHT: return "flight";
|
||||
case SG_INPUT: return "input";
|
||||
case SG_GL: return "opengl";
|
||||
case SG_VIEW: return "view";
|
||||
case SG_COCKPIT: return "cockpit";
|
||||
case SG_GENERAL: return "general";
|
||||
case SG_MATH: return "math";
|
||||
case SG_EVENT: return "event";
|
||||
case SG_AIRCRAFT: return "aircraft";
|
||||
case SG_AUTOPILOT: return "autopilot";
|
||||
case SG_IO: return "io";
|
||||
case SG_CLIPPER: return "clipper";
|
||||
case SG_NETWORK: return "network";
|
||||
case SG_ATC: return "atc";
|
||||
case SG_NASAL: return "nasal";
|
||||
case SG_INSTR: return "instruments";
|
||||
case SG_SYSTEMS: return "systems";
|
||||
case SG_AI: return "ai";
|
||||
case SG_ENVIRONMENT:return "environment";
|
||||
case SG_SOUND: return "sound";
|
||||
case SG_NAVAID: return "navaid";
|
||||
case SG_GUI: return "gui";
|
||||
case SG_TERRASYNC: return "terrasync";
|
||||
case SG_PARTICLES: return "particles";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -99,6 +70,40 @@ void LogCallback::setLogLevels( sgDebugClass c, sgDebugPriority p )
|
||||
m_class = c;
|
||||
}
|
||||
|
||||
const char* LogCallback::debugClassToString(sgDebugClass c)
|
||||
{
|
||||
switch (c) {
|
||||
case SG_NONE: return "none";
|
||||
case SG_TERRAIN: return "terrain";
|
||||
case SG_ASTRO: return "astro";
|
||||
case SG_FLIGHT: return "flight";
|
||||
case SG_INPUT: return "input";
|
||||
case SG_GL: return "opengl";
|
||||
case SG_VIEW: return "view";
|
||||
case SG_COCKPIT: return "cockpit";
|
||||
case SG_GENERAL: return "general";
|
||||
case SG_MATH: return "math";
|
||||
case SG_EVENT: return "event";
|
||||
case SG_AIRCRAFT: return "aircraft";
|
||||
case SG_AUTOPILOT: return "autopilot";
|
||||
case SG_IO: return "io";
|
||||
case SG_CLIPPER: return "clipper";
|
||||
case SG_NETWORK: return "network";
|
||||
case SG_ATC: return "atc";
|
||||
case SG_NASAL: return "nasal";
|
||||
case SG_INSTR: return "instruments";
|
||||
case SG_SYSTEMS: return "systems";
|
||||
case SG_AI: return "ai";
|
||||
case SG_ENVIRONMENT:return "environment";
|
||||
case SG_SOUND: return "sound";
|
||||
case SG_NAVAID: return "navaid";
|
||||
case SG_GUI: return "gui";
|
||||
case SG_TERRASYNC: return "terrasync";
|
||||
case SG_PARTICLES: return "particles";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
@@ -106,13 +111,13 @@ void LogCallback::setLogLevels( sgDebugClass c, sgDebugPriority p )
|
||||
class FileLogCallback : public simgear::LogCallback
|
||||
{
|
||||
public:
|
||||
FileLogCallback(const std::string& aPath, sgDebugClass c, sgDebugPriority p) :
|
||||
simgear::LogCallback(c, p),
|
||||
m_file(aPath.c_str(), std::ios_base::out | std::ios_base::trunc)
|
||||
FileLogCallback(const SGPath& aPath, sgDebugClass c, sgDebugPriority p) :
|
||||
simgear::LogCallback(c, p)
|
||||
{
|
||||
m_file.open(aPath, std::ios_base::out | std::ios_base::trunc);
|
||||
}
|
||||
|
||||
virtual void operator()(sgDebugClass c, sgDebugPriority p,
|
||||
|
||||
virtual void operator()(sgDebugClass c, sgDebugPriority p,
|
||||
const char* file, int line, const std::string& message)
|
||||
{
|
||||
if (!shouldLog(c, p)) return;
|
||||
@@ -120,28 +125,29 @@ public:
|
||||
<< ":" << file << ":" << line << ":" << message << std::endl;
|
||||
}
|
||||
private:
|
||||
std::ofstream m_file;
|
||||
sg_ofstream m_file;
|
||||
};
|
||||
|
||||
|
||||
class StderrLogCallback : public simgear::LogCallback
|
||||
{
|
||||
public:
|
||||
StderrLogCallback(sgDebugClass c, sgDebugPriority p) :
|
||||
simgear::LogCallback(c, p)
|
||||
{
|
||||
#ifdef SG_WINDOWS
|
||||
AllocConsole(); // but only if we want a console
|
||||
freopen("conin$", "r", stdin);
|
||||
freopen("conout$", "w", stdout);
|
||||
freopen("conout$", "w", stderr);
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual void operator()(sgDebugClass c, sgDebugPriority p,
|
||||
|
||||
#if defined (SG_WINDOWS)
|
||||
~StderrLogCallback()
|
||||
{
|
||||
FreeConsole();
|
||||
}
|
||||
#endif
|
||||
|
||||
virtual void operator()(sgDebugClass c, sgDebugPriority p,
|
||||
const char* file, int line, const std::string& aMessage)
|
||||
{
|
||||
if (!shouldLog(c, p)) return;
|
||||
|
||||
|
||||
fprintf(stderr, "%s\n", aMessage.c_str());
|
||||
//fprintf(stderr, "%s:%d:%s:%d:%s\n", debugClassToString(c), p,
|
||||
// file, line, aMessage.c_str());
|
||||
@@ -159,12 +165,12 @@ public:
|
||||
simgear::LogCallback(c, p)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void operator()(sgDebugClass c, sgDebugPriority p,
|
||||
|
||||
virtual void operator()(sgDebugClass c, sgDebugPriority p,
|
||||
const char* file, int line, const std::string& aMessage)
|
||||
{
|
||||
if (!shouldLog(c, p)) return;
|
||||
|
||||
|
||||
std::ostringstream os;
|
||||
os << debugClassToString(c) << ":" << aMessage << std::endl;
|
||||
OutputDebugStringA(os.str().c_str());
|
||||
@@ -173,13 +179,13 @@ public:
|
||||
|
||||
#endif
|
||||
|
||||
class LogStreamPrivate : public SGThread
|
||||
class logstream::LogStreamPrivate : public SGThread
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* storage of a single log entry. Note this is not used for a persistent
|
||||
* store, but rather for short term buffering between the submitting
|
||||
* and output threads.
|
||||
* storage of a single log entry. This is used to pass log entries from
|
||||
* the various threads to the logging thread, and also to store the startup
|
||||
* entries
|
||||
*/
|
||||
class LogEntry
|
||||
{
|
||||
@@ -188,24 +194,29 @@ private:
|
||||
const char* f, int l, const std::string& msg) :
|
||||
debugClass(c), debugPriority(p), file(f), line(l),
|
||||
message(msg)
|
||||
{
|
||||
{
|
||||
}
|
||||
|
||||
sgDebugClass debugClass;
|
||||
sgDebugPriority debugPriority;
|
||||
|
||||
const sgDebugClass debugClass;
|
||||
const sgDebugPriority debugPriority;
|
||||
const char* file;
|
||||
int line;
|
||||
std::string message;
|
||||
const int line;
|
||||
const std::string message;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* RAII object to pause the logging thread if it's running, and restart it.
|
||||
* used to safely make configuration changes.
|
||||
*/
|
||||
class PauseThread
|
||||
{
|
||||
public:
|
||||
PauseThread(LogStreamPrivate* parent) : m_parent(parent)
|
||||
PauseThread(LogStreamPrivate* parent)
|
||||
: m_parent(parent)
|
||||
, m_wasRunning(m_parent->stop())
|
||||
{
|
||||
m_wasRunning = m_parent->stop();
|
||||
}
|
||||
|
||||
|
||||
~PauseThread()
|
||||
{
|
||||
if (m_wasRunning) {
|
||||
@@ -214,42 +225,144 @@ private:
|
||||
}
|
||||
private:
|
||||
LogStreamPrivate* m_parent;
|
||||
bool m_wasRunning;
|
||||
const bool m_wasRunning;
|
||||
};
|
||||
|
||||
public:
|
||||
LogStreamPrivate() :
|
||||
m_logClass(SG_ALL),
|
||||
m_logPriority(SG_ALERT),
|
||||
m_isRunning(false),
|
||||
m_consoleRequested(false)
|
||||
{
|
||||
m_logClass(SG_ALL),
|
||||
m_logPriority(SG_ALERT)
|
||||
{
|
||||
#if defined (SG_WINDOWS)
|
||||
/*
|
||||
* 2016-09-20(RJH) - Reworked console handling
|
||||
* 1) When started from the console use the console (when no --console)
|
||||
* 2) When started from the GUI (with --console) open a new console window
|
||||
* 3) When started from the GUI (without --console) don't open a new console
|
||||
* window; stdout/stderr will not appear (except in logfiles as they do now)
|
||||
* 4) When started from the Console (with --console) open a new console window
|
||||
* 5) Ensure that IO redirection still works when started from the console
|
||||
*
|
||||
* Notes:
|
||||
* 1) fgfs needs to be a GUI subsystem app - which it already is
|
||||
* 2) What can't be done is to make the cmd prompt run fgfs synchronously;
|
||||
* this is only something that can be done via "start /wait fgfs".
|
||||
*/
|
||||
|
||||
#if !defined(SG_WINDOWS)
|
||||
m_callbacks.push_back(new StderrLogCallback(m_logClass, m_logPriority));
|
||||
m_consoleCallbacks.push_back(m_callbacks.back());
|
||||
m_consoleRequested = true;
|
||||
int stderr_handle_type = GetFileType(GetStdHandle(STD_ERROR_HANDLE));
|
||||
int stdout_handle_type = GetFileType(GetStdHandle(STD_OUTPUT_HANDLE));
|
||||
int stdout_isNull = 0;
|
||||
int stderr_isNull = 0;
|
||||
|
||||
m_stderr_isRedirectedAlready = stderr_handle_type == FILE_TYPE_DISK || stderr_handle_type == FILE_TYPE_PIPE || stderr_handle_type == FILE_TYPE_CHAR;
|
||||
m_stdout_isRedirectedAlready = stdout_handle_type == FILE_TYPE_DISK || stdout_handle_type == FILE_TYPE_PIPE || stdout_handle_type == FILE_TYPE_CHAR;
|
||||
|
||||
/*
|
||||
* We don't want to attach to the console if either stream has been redirected - so in this case ensure that both streams
|
||||
* are redirected as otherwise something will be lost (as Alloc or Attach Console will cause the handles that were bound
|
||||
* to disappear)
|
||||
*/
|
||||
if (m_stdout_isRedirectedAlready){
|
||||
if (!m_stderr_isRedirectedAlready) {
|
||||
MessageBox(0, "Redirection only works when you use 2>&1 before using > or |\r\n(e.g. fgfs 2>&1 | more)", "Simgear Error", MB_OK | MB_ICONERROR);
|
||||
exit(3);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Attempt to attach to the console process of the parent process; when launched from cmd.exe this should be the console,
|
||||
* when launched via the RUN menu explorer, or another GUI app that wasn't started from the console this will fail.
|
||||
* When it fails we will redirect to the NUL device. This is to ensure that we have valid streams.
|
||||
* Later on in the initialisation sequence the --console option will be processed and this will cause the requestConsole() to
|
||||
* always open a new console, except for streams that are redirected. The same rules apply there, if both streams are redirected
|
||||
* the console will be opened, and it will contain a message to indicate that no output will be present because the streams are redirected
|
||||
*/
|
||||
if (AttachConsole(ATTACH_PARENT_PROCESS) == 0) {
|
||||
/*
|
||||
* attach failed - so ensure that the streams are bound to the null device - but only when not already redirected
|
||||
*/
|
||||
if (!m_stdout_isRedirectedAlready)
|
||||
{
|
||||
stdout_isNull = true;
|
||||
freopen("NUL", "w", stdout);
|
||||
}
|
||||
|
||||
if (!m_stderr_isRedirectedAlready)
|
||||
{
|
||||
stderr_isNull = true;
|
||||
freopen("NUL", "w", stderr);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* providing that AttachConsole succeeded - we can then either reopen the stream onto the console, or use
|
||||
* _fdopen to attached to the currently redirected (and open stream)
|
||||
*/
|
||||
if (!stdout_isNull){
|
||||
if (!m_stdout_isRedirectedAlready)
|
||||
freopen("conout$", "w", stdout);
|
||||
else
|
||||
/*
|
||||
* for already redirected streams we need to attach the stream to the OS handle that is open.
|
||||
* - this comes from part of the answer http://stackoverflow.com/a/13841522
|
||||
* _open_osfhandle returns an FD for the Win32 Handle, which is then opened using fdopen and
|
||||
* hopefully safely assigned to the stream (although it does look wrong to me it works)
|
||||
* Removing this bit will stop pipes and command line redirection (> 2> and 2>&1 from working)
|
||||
*/
|
||||
*stdout = *_fdopen(_open_osfhandle((intptr_t) GetStdHandle(STD_OUTPUT_HANDLE), _O_WRONLY), "a");
|
||||
}
|
||||
|
||||
if (!stderr_isNull){
|
||||
if (!m_stderr_isRedirectedAlready)
|
||||
freopen("conout$", "w", stderr);
|
||||
else
|
||||
*stderr = *_fdopen(_open_osfhandle((intptr_t) GetStdHandle(STD_ERROR_HANDLE), _O_WRONLY), "a");
|
||||
}
|
||||
}
|
||||
//http://stackoverflow.com/a/25927081
|
||||
//Clear the error state for each of the C++ standard stream objects.
|
||||
std::wcout.clear();
|
||||
std::cout.clear();
|
||||
std::wcerr.clear();
|
||||
std::cerr.clear();
|
||||
#endif
|
||||
|
||||
m_callbacks.push_back(new StderrLogCallback(m_logClass, m_logPriority));
|
||||
m_consoleCallbacks.push_back(m_callbacks.back());
|
||||
#if defined (SG_WINDOWS) && !defined(NDEBUG)
|
||||
m_callbacks.push_back(new WinDebugLogCallback(m_logClass, m_logPriority));
|
||||
m_consoleCallbacks.push_back(m_callbacks.back());
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
~LogStreamPrivate()
|
||||
{
|
||||
for (simgear::LogCallback* cb : m_callbacks) {
|
||||
delete cb;
|
||||
}
|
||||
}
|
||||
|
||||
SGMutex m_lock;
|
||||
SGBlockingQueue<LogEntry> m_entries;
|
||||
|
||||
|
||||
// log entries posted during startup
|
||||
std::vector<LogEntry> m_startupEntries;
|
||||
bool m_startupLogging = false;
|
||||
|
||||
typedef std::vector<simgear::LogCallback*> CallbackVec;
|
||||
CallbackVec m_callbacks;
|
||||
CallbackVec m_callbacks;
|
||||
/// subset of callbacks which correspond to stdout / console,
|
||||
/// and hence should dynamically reflect console logging settings
|
||||
CallbackVec m_consoleCallbacks;
|
||||
|
||||
sgDebugClass m_logClass;
|
||||
sgDebugPriority m_logPriority;
|
||||
bool m_isRunning;
|
||||
bool m_consoleRequested;
|
||||
|
||||
bool m_isRunning = false;
|
||||
#if defined (SG_WINDOWS)
|
||||
// track whether the console was redirected on launch (in the constructor, which is called early on)
|
||||
bool m_stderr_isRedirectedAlready = false;
|
||||
bool m_stdout_isRedirectedAlready = false;
|
||||
#endif
|
||||
bool m_developerMode = false;
|
||||
|
||||
void startLog()
|
||||
{
|
||||
SGGuard<SGMutex> g(m_lock);
|
||||
@@ -257,7 +370,17 @@ public:
|
||||
m_isRunning = true;
|
||||
start();
|
||||
}
|
||||
|
||||
|
||||
void setStartupLoggingEnabled(bool on)
|
||||
{
|
||||
if (m_startupLogging == on) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_startupLogging = on;
|
||||
m_startupEntries.clear();
|
||||
}
|
||||
|
||||
virtual void run()
|
||||
{
|
||||
while (1) {
|
||||
@@ -267,37 +390,50 @@ public:
|
||||
if ((entry.debugClass == SG_NONE) && !strcmp(entry.file, "done")) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (m_startupLogging) {
|
||||
// save to the startup list for not-yet-added callbacks to
|
||||
// pull down on startup
|
||||
m_startupEntries.push_back(entry);
|
||||
}
|
||||
|
||||
// submit to each installed callback in turn
|
||||
BOOST_FOREACH(simgear::LogCallback* cb, m_callbacks) {
|
||||
for (simgear::LogCallback* cb : m_callbacks) {
|
||||
(*cb)(entry.debugClass, entry.debugPriority,
|
||||
entry.file, entry.line, entry.message);
|
||||
}
|
||||
}
|
||||
} // of main thread loop
|
||||
}
|
||||
|
||||
|
||||
bool stop()
|
||||
{
|
||||
SGGuard<SGMutex> g(m_lock);
|
||||
if (!m_isRunning) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// log a special marker value, which will cause the thread to wakeup,
|
||||
// and then exit
|
||||
log(SG_NONE, SG_ALERT, "done", -1, "");
|
||||
join();
|
||||
|
||||
|
||||
m_isRunning = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void addCallback(simgear::LogCallback* cb)
|
||||
{
|
||||
PauseThread pause(this);
|
||||
m_callbacks.push_back(cb);
|
||||
|
||||
// we clear startup entries not using this, so always safe to run
|
||||
// this code, container will simply be empty
|
||||
for (auto entry : m_startupEntries) {
|
||||
(*cb)(entry.debugClass, entry.debugPriority,
|
||||
entry.file, entry.line, entry.message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void removeCallback(simgear::LogCallback* cb)
|
||||
{
|
||||
PauseThread pause(this);
|
||||
@@ -306,7 +442,7 @@ public:
|
||||
m_callbacks.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void setLogLevels( sgDebugClass c, sgDebugPriority p )
|
||||
{
|
||||
PauseThread pause(this);
|
||||
@@ -316,129 +452,279 @@ public:
|
||||
cb->setLogLevels(c, p);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool would_log( sgDebugClass c, sgDebugPriority p ) const
|
||||
{
|
||||
p = translatePriority(p);
|
||||
if (p >= SG_INFO) return true;
|
||||
return ((c & m_logClass) != 0 && p >= m_logPriority);
|
||||
}
|
||||
|
||||
|
||||
void log( sgDebugClass c, sgDebugPriority p,
|
||||
const char* fileName, int line, const std::string& msg)
|
||||
{
|
||||
p = translatePriority(p);
|
||||
LogEntry entry(c, p, fileName, line, msg);
|
||||
m_entries.push(entry);
|
||||
}
|
||||
|
||||
void requestConsole()
|
||||
{
|
||||
PauseThread pause(this);
|
||||
if (m_consoleRequested) {
|
||||
return;
|
||||
}
|
||||
sgDebugPriority translatePriority(sgDebugPriority in) const
|
||||
{
|
||||
if (in == SG_DEV_WARN) {
|
||||
return m_developerMode ? SG_WARN : SG_DEBUG;
|
||||
}
|
||||
|
||||
m_consoleRequested = true;
|
||||
m_callbacks.push_back(new StderrLogCallback(m_logClass, m_logPriority));
|
||||
m_consoleCallbacks.push_back(m_callbacks.back());
|
||||
}
|
||||
if (in == SG_DEV_ALERT) {
|
||||
return m_developerMode ? SG_POPUP : SG_WARN;
|
||||
}
|
||||
|
||||
return in;
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static logstream* global_logstream = NULL;
|
||||
static LogStreamPrivate* global_privateLogstream = NULL;
|
||||
static std::unique_ptr<logstream> global_logstream;
|
||||
static SGMutex global_logStreamLock;
|
||||
|
||||
logstream::logstream()
|
||||
{
|
||||
global_privateLogstream = new LogStreamPrivate;
|
||||
global_privateLogstream->startLog();
|
||||
d.reset(new LogStreamPrivate);
|
||||
d->startLog();
|
||||
}
|
||||
|
||||
logstream::~logstream()
|
||||
{
|
||||
popup_msgs.clear();
|
||||
d->stop();
|
||||
}
|
||||
|
||||
void
|
||||
logstream::setLogLevels( sgDebugClass c, sgDebugPriority p )
|
||||
{
|
||||
global_privateLogstream->setLogLevels(c, p);
|
||||
d->setLogLevels(c, p);
|
||||
}
|
||||
|
||||
void logstream::setDeveloperMode(bool devMode)
|
||||
{
|
||||
d->m_developerMode = devMode;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
logstream::addCallback(simgear::LogCallback* cb)
|
||||
{
|
||||
global_privateLogstream->addCallback(cb);
|
||||
{
|
||||
d->addCallback(cb);
|
||||
}
|
||||
|
||||
void
|
||||
logstream::removeCallback(simgear::LogCallback* cb)
|
||||
{
|
||||
global_privateLogstream->removeCallback(cb);
|
||||
{
|
||||
d->removeCallback(cb);
|
||||
}
|
||||
|
||||
void
|
||||
logstream::log( sgDebugClass c, sgDebugPriority p,
|
||||
const char* fileName, int line, const std::string& msg)
|
||||
{
|
||||
global_privateLogstream->log(c, p, fileName, line, msg);
|
||||
d->log(c, p, fileName, line, msg);
|
||||
}
|
||||
|
||||
|
||||
void logstream::hexdump(sgDebugClass c, sgDebugPriority p, const char* fileName, int line, const void *mem, unsigned int len, unsigned int columns)
|
||||
{
|
||||
unsigned int i, j;
|
||||
char temp[3000], temp1[3000];
|
||||
*temp = 0;
|
||||
|
||||
for (i = 0; i < len + ((len % columns) ? (columns - len % columns) : 0); i++)
|
||||
{
|
||||
if (strlen(temp) > 500) return;
|
||||
/* print offset */
|
||||
if (i % columns == 0)
|
||||
{
|
||||
sprintf(temp1, "0x%06x: ", i);
|
||||
strcat(temp, temp1);
|
||||
}
|
||||
|
||||
/* print hex data */
|
||||
if (i < len)
|
||||
{
|
||||
sprintf(temp1, "%02x ", 0xFF & ((char*)mem)[i]);
|
||||
strcat(temp, temp1);
|
||||
}
|
||||
else /* end of block, just aligning for ASCII dump */
|
||||
{
|
||||
strcat(temp, " ");
|
||||
}
|
||||
|
||||
/* print ASCII dump */
|
||||
if (i % columns == (columns - 1))
|
||||
{
|
||||
for (j = i - (columns - 1); j <= i; j++)
|
||||
{
|
||||
if (j >= len) /* end of block, not really printing */
|
||||
{
|
||||
strcat(temp, " ");
|
||||
}
|
||||
else if (((((char*)mem)[j]) & (char)0x7f) > 32) /* printable char */
|
||||
{
|
||||
char t2[2];
|
||||
t2[0] = 0xFF & ((char*)mem)[j];
|
||||
t2[1] = 0;
|
||||
strcat(temp, t2);
|
||||
}
|
||||
else /* other char */
|
||||
{
|
||||
strcat(temp, ".");
|
||||
}
|
||||
}
|
||||
log(c, p, fileName, line, temp );
|
||||
*temp = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
logstream::popup( const std::string& msg)
|
||||
{
|
||||
popup_msgs.push_back(msg);
|
||||
}
|
||||
|
||||
std::string
|
||||
logstream::get_popup()
|
||||
{
|
||||
std::string rv = "";
|
||||
if (!popup_msgs.empty())
|
||||
{
|
||||
rv = popup_msgs.front();
|
||||
popup_msgs.erase(popup_msgs.begin());
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool
|
||||
logstream::has_popup()
|
||||
{
|
||||
return (popup_msgs.size() > 0) ? true : false;
|
||||
}
|
||||
|
||||
bool
|
||||
logstream::would_log( sgDebugClass c, sgDebugPriority p ) const
|
||||
{
|
||||
return global_privateLogstream->would_log(c,p);
|
||||
return d->would_log(c,p);
|
||||
}
|
||||
|
||||
sgDebugClass
|
||||
logstream::get_log_classes() const
|
||||
{
|
||||
return global_privateLogstream->m_logClass;
|
||||
return d->m_logClass;
|
||||
}
|
||||
|
||||
|
||||
sgDebugPriority
|
||||
logstream::get_log_priority() const
|
||||
{
|
||||
return global_privateLogstream->m_logPriority;
|
||||
return d->m_logPriority;
|
||||
}
|
||||
|
||||
void
|
||||
logstream::set_log_priority( sgDebugPriority p)
|
||||
{
|
||||
global_privateLogstream->setLogLevels(global_privateLogstream->m_logClass, p);
|
||||
d->setLogLevels(d->m_logClass, p);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
logstream::set_log_classes( sgDebugClass c)
|
||||
{
|
||||
global_privateLogstream->setLogLevels(c, global_privateLogstream->m_logPriority);
|
||||
d->setLogLevels(c, d->m_logPriority);
|
||||
}
|
||||
|
||||
|
||||
logstream&
|
||||
sglog()
|
||||
{
|
||||
// Force initialization of cerr.
|
||||
static std::ios_base::Init initializer;
|
||||
|
||||
|
||||
// http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
|
||||
// in the absence of portable memory barrier ops in Simgear,
|
||||
// let's keep this correct & safe
|
||||
static SGMutex m;
|
||||
SGGuard<SGMutex> g(m);
|
||||
|
||||
SGGuard<SGMutex> g(global_logStreamLock);
|
||||
|
||||
if( !global_logstream )
|
||||
global_logstream = new logstream();
|
||||
return *global_logstream;
|
||||
global_logstream.reset(new logstream);
|
||||
return *(global_logstream.get());
|
||||
}
|
||||
|
||||
void
|
||||
logstream::logToFile( const SGPath& aPath, sgDebugClass c, sgDebugPriority p )
|
||||
{
|
||||
global_privateLogstream->addCallback(new FileLogCallback(aPath.str(), c, p));
|
||||
d->addCallback(new FileLogCallback(aPath, c, p));
|
||||
}
|
||||
|
||||
void logstream::setStartupLoggingEnabled(bool enabled)
|
||||
{
|
||||
d->setStartupLoggingEnabled(enabled);
|
||||
}
|
||||
|
||||
void logstream::requestConsole()
|
||||
{
|
||||
#if defined (SG_WINDOWS)
|
||||
const bool stderrAlreadyRedirected = d->m_stderr_isRedirectedAlready;
|
||||
const bool stdoutAlreadyRedirected = d->m_stdout_isRedirectedAlready;
|
||||
|
||||
/*
|
||||
* 2016-09-20(RJH) - Reworked console handling
|
||||
* This is part of the reworked console handling for Win32. This is for building as a Win32 GUI Subsystem where no
|
||||
* console is allocated on launch. If building as a console app then the startup will ensure that a console is created - but
|
||||
* we don't need to handle that.
|
||||
* The new handling is quite simple:
|
||||
* 1. The constructor will ensure that these streams exists. It will attach to the
|
||||
* parent command prompt if started from the command prompt, otherwise the
|
||||
* stdout/stderr will be bound to the NUL device.
|
||||
* 2. with --console a window will always appear regardless of where the process was
|
||||
* started from. Any non redirected streams will be redirected
|
||||
* 3. You cannot use --console and either redirected stream.
|
||||
*
|
||||
* This is called after the Private Log Stream constructor so we need to undo any console that it has attached to.
|
||||
*/
|
||||
|
||||
if (!stderrAlreadyRedirected && !stdoutAlreadyRedirected) {
|
||||
FreeConsole();
|
||||
if (AllocConsole()) {
|
||||
if (!stdoutAlreadyRedirected)
|
||||
freopen("conout$", "w", stdout);
|
||||
|
||||
if (!stderrAlreadyRedirected)
|
||||
freopen("conout$", "w", stderr);
|
||||
|
||||
//http://stackoverflow.com/a/25927081
|
||||
//Clear the error state for each of the C++ standard stream objects.
|
||||
std::wcout.clear();
|
||||
std::cout.clear();
|
||||
std::wcerr.clear();
|
||||
std::cerr.clear();
|
||||
}
|
||||
} else {
|
||||
MessageBox(0, "--console ignored because stdout or stderr redirected with > or 2>", "Simgear Error", MB_OK | MB_ICONERROR);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
void requestConsole()
|
||||
{
|
||||
sglog().requestConsole();
|
||||
}
|
||||
|
||||
|
||||
void shutdownLogging()
|
||||
{
|
||||
sglog(); // force creation
|
||||
global_privateLogstream->requestConsole();
|
||||
SGGuard<SGMutex> g(global_logStreamLock);
|
||||
global_logstream.reset();
|
||||
}
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
@@ -29,7 +29,9 @@
|
||||
#include <simgear/debug/debug_types.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
// forward decls
|
||||
class SGPath;
|
||||
|
||||
@@ -48,6 +50,8 @@ protected:
|
||||
LogCallback(sgDebugClass c, sgDebugPriority p);
|
||||
|
||||
bool shouldLog(sgDebugClass c, sgDebugPriority p) const;
|
||||
|
||||
static const char* debugClassToString(sgDebugClass c);
|
||||
private:
|
||||
sgDebugClass m_class;
|
||||
sgDebugPriority m_priority;
|
||||
@@ -59,7 +63,9 @@ private:
|
||||
* moment - on other plaforms it's a no-op
|
||||
*/
|
||||
void requestConsole();
|
||||
|
||||
|
||||
void shutdownLogging();
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
/**
|
||||
@@ -68,7 +74,17 @@ void requestConsole();
|
||||
class logstream
|
||||
{
|
||||
public:
|
||||
~logstream();
|
||||
|
||||
static void initGlobalLogstream();
|
||||
|
||||
/**
|
||||
* Helper force a console on platforms where it might optional, when
|
||||
* we need to show a console. This basically means Windows at the
|
||||
* moment - on other plaforms it's a no-op
|
||||
*/
|
||||
void requestConsole();
|
||||
|
||||
/**
|
||||
* Set the global log class and priority level.
|
||||
* @param c debug class
|
||||
@@ -88,12 +104,42 @@ public:
|
||||
|
||||
sgDebugPriority get_log_priority() const;
|
||||
|
||||
/**
|
||||
* set developer mode on/off. In developer mode, SG_DEV_WARN messags
|
||||
* are treated as warnings. In normal (non-developer) mode they are
|
||||
* treated as SG_DEBUG.
|
||||
*/
|
||||
void setDeveloperMode(bool devMode);
|
||||
|
||||
/**
|
||||
* the core logging method
|
||||
*/
|
||||
void log( sgDebugClass c, sgDebugPriority p,
|
||||
const char* fileName, int line, const std::string& msg);
|
||||
|
||||
/**
|
||||
* output formatted hex dump of memory block
|
||||
*/
|
||||
void hexdump(sgDebugClass c, sgDebugPriority p, const char* fileName, int line, const void *mem, unsigned int len, unsigned int columns = 16);
|
||||
|
||||
|
||||
/**
|
||||
* support for the SG_POPUP logging class
|
||||
* set the content of the popup message
|
||||
*/
|
||||
void popup( const std::string& msg);
|
||||
|
||||
/**
|
||||
* retrieve the contents of the popup message and clear it's internal
|
||||
* content. The return value may be an empty string.
|
||||
*/
|
||||
std::string get_popup();
|
||||
|
||||
/**
|
||||
* return true if a new popup message is available. false otherwise.
|
||||
*/
|
||||
bool has_popup();
|
||||
|
||||
/**
|
||||
* \relates logstream
|
||||
* Return the one and only logstream instance.
|
||||
@@ -112,9 +158,21 @@ public:
|
||||
|
||||
void removeCallback(simgear::LogCallback* cb);
|
||||
|
||||
/**
|
||||
* optionally record all entries and submit them to new log callbacks that
|
||||
* are added. This allows simplified logging configuration, but still including
|
||||
* early startup information in all logs.
|
||||
*/
|
||||
void setStartupLoggingEnabled(bool enabled);
|
||||
private:
|
||||
// constructor
|
||||
logstream();
|
||||
|
||||
std::vector<std::string> popup_msgs;
|
||||
|
||||
class LogStreamPrivate;
|
||||
|
||||
std::unique_ptr<LogStreamPrivate> d;
|
||||
};
|
||||
|
||||
logstream& sglog();
|
||||
@@ -127,16 +185,18 @@ logstream& sglog();
|
||||
* @param P priority
|
||||
* @param M message
|
||||
*/
|
||||
#ifdef FG_NDEBUG
|
||||
# define SG_LOG(C,P,M)
|
||||
#else
|
||||
# define SG_LOG(C,P,M) do { \
|
||||
if(sglog().would_log(C,P)) { \
|
||||
std::ostringstream os; \
|
||||
os << M; \
|
||||
# define SG_LOGX(C,P,M) \
|
||||
do { if(sglog().would_log(C,P)) { \
|
||||
std::ostringstream os; os << M; \
|
||||
sglog().log(C, P, __FILE__, __LINE__, os.str()); \
|
||||
} \
|
||||
} while(0)
|
||||
if ((P) == SG_POPUP) sglog().popup(os.str()); \
|
||||
} } while(0)
|
||||
#ifdef FG_NDEBUG
|
||||
# define SG_LOG(C,P,M) do { if((P) == SG_POPUP) SG_LOGX(C,P,M) } while(0)
|
||||
# define SG_HEXDUMP(C,P,MEM,LEN)
|
||||
#else
|
||||
# define SG_LOG(C,P,M) SG_LOGX(C,P,M)
|
||||
# define SG_LOG_HEXDUMP(C,P,MEM,LEN) if(sglog().would_log(C,P)) sglog().hexdump(C, P, __FILE__, __LINE__, MEM, LEN)
|
||||
#endif
|
||||
|
||||
#define SG_ORIGIN __FILE__ ":" SG_STRINGIZE(__LINE__)
|
||||
|
||||
18
simgear/embedded_resources/CMakeLists.txt
Normal file
18
simgear/embedded_resources/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
include (SimGearComponent)
|
||||
|
||||
set(HEADERS EmbeddedResource.hxx
|
||||
EmbeddedResourceManager.hxx
|
||||
EmbeddedResourceProxy.hxx)
|
||||
set(SOURCES EmbeddedResource.cxx
|
||||
EmbeddedResourceManager.cxx
|
||||
EmbeddedResourceProxy.cxx)
|
||||
|
||||
simgear_component(embedded_resources embedded_resources
|
||||
"${SOURCES}" "${HEADERS}")
|
||||
|
||||
if(ENABLE_TESTS)
|
||||
add_executable(test_embedded_resources embedded_resources_test.cxx)
|
||||
target_link_libraries(test_embedded_resources ${TEST_LIBS})
|
||||
add_test(embedded_resources
|
||||
${EXECUTABLE_OUTPUT_PATH}/test_embedded_resources)
|
||||
endif(ENABLE_TESTS)
|
||||
265
simgear/embedded_resources/EmbeddedResource.cxx
Normal file
265
simgear/embedded_resources/EmbeddedResource.cxx
Normal file
@@ -0,0 +1,265 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// EmbeddedResource.cxx --- Class for pointing to/accessing an embedded resource
|
||||
// Copyright (C) 2017 Florent Rougon
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA.
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <string>
|
||||
#include <iosfwd>
|
||||
#include <ios> // std::streamsize
|
||||
#include <ostream>
|
||||
#include <memory> // std::unique_ptr
|
||||
#include <utility> // std::move()
|
||||
#include <algorithm> // std::min()
|
||||
#include <limits> // std::numeric_limits
|
||||
#include <cstddef> // std::size_t, std::ptrdiff_t
|
||||
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/io/iostreams/CharArrayStream.hxx>
|
||||
#include <simgear/io/iostreams/zlibstream.hxx>
|
||||
#include "EmbeddedResource.hxx"
|
||||
|
||||
using std::string;
|
||||
using std::unique_ptr;
|
||||
|
||||
// Inspired by <http://stackoverflow.com/a/21174979/4756009>
|
||||
template<typename Derived, typename Base>
|
||||
static unique_ptr<Derived> static_unique_ptr_cast(unique_ptr<Base> p)
|
||||
{
|
||||
auto d = static_cast<Derived *>(p.release());
|
||||
return unique_ptr<Derived>(d);
|
||||
}
|
||||
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
// ***************************************************************************
|
||||
// * AbstractEmbeddedResource class *
|
||||
// ***************************************************************************
|
||||
|
||||
AbstractEmbeddedResource::AbstractEmbeddedResource(const char *data,
|
||||
std::size_t size)
|
||||
: _data(data),
|
||||
_size(size)
|
||||
{ }
|
||||
|
||||
const char *AbstractEmbeddedResource::rawPtr() const
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
std::size_t AbstractEmbeddedResource::rawSize() const
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
|
||||
string AbstractEmbeddedResource::str() const
|
||||
{
|
||||
if (_size > std::numeric_limits<string::size_type>::max()) {
|
||||
throw sg_range_exception(
|
||||
"Resource too large to fit in an std::string (size: " +
|
||||
std::to_string(_size) + " bytes)");
|
||||
}
|
||||
|
||||
return string(_data, _size);
|
||||
}
|
||||
|
||||
// ***************************************************************************
|
||||
// * RawEmbeddedResource class *
|
||||
// ***************************************************************************
|
||||
|
||||
RawEmbeddedResource::RawEmbeddedResource(const char *data, std::size_t size)
|
||||
: AbstractEmbeddedResource(data, size)
|
||||
{ }
|
||||
|
||||
AbstractEmbeddedResource::CompressionType
|
||||
RawEmbeddedResource::compressionType() const
|
||||
{
|
||||
return AbstractEmbeddedResource::CompressionType::NONE;
|
||||
}
|
||||
|
||||
string RawEmbeddedResource::compressionDescr() const
|
||||
{
|
||||
return string("none");
|
||||
}
|
||||
|
||||
unique_ptr<std::streambuf> RawEmbeddedResource::streambuf() const
|
||||
{
|
||||
// This is a read-only variant of CharArrayStreambuf
|
||||
return unique_ptr<std::streambuf>(
|
||||
new ROCharArrayStreambuf(rawPtr(), rawSize()));
|
||||
}
|
||||
|
||||
unique_ptr<std::istream> RawEmbeddedResource::istream() const
|
||||
{
|
||||
return unique_ptr<std::istream>(new CharArrayIStream(rawPtr(), rawSize()));
|
||||
}
|
||||
|
||||
// ***************************************************************************
|
||||
// * ZlibEmbeddedResource class *
|
||||
// ***************************************************************************
|
||||
|
||||
ZlibEmbeddedResource::ZlibEmbeddedResource(const char *data,
|
||||
std::size_t compressedSize,
|
||||
std::size_t uncompressedSize)
|
||||
: AbstractEmbeddedResource(data, compressedSize),
|
||||
_uncompressedSize(uncompressedSize),
|
||||
_inBuf(nullptr),
|
||||
_inBufSize(262144), // adjusted below in the constructor body
|
||||
_outBuf(nullptr),
|
||||
_outBufSize(262144),
|
||||
_putbackSize(0) // default for best performance
|
||||
{
|
||||
static_assert(262144 <= std::numeric_limits<std::size_t>::max(),
|
||||
"The std::size_t type is unexpectedly small.");
|
||||
// No need to use an input buffer (where compressed data chunks are put for
|
||||
// zlib to read and decompress) larger than the whole compressed resource!
|
||||
_inBufSize = std::min(rawSize(), _inBufSize);
|
||||
}
|
||||
|
||||
AbstractEmbeddedResource::CompressionType
|
||||
ZlibEmbeddedResource::compressionType() const
|
||||
{ return AbstractEmbeddedResource::CompressionType::ZLIB; }
|
||||
|
||||
string ZlibEmbeddedResource::compressionDescr() const
|
||||
{ return string("zlib"); }
|
||||
|
||||
std::size_t ZlibEmbeddedResource::uncompressedSize() const
|
||||
{ return _uncompressedSize; }
|
||||
|
||||
char* ZlibEmbeddedResource::getInputBufferStart()
|
||||
{ return _inBuf; }
|
||||
|
||||
void ZlibEmbeddedResource::setInputBufferStart(char* inBuf)
|
||||
{ _inBuf = inBuf; }
|
||||
|
||||
std::size_t ZlibEmbeddedResource::getInputBufferSize()
|
||||
{ return _inBufSize; }
|
||||
|
||||
void ZlibEmbeddedResource::setInputBufferSize(std::size_t size)
|
||||
{ _inBufSize = size; }
|
||||
|
||||
char* ZlibEmbeddedResource::getOutputBufferStart()
|
||||
{ return _outBuf; }
|
||||
|
||||
void ZlibEmbeddedResource::setOutputBufferStart(char* outBuf)
|
||||
{ _outBuf = outBuf; }
|
||||
|
||||
std::size_t ZlibEmbeddedResource::getOutputBufferSize()
|
||||
{ return _outBufSize; }
|
||||
|
||||
void ZlibEmbeddedResource::setOutputBufferSize(std::size_t size)
|
||||
{ _outBufSize = size; }
|
||||
|
||||
std::size_t ZlibEmbeddedResource::getPutbackSize()
|
||||
{ return _putbackSize; }
|
||||
|
||||
void ZlibEmbeddedResource::setPutbackSize(std::size_t size)
|
||||
{ _putbackSize = size; }
|
||||
|
||||
unique_ptr<std::streambuf> ZlibEmbeddedResource::streambuf() const
|
||||
{
|
||||
unique_ptr<CharArrayIStream> rawReaderIStream(
|
||||
new CharArrayIStream(rawPtr(), rawSize()));
|
||||
|
||||
return unique_ptr<std::streambuf>(
|
||||
new ZlibDecompressorIStreambuf(
|
||||
std::move(rawReaderIStream),
|
||||
SGPath(), // rawReaderIStream isn't bound to a file
|
||||
ZLibCompressionFormat::ZLIB,
|
||||
_inBuf, _inBufSize, _outBuf, _outBufSize, _putbackSize));
|
||||
}
|
||||
|
||||
unique_ptr<std::istream> ZlibEmbeddedResource::istream() const
|
||||
{
|
||||
unique_ptr<CharArrayIStream> rawReaderIStream(
|
||||
new CharArrayIStream(rawPtr(), rawSize()));
|
||||
|
||||
return unique_ptr<std::istream>(
|
||||
new ZlibDecompressorIStream(
|
||||
std::move(rawReaderIStream),
|
||||
SGPath(), // rawReaderIStream isn't bound to a file
|
||||
ZLibCompressionFormat::ZLIB,
|
||||
_inBuf, _inBufSize, _outBuf, _outBufSize, _putbackSize));
|
||||
}
|
||||
|
||||
std::string ZlibEmbeddedResource::str() const
|
||||
{
|
||||
static constexpr std::size_t bufSize = 65536;
|
||||
static_assert(bufSize <= std::numeric_limits<std::streamsize>::max(),
|
||||
"Type std::streamsize is unexpectedly small");
|
||||
static_assert(bufSize <= std::numeric_limits<string::size_type>::max(),
|
||||
"Type std::string::size_type is unexpectedly small");
|
||||
unique_ptr<char[]> buf(new char[bufSize]);
|
||||
|
||||
auto decompressor =
|
||||
static_unique_ptr_cast<ZlibDecompressorIStream>(istream());
|
||||
std::streamsize nbCharsRead;
|
||||
string result;
|
||||
|
||||
if (_uncompressedSize > std::numeric_limits<string::size_type>::max()) {
|
||||
throw sg_range_exception(
|
||||
"Resource too large to fit in an std::string (uncompressed size: "
|
||||
+ std::to_string(_uncompressedSize) + " bytes)");
|
||||
} else {
|
||||
result.reserve(static_cast<string::size_type>(_uncompressedSize));
|
||||
}
|
||||
|
||||
do {
|
||||
decompressor->read(buf.get(), bufSize);
|
||||
nbCharsRead = decompressor->gcount();
|
||||
|
||||
if (nbCharsRead > 0) {
|
||||
result.append(buf.get(), nbCharsRead);
|
||||
}
|
||||
} while (*decompressor);
|
||||
|
||||
// decompressor->fail() would *not* indicate an error, due to the semantics
|
||||
// of std::istream::read().
|
||||
if (decompressor->bad()) {
|
||||
throw sg_io_exception("Error while extracting a compressed resource");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// ***************************************************************************
|
||||
// * Stream insertion operators *
|
||||
// ***************************************************************************
|
||||
std::ostream& operator<<(std::ostream& os,
|
||||
const RawEmbeddedResource& resource)
|
||||
{ // This won't escape double quotes, backslashes, etc. in resource.str().
|
||||
return os << "RawEmbeddedResource:\n"
|
||||
" compressionType = \"" << resource.compressionDescr() << "\"\n"
|
||||
" rawPtr = " << (void*) resource.rawPtr() << "\n"
|
||||
" rawSize = " << resource.rawSize();
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os,
|
||||
const ZlibEmbeddedResource& resource)
|
||||
{ // This won't escape double quotes, backslashes, etc. in resource.str().
|
||||
return os << "ZlibEmbeddedResource:\n"
|
||||
" compressionType = \"" << resource.compressionDescr() << "\"\n"
|
||||
" rawPtr = " << (void*) resource.rawPtr() << "\n"
|
||||
" rawSize = " << resource.rawSize() << "\n"
|
||||
" uncompressedSize = " << resource.uncompressedSize();
|
||||
}
|
||||
|
||||
} // of namespace simgear
|
||||
163
simgear/embedded_resources/EmbeddedResource.hxx
Normal file
163
simgear/embedded_resources/EmbeddedResource.hxx
Normal file
@@ -0,0 +1,163 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// EmbeddedResource.hxx --- Class for pointing to/accessing an embedded resource
|
||||
// Copyright (C) 2017 Florent Rougon
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA.
|
||||
|
||||
#ifndef FG_EMBEDDEDRESOURCE_HXX
|
||||
#define FG_EMBEDDEDRESOURCE_HXX
|
||||
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
#include <ostream>
|
||||
#include <memory> // std::unique_ptr
|
||||
#include <cstddef> // std::size_t, std::ptrdiff_t
|
||||
|
||||
#include <simgear/io/iostreams/zlibstream.hxx>
|
||||
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
// Abstract base class for embedded resources
|
||||
class AbstractEmbeddedResource
|
||||
{
|
||||
public:
|
||||
enum class CompressionType {
|
||||
NONE = 0,
|
||||
ZLIB
|
||||
};
|
||||
|
||||
// Constructor.
|
||||
//
|
||||
// 'data' and 'size' indicate the resource contents. There is no requirement
|
||||
// of null-termination, including for text data (given how
|
||||
// EmbeddedResourceManager::getString() works, including a null terminator
|
||||
// for text contents is actually counter-productive). The data may be of
|
||||
// arbitrary type and size: binary, text, whatever. The constructed object
|
||||
// (for derived classes since this one is abstract) does *not* hold a copy
|
||||
// of the data, it just keeps a pointer to it and provides methods to access
|
||||
// it. The data must therefore remain available as long as the object is in
|
||||
// use---this class was designed for use with data stored in static
|
||||
// variables.
|
||||
explicit AbstractEmbeddedResource(const char *data, std::size_t size);
|
||||
AbstractEmbeddedResource(const AbstractEmbeddedResource&) = default;
|
||||
AbstractEmbeddedResource(AbstractEmbeddedResource&&) = default;
|
||||
AbstractEmbeddedResource& operator=(const AbstractEmbeddedResource&) = default;
|
||||
AbstractEmbeddedResource& operator=(AbstractEmbeddedResource&&) = default;
|
||||
virtual ~AbstractEmbeddedResource() = default;
|
||||
|
||||
// Return the pointer to beginning-of-resource contents---the same that was
|
||||
// passed to the constructor.
|
||||
const char *rawPtr() const;
|
||||
// Return the resource size, as passed to the constructor. For a compressed
|
||||
// resource, this is the compressed size; such resources provide an
|
||||
// additional uncompressedSize() method.
|
||||
std::size_t rawSize() const;
|
||||
|
||||
// Return an std::string object containing a copy of the resource contents.
|
||||
// For a compressed resource, this is the data obtained after decompression.
|
||||
virtual std::string str() const;
|
||||
// Return an std::streambuf instance providing read-only access to the
|
||||
// resource contents (in uncompressed form for compressed resources). This
|
||||
// allows memory-friendly access to large resources by enabling incremental
|
||||
// processing with transparent decompression for compressed resources.
|
||||
virtual std::unique_ptr<std::streambuf> streambuf() const = 0;
|
||||
// Return an std::istream instance providing read-only access to the
|
||||
// resource contents (in uncompressed form for compressed resources).
|
||||
//
|
||||
// The same remark as for streambuf() applies. std::istream is simply a
|
||||
// higher-level interface than std::streambuf, otherwise both allow the same
|
||||
// kind of processing.
|
||||
virtual std::unique_ptr<std::istream> istream() const = 0;
|
||||
|
||||
// Return the resource compression type.
|
||||
virtual CompressionType compressionType() const = 0;
|
||||
// Return a string description of the resource compression type. Examples:
|
||||
// "none", "zlib".
|
||||
virtual std::string compressionDescr() const = 0;
|
||||
|
||||
private:
|
||||
// Pointer to the start of resource contents
|
||||
const char *_data;
|
||||
// Size of resource contents, in bytes
|
||||
std::size_t _size;
|
||||
};
|
||||
|
||||
// Class to describe an uncompressed resource. See AbstractEmbeddedResource.
|
||||
class RawEmbeddedResource : public AbstractEmbeddedResource
|
||||
{
|
||||
public:
|
||||
explicit RawEmbeddedResource(const char *data, std::size_t size);
|
||||
|
||||
AbstractEmbeddedResource::CompressionType compressionType() const override;
|
||||
std::string compressionDescr() const override;
|
||||
|
||||
// The str() method is inherited from AbstractEmbeddedResource
|
||||
std::unique_ptr<std::streambuf> streambuf() const override;
|
||||
std::unique_ptr<std::istream> istream() const override;
|
||||
};
|
||||
|
||||
// Class to describe a zlib-compressed resource.
|
||||
//
|
||||
// Instances of this class point to resource contents stored in the stream
|
||||
// format documented in RFC 1950.
|
||||
class ZlibEmbeddedResource : public AbstractEmbeddedResource
|
||||
{
|
||||
public:
|
||||
explicit ZlibEmbeddedResource(const char *data, std::size_t compressedSize,
|
||||
std::size_t uncompressedSize);
|
||||
|
||||
AbstractEmbeddedResource::CompressionType compressionType() const override;
|
||||
std::string compressionDescr() const override;
|
||||
// Return the resource uncompressed size, in bytes.
|
||||
std::size_t uncompressedSize() const;
|
||||
|
||||
std::string str() const override;
|
||||
std::unique_ptr<std::streambuf> streambuf() const override;
|
||||
std::unique_ptr<std::istream> istream() const override;
|
||||
|
||||
// Getters and setters for parameters used in streambuf() and istream().
|
||||
// Calling any of the setters affects the subsequent streambuf() and
|
||||
// istream() calls.
|
||||
char* getInputBufferStart();
|
||||
void setInputBufferStart(char* inBuf);
|
||||
std::size_t getInputBufferSize();
|
||||
void setInputBufferSize(std::size_t size);
|
||||
char* getOutputBufferStart();
|
||||
void setOutputBufferStart(char* outBuf);
|
||||
std::size_t getOutputBufferSize();
|
||||
void setOutputBufferSize(std::size_t size);
|
||||
std::size_t getPutbackSize();
|
||||
void setPutbackSize(std::size_t size);
|
||||
|
||||
private:
|
||||
std::size_t _uncompressedSize;
|
||||
char* _inBuf;
|
||||
std::size_t _inBufSize;
|
||||
char* _outBuf;
|
||||
std::size_t _outBufSize;
|
||||
std::size_t _putbackSize;
|
||||
};
|
||||
|
||||
// These functions are essentially intended for troubleshooting purposes.
|
||||
std::ostream& operator<<(std::ostream&, const RawEmbeddedResource&);
|
||||
std::ostream& operator<<(std::ostream&, const ZlibEmbeddedResource&);
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
#endif // of FG_EMBEDDEDRESOURCE_HXX
|
||||
235
simgear/embedded_resources/EmbeddedResourceManager.cxx
Normal file
235
simgear/embedded_resources/EmbeddedResourceManager.cxx
Normal file
@@ -0,0 +1,235 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// EmbeddedResourceManager.cxx --- Manager class for resources embedded in an
|
||||
// executable
|
||||
// Copyright (C) 2017 Florent Rougon
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA.
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <memory>
|
||||
#include <utility> // std::move()
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include "EmbeddedResource.hxx"
|
||||
#include "EmbeddedResourceManager.hxx"
|
||||
#include "EmbeddedResourceManager_private.hxx"
|
||||
|
||||
using std::string;
|
||||
using std::shared_ptr;
|
||||
using std::unique_ptr;
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
static unique_ptr<EmbeddedResourceManager> staticInstance;
|
||||
|
||||
// ***************************************************************************
|
||||
// * EmbeddedResourceManager::Impl *
|
||||
// ***************************************************************************
|
||||
EmbeddedResourceManager::Impl::Impl()
|
||||
: dirty(true)
|
||||
{ }
|
||||
|
||||
void
|
||||
EmbeddedResourceManager::Impl::rehash()
|
||||
{
|
||||
// Update the list of resource pools to search when looking up a resource.
|
||||
// This allows to optimize resource lookup: no need to parse, split and hash
|
||||
// the same locale string every time to find the corresponding resource
|
||||
// pools.
|
||||
poolSearchList = listOfResourcePoolsToSearch(selectedLocale);
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
string
|
||||
EmbeddedResourceManager::Impl::getLocale() const
|
||||
{
|
||||
return selectedLocale;
|
||||
}
|
||||
|
||||
string
|
||||
EmbeddedResourceManager::Impl::selectLocale(const std::string& locale)
|
||||
{
|
||||
string previousLocale = std::move(selectedLocale);
|
||||
selectedLocale = locale;
|
||||
dirty = true;
|
||||
|
||||
return previousLocale;
|
||||
}
|
||||
|
||||
// Static method
|
||||
std::vector<string>
|
||||
EmbeddedResourceManager::Impl::localesSearchList(const string& locale)
|
||||
{
|
||||
std::vector<string> result;
|
||||
|
||||
if (locale.empty()) {
|
||||
result.push_back(string()); // only the default locale
|
||||
} else {
|
||||
std::size_t sepIdx = locale.find_first_of('_');
|
||||
|
||||
if (sepIdx == string::npos) {
|
||||
// Try the given “locale” first (e.g., fr), then the default locale
|
||||
result = std::vector<string>({locale, string()});
|
||||
} else {
|
||||
string langCode = locale.substr(0, sepIdx);
|
||||
// Try the given “locale” first (e.g., fr_FR), then the language code
|
||||
// (e.g., fr) and finally the default locale
|
||||
result = std::vector<string>({locale, langCode, string()});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
auto
|
||||
EmbeddedResourceManager::Impl::listOfResourcePoolsToSearch(
|
||||
const string& locale) const
|
||||
-> std::vector< shared_ptr<ResourcePool> >
|
||||
{
|
||||
std::vector<string> searchedLocales = localesSearchList(locale);
|
||||
std::vector< shared_ptr<ResourcePool> > result;
|
||||
|
||||
for (const string& loc: searchedLocales) {
|
||||
auto poolPtrIt = localeToResourcePoolMap.find(loc);
|
||||
// Don't store pointers to empty resource pools in 'result'. This
|
||||
// optimizes resource fetching a little bit, but requires that all
|
||||
// resources are added before this method is called.
|
||||
if (poolPtrIt != localeToResourcePoolMap.end()) {
|
||||
// Copy a shared_ptr<ResourcePool>
|
||||
result.push_back(poolPtrIt->second);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Static method
|
||||
shared_ptr<const AbstractEmbeddedResource>
|
||||
EmbeddedResourceManager::Impl::lookupResourceInPools(
|
||||
const string& virtualPath,
|
||||
const std::vector< shared_ptr<ResourcePool> >& aPoolSearchList)
|
||||
{
|
||||
// Search the provided resource pools in proper order. For instance, the one
|
||||
// for 'fr_FR', then the one for 'fr' and finally the one for the default
|
||||
// locale. Return the first resource found in one of these pools.
|
||||
for (const shared_ptr<ResourcePool>& poolPtr: aPoolSearchList) {
|
||||
auto resourcePtrIt = poolPtr->find(virtualPath);
|
||||
|
||||
if (resourcePtrIt != poolPtr->end()) {
|
||||
// Copy a shared_ptr<const AbstractEmbeddedResource>
|
||||
return resourcePtrIt->second;
|
||||
}
|
||||
}
|
||||
|
||||
return shared_ptr<const AbstractEmbeddedResource>(); // null shared_ptr object
|
||||
}
|
||||
|
||||
void
|
||||
EmbeddedResourceManager::Impl::addResource(
|
||||
const string& virtualPath,
|
||||
unique_ptr<const AbstractEmbeddedResource> resourcePtr,
|
||||
const string& locale)
|
||||
{
|
||||
// Find the resource pool corresponding to the specified locale
|
||||
shared_ptr<ResourcePool>& resPoolPtr = localeToResourcePoolMap[locale];
|
||||
if (!resPoolPtr) {
|
||||
resPoolPtr.reset(new ResourcePool());
|
||||
}
|
||||
|
||||
auto emplaceRetval = resPoolPtr->emplace(virtualPath, std::move(resourcePtr));
|
||||
|
||||
if (!emplaceRetval.second) {
|
||||
const string localeDescr =
|
||||
(locale.empty()) ? "the default locale" : "locale '" + locale + "'";
|
||||
throw sg_error(
|
||||
"Virtual path already in use for " + localeDescr +
|
||||
" in the EmbeddedResourceManager: '" + virtualPath + "'");
|
||||
}
|
||||
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
// ***************************************************************************
|
||||
// * EmbeddedResourceManager *
|
||||
// ***************************************************************************
|
||||
EmbeddedResourceManager::EmbeddedResourceManager()
|
||||
: p(unique_ptr<Impl>(new Impl))
|
||||
{ }
|
||||
|
||||
const unique_ptr<EmbeddedResourceManager>&
|
||||
EmbeddedResourceManager::createInstance()
|
||||
{
|
||||
staticInstance.reset(new EmbeddedResourceManager);
|
||||
return staticInstance;
|
||||
}
|
||||
|
||||
const unique_ptr<EmbeddedResourceManager>&
|
||||
EmbeddedResourceManager::instance()
|
||||
{
|
||||
return staticInstance;
|
||||
}
|
||||
|
||||
string
|
||||
EmbeddedResourceManager::getLocale() const
|
||||
{
|
||||
return p->getLocale();
|
||||
}
|
||||
|
||||
string
|
||||
EmbeddedResourceManager::selectLocale(const std::string& locale)
|
||||
{
|
||||
return p->selectLocale(locale);
|
||||
}
|
||||
|
||||
void
|
||||
EmbeddedResourceManager::addResource(
|
||||
const string& virtualPath,
|
||||
unique_ptr<const AbstractEmbeddedResource> resourcePtr,
|
||||
const string& locale)
|
||||
{
|
||||
p->addResource(virtualPath, std::move(resourcePtr), locale);
|
||||
}
|
||||
|
||||
shared_ptr<const AbstractEmbeddedResource>
|
||||
EmbeddedResourceManager::getResourceOrNullPtr(const string& virtualPath) const
|
||||
{
|
||||
if (p->dirty) {
|
||||
p->rehash(); // update p->poolSearchList
|
||||
}
|
||||
|
||||
// Use the selected locale
|
||||
return p->lookupResourceInPools(virtualPath, p->poolSearchList);
|
||||
}
|
||||
|
||||
shared_ptr<const AbstractEmbeddedResource>
|
||||
EmbeddedResourceManager::getResourceOrNullPtr(const string& virtualPath,
|
||||
const string& locale) const
|
||||
{
|
||||
// In this overload, we don't use the cached list of pools
|
||||
// (p->poolSearchList), therefore there is no need to check the 'dirty' flag
|
||||
// or to rehash().
|
||||
return p->lookupResourceInPools(virtualPath,
|
||||
p->listOfResourcePoolsToSearch(locale));
|
||||
}
|
||||
|
||||
} // of namespace simgear
|
||||
204
simgear/embedded_resources/EmbeddedResourceManager.hxx
Normal file
204
simgear/embedded_resources/EmbeddedResourceManager.hxx
Normal file
@@ -0,0 +1,204 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// EmbeddedResourceManager.hxx --- Manager class for resources embedded in an
|
||||
// executable
|
||||
// Copyright (C) 2017 Florent Rougon
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA.
|
||||
|
||||
#ifndef FG_EMBEDDEDRESOURCEMANAGER_HXX
|
||||
#define FG_EMBEDDEDRESOURCEMANAGER_HXX
|
||||
|
||||
#include <string>
|
||||
#include <memory> // std::unique_ptr, std::shared_ptr
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <utility> // std::forward()
|
||||
#include <cstddef> // std::size_t
|
||||
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include "EmbeddedResource.hxx"
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
class EmbeddedResourceManager
|
||||
{
|
||||
public:
|
||||
EmbeddedResourceManager(const EmbeddedResourceManager&) = delete;
|
||||
EmbeddedResourceManager& operator=(const EmbeddedResourceManager&) = delete;
|
||||
EmbeddedResourceManager(EmbeddedResourceManager&&) = delete;
|
||||
EmbeddedResourceManager& operator=(EmbeddedResourceManager&&) = delete;
|
||||
// The instance is created by createInstance() -> private constructor
|
||||
// but it should be deleted by its owning std::unique_ptr -> public destructor
|
||||
~EmbeddedResourceManager() = default;
|
||||
|
||||
// Static creator
|
||||
static const std::unique_ptr<EmbeddedResourceManager>& createInstance();
|
||||
|
||||
// Singleton accessor
|
||||
static const std::unique_ptr<EmbeddedResourceManager>& instance();
|
||||
|
||||
// Return the currently-selected “locale”[*] for resource fetching.
|
||||
//
|
||||
// [*] For instance: std::string("") for the default locale
|
||||
// std::string("fr") for French
|
||||
// std::string("fr_FR") for French from France
|
||||
std::string getLocale() const;
|
||||
// Select the locale for which resources will be returned in the future, for
|
||||
// the getResourceOrNullPtr(), getResource(), getString(), getStreambuf()
|
||||
// and getIStream() overloads that don't have a 'locale' parameter.
|
||||
// May be called several times. Return the previously-selected locale.
|
||||
//
|
||||
// If you just want to fetch one or two resources in a particular “locale”
|
||||
// (language), it is simpler to use an overload of one of the
|
||||
// getResourceOrNullPtr(), getResource(), ..., getIStream() methods that has
|
||||
// a 'locale' parameter.
|
||||
std::string selectLocale(const std::string& locale);
|
||||
|
||||
// Add a resource for the specified locale to the embedded resource manager.
|
||||
// This method acts as a sink for its second argument (the std::unique_ptr
|
||||
// typically has to be std::move()d). If 'locale' is empty, the resource is
|
||||
// added for the default locale.
|
||||
void addResource(const std::string& virtualPath,
|
||||
std::unique_ptr<const AbstractEmbeddedResource> resourcePtr,
|
||||
const std::string& locale = std::string());
|
||||
|
||||
// Get access to a resource.
|
||||
//
|
||||
// Fetch the resource for the selected locale (cf. selectLocale()), with
|
||||
// fallback behavior[1]. If no resource is found for the given
|
||||
// 'virtualPath', return a null
|
||||
// std::shared_ptr<const AbstractEmbeddedResource>.
|
||||
//
|
||||
// [1] This means that for instance, if the selected locale is 'es_ES', the
|
||||
// resource is first looked up for the 'es_ES' “locale”; then, if not
|
||||
// found, for 'es'; and finally, if still not found, for the default
|
||||
// locale ''.
|
||||
std::shared_ptr<const AbstractEmbeddedResource> getResourceOrNullPtr(
|
||||
const std::string& virtualPath) const;
|
||||
// Same as the previous overload, except the resource is fetched for the
|
||||
// specified locale (with fallback behavior) instead of for the selected
|
||||
// locale. Use an empty 'locale' parameter to fetch the resource for the
|
||||
// default locale.
|
||||
std::shared_ptr<const AbstractEmbeddedResource> getResourceOrNullPtr(
|
||||
const std::string& virtualPath,
|
||||
const std::string& locale) const;
|
||||
|
||||
// Same overloads as for getResourceOrNullPtr(), except that if the resource
|
||||
// isn't found, then an sg_exception is raised. These methods never return
|
||||
// a null or empty std::shared_ptr<const AbstractEmbeddedResource>.
|
||||
template <typename ...Args>
|
||||
std::shared_ptr<const AbstractEmbeddedResource> getResource(
|
||||
const std::string& virtualPath, Args&& ...args) const
|
||||
{
|
||||
const auto resPtr = getResourceOrNullPtr(virtualPath,
|
||||
std::forward<Args>(args)...);
|
||||
|
||||
if (!resPtr) {
|
||||
throw sg_exception("No embedded resource found at virtual path '" +
|
||||
virtualPath + "'");
|
||||
}
|
||||
|
||||
return resPtr;
|
||||
}
|
||||
|
||||
// Get a resource contents in the form of an std::string. Raise an
|
||||
// sg_exception if no resource is found for the specified 'virtualPath'.
|
||||
//
|
||||
// The returned std::string is a copy of the resource contents (possibly
|
||||
// transparently decompressed, cf. simgear::ZlibEmbeddedResource).
|
||||
template <typename ...Args>
|
||||
std::string getString(const std::string& virtualPath, Args&& ...args) const
|
||||
{
|
||||
return getResource(virtualPath, std::forward<Args>(args)...)->str();
|
||||
}
|
||||
|
||||
// Get access to a resource via an std::streambuf instance. Raise an
|
||||
// sg_exception if no resource is found for the specified 'virtualPath'.
|
||||
//
|
||||
// This allows one to incrementally process the resource contents without
|
||||
// ever making a copy of it (including incremental, transparent
|
||||
// decompression if the resource happens to be compressed---cf.
|
||||
// simgear::ZlibEmbeddedResource).
|
||||
template <typename ...Args>
|
||||
std::unique_ptr<std::streambuf> getStreambuf(const std::string& virtualPath,
|
||||
Args&& ...args) const
|
||||
{
|
||||
return getResource(virtualPath, std::forward<Args>(args)...)->streambuf();
|
||||
}
|
||||
|
||||
// Get access to a resource via an std::istream instance. Raise an
|
||||
// sg_exception if no resource is found for the specified 'virtualPath'.
|
||||
//
|
||||
// The same remarks made for getStreambuf() apply here too.
|
||||
template <typename ...Args>
|
||||
std::unique_ptr<std::istream> getIStream(const std::string& virtualPath,
|
||||
Args&& ...args) const
|
||||
{
|
||||
return getResource(virtualPath, std::forward<Args>(args)...)->istream();
|
||||
}
|
||||
|
||||
private:
|
||||
// Constructor called from createInstance() only
|
||||
explicit EmbeddedResourceManager();
|
||||
|
||||
class Impl;
|
||||
const std::unique_ptr<Impl> p; // Pimpl idiom
|
||||
};
|
||||
|
||||
// Explicit template instantiations
|
||||
template
|
||||
std::shared_ptr<const AbstractEmbeddedResource>
|
||||
EmbeddedResourceManager::getResource(const std::string& virtualPath) const;
|
||||
|
||||
template
|
||||
std::string
|
||||
EmbeddedResourceManager::getString(const std::string& virtualPath) const;
|
||||
|
||||
template
|
||||
std::unique_ptr<std::streambuf>
|
||||
EmbeddedResourceManager::getStreambuf(const std::string& virtualPath) const;
|
||||
|
||||
template
|
||||
std::unique_ptr<std::istream>
|
||||
EmbeddedResourceManager::getIStream(const std::string& virtualPath) const;
|
||||
|
||||
// MSVC doesn't recognize these as template instantiations of what we defined
|
||||
// above (this seems to be with “Visual Studio 14 2015”), therefore only
|
||||
// include them with other compilers.
|
||||
#ifndef _MSC_VER
|
||||
template
|
||||
std::shared_ptr<const AbstractEmbeddedResource>
|
||||
EmbeddedResourceManager::getResource(const std::string& virtualPath,
|
||||
const std::string& locale) const;
|
||||
template
|
||||
std::string
|
||||
EmbeddedResourceManager::getString(const std::string& virtualPath,
|
||||
const std::string& locale) const;
|
||||
template
|
||||
std::unique_ptr<std::streambuf>
|
||||
EmbeddedResourceManager::getStreambuf(const std::string& virtualPath,
|
||||
const std::string& locale) const;
|
||||
template
|
||||
std::unique_ptr<std::istream>
|
||||
EmbeddedResourceManager::getIStream(const std::string& virtualPath,
|
||||
const std::string& locale) const;
|
||||
#endif // #ifndef _MSC_VER
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
#endif // of FG_EMBEDDEDRESOURCEMANAGER_HXX
|
||||
102
simgear/embedded_resources/EmbeddedResourceManager_private.hxx
Normal file
102
simgear/embedded_resources/EmbeddedResourceManager_private.hxx
Normal file
@@ -0,0 +1,102 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// EmbeddedResourceManager_private.hxx --- Private implementation class for
|
||||
// SimGear's EmbeddedResourceManager
|
||||
// Copyright (C) 2017 Florent Rougon
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA.
|
||||
|
||||
#ifndef FG_EMBEDDEDRESOURCEMANAGERPRIVATE_HXX
|
||||
#define FG_EMBEDDEDRESOURCEMANAGERPRIVATE_HXX
|
||||
|
||||
#include <string>
|
||||
#include <memory> // std::unique_ptr, std::shared_ptr
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "EmbeddedResource.hxx"
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
class EmbeddedResourceManager::Impl
|
||||
{
|
||||
public:
|
||||
explicit Impl();
|
||||
|
||||
// Each “locale” for which addResource() has been used has an associated
|
||||
// resource pool, that is a sort of directory of all resources declared in
|
||||
// this locale. The resource pool for a given locale (e.g., 'fr' or 'de_DE')
|
||||
// maps resource virtual paths to the corresponding resource descriptors
|
||||
// (via std::shared_ptr<const AbstractEmbeddedResource> instances).
|
||||
//
|
||||
// Note: for optimal lookup performance, a tree would probably be better,
|
||||
// since the expected use for each key here is to store a virtual
|
||||
// path. But such an optimization is likely unneeded in most cases.
|
||||
typedef std::unordered_map< std::string,
|
||||
std::shared_ptr<const AbstractEmbeddedResource> >
|
||||
ResourcePool;
|
||||
|
||||
// Return the list of “locales” to scan to implement fallback behavior when
|
||||
// fetching a resource for the specified locale. This list will be searched
|
||||
// from left to right. Examples:
|
||||
//
|
||||
// "" -> [""]
|
||||
// "fr" -> ["fr", ""]
|
||||
// "fr_FR" -> ["fr_FR", "fr", ""]
|
||||
static std::vector<std::string> localesSearchList(const std::string& locale);
|
||||
// Same as localesSearchList(), except it returns the resource pools instead
|
||||
// of the “locale” strings, and only those pools that are not empty.
|
||||
std::vector< std::shared_ptr<ResourcePool> > listOfResourcePoolsToSearch(
|
||||
const std::string& locale) const;
|
||||
// Look up, in each of the pools referred to by 'poolSearchList', the
|
||||
// resource associated to 'virtualPath'. Return the first match.
|
||||
static std::shared_ptr<const AbstractEmbeddedResource> lookupResourceInPools(
|
||||
const std::string& virtualPath,
|
||||
const std::vector< std::shared_ptr<ResourcePool> >& poolSearchList);
|
||||
|
||||
// Recompute p->poolSearchList. This method is automatically called whenever
|
||||
// needed (lazily), so it doesn't need to be part of the public interface.
|
||||
void rehash();
|
||||
|
||||
// Implement the corresponding EmbeddedResourceManager public methods
|
||||
std::string getLocale() const;
|
||||
std::string selectLocale(const std::string& locale);
|
||||
|
||||
// Ditto
|
||||
void addResource(const std::string& virtualPath,
|
||||
std::unique_ptr<const AbstractEmbeddedResource> resourcePtr,
|
||||
const std::string& locale);
|
||||
|
||||
std::string selectedLocale;
|
||||
// Each call to rehash() updates this member to contain precisely the
|
||||
// (ordered) list of pools to search for a resource in the selected
|
||||
// “locale”. This allows relatively cheap resource lookups, assuming the
|
||||
// desired “locale” doesn't change all the time.
|
||||
std::vector< std::shared_ptr<ResourcePool> > poolSearchList;
|
||||
// Indicate whether 'poolSearchList' must be updated (i.e., resources have
|
||||
// been added or the selected locale was changed without rehash() being
|
||||
// called afterwards).
|
||||
bool dirty;
|
||||
|
||||
// Maps each “locale name” to the corresponding resource pool.
|
||||
std::unordered_map< std::string,
|
||||
std::shared_ptr<ResourcePool> > localeToResourcePoolMap;
|
||||
};
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
#endif // of FG_EMBEDDEDRESOURCEMANAGERPRIVATE_HXX
|
||||
257
simgear/embedded_resources/EmbeddedResourceProxy.cxx
Normal file
257
simgear/embedded_resources/EmbeddedResourceProxy.cxx
Normal file
@@ -0,0 +1,257 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// EmbeddedResourceProxy.cxx --- Unified access to real files or embedded
|
||||
// resources
|
||||
// Copyright (C) 2017 Florent Rougon
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA.
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <algorithm> // std::find()
|
||||
#include <ios> // std::streamsize
|
||||
#include <istream>
|
||||
#include <limits> // std::numeric_limits
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstdlib> // std::size_t
|
||||
#include <cassert>
|
||||
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include "EmbeddedResourceManager.hxx"
|
||||
#include "EmbeddedResourceProxy.hxx"
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using std::shared_ptr;
|
||||
using std::unique_ptr;
|
||||
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
EmbeddedResourceProxy::EmbeddedResourceProxy(const SGPath& realRoot,
|
||||
const string& virtualRoot,
|
||||
bool useEmbeddedResourcesByDefault)
|
||||
: _realRoot(realRoot),
|
||||
_virtualRoot(normalizeVirtualRoot(virtualRoot)),
|
||||
_useEmbeddedResourcesByDefault(useEmbeddedResourcesByDefault)
|
||||
{ }
|
||||
|
||||
SGPath
|
||||
EmbeddedResourceProxy::getRealRoot() const
|
||||
{ return _realRoot; }
|
||||
|
||||
void
|
||||
EmbeddedResourceProxy::setRealRoot(const SGPath& realRoot)
|
||||
{ _realRoot = realRoot; }
|
||||
|
||||
string
|
||||
EmbeddedResourceProxy::getVirtualRoot() const
|
||||
{ return _virtualRoot; }
|
||||
|
||||
void
|
||||
EmbeddedResourceProxy::setVirtualRoot(const string& virtualRoot)
|
||||
{ _virtualRoot = normalizeVirtualRoot(virtualRoot); }
|
||||
|
||||
bool
|
||||
EmbeddedResourceProxy::getUseEmbeddedResources() const
|
||||
{ return _useEmbeddedResourcesByDefault; }
|
||||
|
||||
void
|
||||
EmbeddedResourceProxy::setUseEmbeddedResources(bool useEmbeddedResources)
|
||||
{ _useEmbeddedResourcesByDefault = useEmbeddedResources; }
|
||||
|
||||
// Static method: normalize the 'virtualRoot' argument of the constructor
|
||||
//
|
||||
// The argument must start with a slash and mustn't contain any '.' or '..'
|
||||
// component. The return value never ends with a slash.
|
||||
string
|
||||
EmbeddedResourceProxy::normalizeVirtualRoot(const string& path)
|
||||
{
|
||||
EmbeddedResourceProxy::checkPath(__func__, path,
|
||||
false /* allowStartWithColon */);
|
||||
string res = path;
|
||||
|
||||
// Make sure 'res' doesn't end with a '/'.
|
||||
while (!res.empty() && res.back() == '/') {
|
||||
res.pop_back(); // This will ease path concatenation
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// Static method
|
||||
void
|
||||
EmbeddedResourceProxy::checkPath(const string& callerMethod, const string& path,
|
||||
bool allowStartWithColon)
|
||||
{
|
||||
if (path.empty()) {
|
||||
throw sg_format_exception(
|
||||
"Invalid empty path for EmbeddedResourceProxy::" +
|
||||
callerMethod + "(): '" + path + "'", path);
|
||||
} else if (allowStartWithColon &&
|
||||
!simgear::strutils::starts_with(path, ":/") && path[0] != '/') {
|
||||
throw sg_format_exception(
|
||||
"Invalid path for EmbeddedResourceProxy::" + callerMethod + "(): "
|
||||
"it should start with either ':/' or '/'", path);
|
||||
} else if (!allowStartWithColon && path[0] != '/') {
|
||||
throw sg_format_exception(
|
||||
"Invalid path for EmbeddedResourceProxy::" + callerMethod + "(): "
|
||||
"it should start with a slash ('/')", path);
|
||||
} else {
|
||||
const vector<string> components = simgear::strutils::split(path, "/");
|
||||
auto find = [&components](const string& s) -> bool {
|
||||
return (std::find(components.begin(), components.end(), s) !=
|
||||
components.end());
|
||||
};
|
||||
|
||||
if (find(".") || find("..")) {
|
||||
throw sg_format_exception(
|
||||
"Invalid path for EmbeddedResourceProxy::" + callerMethod + "(): "
|
||||
"'.' and '..' components are not allowed", path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unique_ptr<std::istream>
|
||||
EmbeddedResourceProxy::getIStream(const string& path, bool fromEmbeddedResource)
|
||||
const
|
||||
{
|
||||
EmbeddedResourceProxy::checkPath(__func__, path,
|
||||
false /* allowStartWithColon */);
|
||||
assert(!path.empty() && path.front() == '/');
|
||||
|
||||
if (fromEmbeddedResource) {
|
||||
const auto& embeddedResMgr = simgear::EmbeddedResourceManager::instance();
|
||||
return embeddedResMgr->getIStream(
|
||||
_virtualRoot + path,
|
||||
""); // fetch the default-locale version of the resource
|
||||
} else {
|
||||
const SGPath sgPath = _realRoot / path.substr(std::size_t(1));
|
||||
return unique_ptr<std::istream>(new sg_ifstream(sgPath));
|
||||
}
|
||||
}
|
||||
|
||||
unique_ptr<std::istream>
|
||||
EmbeddedResourceProxy::getIStream(const string& path) const
|
||||
{
|
||||
return getIStream(path, _useEmbeddedResourcesByDefault);
|
||||
}
|
||||
|
||||
unique_ptr<std::istream>
|
||||
EmbeddedResourceProxy::getIStreamDecideOnPrefix(const string& path) const
|
||||
{
|
||||
EmbeddedResourceProxy::checkPath(__func__, path,
|
||||
true /* allowStartWithColon */);
|
||||
|
||||
// 'path' is non-empty
|
||||
if (path.front() == '/') {
|
||||
return getIStream(path, false /* fromEmbeddedResource */);
|
||||
} else if (path.front() == ':') {
|
||||
assert(path.size() >= 2 && path[1] == '/');
|
||||
// Skip the leading ':'
|
||||
return getIStream(path.substr(std::size_t(1)),
|
||||
true /* fromEmbeddedResource */);
|
||||
} else {
|
||||
// The checkPath() call should make it impossible to reach this point.
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
string
|
||||
EmbeddedResourceProxy::getString(const string& path, bool fromEmbeddedResource)
|
||||
const
|
||||
{
|
||||
string result;
|
||||
|
||||
EmbeddedResourceProxy::checkPath(__func__, path,
|
||||
false /* allowStartWithColon */);
|
||||
assert(!path.empty() && path.front() == '/');
|
||||
|
||||
if (fromEmbeddedResource) {
|
||||
const auto& embeddedResMgr = simgear::EmbeddedResourceManager::instance();
|
||||
// Fetch the default-locale version of the resource
|
||||
result = embeddedResMgr->getString(_virtualRoot + path, "");
|
||||
} else {
|
||||
const SGPath sgPath = _realRoot / path.substr(std::size_t(1));
|
||||
result.reserve(sgPath.sizeInBytes());
|
||||
const unique_ptr<std::istream> streamp = getIStream(path,
|
||||
fromEmbeddedResource);
|
||||
std::streamsize nbCharsRead;
|
||||
|
||||
// Allocate a buffer
|
||||
static constexpr std::size_t bufSize = 65536;
|
||||
static_assert(bufSize <= std::numeric_limits<std::streamsize>::max(),
|
||||
"Type std::streamsize is unexpectedly small");
|
||||
static_assert(bufSize <= std::numeric_limits<string::size_type>::max(),
|
||||
"Type std::string::size_type is unexpectedly small");
|
||||
unique_ptr<char[]> buf(new char[bufSize]);
|
||||
|
||||
do {
|
||||
streamp->read(buf.get(), bufSize);
|
||||
nbCharsRead = streamp->gcount();
|
||||
|
||||
if (nbCharsRead > 0) {
|
||||
result.append(buf.get(), nbCharsRead);
|
||||
}
|
||||
} while (*streamp);
|
||||
|
||||
// streamp->fail() would *not* indicate an error, due to the semantics
|
||||
// of std::istream::read().
|
||||
if (streamp->bad()) {
|
||||
throw sg_io_exception("Error reading from file", sg_location(path));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
string
|
||||
EmbeddedResourceProxy::getString(const string& path) const
|
||||
{
|
||||
return getString(path, _useEmbeddedResourcesByDefault);
|
||||
}
|
||||
|
||||
string
|
||||
EmbeddedResourceProxy::getStringDecideOnPrefix(const string& path) const
|
||||
{
|
||||
string result;
|
||||
|
||||
EmbeddedResourceProxy::checkPath(__func__, path,
|
||||
true /* allowStartWithColon */);
|
||||
|
||||
// 'path' is non-empty
|
||||
if (path.front() == '/') {
|
||||
result = getString(path, false /* fromEmbeddedResource */);
|
||||
} else if (path.front() == ':') {
|
||||
assert(path.size() >= 2 && path[1] == '/');
|
||||
// Skip the leading ':'
|
||||
result = getString(path.substr(std::size_t(1)),
|
||||
true /* fromEmbeddedResource */);
|
||||
} else {
|
||||
// The checkPath() call should make it impossible to reach this point.
|
||||
std::abort();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // of namespace simgear
|
||||
155
simgear/embedded_resources/EmbeddedResourceProxy.hxx
Normal file
155
simgear/embedded_resources/EmbeddedResourceProxy.hxx
Normal file
@@ -0,0 +1,155 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// EmbeddedResourceProxy.hxx --- Unified access to real files or embedded
|
||||
// resources
|
||||
// Copyright (C) 2017 Florent Rougon
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA.
|
||||
|
||||
#ifndef FG_EMBEDDEDRESOURCEPROXY_HXX
|
||||
#define FG_EMBEDDEDRESOURCEPROXY_HXX
|
||||
|
||||
#include <istream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
// The EmbeddedResourceProxy class allows one to access real files or embedded
|
||||
// resources in a unified way. When using it, one can switch from one data
|
||||
// source to the other with minimal code changes, possibly even at runtime (in
|
||||
// which case there is obviously no code change at all).
|
||||
//
|
||||
// Sample usage of the EmbeddedResourceProxy class (from FlightGear):
|
||||
//
|
||||
// simgear::EmbeddedResourceProxy proxy(globals->get_fg_root(), "/FGData");
|
||||
// std::string s = proxy.getString("/some/path");
|
||||
// std::unique_ptr<std::istream> streamp = proxy.getIStream("/some/path");
|
||||
//
|
||||
// The methods getString(const std::string& path) and
|
||||
// getIStream(const std::string& path) of EmbeddedResourceProxy decide whether
|
||||
// to use embedded resources or real files depending on the boolean value
|
||||
// passed to EmbeddedResourceProxy::setUseEmbeddedResources() (also available
|
||||
// as an optional parameter to the EmbeddedResourceProxy constructor,
|
||||
// defaulting to true). It is often most convenient to set this boolean once
|
||||
// and then don't worry about it anymore (it is stored as a data member of
|
||||
// EmbeddedResourceProxy). Otherwise, if you want to fetch resources some
|
||||
// times from real files, other times from embedded resources, you may use the
|
||||
// following methods:
|
||||
//
|
||||
// // Retrieve contents using embedded resources
|
||||
// std:string s = proxy.getString("/some/path", true);
|
||||
// std:string s = proxy.getStringDecideOnPrefix(":/some/path");
|
||||
//
|
||||
// // Retrieve contents using real files
|
||||
// std:string s = proxy.getString("/some/path", false);
|
||||
// std:string s = proxy.getStringDecideOnPrefix("/some/path");
|
||||
//
|
||||
// You can do exactly the same with EmbeddedResourceProxy::getIStream() and
|
||||
// EmbeddedResourceProxy::getIStreamDecideOnPrefix(), except they return an
|
||||
// std::unique_ptr<std::istream> instead of an std::string.
|
||||
//
|
||||
// Given how the 'proxy' object was constructed above, each of these calls
|
||||
// will fetch data from either the real file $FG_ROOT/some/path or the
|
||||
// embedded resource whose virtual path is '/FGData/some/path' (more
|
||||
// precisely: the default-locale version of this resource).
|
||||
//
|
||||
// The 'path' argument of EmbeddedResourceProxy's methods getString(),
|
||||
// getIStream(), getStringDecideOnPrefix() and getIStreamDecideOnPrefix()
|
||||
// must:
|
||||
//
|
||||
// - use UTF-8 encoding;
|
||||
//
|
||||
// - start with:
|
||||
// * either '/' or ':/' for the 'DecideOnPrefix' variants;
|
||||
// * only '/' for the other methods.
|
||||
//
|
||||
// - have its components separated by slashes;
|
||||
//
|
||||
// - not contain any '.' or '..' component.
|
||||
//
|
||||
// For the 'DecideOnPrefix' variants:
|
||||
//
|
||||
// - if the path starts with a slash ('/'), a real file access is done;
|
||||
//
|
||||
// - if, on the other hand, it starts with ':/', EmbeddedResourceProxy uses
|
||||
// the embedded resource whose virtual path is the specified path without
|
||||
// its leading ':' (more precisely: the default-locale version of this
|
||||
// resource).
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
class EmbeddedResourceProxy
|
||||
{
|
||||
public:
|
||||
// 'virtualRoot' must start with a '/', e.g: '/FGData'. Whether it ends
|
||||
// with a '/' doesn't make a difference.
|
||||
explicit EmbeddedResourceProxy(const SGPath& realRoot,
|
||||
const std::string& virtualRoot,
|
||||
bool useEmbeddedResourcesByDefault = true);
|
||||
|
||||
// Getters and setters for the corresponding data members
|
||||
SGPath getRealRoot() const;
|
||||
void setRealRoot(const SGPath& realRoot);
|
||||
|
||||
std::string getVirtualRoot() const;
|
||||
void setVirtualRoot(const std::string& virtualRoot);
|
||||
|
||||
bool getUseEmbeddedResources() const;
|
||||
void setUseEmbeddedResources(bool useEmbeddedResources);
|
||||
|
||||
// Get an std::istream to read from a file or from an embedded resource.
|
||||
std::unique_ptr<std::istream>
|
||||
getIStream(const std::string& path, bool fromEmbeddedResource) const;
|
||||
|
||||
std::unique_ptr<std::istream>
|
||||
getIStream(const std::string& path) const;
|
||||
|
||||
std::unique_ptr<std::istream>
|
||||
getIStreamDecideOnPrefix(const std::string& path) const;
|
||||
|
||||
// Get a file or embedded resource contents as a string.
|
||||
std::string
|
||||
getString(const std::string& path, bool fromEmbeddedResource) const;
|
||||
|
||||
std::string
|
||||
getString(const std::string& path) const;
|
||||
|
||||
std::string
|
||||
getStringDecideOnPrefix(const std::string& path) const;
|
||||
|
||||
private:
|
||||
// Check that 'path' starts with either ':/' or '/', and doesn't contain any
|
||||
// '..' component ('path' may only start with ':/' if 'allowStartWithColon'
|
||||
// is true).
|
||||
static void
|
||||
checkPath(const std::string& callerMethod, const std::string& path,
|
||||
bool allowStartWithColon);
|
||||
|
||||
// Normalize the 'virtualRoot' argument of the constructor. The argument
|
||||
// must start with a '/' and mustn't contain any '.' or '..' component. The
|
||||
// return value never ends with a '/'.
|
||||
static std::string
|
||||
normalizeVirtualRoot(const std::string& path);
|
||||
|
||||
SGPath _realRoot;
|
||||
std::string _virtualRoot;
|
||||
bool _useEmbeddedResourcesByDefault;
|
||||
};
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
#endif // of FG_EMBEDDEDRESOURCEPROXY_HXX
|
||||
516
simgear/embedded_resources/embedded_resources_test.cxx
Normal file
516
simgear/embedded_resources/embedded_resources_test.cxx
Normal file
@@ -0,0 +1,516 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// embedded_resources_test.cxx --- Automated tests for the embedded resources
|
||||
// system in SimGear
|
||||
//
|
||||
// Copyright (C) 2017 Florent Rougon
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA.
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <ios> // std::streamsize
|
||||
#include <iostream> // std::cout (used for progress info)
|
||||
#include <limits> // std::numeric_limits
|
||||
#include <type_traits> // std::make_unsigned()
|
||||
#include <sstream>
|
||||
#include <cstdlib> // EXIT_SUCCESS
|
||||
#include <cstddef> // std::size_t
|
||||
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/io/iostreams/CharArrayStream.hxx>
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
#include <simgear/io/iostreams/zlibstream.hxx>
|
||||
#include "EmbeddedResource.hxx"
|
||||
#include "EmbeddedResourceManager.hxx"
|
||||
#include "EmbeddedResourceProxy.hxx"
|
||||
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
using std::string;
|
||||
using std::unique_ptr;
|
||||
using std::shared_ptr;
|
||||
using simgear::AbstractEmbeddedResource;
|
||||
using simgear::RawEmbeddedResource;
|
||||
using simgear::ZlibEmbeddedResource;
|
||||
using simgear::EmbeddedResourceManager;
|
||||
|
||||
typedef typename std::make_unsigned<std::streamsize>::type uStreamSize;
|
||||
|
||||
// Safely convert a non-negative std::streamsize into an std::size_t. If
|
||||
// impossible, bail out.
|
||||
std::size_t streamsizeToSize_t(std::streamsize n)
|
||||
{
|
||||
SG_CHECK_GE(n, 0);
|
||||
SG_CHECK_LE(static_cast<uStreamSize>(n),
|
||||
std::numeric_limits<std::size_t>::max());
|
||||
|
||||
return static_cast<std::size_t>(n);
|
||||
}
|
||||
|
||||
// This array is null-terminated, but we'll declare the resource size as
|
||||
// sizeof(res1Array) - 1 so that the null char is *not* part of it. This
|
||||
// way allows one to treat text and binary resources exactly the same way,
|
||||
// with the conversion to std::string via a simple
|
||||
// std::string(res1Array, resourceSize) not producing a bizarre std::string
|
||||
// instance whose last character would be '\0' (followed in memory by the same
|
||||
// '\0' used as C-style string terminator this time!).
|
||||
static const char res1Array[] = "This is a simple embedded resource test.";
|
||||
static const char res1frArray[] = "Ceci est un petit test de ressource "
|
||||
"embarquée.";
|
||||
static const char res1fr_FRArray[] = "Ceci est un petit test de ressource "
|
||||
"embarquée (variante fr_FR).";
|
||||
static const string lipsum = "\
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque congue ornare\n\
|
||||
congue. Mauris mollis est et porttitor condimentum. Vivamus laoreet blandit\n\
|
||||
odio eget consectetur. Etiam quis magna eu enim luctus pretium. In et\n\
|
||||
tristique nunc, non efficitur metus. Nullam efficitur tristique velit.\n\
|
||||
Praesent et luctus nunc. Mauris eros eros, rutrum at molestie quis, egestas et\n\
|
||||
lorem. Ut nulla turpis, eleifend sed mauris ac, faucibus molestie nulla.\n\
|
||||
Quisque viverra vel turpis nec efficitur. Proin non rutrum velit. Nam sodales\n\
|
||||
metus felis, eu pharetra velit posuere ut.";
|
||||
// Should be enough to store the compressed lipsum (320 bytes are required
|
||||
// with zlib 1.2.8, keeping some room to account for possible future format
|
||||
// changes in the zlib output...). In any case, there is no risk of buffer
|
||||
// overflow, because simgear::CharArrayOStream prevents this by design.
|
||||
static char res2Array[350];
|
||||
static const char res2frArray[] = "Un lorem ipsum un peu plus court...";
|
||||
|
||||
|
||||
// Read data from a string and write it in compressed form to the specified
|
||||
// buffer.
|
||||
std::size_t writeCompressedDataToBuffer(const string& inputString,
|
||||
char *outBuf,
|
||||
std::size_t outBufSize)
|
||||
{
|
||||
simgear::CharArrayOStream res2Writer(outBuf, outBufSize);
|
||||
std::istringstream iss(inputString);
|
||||
simgear::ZlibCompressorIStream compressor(
|
||||
iss,
|
||||
SGPath(), /* no associated file */
|
||||
9 /* highest compression level */);
|
||||
static constexpr std::size_t bufSize = 1024;
|
||||
unique_ptr<char[]> buf(new char[bufSize]);
|
||||
std::size_t res2Size = 0;
|
||||
|
||||
do {
|
||||
compressor.read(buf.get(), bufSize);
|
||||
std::streamsize nBytes = compressor.gcount();
|
||||
if (nBytes > 0) { // at least one char could be read
|
||||
res2Writer.write(buf.get(), nBytes);
|
||||
res2Size += nBytes;
|
||||
}
|
||||
} while (compressor && res2Writer);
|
||||
|
||||
SG_VERIFY(compressor.eof()); // all the compressed data has been read
|
||||
// This would fail (among other causes) if the output buffer were too small
|
||||
// to hold all of the compressed data.
|
||||
SG_VERIFY(res2Writer);
|
||||
|
||||
return res2Size;
|
||||
}
|
||||
|
||||
void initResources()
|
||||
{
|
||||
cout << "Creating the EmbeddedResourceManager instance and adding a few "
|
||||
"resources to it" << endl;
|
||||
const auto& resMgr = EmbeddedResourceManager::createInstance();
|
||||
|
||||
// The resource will *not* consider the null terminator to be in.
|
||||
unique_ptr<const RawEmbeddedResource> res1(
|
||||
new RawEmbeddedResource(res1Array, sizeof(res1Array) - 1));
|
||||
resMgr->addResource("/path/to/resource1", std::move(res1));
|
||||
|
||||
unique_ptr<const RawEmbeddedResource> res1fr(
|
||||
new RawEmbeddedResource(res1frArray, sizeof(res1frArray) - 1));
|
||||
resMgr->addResource("/path/to/resource1", std::move(res1fr), "fr");
|
||||
|
||||
unique_ptr<const RawEmbeddedResource> res1fr_FR(
|
||||
new RawEmbeddedResource(res1fr_FRArray, sizeof(res1fr_FRArray) - 1));
|
||||
resMgr->addResource("/path/to/resource1", std::move(res1fr_FR), "fr_FR");
|
||||
|
||||
// Write the contents of 'lipsum' in compressed form to the 'res2Array'
|
||||
// static buffer.
|
||||
std::size_t res2Size = writeCompressedDataToBuffer(lipsum, res2Array,
|
||||
sizeof(res2Array));
|
||||
// Now we have a compressed resource to work with, plus the corresponding
|
||||
// uncompressed output -> perfect for tests!
|
||||
unique_ptr<const ZlibEmbeddedResource> res2(
|
||||
new ZlibEmbeddedResource(res2Array, res2Size, lipsum.size()));
|
||||
resMgr->addResource("/path/to/resource2", std::move(res2));
|
||||
|
||||
unique_ptr<const RawEmbeddedResource> res2fr(
|
||||
new RawEmbeddedResource(res2frArray, sizeof(res2frArray) - 1));
|
||||
resMgr->addResource("/path/to/resource2", std::move(res2fr), "fr");
|
||||
|
||||
// Explicitly select the default locale (typically, English). This is for
|
||||
// clarity, but isn't required.
|
||||
resMgr->selectLocale("");
|
||||
}
|
||||
|
||||
// Auxiliary function for test_RawEmbeddedResource()
|
||||
void auxTest_RawEmbeddedResource_streambuf()
|
||||
{
|
||||
cout << "Testing EmbeddedResourceManager::getStreambuf()" << endl;
|
||||
const auto& resMgr = EmbeddedResourceManager::instance();
|
||||
|
||||
unique_ptr<std::streambuf> sbuf(resMgr->getStreambuf("/path/to/resource1"));
|
||||
// Just to show an efficient algorithm. For real applications, use larger
|
||||
// buffer sizes!
|
||||
static constexpr std::size_t bufSize = 4;
|
||||
unique_ptr<char[]> buf(new char[bufSize]); // intermediate buffer
|
||||
std::streamsize nbCharsRead;
|
||||
string result;
|
||||
|
||||
do {
|
||||
nbCharsRead = sbuf->sgetn(buf.get(), bufSize);
|
||||
// The conversion to std::size_t is safe because sbuf->sgetn() returned a
|
||||
// non-negative value which, in this case, can't exceed bufSize.
|
||||
result.append(buf.get(), streamsizeToSize_t((nbCharsRead)));
|
||||
} while (nbCharsRead == bufSize);
|
||||
|
||||
SG_CHECK_EQUAL(result, "This is a simple embedded resource test.");
|
||||
}
|
||||
|
||||
// Auxiliary function for test_RawEmbeddedResource()
|
||||
void auxTest_RawEmbeddedResource_istream()
|
||||
{
|
||||
cout << "Testing EmbeddedResourceManager::getIStream()" << endl;
|
||||
const auto& resMgr = EmbeddedResourceManager::instance();
|
||||
|
||||
unique_ptr<std::istream> iStream(resMgr->getIStream("/path/to/resource1"));
|
||||
// This is convenient, but be aware that still in 2017, some buggy C++
|
||||
// compilers don't allow the exception to be caught: cf.
|
||||
// <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66145>.
|
||||
iStream->exceptions(std::ios_base::badbit);
|
||||
// Just to show an efficient algorithm. For real applications, use larger
|
||||
// buffer sizes!
|
||||
static constexpr std::size_t bufSize = 4;
|
||||
unique_ptr<char[]> buf(new char[bufSize]); // intermediate buffer
|
||||
string result;
|
||||
|
||||
do {
|
||||
iStream->read(buf.get(), bufSize);
|
||||
result.append(buf.get(), iStream->gcount());
|
||||
} while (*iStream); // iStream *points* to an std::istream
|
||||
|
||||
// 1) If set, badbit would have caused an exception to be raised (see above).
|
||||
// 2) failbit doesn't necessarily indicate an error here: it is set as soon
|
||||
// as the read() call can't provide the requested number of characters.
|
||||
SG_VERIFY(iStream->eof() && !iStream->bad());
|
||||
SG_CHECK_EQUAL(result, "This is a simple embedded resource test.");
|
||||
}
|
||||
|
||||
void test_RawEmbeddedResource()
|
||||
{
|
||||
cout << "Testing resource fetching methods of EmbeddedResourceManager with "
|
||||
"a RawEmbeddedResource" << endl;
|
||||
const auto& resMgr = EmbeddedResourceManager::instance();
|
||||
|
||||
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1"),
|
||||
string("This is a simple embedded resource test."));
|
||||
|
||||
// Get a shared_ptr to a const AbstractEmbeddedResource
|
||||
const auto res1abs = resMgr->getResource("/path/to/resource1");
|
||||
// Okay because we know this resource is not a compressed one
|
||||
const auto res1 =
|
||||
std::dynamic_pointer_cast<const RawEmbeddedResource>(res1abs);
|
||||
SG_VERIFY(res1);
|
||||
|
||||
// Print a representation of the resource metadata
|
||||
std::cout << "\n/path/to/resource1 -> " << *res1 << "\n\n";
|
||||
|
||||
// The following methods would work the same with res1abs
|
||||
SG_CHECK_EQUAL_NOSTREAM(res1->compressionType(),
|
||||
AbstractEmbeddedResource::CompressionType::NONE);
|
||||
SG_CHECK_EQUAL(res1->compressionDescr(), "none");
|
||||
|
||||
SG_CHECK_EQUAL(res1->rawPtr(), res1Array);
|
||||
SG_CHECK_EQUAL(res1->rawSize(), sizeof(res1Array) - 1); // see above
|
||||
SG_CHECK_EQUAL(res1->str(),
|
||||
string("This is a simple embedded resource test."));
|
||||
|
||||
auxTest_RawEmbeddedResource_streambuf();
|
||||
auxTest_RawEmbeddedResource_istream();
|
||||
|
||||
// Just reload and recheck the resource, because we can :)
|
||||
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1"),
|
||||
string("This is a simple embedded resource test."));
|
||||
}
|
||||
|
||||
void test_ZlibEmbeddedResource()
|
||||
{
|
||||
cout << "Testing resource fetching methods of EmbeddedResourceManager with "
|
||||
"a ZlibEmbeddedResource" << endl;
|
||||
const auto& resMgr = EmbeddedResourceManager::instance();
|
||||
|
||||
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource2"),
|
||||
lipsum);
|
||||
|
||||
// Get a shared_ptr to a const AbstractEmbeddedResource
|
||||
const auto res2abs = resMgr->getResource("/path/to/resource2");
|
||||
// Okay because we know this resource is a Zlib-compressed one
|
||||
const auto res2 =
|
||||
std::dynamic_pointer_cast<const ZlibEmbeddedResource>(res2abs);
|
||||
SG_VERIFY(res2);
|
||||
|
||||
SG_CHECK_EQUAL(res2->uncompressedSize(), lipsum.size());
|
||||
|
||||
// Print a representation of the resource metadata
|
||||
std::cout << "\n/path/to/resource2 -> " << *res2 << "\n\n";
|
||||
cout << "Resource 2 compression ratio: " <<
|
||||
static_cast<float>(res2->uncompressedSize()) /
|
||||
static_cast<float>(res2->rawSize()) << "\n";
|
||||
|
||||
// Just reload and recheck the resource
|
||||
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource2"), lipsum);
|
||||
}
|
||||
|
||||
void test_getMissingResources()
|
||||
{
|
||||
cout << "Testing the behavior of EmbeddedResourceManager when trying to "
|
||||
"fetch inexistent resources" << endl;
|
||||
const auto& resMgr = EmbeddedResourceManager::instance();
|
||||
SG_VERIFY(!resMgr->getResourceOrNullPtr("/inexistant/resource"));
|
||||
|
||||
bool gotException = false;
|
||||
try {
|
||||
resMgr->getResource("/inexistant/resource");
|
||||
} catch (const sg_exception&) {
|
||||
gotException = true;
|
||||
}
|
||||
SG_VERIFY(gotException);
|
||||
|
||||
gotException = false;
|
||||
try {
|
||||
resMgr->getString("/other/inexistant/resource");
|
||||
} catch (const sg_exception&) {
|
||||
gotException = true;
|
||||
}
|
||||
SG_VERIFY(gotException);
|
||||
}
|
||||
|
||||
void test_addAlreadyExistingResource()
|
||||
{
|
||||
cout << "Testing the behavior of EmbeddedResourceManager when trying to "
|
||||
"add an already existing resource" << endl;
|
||||
const auto& resMgr = EmbeddedResourceManager::instance();
|
||||
|
||||
for (const string& locale: {"", "fr", "fr_FR"}) {
|
||||
// For these tests, we don't care about the resource contents -> no need
|
||||
// to substract 1 from the result of sizeof() as we did above.
|
||||
unique_ptr<const RawEmbeddedResource> someRes(
|
||||
new RawEmbeddedResource(res1fr_FRArray, sizeof(res1fr_FRArray)));
|
||||
|
||||
bool gotException = false;
|
||||
try {
|
||||
resMgr->addResource("/path/to/resource1", std::move(someRes), locale);
|
||||
} catch (const sg_error&) {
|
||||
gotException = true;
|
||||
}
|
||||
SG_VERIFY(gotException);
|
||||
}
|
||||
}
|
||||
|
||||
void test_localeDependencyOfResourceFetching()
|
||||
{
|
||||
cout << "Testing the locale-dependency of resource fetching from "
|
||||
"EmbeddedResourceManager" << endl;
|
||||
const auto& resMgr = EmbeddedResourceManager::instance();
|
||||
resMgr->selectLocale(""); // select the default locale
|
||||
|
||||
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1"),
|
||||
"This is a simple embedded resource test.");
|
||||
|
||||
// Switch to the 'fr_FR' locale (French from France)
|
||||
resMgr->selectLocale("fr_FR");
|
||||
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1"),
|
||||
"Ceci est un petit test de ressource embarquée (variante "
|
||||
"fr_FR).");
|
||||
|
||||
// This one is for the 'fr' “locale”, obtained as fallback since there is no
|
||||
// resource mapped to /path/to/resource2 for the 'fr_FR' “locale”.
|
||||
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource2"),
|
||||
"Un lorem ipsum un peu plus court...");
|
||||
|
||||
// Explicitly ask for the resource in the default locale
|
||||
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1", ""),
|
||||
"This is a simple embedded resource test.");
|
||||
|
||||
// Switch to the 'fr' locale (French)
|
||||
resMgr->selectLocale("fr");
|
||||
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1"),
|
||||
"Ceci est un petit test de ressource embarquée.");
|
||||
|
||||
// Explicitly ask for the resource in the 'fr_FR' locale
|
||||
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1", "fr_FR"),
|
||||
"Ceci est un petit test de ressource embarquée "
|
||||
"(variante fr_FR).");
|
||||
|
||||
// Switch to the default locale
|
||||
resMgr->selectLocale("");
|
||||
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1"),
|
||||
"This is a simple embedded resource test.");
|
||||
|
||||
// Explicitly ask for the resource in the 'fr' locale
|
||||
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1", "fr"),
|
||||
"Ceci est un petit test de ressource embarquée.");
|
||||
|
||||
// Explicitly ask for the resource in the 'fr_FR' locale
|
||||
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1", "fr_FR"),
|
||||
"Ceci est un petit test de ressource embarquée "
|
||||
"(variante fr_FR).");
|
||||
|
||||
// Explicitly ask for the resource in the default locale
|
||||
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1", ""),
|
||||
"This is a simple embedded resource test.");
|
||||
}
|
||||
|
||||
void test_getLocaleAndSelectLocale()
|
||||
{
|
||||
cout << "Testing the getLocale() and selectLocale() methods of "
|
||||
"EmbeddedResourceManager" << endl;
|
||||
const auto& resMgr = EmbeddedResourceManager::instance();
|
||||
|
||||
for (const string& locale: {"", "fr", "fr_FR", "de_DE"}) {
|
||||
// The important effects of setLocale() are tested in
|
||||
// test_localeDependencyOfResourceFetching()
|
||||
resMgr->selectLocale(locale);
|
||||
SG_CHECK_EQUAL(resMgr->getLocale(), locale);
|
||||
}
|
||||
}
|
||||
|
||||
// Auxiliary function for test_EmbeddedResourceProxy()
|
||||
void auxTest_EmbeddedResourceProxy_getIStream(unique_ptr<std::istream> iStream,
|
||||
const string& contents)
|
||||
{
|
||||
cout << "Testing EmbeddedResourceProxy::getIStream()" << endl;
|
||||
|
||||
iStream->exceptions(std::ios_base::badbit);
|
||||
static constexpr std::size_t bufSize = 65536;
|
||||
unique_ptr<char[]> buf(new char[bufSize]); // intermediate buffer
|
||||
string result;
|
||||
|
||||
do {
|
||||
iStream->read(buf.get(), bufSize);
|
||||
result.append(buf.get(), iStream->gcount());
|
||||
} while (*iStream); // iStream *points* to an std::istream
|
||||
|
||||
// 1) If set, badbit would have caused an exception to be raised (see above).
|
||||
// 2) failbit doesn't necessarily indicate an error here: it is set as soon
|
||||
// as the read() call can't provide the requested number of characters.
|
||||
SG_VERIFY(iStream->eof() && !iStream->bad());
|
||||
SG_CHECK_EQUAL(result, contents);
|
||||
}
|
||||
|
||||
void test_EmbeddedResourceProxy()
|
||||
{
|
||||
cout << "Testing the EmbeddedResourceProxy class" << endl;
|
||||
|
||||
// Initialize stuff we need and create two files containing the contents of
|
||||
// the default-locale version of two embedded resources: those with virtual
|
||||
// paths '/path/to/resource1' and '/path/to/resource2'.
|
||||
const auto& resMgr = EmbeddedResourceManager::instance();
|
||||
simgear::Dir tmpDir = simgear::Dir::tempDir("FlightGear");
|
||||
tmpDir.setRemoveOnDestroy();
|
||||
|
||||
const SGPath path1 = tmpDir.path() / "resource1";
|
||||
const SGPath path2 = tmpDir.path() / "resource2";
|
||||
|
||||
sg_ofstream out1(path1);
|
||||
sg_ofstream out2(path2);
|
||||
const string s1 = resMgr->getString("/path/to/resource1", "");
|
||||
// To make sure in these tests that we can tell whether something came from
|
||||
// a real file or from an embedded resource.
|
||||
const string rs1 = s1 + " from real file";
|
||||
const string rlipsum = lipsum + " from real file";
|
||||
|
||||
out1 << rs1;
|
||||
out1.close();
|
||||
if (!out1) {
|
||||
throw sg_io_exception("Error writing to file", sg_location(path1));
|
||||
}
|
||||
|
||||
out2 << rlipsum;
|
||||
out2.close();
|
||||
if (!out2) {
|
||||
throw sg_io_exception("Error writing to file", sg_location(path2));
|
||||
}
|
||||
|
||||
// 'proxy' defaults to using embedded resources
|
||||
const simgear::EmbeddedResourceProxy proxy(tmpDir.path(), "/path/to",
|
||||
/* useEmbeddedResourcesByDefault */
|
||||
true);
|
||||
simgear::EmbeddedResourceProxy rproxy(tmpDir.path(), "/path/to");
|
||||
// 'rproxy' defaults to using real files
|
||||
rproxy.setUseEmbeddedResources(false); // could be done from the ctor too
|
||||
|
||||
// Test EmbeddedResourceProxy::getString()
|
||||
SG_CHECK_EQUAL(proxy.getStringDecideOnPrefix("/resource1"), rs1);
|
||||
SG_CHECK_EQUAL(proxy.getStringDecideOnPrefix(":/resource1"), s1);
|
||||
SG_CHECK_EQUAL(proxy.getString("/resource1", false), rs1);
|
||||
SG_CHECK_EQUAL(proxy.getString("/resource1", true), s1);
|
||||
SG_CHECK_EQUAL(proxy.getString("/resource1"), s1);
|
||||
SG_CHECK_EQUAL(rproxy.getString("/resource1"), rs1);
|
||||
|
||||
SG_CHECK_EQUAL(proxy.getStringDecideOnPrefix("/resource2"), rlipsum);
|
||||
SG_CHECK_EQUAL(proxy.getStringDecideOnPrefix(":/resource2"), lipsum);
|
||||
SG_CHECK_EQUAL(proxy.getString("/resource2", false), rlipsum);
|
||||
SG_CHECK_EQUAL(proxy.getString("/resource2", true), lipsum);
|
||||
SG_CHECK_EQUAL(proxy.getString("/resource2"), lipsum);
|
||||
SG_CHECK_EQUAL(rproxy.getString("/resource2"), rlipsum);
|
||||
|
||||
// Test EmbeddedResourceProxy::getIStream()
|
||||
auxTest_EmbeddedResourceProxy_getIStream(
|
||||
proxy.getIStreamDecideOnPrefix("/resource1"),
|
||||
rs1);
|
||||
auxTest_EmbeddedResourceProxy_getIStream(
|
||||
proxy.getIStreamDecideOnPrefix(":/resource1"),
|
||||
s1);
|
||||
auxTest_EmbeddedResourceProxy_getIStream(proxy.getIStream("/resource1"), s1);
|
||||
auxTest_EmbeddedResourceProxy_getIStream(rproxy.getIStream("/resource1"), rs1);
|
||||
|
||||
auxTest_EmbeddedResourceProxy_getIStream(proxy.getIStream("/resource2", false),
|
||||
rlipsum);
|
||||
auxTest_EmbeddedResourceProxy_getIStream(proxy.getIStream("/resource2", true),
|
||||
lipsum);
|
||||
auxTest_EmbeddedResourceProxy_getIStream(proxy.getIStream("/resource2"),
|
||||
lipsum);
|
||||
auxTest_EmbeddedResourceProxy_getIStream(rproxy.getIStream("/resource2"),
|
||||
rlipsum);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// Initialize the EmbeddedResourceManager instance, add a few resources
|
||||
// to it and call its selectLocale() method.
|
||||
initResources();
|
||||
|
||||
test_RawEmbeddedResource();
|
||||
test_ZlibEmbeddedResource();
|
||||
test_getMissingResources();
|
||||
test_addAlreadyExistingResource();
|
||||
test_localeDependencyOfResourceFetching();
|
||||
test_getLocaleAndSelectLocale();
|
||||
test_EmbeddedResourceProxy();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -10,14 +10,20 @@ if(ENABLE_TESTS)
|
||||
add_executable(test_metar test_metar.cxx)
|
||||
|
||||
if (SIMGEAR_SHARED)
|
||||
target_link_libraries(test_metar SimGearScene)
|
||||
target_link_libraries(test_metar SimGearScene ${GDAL_LIBRARY})
|
||||
else()
|
||||
target_link_libraries(test_metar
|
||||
SimGearScene SimGearCore
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${ZLIB_LIBRARY}
|
||||
${RT_LIBRARY})
|
||||
${RT_LIBRARY}
|
||||
${GDAL_LIBRARY})
|
||||
endif()
|
||||
|
||||
add_test(metar ${EXECUTABLE_OUTPUT_PATH}/test_metar)
|
||||
|
||||
add_executable(test_precipitation test_precipitation.cxx)
|
||||
target_link_libraries(test_precipitation ${TEST_LIBS})
|
||||
add_test(precipitation ${EXECUTABLE_OUTPUT_PATH}/test_precipitation)
|
||||
|
||||
endif(ENABLE_TESTS)
|
||||
|
||||
@@ -648,10 +648,11 @@ bool SGMetar::scanWeather()
|
||||
weather = pre + weather + post;
|
||||
weather.erase(weather.length() - 1);
|
||||
_weather.push_back(weather);
|
||||
if( ! w.phenomena.empty() )
|
||||
if( ! w.phenomena.empty() ) {
|
||||
_weather2.push_back( w );
|
||||
_grpcount++;
|
||||
return true;
|
||||
}
|
||||
_grpcount++;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
* @brief Precipitation effects to draw rain and snow.
|
||||
*
|
||||
* @par Licences
|
||||
* @par License
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
@@ -25,7 +25,6 @@
|
||||
*/
|
||||
|
||||
#include "precipitation.hxx"
|
||||
//#include "visual_enviro.hxx"
|
||||
|
||||
#include <simgear/constants.h>
|
||||
#include <osg/ClipNode>
|
||||
@@ -65,7 +64,7 @@ bool SGPrecipitation::getEnabled() const
|
||||
*/
|
||||
osg::Group* SGPrecipitation::build(void)
|
||||
{
|
||||
osg::Group* group = new osg::Group;
|
||||
osg::ref_ptr<osg::Group> group = new osg::Group;
|
||||
|
||||
_precipitationEffect->snow(0);
|
||||
_precipitationEffect->rain(0);
|
||||
@@ -73,8 +72,9 @@ osg::Group* SGPrecipitation::build(void)
|
||||
if (_clip_distance!=0.0)
|
||||
{
|
||||
osg::ref_ptr<osg::ClipNode> clipNode = new osg::ClipNode;
|
||||
clipNode->addClipPlane( new osg::ClipPlane( 0 ) );
|
||||
clipNode->getClipPlane(0)->setClipPlane( 0.0, 0.0, -1.0, -_clip_distance );
|
||||
osg::ref_ptr<osg::ClipPlane> clipPlane = new osg::ClipPlane(0);
|
||||
clipNode->addClipPlane(clipPlane.get());
|
||||
clipNode->getClipPlane(0)->setClipPlane(0.0, 0.0, -1.0, -_clip_distance);
|
||||
clipNode->setReferenceFrame(osg::ClipNode::ABSOLUTE_RF);
|
||||
clipNode->addChild(_precipitationEffect.get());
|
||||
|
||||
@@ -87,7 +87,7 @@ osg::Group* SGPrecipitation::build(void)
|
||||
|
||||
group->setNodeMask( ~(simgear::CASTSHADOW_BIT | simgear::MODELLIGHT_BIT) );
|
||||
|
||||
return group;
|
||||
return group.release();
|
||||
}
|
||||
|
||||
|
||||
@@ -140,8 +140,8 @@ void SGPrecipitation::setRainDropletSize(float size)
|
||||
/**
|
||||
* @brief Define the illumination multiplier
|
||||
*
|
||||
* This function permits you to define and change the rain droplet size
|
||||
* which is used if external droplet size control is enabled
|
||||
* This function permits you to define and change the brightness
|
||||
* of the precipitation.
|
||||
*/
|
||||
|
||||
void SGPrecipitation::setIllumination(float illumination)
|
||||
@@ -163,10 +163,9 @@ void SGPrecipitation::setSnowFlakeSize(float size)
|
||||
|
||||
|
||||
/**
|
||||
* @brief Define the rain droplet size
|
||||
* @brief Define the clip plane distance
|
||||
*
|
||||
* This function permits you to define and change the rain droplet size
|
||||
* which is used if external droplet size control is enabled
|
||||
* This function permits you to define and change the clip plane distance.
|
||||
*/
|
||||
|
||||
void SGPrecipitation::setClipDistance(float distance)
|
||||
@@ -224,7 +223,7 @@ void SGPrecipitation::setWindProperty(double heading, double speed)
|
||||
* Be careful, if snow and rain intensity are greater than '0', snow effect
|
||||
* will be first.
|
||||
*
|
||||
* The settings come from the osgParticule/PrecipitationEffect.cpp exemple.
|
||||
* The settings come from the osgParticle/PrecipitationEffect.cpp example.
|
||||
*/
|
||||
bool SGPrecipitation::update(void)
|
||||
{
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
class SGPrecipitation : public osg::Referenced
|
||||
{
|
||||
private:
|
||||
protected:
|
||||
bool _freeze;
|
||||
bool _enabled;
|
||||
bool _droplet_external;
|
||||
@@ -66,7 +66,7 @@ public:
|
||||
void setIllumination(float);
|
||||
void setClipDistance(float);
|
||||
|
||||
void setEnabled( bool );
|
||||
void setEnabled(bool);
|
||||
bool getEnabled() const;
|
||||
};
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#endif
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
@@ -23,75 +24,56 @@ using std::cerr;
|
||||
using std::endl;
|
||||
using std::string;
|
||||
|
||||
#define COMPARE(a, b) \
|
||||
if ((a) != (b)) { \
|
||||
cerr << "failed:" << #a << " != " << #b << endl; \
|
||||
cerr << "\tgot:" << a << endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
#define FUZZY_COMPARE(a, b, epsilon) \
|
||||
if (fabs(a - b) > epsilon) { \
|
||||
cerr << "failed:" << #a << " != " << #b << endl; \
|
||||
cerr << "\tgot:" << a << endl; \
|
||||
cerr << "\tepsilon:" << epsilon << endl; \
|
||||
}
|
||||
|
||||
#define VERIFY(a) \
|
||||
if (!(a)) { \
|
||||
cerr << "failed:" << #a << endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
const double TEST_EPSILON = 1e-9;
|
||||
|
||||
void test_basic()
|
||||
{
|
||||
SGMetar m1("2011/10/20 11:25 EHAM 201125Z 27012KT 240V300 9999 VCSH FEW025CB SCT048 10/05 Q1025 TEMPO VRB03KT");
|
||||
COMPARE(m1.getYear(), 2011);
|
||||
COMPARE(m1.getMonth(), 10);
|
||||
COMPARE(m1.getDay(), 20);
|
||||
COMPARE(m1.getHour(), 11);
|
||||
COMPARE(m1.getMinute(), 25);
|
||||
COMPARE(m1.getReportType(), -1); // should default to NIL?
|
||||
|
||||
COMPARE(m1.getWindDir(), 270);
|
||||
FUZZY_COMPARE(m1.getWindSpeed_kt(), 12, TEST_EPSILON);
|
||||
|
||||
COMPARE(m1.getWeather().size(), 1);
|
||||
COMPARE(m1.getClouds().size(), 2);
|
||||
SG_CHECK_EQUAL(m1.getYear(), 2011);
|
||||
SG_CHECK_EQUAL(m1.getMonth(), 10);
|
||||
SG_CHECK_EQUAL(m1.getDay(), 20);
|
||||
SG_CHECK_EQUAL(m1.getHour(), 11);
|
||||
SG_CHECK_EQUAL(m1.getMinute(), 25);
|
||||
SG_CHECK_EQUAL(m1.getReportType(), -1); // should default to NIL?
|
||||
|
||||
FUZZY_COMPARE(m1.getTemperature_C(), 10, TEST_EPSILON);
|
||||
FUZZY_COMPARE(m1.getDewpoint_C(), 5, TEST_EPSILON);
|
||||
FUZZY_COMPARE(m1.getPressure_hPa(), 1025, TEST_EPSILON);
|
||||
SG_CHECK_EQUAL(m1.getWindDir(), 270);
|
||||
SG_CHECK_EQUAL_EP2(m1.getWindSpeed_kt(), 12, TEST_EPSILON);
|
||||
|
||||
SG_CHECK_EQUAL(m1.getWeather().size(), 1);
|
||||
SG_CHECK_EQUAL(m1.getClouds().size(), 2);
|
||||
|
||||
SG_CHECK_EQUAL_EP2(m1.getTemperature_C(), 10, TEST_EPSILON);
|
||||
SG_CHECK_EQUAL_EP2(m1.getDewpoint_C(), 5, TEST_EPSILON);
|
||||
SG_CHECK_EQUAL_EP2(m1.getPressure_hPa(), 1025, TEST_EPSILON);
|
||||
}
|
||||
|
||||
void test_sensor_failure_weather()
|
||||
{
|
||||
SGMetar m1("2011/10/20 11:25 EHAM 201125Z 27012KT 240V300 9999 // FEW025CB SCT048 10/05 Q1025");
|
||||
COMPARE(m1.getWindDir(), 270);
|
||||
FUZZY_COMPARE(m1.getWindSpeed_kt(), 12, TEST_EPSILON);
|
||||
SG_CHECK_EQUAL(m1.getWindDir(), 270);
|
||||
SG_CHECK_EQUAL_EP2(m1.getWindSpeed_kt(), 12, TEST_EPSILON);
|
||||
|
||||
COMPARE(m1.getWeather().size(), 0);
|
||||
COMPARE(m1.getClouds().size(), 2);
|
||||
SG_CHECK_EQUAL(m1.getWeather().size(), 0);
|
||||
SG_CHECK_EQUAL(m1.getClouds().size(), 2);
|
||||
|
||||
FUZZY_COMPARE(m1.getTemperature_C(), 10, TEST_EPSILON);
|
||||
FUZZY_COMPARE(m1.getDewpoint_C(), 5, TEST_EPSILON);
|
||||
FUZZY_COMPARE(m1.getPressure_hPa(), 1025, TEST_EPSILON);
|
||||
SG_CHECK_EQUAL_EP2(m1.getTemperature_C(), 10, TEST_EPSILON);
|
||||
SG_CHECK_EQUAL_EP2(m1.getDewpoint_C(), 5, TEST_EPSILON);
|
||||
SG_CHECK_EQUAL_EP2(m1.getPressure_hPa(), 1025, TEST_EPSILON);
|
||||
}
|
||||
|
||||
void test_sensor_failure_cloud()
|
||||
{
|
||||
SGMetar m1("2011/10/20 11:25 EHAM 201125Z 27012KT 240V300 9999 FEW025CB/// SCT048/// 10/05 Q1025");
|
||||
COMPARE(m1.getWindDir(), 270);
|
||||
FUZZY_COMPARE(m1.getWindSpeed_kt(), 12, TEST_EPSILON);
|
||||
SG_CHECK_EQUAL(m1.getWindDir(), 270);
|
||||
SG_CHECK_EQUAL_EP2(m1.getWindSpeed_kt(), 12, TEST_EPSILON);
|
||||
|
||||
COMPARE(m1.getWeather().size(), 0);
|
||||
COMPARE(m1.getClouds().size(), 2);
|
||||
SG_CHECK_EQUAL(m1.getWeather().size(), 0);
|
||||
SG_CHECK_EQUAL(m1.getClouds().size(), 2);
|
||||
|
||||
FUZZY_COMPARE(m1.getTemperature_C(), 10, TEST_EPSILON);
|
||||
FUZZY_COMPARE(m1.getDewpoint_C(), 5, TEST_EPSILON);
|
||||
FUZZY_COMPARE(m1.getPressure_hPa(), 1025, TEST_EPSILON);
|
||||
SG_CHECK_EQUAL_EP2(m1.getTemperature_C(), 10, TEST_EPSILON);
|
||||
SG_CHECK_EQUAL_EP2(m1.getDewpoint_C(), 5, TEST_EPSILON);
|
||||
SG_CHECK_EQUAL_EP2(m1.getPressure_hPa(), 1025, TEST_EPSILON);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
|
||||
239
simgear/environment/test_precipitation.cxx
Normal file
239
simgear/environment/test_precipitation.cxx
Normal file
@@ -0,0 +1,239 @@
|
||||
/**************************************************************************
|
||||
* test_precipitation.cxx -- unit-tests for SGPrecipitation class
|
||||
*
|
||||
* Copyright (C) 2017 Scott Giese (xDraconian) - <scttgs0@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Id$
|
||||
**************************************************************************/
|
||||
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
|
||||
#include <osg/Group>
|
||||
|
||||
#include <simgear/constants.h>
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
|
||||
#include "precipitation.hxx"
|
||||
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
|
||||
|
||||
class SGPrecipitationTestFixture : public SGPrecipitation
|
||||
{
|
||||
public:
|
||||
osg::ref_ptr<osg::Group> _group;
|
||||
|
||||
void test_configuration()
|
||||
{
|
||||
_group = build();
|
||||
|
||||
SG_VERIFY(getEnabled());
|
||||
SG_VERIFY(_precipitationEffect);
|
||||
SG_CHECK_EQUAL_EP(_rain_intensity, 0.0f);
|
||||
SG_CHECK_EQUAL_EP(_snow_intensity, 0.0f);
|
||||
SG_VERIFY(!_freeze);
|
||||
}
|
||||
|
||||
void test_rain()
|
||||
{
|
||||
int count = 500;
|
||||
|
||||
do {
|
||||
if (--count == 0) {
|
||||
cerr << "error: method setRainIntensity took too long.";
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
float saveRainIntensity = _rain_intensity;
|
||||
|
||||
setRainIntensity(0.4f);
|
||||
update();
|
||||
|
||||
SG_CHECK_GE(_rain_intensity, saveRainIntensity);
|
||||
} while (std::fabs(_rain_intensity - 0.4f) > 0.00001f);
|
||||
|
||||
SG_CHECK_EQUAL_EP2(_rain_intensity, 0.4f, 0.00001f);
|
||||
}
|
||||
|
||||
void test_rain_external()
|
||||
{
|
||||
int count = 500;
|
||||
|
||||
do {
|
||||
if (--count == 0) {
|
||||
cerr << "error: method setRainIntensity-external took too long.";
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
float saveRainIntensity = _rain_intensity;
|
||||
|
||||
setRainIntensity(0.25f);
|
||||
update();
|
||||
|
||||
SG_CHECK_LE(_rain_intensity, saveRainIntensity);
|
||||
} while (std::fabs(_rain_intensity - 0.25f) > 0.00001f);
|
||||
|
||||
// call once more to ensure we have intensity precisely set
|
||||
setRainIntensity(0.25f);
|
||||
update();
|
||||
|
||||
SG_CHECK_EQUAL_EP2(_rain_intensity, 0.25f, 0.00001f);
|
||||
}
|
||||
|
||||
void test_freeze()
|
||||
{
|
||||
float saveParticleSize = _precipitationEffect->getParticleSize();
|
||||
float saveParticleSpeed = std::fabs(_precipitationEffect->getParticleSpeed());
|
||||
|
||||
// change rain to snow
|
||||
// expect particle size to increase
|
||||
// expect particle speed to decrease
|
||||
setFreezing(true);
|
||||
update();
|
||||
|
||||
SG_CHECK_GT(_precipitationEffect->getParticleSize(), saveParticleSize);
|
||||
SG_CHECK_LT(std::fabs(_precipitationEffect->getParticleSpeed()), saveParticleSpeed);
|
||||
}
|
||||
|
||||
void test_snow()
|
||||
{
|
||||
int count = 500;
|
||||
|
||||
do {
|
||||
if (--count == 0) {
|
||||
cerr << "error: method setSnowIntensity took too long.";
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
float saveSnowIntensity = _snow_intensity;
|
||||
|
||||
// not a typo - when freezing is enabled, snow intensity is keep in sync with rain intensity
|
||||
setRainIntensity(0.3f);
|
||||
update();
|
||||
|
||||
SG_CHECK_LE(_snow_intensity, saveSnowIntensity);
|
||||
} while (std::fabs(_snow_intensity - 0.3f) > 0.00001f);
|
||||
|
||||
SG_CHECK_EQUAL_EP2(_snow_intensity, 0.3f, 0.00001f);
|
||||
}
|
||||
|
||||
void test_snow_external()
|
||||
{
|
||||
setRainDropletSize(0.025f);
|
||||
setSnowFlakeSize(0.04f);
|
||||
setDropletExternal(true);
|
||||
|
||||
int count = 600;
|
||||
|
||||
do {
|
||||
if (--count == 0) {
|
||||
cerr << "error: method setSnowIntensity-external took too long.";
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
float saveSnowIntensity = _snow_intensity;
|
||||
|
||||
setSnowIntensity(0.55f);
|
||||
update();
|
||||
|
||||
SG_CHECK_GE(_snow_intensity, saveSnowIntensity);
|
||||
} while (std::fabs(_snow_intensity - 0.55f) > 0.00001f);
|
||||
|
||||
// call once more to ensure we have intensity precisely set
|
||||
setSnowIntensity(0.55f);
|
||||
update();
|
||||
|
||||
SG_CHECK_EQUAL_EP2(_snow_intensity, 0.55f, 0.00001f);
|
||||
}
|
||||
|
||||
void test_unfreeze()
|
||||
{
|
||||
float saveParticleSize = _precipitationEffect->getParticleSize();
|
||||
float saveParticleSpeed = std::fabs(_precipitationEffect->getParticleSpeed());
|
||||
|
||||
// change snow to rain
|
||||
// expect particle size to decrease
|
||||
// expect particle speed to increase
|
||||
setFreezing(false);
|
||||
update();
|
||||
|
||||
SG_CHECK_LT(_precipitationEffect->getParticleSize(), saveParticleSize);
|
||||
SG_CHECK_GT(std::fabs(_precipitationEffect->getParticleSpeed()), saveParticleSpeed);
|
||||
}
|
||||
|
||||
void test_no_precipitation()
|
||||
{
|
||||
setEnabled(false);
|
||||
SG_VERIFY(!getEnabled());
|
||||
|
||||
update();
|
||||
|
||||
// intensity drops to zero, so we should see this reflected in the particles
|
||||
SG_CHECK_EQUAL_EP(_precipitationEffect->getParticleSize(), 0.01f);
|
||||
SG_CHECK_EQUAL_EP(_precipitationEffect->getParticleSpeed(), -2.0f);
|
||||
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
void test_illumination()
|
||||
{
|
||||
setIllumination(0.87f);
|
||||
SG_CHECK_EQUAL_EP(_illumination, 0.87f);
|
||||
}
|
||||
|
||||
void test_clipping()
|
||||
{
|
||||
setClipDistance(6.5);
|
||||
SG_CHECK_EQUAL_EP(_clip_distance, 6.5f);
|
||||
}
|
||||
|
||||
void test_wind()
|
||||
{
|
||||
setWindProperty(87.0f, 0.7f);
|
||||
auto vec = _wind_vec;
|
||||
SG_CHECK_EQUAL_EP2(vec[0], 0.011166f, 0.00001f);
|
||||
SG_CHECK_EQUAL_EP2(vec[1], -0.213068f, 0.00001f);
|
||||
SG_CHECK_EQUAL_EP2(vec[2], 0.0f, 0.00001f);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
auto fixture = std::unique_ptr<SGPrecipitationTestFixture>(new SGPrecipitationTestFixture);
|
||||
|
||||
fixture->test_configuration();
|
||||
fixture->test_rain();
|
||||
fixture->test_freeze();
|
||||
fixture->test_snow();
|
||||
fixture->test_unfreeze();
|
||||
fixture->test_no_precipitation();
|
||||
|
||||
fixture->test_snow_external();
|
||||
fixture->test_rain_external();
|
||||
fixture->test_illumination();
|
||||
fixture->test_clipping();
|
||||
fixture->test_wind();
|
||||
fixture->test_no_precipitation();
|
||||
|
||||
cout << "all tests passed OK" << endl;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -22,6 +22,7 @@
|
||||
* $Id$
|
||||
**************************************************************************/
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
#include <math.h>
|
||||
@@ -52,7 +53,8 @@
|
||||
void CelestialBody::updatePosition(double mjd, Star *ourSun)
|
||||
{
|
||||
double eccAnom, v, ecl, actTime,
|
||||
xv, yv, xh, yh, zh, xg, yg, zg, xe, ye, ze;
|
||||
xv, yv, xh, yh, zh, xg, yg, zg, xe, ye, ze,
|
||||
cosN, sinN, cosvw, sinvw, sinvw_cosi, cosecl, sinecl;
|
||||
|
||||
updateOrbElements(mjd);
|
||||
actTime = sgCalcActTime(mjd);
|
||||
@@ -66,10 +68,19 @@ void CelestialBody::updatePosition(double mjd, Star *ourSun)
|
||||
v = atan2(yv, xv); // the planet's true anomaly
|
||||
r = sqrt (xv*xv + yv*yv); // the planet's distance
|
||||
|
||||
// repetitive calculations, minimised for speed
|
||||
cosN = cos(N);
|
||||
sinN = sin(N);
|
||||
cosvw = cos(v+w);
|
||||
sinvw = sin(v+w);
|
||||
sinvw_cosi = sinvw * cos(i);
|
||||
cosecl = cos(ecl);
|
||||
sinecl = sin(ecl);
|
||||
|
||||
// calculate the planet's position in 3D space
|
||||
xh = r * (cos(N) * cos(v+w) - sin(N) * sin(v+w) * cos(i));
|
||||
yh = r * (sin(N) * cos(v+w) + cos(N) * sin(v+w) * cos(i));
|
||||
zh = r * (sin(v+w) * sin(i));
|
||||
xh = r * (cosN * cosvw - sinN * sinvw_cosi);
|
||||
yh = r * (sinN * cosvw + cosN * sinvw_cosi);
|
||||
zh = r * (sinvw * sin(i));
|
||||
|
||||
// calculate the ecliptic longitude and latitude
|
||||
xg = xh + ourSun->getxs();
|
||||
@@ -80,8 +91,8 @@ void CelestialBody::updatePosition(double mjd, Star *ourSun)
|
||||
latEcl = atan2(zh, sqrt(xh*xh+yh*yh));
|
||||
|
||||
xe = xg;
|
||||
ye = yg * cos(ecl) - zg * sin(ecl);
|
||||
ze = yg * sin(ecl) + zg * cos(ecl);
|
||||
ye = yg * cosecl - zg * sinecl;
|
||||
ze = yg * sinecl + zg * cosecl;
|
||||
rightAscension = atan2(ye, xe);
|
||||
declination = atan2(ze, sqrt(xe*xe + ye*ye));
|
||||
/* SG_LOG(SG_GENERAL, SG_INFO, "Planet found at : "
|
||||
|
||||
@@ -22,10 +22,11 @@
|
||||
* $Id$
|
||||
**************************************************************************/
|
||||
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
@@ -78,69 +79,90 @@ void MoonPos::updatePosition(double mjd, double lst, double lat, Star *ourSun)
|
||||
{
|
||||
double
|
||||
eccAnom, ecl, actTime,
|
||||
xv, yv, v, r, xh, yh, zh, xg, yg, zg, xe, ye, ze,
|
||||
xv, yv, v, r, xh, yh, zh, zg, xe,
|
||||
Ls, Lm, D, F, mpar, gclat, rho, HA, g,
|
||||
geoRa, geoDec;
|
||||
geoRa, geoDec,
|
||||
cosN, sinN, cosvw, sinvw, sinvw_cosi, cosecl, sinecl, rcoslatEcl,
|
||||
FlesstwoD, MlesstwoD, twoD, twoM, twolat, alpha;
|
||||
|
||||
double max_loglux = -0.504030345621;
|
||||
double min_loglux = -4.39964634562;
|
||||
double conv = 1.0319696543787917; // The log foot-candle to log lux conversion factor.
|
||||
updateOrbElements(mjd);
|
||||
actTime = sgCalcActTime(mjd);
|
||||
|
||||
// calculate the angle between ecliptic and equatorial coordinate system
|
||||
// in Radians
|
||||
ecl = ((SGD_DEGREES_TO_RADIANS * 23.4393) - (SGD_DEGREES_TO_RADIANS * 3.563E-7) * actTime);
|
||||
ecl = SGD_DEGREES_TO_RADIANS * (23.4393 - 3.563E-7 * actTime);
|
||||
eccAnom = sgCalcEccAnom(M, e); // Calculate the eccentric anomaly
|
||||
xv = a * (cos(eccAnom) - e);
|
||||
yv = a * (sqrt(1.0 - e*e) * sin(eccAnom));
|
||||
v = atan2(yv, xv); // the moon's true anomaly
|
||||
r = sqrt (xv*xv + yv*yv); // and its distance
|
||||
|
||||
// repetitive calculations, minimised for speed
|
||||
cosN = cos(N);
|
||||
sinN = sin(N);
|
||||
cosvw = cos(v+w);
|
||||
sinvw = sin(v+w);
|
||||
sinvw_cosi = sinvw * cos(i);
|
||||
cosecl = cos(ecl);
|
||||
sinecl = sin(ecl);
|
||||
|
||||
// estimate the geocentric rectangular coordinates here
|
||||
xh = r * (cos(N) * cos (v+w) - sin (N) * sin(v+w) * cos(i));
|
||||
yh = r * (sin(N) * cos (v+w) + cos (N) * sin(v+w) * cos(i));
|
||||
zh = r * (sin(v+w) * sin(i));
|
||||
xh = r * (cosN * cosvw - sinN * sinvw_cosi);
|
||||
yh = r * (sinN * cosvw + cosN * sinvw_cosi);
|
||||
zh = r * (sinvw * sin(i));
|
||||
|
||||
// calculate the ecliptic latitude and longitude here
|
||||
lonEcl = atan2 (yh, xh);
|
||||
latEcl = atan2(zh, sqrt(xh*xh + yh*yh));
|
||||
|
||||
/* Calculate a number of perturbatioin, i.e. disturbances caused by the
|
||||
* gravitational infuence of the sun and the other major planets.
|
||||
/* Calculate a number of perturbation, i.e. disturbances caused by the
|
||||
* gravitational influence of the sun and the other major planets.
|
||||
* The largest of these even have a name */
|
||||
Ls = ourSun->getM() + ourSun->getw();
|
||||
Lm = M + w + N;
|
||||
D = Lm - Ls;
|
||||
F = Lm - N;
|
||||
|
||||
twoD = 2 * D;
|
||||
twoM = 2 * M;
|
||||
FlesstwoD = F - twoD;
|
||||
MlesstwoD = M - twoD;
|
||||
|
||||
lonEcl += SGD_DEGREES_TO_RADIANS * (-1.274 * sin (M - 2*D)
|
||||
+0.658 * sin (2*D)
|
||||
lonEcl += SGD_DEGREES_TO_RADIANS * (-1.274 * sin(MlesstwoD)
|
||||
+0.658 * sin(twoD)
|
||||
-0.186 * sin(ourSun->getM())
|
||||
-0.059 * sin(2*M - 2*D)
|
||||
-0.057 * sin(M - 2*D + ourSun->getM())
|
||||
+0.053 * sin(M + 2*D)
|
||||
+0.046 * sin(2*D - ourSun->getM())
|
||||
-0.059 * sin(twoM - twoD)
|
||||
-0.057 * sin(MlesstwoD + ourSun->getM())
|
||||
+0.053 * sin(M + twoD)
|
||||
+0.046 * sin(twoD - ourSun->getM())
|
||||
+0.041 * sin(M - ourSun->getM())
|
||||
-0.035 * sin(D)
|
||||
-0.031 * sin(M + ourSun->getM())
|
||||
-0.015 * sin(2*F - 2*D)
|
||||
-0.015 * sin(2*F - twoD)
|
||||
+0.011 * sin(M - 4*D)
|
||||
);
|
||||
latEcl += SGD_DEGREES_TO_RADIANS * (-0.173 * sin(F-2*D)
|
||||
-0.055 * sin(M - F - 2*D)
|
||||
-0.046 * sin(M + F - 2*D)
|
||||
+0.033 * sin(F + 2*D)
|
||||
+0.017 * sin(2*M + F)
|
||||
latEcl += SGD_DEGREES_TO_RADIANS * (-0.173 * sin(FlesstwoD)
|
||||
-0.055 * sin(M - FlesstwoD)
|
||||
-0.046 * sin(M + FlesstwoD)
|
||||
+0.033 * sin(F + twoD)
|
||||
+0.017 * sin(twoM + F)
|
||||
);
|
||||
r += (-0.58 * cos(M - 2*D)
|
||||
-0.46 * cos(2*D)
|
||||
r += (-0.58 * cos(MlesstwoD)
|
||||
-0.46 * cos(twoD)
|
||||
);
|
||||
distance = r;
|
||||
// SG_LOG(SG_GENERAL, SG_INFO, "Running moon update");
|
||||
xg = r * cos(lonEcl) * cos(latEcl);
|
||||
yg = r * sin(lonEcl) * cos(latEcl);
|
||||
rcoslatEcl = r * cos(latEcl);
|
||||
xg = cos(lonEcl) * rcoslatEcl;
|
||||
yg = sin(lonEcl) * rcoslatEcl;
|
||||
zg = r * sin(latEcl);
|
||||
|
||||
xe = xg;
|
||||
ye = yg * cos(ecl) -zg * sin(ecl);
|
||||
ze = yg * sin(ecl) +zg * cos(ecl);
|
||||
ye = yg * cosecl -zg * sinecl;
|
||||
ze = yg * sinecl +zg * cosecl;
|
||||
|
||||
geoRa = atan2(ye, xe);
|
||||
geoDec = atan2(ze, sqrt(xe*xe + ye*ye));
|
||||
@@ -154,17 +176,17 @@ void MoonPos::updatePosition(double mjd, double lst, double lat, Star *ourSun)
|
||||
// topocentric ra and dec. i.e. the position as seen from the
|
||||
// surface of the earth, instead of the center of the earth
|
||||
|
||||
// First calculate the moon's parrallax, that is, the apparent size of the
|
||||
// First calculate the moon's parallax, that is, the apparent size of the
|
||||
// (equatorial) radius of the earth, as seen from the moon
|
||||
mpar = asin ( 1 / r);
|
||||
// SG_LOG( SG_GENERAL, SG_INFO, "r = " << r << " mpar = " << mpar );
|
||||
// SG_LOG( SG_GENERAL, SG_INFO, "lat = " << f->get_Latitude() );
|
||||
|
||||
gclat = lat - 0.003358 *
|
||||
sin (2 * SGD_DEGREES_TO_RADIANS * lat );
|
||||
twolat = 2 * SGD_DEGREES_TO_RADIANS * lat;
|
||||
gclat = lat - 0.003358 * sin(twolat);
|
||||
// SG_LOG( SG_GENERAL, SG_INFO, "gclat = " << gclat );
|
||||
|
||||
rho = 0.99883 + 0.00167 * cos(2 * SGD_DEGREES_TO_RADIANS * lat);
|
||||
rho = 0.99883 + 0.00167 * cos(twolat);
|
||||
// SG_LOG( SG_GENERAL, SG_INFO, "rho = " << rho );
|
||||
|
||||
if (geoRa < 0)
|
||||
@@ -193,4 +215,22 @@ void MoonPos::updatePosition(double mjd, double lst, double lat, Star *ourSun)
|
||||
/* SG_LOG( SG_GENERAL, SG_INFO,
|
||||
"Ra = (" << (SGD_RADIANS_TO_DEGREES *rightAscension)
|
||||
<< "), Dec= (" << (SGD_RADIANS_TO_DEGREES *declination) << ")" ); */
|
||||
|
||||
// Moon age and phase calculation
|
||||
age = lonEcl - ourSun->getlonEcl();
|
||||
phase = (1 - cos(age)) / 2;
|
||||
|
||||
// The log of the illuminance of the moon outside the atmosphere.
|
||||
// This is the base 10 log of equation 20 from Krisciunas K. and Schaefer B.E.
|
||||
// (1991). A model of the brightness of moonlight, Publ. Astron. Soc. Pacif.
|
||||
// 103(667), 1033-1039 (DOI: http://dx.doi.org/10.1086/132921).
|
||||
alpha = SGD_RADIANS_TO_DEGREES * SGMiscd::normalizeAngle(age + SGMiscd::pi());
|
||||
log_I = -0.4 * (3.84 + 0.026*fabs(alpha) + 4e-9*pow(alpha, 4.0));
|
||||
|
||||
// Convert from foot-candles to lux.
|
||||
log_I += conv;
|
||||
|
||||
// The moon's illuminance factor, bracketed between 0 and 1.
|
||||
I_factor = (log_I - max_loglux) / (max_loglux - min_loglux) + 1.0;
|
||||
I_factor = SGMiscd::clip(I_factor, 0, 1);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,13 @@ class MoonPos : public CelestialBody
|
||||
|
||||
private:
|
||||
|
||||
double xg, yg; // the moon's rectangular geocentric coordinates
|
||||
double ye, ze; // the moon's rectangular equatorial coordinates
|
||||
double distance; // the moon's distance to the earth
|
||||
double age; // the moon's age from 0 to 2pi
|
||||
double phase; // the moon's phase
|
||||
double log_I; // the moon's illuminance outside the atmosphere (logged)
|
||||
double I_factor; // the illuminance factor for the moon, between 0 and 1.
|
||||
// void TexInit(); // This should move to the constructor eventually.
|
||||
|
||||
// GLUquadricObj *moonObject;
|
||||
@@ -54,7 +61,72 @@ public:
|
||||
~MoonPos();
|
||||
void updatePosition(double mjd, double lst, double lat, Star *ourSun);
|
||||
// void newImage();
|
||||
double getM() const;
|
||||
double getw() const;
|
||||
double getxg() const;
|
||||
double getyg() const;
|
||||
double getye() const;
|
||||
double getze() const;
|
||||
double getDistance() const;
|
||||
double getAge() const;
|
||||
double getPhase() const;
|
||||
double getLogIlluminance() const;
|
||||
double getIlluminanceFactor() const;
|
||||
};
|
||||
|
||||
inline double MoonPos::getM() const
|
||||
{
|
||||
return M;
|
||||
}
|
||||
|
||||
inline double MoonPos::getw() const
|
||||
{
|
||||
return w;
|
||||
}
|
||||
|
||||
inline double MoonPos::getxg() const
|
||||
{
|
||||
return xg;
|
||||
}
|
||||
|
||||
inline double MoonPos::getyg() const
|
||||
{
|
||||
return yg;
|
||||
}
|
||||
|
||||
inline double MoonPos::getye() const
|
||||
{
|
||||
return ye;
|
||||
}
|
||||
|
||||
inline double MoonPos::getze() const
|
||||
{
|
||||
return ze;
|
||||
}
|
||||
|
||||
inline double MoonPos::getDistance() const
|
||||
{
|
||||
return distance;
|
||||
}
|
||||
|
||||
inline double MoonPos::getAge() const
|
||||
{
|
||||
return age;
|
||||
}
|
||||
|
||||
inline double MoonPos::getPhase() const
|
||||
{
|
||||
return phase;
|
||||
}
|
||||
|
||||
inline double MoonPos::getLogIlluminance() const
|
||||
{
|
||||
return log_I;
|
||||
}
|
||||
|
||||
inline double MoonPos::getIlluminanceFactor() const
|
||||
{
|
||||
return I_factor;
|
||||
}
|
||||
|
||||
#endif // _MOONPOS_HXX_
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
* $Id$
|
||||
**************************************************************************/
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include <cmath>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
@@ -33,6 +33,7 @@ class Star : public CelestialBody
|
||||
|
||||
private:
|
||||
|
||||
double lonEcl; // the sun's true longitude
|
||||
double xs, ys; // the sun's rectangular geocentric coordinates
|
||||
double ye, ze; // the sun's rectangularequatorial rectangular geocentric coordinates
|
||||
double distance; // the sun's distance to the earth
|
||||
@@ -50,6 +51,7 @@ public:
|
||||
double getye() const;
|
||||
double getze() const;
|
||||
double getDistance() const;
|
||||
double getlonEcl() const;
|
||||
};
|
||||
|
||||
|
||||
@@ -88,6 +90,10 @@ inline double Star::getDistance() const
|
||||
return distance;
|
||||
}
|
||||
|
||||
inline double Star::getlonEcl() const
|
||||
{
|
||||
return lonEcl;
|
||||
}
|
||||
|
||||
#endif // _STAR_HXX_
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/misc/sgstream.hxx>
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
|
||||
#include "stardata.hxx"
|
||||
|
||||
@@ -55,12 +55,11 @@ bool SGStarData::load( const SGPath& path ) {
|
||||
// build the full path name to the stars data base file
|
||||
SGPath tmp = path;
|
||||
tmp.append( "stars" );
|
||||
SG_LOG( SG_ASTRO, SG_INFO, " Loading stars from " << tmp.str() );
|
||||
SG_LOG( SG_ASTRO, SG_INFO, " Loading stars from " << tmp );
|
||||
|
||||
sg_gzifstream in( tmp.str() );
|
||||
sg_gzifstream in( tmp );
|
||||
if ( ! in.is_open() ) {
|
||||
SG_LOG( SG_ASTRO, SG_ALERT, "Cannot open star file: "
|
||||
<< tmp.str() );
|
||||
SG_LOG( SG_ASTRO, SG_ALERT, "Cannot open star file: " << tmp );
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
# include <simgear_config.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include "HLAArrayDataType.hxx"
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
# include <simgear_config.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include "HLADataType.hxx"
|
||||
|
||||
@@ -21,11 +21,11 @@
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include "HLAEnumeratedDataType.hxx"
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "HLAEnumeratedDataType.hxx"
|
||||
#include "HLADataTypeVisitor.hxx"
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user