Compare commits
674
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
506b9130a8 | ||
|
|
5d1b70a7f2 | ||
|
|
eff3df57d9 | ||
|
|
2931f3380f | ||
|
|
2d00653f6e | ||
|
|
4647ce2da5 | ||
|
|
51d43f809e | ||
|
|
370a115ab9 | ||
|
|
32b60f9b80 | ||
|
|
beeaef3868 | ||
|
|
ddd9691439 | ||
|
|
9da0031039 | ||
|
|
9537876bba | ||
|
|
958ae9bdf0 | ||
|
|
023b5a09f7 | ||
|
|
46fe57fb8d | ||
|
|
07e09bb88c | ||
|
|
1b7f310ea6 | ||
|
|
365ddb84a7 | ||
|
|
5fef44b11c | ||
|
|
1480aa9bb8 | ||
|
|
75271c44a8 | ||
|
|
543f1b7902 | ||
|
|
93a63a0678 | ||
|
|
2bf79a2fa1 | ||
|
|
ab956f15c3 | ||
|
|
deceee8997 | ||
|
|
b3c7d63809 | ||
|
|
2026c665b2 | ||
|
|
36fd005bb9 | ||
|
|
46bed67cce | ||
|
|
5b9af0c0aa | ||
|
|
85090180d0 | ||
|
|
f33ad357e9 | ||
|
|
c30ce67e16 | ||
|
|
3ca7369fb2 | ||
|
|
088ce31f7c | ||
|
|
4f94c22241 | ||
|
|
e1791b3006 | ||
|
|
e608ed5a01 | ||
|
|
35ebb16215 | ||
|
|
68d0891665 | ||
|
|
b11ff19a0f | ||
|
|
e7e616e07c | ||
|
|
14f2f9e917 | ||
|
|
791273c61d | ||
|
|
519326f751 | ||
|
|
d1f5d92a7b | ||
|
|
f448898531 | ||
|
|
d9b66fc0ef | ||
|
|
3bcd0bafd5 | ||
|
|
942181c8ae | ||
|
|
7df39b9fc8 | ||
|
|
f41b18f064 | ||
|
|
c5d649aa0b | ||
|
|
64ac22f50c | ||
|
|
719ee36f5c | ||
|
|
92851135d4 | ||
|
|
76fc47b24b | ||
|
|
f25abad9d7 | ||
|
|
23413b4781 | ||
|
|
e036dfc5bf | ||
|
|
9260bea850 | ||
|
|
c1f09034e0 | ||
|
|
557e4f75b5 | ||
|
|
e71d6b24d7 | ||
|
|
726a4c6d10 | ||
|
|
6af8d32078 | ||
|
|
f6b16e2ba8 | ||
|
|
e415b08da4 | ||
|
|
1d93e8d61e | ||
|
|
4bd34991e9 | ||
|
|
9709cfe20d | ||
|
|
a5e99ea996 | ||
|
|
d9df10fe10 | ||
|
|
f1677f5546 | ||
|
|
cb796e374a | ||
|
|
7c3491c679 | ||
|
|
6200b160ec | ||
|
|
9868fb03a2 | ||
|
|
a69130ff10 | ||
|
|
0b60643053 | ||
|
|
dc4644bf3a | ||
|
|
38b766f845 | ||
|
|
10d0be013e | ||
|
|
5126ae5891 | ||
|
|
49bcf49db1 | ||
|
|
3bbb272ad5 | ||
|
|
645cae184b | ||
|
|
bd9fa7017f | ||
|
|
fc9f3abfbb | ||
|
|
f2f1b36df9 | ||
|
|
03d2a166b7 | ||
|
|
f0a76aa918 | ||
|
|
c144c3562c | ||
|
|
e06f9462ab | ||
|
|
9aa5c3b2ae | ||
|
|
2c41c25fcd | ||
|
|
9ffc0ae8bc | ||
|
|
bb5e07d958 | ||
|
|
5024b62c0a | ||
|
|
82d5c605e5 | ||
|
|
737b1948bf | ||
|
|
1225e53162 | ||
|
|
b58bf443ff | ||
|
|
9bef80fbef | ||
|
|
36cb7a752b | ||
|
|
d3b211e787 | ||
|
|
e3f0fad272 | ||
|
|
e8f10dd2e8 | ||
|
|
edbfbd769e | ||
|
|
dc60cf0e67 | ||
|
|
083b364afd | ||
|
|
dd2bf418b9 | ||
|
|
97cc250047 | ||
|
|
49fc75926e | ||
|
|
9880ba39a7 | ||
|
|
a8c8148068 | ||
|
|
adebbe006b | ||
|
|
c3bc73ab2f | ||
|
|
c716cfbb07 | ||
|
|
ade4627f1a | ||
|
|
e508ff724c | ||
|
|
c54e3f8101 | ||
|
|
6925c2a2be | ||
|
|
398bf740d7 | ||
|
|
e302ad092e | ||
|
|
7a65d42a4c | ||
|
|
d3a14bfd61 | ||
|
|
03cafe4547 | ||
|
|
e5acc3f048 | ||
|
|
4931ce0364 | ||
|
|
e3d53e854a | ||
|
|
bdf6b25338 | ||
|
|
5dddba26ac | ||
|
|
746fa419ed | ||
|
|
7080f919e9 | ||
|
|
c4e0014d74 | ||
|
|
7d9797e091 | ||
|
|
67a8d8049f | ||
|
|
c3670b211f | ||
|
|
32274027ef | ||
|
|
d09943cbb8 | ||
|
|
150039f9ba | ||
|
|
820a315cbe | ||
|
|
8abe4622b9 | ||
|
|
da6ab1eabc | ||
|
|
e3ddcbe2dd | ||
|
|
b36cdebe8b | ||
|
|
3c0544c9a6 | ||
|
|
be358f8d24 | ||
|
|
e1abab393b | ||
|
|
0b21181f1a | ||
|
|
dfdd1c6d5e | ||
|
|
a8e96997cb | ||
|
|
6db07373ee | ||
|
|
36ae8cdce3 | ||
|
|
d77ba7d2db | ||
|
|
6e58fdac85 | ||
|
|
3f6933940e | ||
|
|
4bd8fe5855 | ||
|
|
9c421d55a9 | ||
|
|
1af6cbc1aa | ||
|
|
a9e7af6e6e | ||
|
|
c967fbc0a6 | ||
|
|
6929ba75c0 | ||
|
|
289777bd99 | ||
|
|
7535dfd2d0 | ||
|
|
23279b4d0a | ||
|
|
d34d3ecfb1 | ||
|
|
55f18574aa | ||
|
|
02ac1a43c1 | ||
|
|
b2c3a90adf | ||
|
|
9975c751a7 | ||
|
|
efbec8b367 | ||
|
|
9642f6d946 | ||
|
|
247aa49849 | ||
|
|
edaae885ee | ||
|
|
b101f64cd8 | ||
|
|
f75d1cbcb1 | ||
|
|
ac2e80dc07 | ||
|
|
85b17ae8d4 | ||
|
|
77955e5c3c | ||
|
|
19481983e5 | ||
|
|
5b2b420c48 | ||
|
|
15d3c12139 | ||
|
|
338a748823 | ||
|
|
8c45796dc8 | ||
|
|
a160e176da | ||
|
|
d332da0605 | ||
|
|
6ea55c6851 | ||
|
|
10ee7a901f | ||
|
|
db8b60ec49 | ||
|
|
09d47e7f55 | ||
|
|
13a3ea3503 | ||
|
|
ff53792e4f | ||
|
|
fd51518d92 | ||
|
|
01a43b49a5 | ||
|
|
3525fff8d0 | ||
|
|
6c75e2fe3c | ||
|
|
b322864ef4 | ||
|
|
f28e3fc9bb | ||
|
|
80e77b8372 | ||
|
|
c925b7b601 | ||
|
|
966789de90 | ||
|
|
d44a3117d9 | ||
|
|
c851c449da | ||
|
|
b2d9385f46 | ||
|
|
f40efe66fa | ||
|
|
5560479665 | ||
|
|
4ea8e4774e | ||
|
|
18ff03acdf | ||
|
|
aa27a09801 | ||
|
|
a8d7645004 | ||
|
|
081aafb914 | ||
|
|
5a5ba08c23 | ||
|
|
91b92c5613 | ||
|
|
bd5bc9cda0 | ||
|
|
db684eb33e | ||
|
|
a07cfdb683 | ||
|
|
b956a2c834 | ||
|
|
ba38688a83 | ||
|
|
3429e00721 | ||
|
|
32a6bd78d8 | ||
|
|
b2cedc5332 | ||
|
|
94bbed80d0 | ||
|
|
b4ecb6f2db | ||
|
|
3b5665fe92 | ||
|
|
b4f4ef9c5b | ||
|
|
3c4c05fb3e | ||
|
|
b0cad59508 | ||
|
|
d4030e72e4 | ||
|
|
481f7e3bfa | ||
|
|
c35243e215 | ||
|
|
00d8849a28 | ||
|
|
6b9ce935cd | ||
|
|
e440aba0d2 | ||
|
|
574d459f4e | ||
|
|
5fa0931a89 | ||
|
|
748b13850a | ||
|
|
9a09a6a447 | ||
|
|
79c56c924f | ||
|
|
eef73953cb | ||
|
|
781682f4ee | ||
|
|
4a10806352 | ||
|
|
13ae2f08ee | ||
|
|
3e8aeccc87 | ||
|
|
de39e0093a | ||
|
|
943f52aedf | ||
|
|
46eb2f439d | ||
|
|
4f5c519636 | ||
|
|
8f6677974e | ||
|
|
0f5cbc35a2 | ||
|
|
c54b1e037a | ||
|
|
0d19be2196 | ||
|
|
c1b579ec5d | ||
|
|
0a8246c676 | ||
|
|
ea02453f63 | ||
|
|
d1942da317 | ||
|
|
ed9764f923 | ||
|
|
3b5ed81216 | ||
|
|
12d0d4140e | ||
|
|
d6c8008978 | ||
|
|
488ca0fcc5 | ||
|
|
dfd15cadab | ||
|
|
1613257bdc | ||
|
|
1061580118 | ||
|
|
7184828a58 | ||
|
|
0428adff03 | ||
|
|
04246f0a63 | ||
|
|
61df58d651 | ||
|
|
1bd9a440e8 | ||
|
|
d82c8eb945 | ||
|
|
becbad7ea4 | ||
|
|
2335796a28 | ||
|
|
c6330b64f6 | ||
|
|
df57a23512 | ||
|
|
aea71cf0af | ||
|
|
fb54386f8d | ||
|
|
4d931b6109 | ||
|
|
407c7a10f5 | ||
|
|
16fa4d1d17 | ||
|
|
1ecccd43d4 | ||
|
|
2edbb1cf8b | ||
|
|
8dccbe8029 | ||
|
|
031c833e4d | ||
|
|
890cc46825 | ||
|
|
6b0f7e8512 | ||
|
|
7a220921f0 | ||
|
|
7ef9acda5d | ||
|
|
2a6d62e6f7 | ||
|
|
42b50bd7f7 | ||
|
|
2c3f44ae11 | ||
|
|
b7662f16e5 | ||
|
|
5bc535a8d2 | ||
|
|
3ec9c7ae6e | ||
|
|
e326d0c756 | ||
|
|
7c5434a99f | ||
|
|
26d9256ee9 | ||
|
|
053e761d8f | ||
|
|
5861b52cff | ||
|
|
66193d7283 | ||
|
|
dfb07f359e | ||
|
|
6fe1433497 | ||
|
|
01ace109ba | ||
|
|
8e01acaa12 | ||
|
|
7bbec8dbcb | ||
|
|
10375086ed | ||
|
|
dd367daac9 | ||
|
|
27063d0c32 | ||
|
|
80ff0282c3 | ||
|
|
e973e406a3 | ||
|
|
c64776029e | ||
|
|
5d7d0a922d | ||
|
|
c2c96c4219 | ||
|
|
3e9ed08c53 | ||
|
|
b7df9026c0 | ||
|
|
6527c6357b | ||
|
|
fad333256c | ||
|
|
8753811fe1 | ||
|
|
237752ff62 | ||
|
|
743244a69c | ||
|
|
9f89737986 | ||
|
|
508a5c5f66 | ||
|
|
d84394efa0 | ||
|
|
dd613d48bc | ||
|
|
2e8e500d4f | ||
|
|
48f866dcc6 | ||
|
|
ec4d3e4f9b | ||
|
|
38ddfab1e0 | ||
|
|
353b7e4438 | ||
|
|
7076c9a0ff | ||
|
|
2f42a8714c | ||
|
|
fdf6fc32ff | ||
|
|
f2188b33c6 | ||
|
|
f93fead8f2 | ||
|
|
050f3791cc | ||
|
|
94326fd964 | ||
|
|
f26f3b606d | ||
|
|
7cbfa76be4 | ||
|
|
d896e71ae9 | ||
|
|
ca79d99ec4 | ||
|
|
95f77ef81d | ||
|
|
c4e7d26b70 | ||
|
|
ea49a1a07b | ||
|
|
784223c67f | ||
|
|
77aa2c9a9d | ||
|
|
48145fb234 | ||
|
|
49730cadee | ||
|
|
0b197501e1 | ||
|
|
8b0c246a7b | ||
|
|
9c4face1ae | ||
|
|
952e2e6631 | ||
|
|
4766910413 | ||
|
|
3c2f97a8d2 | ||
|
|
f75730f165 | ||
|
|
b596e62a61 | ||
|
|
1349d48339 | ||
|
|
d69ea5fda6 | ||
|
|
9b68062ba7 | ||
|
|
b217019723 | ||
|
|
a37a254a68 | ||
|
|
5258699f20 | ||
|
|
483bebb003 | ||
|
|
8ca8052a8d | ||
|
|
d68b1144b8 | ||
|
|
42c39b6be3 | ||
|
|
bb82b9d168 | ||
|
|
b1f865d461 | ||
|
|
8e75c6be50 | ||
|
|
06a5f9188d | ||
|
|
370a991208 | ||
|
|
6deb77dd4d | ||
|
|
9e3172cb04 | ||
|
|
426c6b9a72 | ||
|
|
d148bdedcd | ||
|
|
23140e3bf7 | ||
|
|
2f023803e7 | ||
|
|
d658b5fc38 | ||
|
|
68cd84c330 | ||
|
|
0bd82a43d3 | ||
|
|
7fdf42b699 | ||
|
|
aeb0e9aac3 | ||
|
|
1099a3bdf0 | ||
|
|
aa3458f69c | ||
|
|
0186cbb7b7 | ||
|
|
fa36e94c4b | ||
|
|
ad83e70cf5 | ||
|
|
4a0377c0a1 | ||
|
|
83a3241830 | ||
|
|
f299b351e3 | ||
|
|
3880e4ef47 | ||
|
|
f205e918d9 | ||
|
|
42b0b9306b | ||
|
|
483659c319 | ||
|
|
add14dd27c | ||
|
|
61516a5e97 | ||
|
|
17418039e1 | ||
|
|
25cae61211 | ||
|
|
db98c7440e | ||
|
|
3783ae2234 | ||
|
|
3e8732b230 | ||
|
|
b39bca9458 | ||
|
|
d263334030 | ||
|
|
b2cea62189 | ||
|
|
d6b886c69b | ||
|
|
38fb9ea41e | ||
|
|
a922aaa68e | ||
|
|
4f2e36ca46 | ||
|
|
f367627cac | ||
|
|
7a7fcf10ad | ||
|
|
b9bd2734eb | ||
|
|
9c7bd4f5d5 | ||
|
|
362d47f91f | ||
|
|
a18792c397 | ||
|
|
0e2ddb2f16 | ||
|
|
bcfadd6657 | ||
|
|
021cf1a2b4 | ||
|
|
acf86be516 | ||
|
|
dd93eb88f2 | ||
|
|
e06f9e35b3 | ||
|
|
c0424aba7e | ||
|
|
b468254d73 | ||
|
|
15e3e92ec2 | ||
|
|
ee403fd83a | ||
|
|
05f266ac10 | ||
|
|
f52c0026dd | ||
|
|
a284107249 | ||
|
|
a2e25e7940 | ||
|
|
d45a0161cf | ||
|
|
c7a5d15e7a | ||
|
|
3cb2241b1d | ||
|
|
b236821661 | ||
|
|
fc75b0bd21 | ||
|
|
a9fc84d568 | ||
|
|
b15c6d887d | ||
|
|
c2f89978ff | ||
|
|
a004153223 | ||
|
|
dde2bbcbe7 | ||
|
|
7bd572f3ec | ||
|
|
486011dceb | ||
|
|
01c45633b7 | ||
|
|
5320d0ecaa | ||
|
|
757ba03918 | ||
|
|
948db69cc9 | ||
|
|
92f363926e | ||
|
|
d0ff144753 | ||
|
|
943efbb3ed | ||
|
|
6962de4b1f | ||
|
|
6f7c0c23d1 | ||
|
|
14eccc70da | ||
|
|
d4b48cec5d | ||
|
|
2d23c5351f | ||
|
|
b5dfaf170a | ||
|
|
c372591f36 | ||
|
|
36d1308aa6 | ||
|
|
b0d6ed3844 | ||
|
|
5818366d92 | ||
|
|
e5bca74b9d | ||
|
|
82dd1e4be9 | ||
|
|
d0b77639c2 | ||
|
|
6edb0f66d5 | ||
|
|
c458c13af7 | ||
|
|
bb7875edd4 | ||
|
|
6f54fb90eb | ||
|
|
4d1e7c536f | ||
|
|
59cc200f92 | ||
|
|
c6093430ae | ||
|
|
23172bcdd0 | ||
|
|
aea2dab0d7 | ||
|
|
4d26e144ab | ||
|
|
c391fcd345 | ||
|
|
ece743342a | ||
|
|
02babbe340 | ||
|
|
e27b6d050c | ||
|
|
86e2de8d10 | ||
|
|
b4526f926d | ||
|
|
14f04878d1 | ||
|
|
830fb3b752 | ||
|
|
9a336da359 | ||
|
|
6cec49ce7e | ||
|
|
6c28edea55 | ||
|
|
d7870d672e | ||
|
|
5bd9228b4a | ||
|
|
6392cf5a2f | ||
|
|
6a2e2b704e | ||
|
|
ecec803388 | ||
|
|
848965e7f0 | ||
|
|
478af5f01e | ||
|
|
14decb7387 | ||
|
|
323d0d5ba0 | ||
|
|
9bfa6ac1a1 | ||
|
|
c8af817eeb | ||
|
|
d24d3ce487 | ||
|
|
60a81dfbd8 | ||
|
|
8896a59dff | ||
|
|
7fe16d99be | ||
|
|
c3af88dfc1 | ||
|
|
5c8f0cc966 | ||
|
|
2a6c50c893 | ||
|
|
7a8d796ac1 | ||
|
|
8b3b71bce3 | ||
|
|
83da4e9248 | ||
|
|
2c879095d9 | ||
|
|
13344fbb62 | ||
|
|
2fe9ad595f | ||
|
|
0f798289f0 | ||
|
|
01104cc1d3 | ||
|
|
d61b5827fd | ||
|
|
d18cc81531 | ||
|
|
c96d7ae226 | ||
|
|
9535aef59b | ||
|
|
82f6fca06f | ||
|
|
627b36a53b | ||
|
|
2af78a3874 | ||
|
|
7c7109edf4 | ||
|
|
0fa23b83e6 | ||
|
|
e53c71543a | ||
|
|
fa64f7b6aa | ||
|
|
eaac3a64f8 | ||
|
|
4eabc45dfc | ||
|
|
5c5e2a0f2f | ||
|
|
559a5d146a | ||
|
|
df46c58cb8 | ||
|
|
1f43132363 | ||
|
|
17369a20de | ||
|
|
c83dae4c70 | ||
|
|
c2f44a51c4 | ||
|
|
3da44d1215 | ||
|
|
f6709e357f | ||
|
|
45d5511fd7 | ||
|
|
198c5d23fd | ||
|
|
c41caeaf64 | ||
|
|
b6c542b0e7 | ||
|
|
2d62275a08 | ||
|
|
971ea81861 | ||
|
|
9e9cc7859c | ||
|
|
8898f5fe52 | ||
|
|
40be69ae8e | ||
|
|
17eec81071 | ||
|
|
c9bbbd18ec | ||
|
|
e08eda18d5 | ||
|
|
d2c03a6651 | ||
|
|
0a1e920659 | ||
|
|
cd58df820e | ||
|
|
1a5467aec8 | ||
|
|
0dcb64dca3 | ||
|
|
b703102d9b | ||
|
|
3c2ef75b50 | ||
|
|
4fa530354d | ||
|
|
eeeb30dc3f | ||
|
|
870fc2e53e | ||
|
|
79ae0da927 | ||
|
|
e179bda57a | ||
|
|
8e0c15e32e | ||
|
|
ff844f6760 | ||
|
|
bc96ac85f4 | ||
|
|
33b328e3d5 | ||
|
|
dcda8d1c7a | ||
|
|
5fd2bd225f | ||
|
|
2db8859076 | ||
|
|
6eff167a28 | ||
|
|
9fecb69b84 | ||
|
|
0539aa38e5 | ||
|
|
fe9caad391 | ||
|
|
ceae2928aa | ||
|
|
0e6934abe5 | ||
|
|
a2e7c92f99 | ||
|
|
643a8717b4 | ||
|
|
f21127fd4a | ||
|
|
0bc8005100 | ||
|
|
67efc1f68f | ||
|
|
dfebf60e68 | ||
|
|
26e7d134ce | ||
|
|
c414242f13 | ||
|
|
33e60725b1 | ||
|
|
f21b0985cc | ||
|
|
71e9548c20 | ||
|
|
fc64abea5c | ||
|
|
413e89c955 | ||
|
|
439041c2f4 | ||
|
|
530de4d809 | ||
|
|
5e45bdeeda | ||
|
|
081eba903f | ||
|
|
0def045611 | ||
|
|
e20cc6a90f | ||
|
|
7dd83dd2d1 | ||
|
|
1240807b9f | ||
|
|
ad7d6624de | ||
|
|
06b089ba13 | ||
|
|
e1302bcf17 | ||
|
|
cfdc7db79a | ||
|
|
50de873453 | ||
|
|
a4ae7b2059 | ||
|
|
d90abdcb44 | ||
|
|
4a31045fd9 | ||
|
|
249155987d | ||
|
|
2ae7fc244b | ||
|
|
fafa70c6bc | ||
|
|
49162a7a64 | ||
|
|
328e6b3e28 | ||
|
|
5441db9b18 | ||
|
|
06bfa4f1a1 | ||
|
|
a530712491 | ||
|
|
3c8cfe9b76 | ||
|
|
57b4ce96e4 | ||
|
|
63c7d64143 | ||
|
|
1784327c47 | ||
|
|
a63ec83d5f | ||
|
|
d661516b02 | ||
|
|
ba020245f9 | ||
|
|
79fefc1ff9 | ||
|
|
eac169b6da | ||
|
|
a60d293759 | ||
|
|
b0063f8db6 | ||
|
|
632abdd87b | ||
|
|
d9b01ca5de | ||
|
|
adb7db9229 | ||
|
|
e4f7aec965 | ||
|
|
768a8c1062 | ||
|
|
318c5000ce | ||
|
|
70f334e1a3 | ||
|
|
279b53a705 | ||
|
|
a72a3ce5f3 | ||
|
|
979aea5212 | ||
|
|
34d3c63384 | ||
|
|
cf4af2692b | ||
|
|
d335ca5a8d | ||
|
|
9cac2c1457 | ||
|
|
523b992b4c | ||
|
|
02be490466 | ||
|
|
45ae3978f6 | ||
|
|
f983194d7e | ||
|
|
ab8373b989 | ||
|
|
4bef2c48eb | ||
|
|
21e2a769eb | ||
|
|
fd8369142a | ||
|
|
5dea221ad5 | ||
|
|
235c29913a | ||
|
|
0870407f65 | ||
|
|
e8156c6bd9 | ||
|
|
e7f9486aa1 | ||
|
|
53b9fd2110 | ||
|
|
4d4e474464 | ||
|
|
a58b41e7d2 | ||
|
|
4db05e97c5 | ||
|
|
3a668232c8 | ||
|
|
d2622a5d86 | ||
|
|
d11db8a67a | ||
|
|
98b6102e56 | ||
|
|
0db8613a21 | ||
|
|
94a6cb2cff | ||
|
|
5b92575ed3 | ||
|
|
47b02c0480 | ||
|
|
691be54ca2 | ||
|
|
22ea8ebe25 | ||
|
|
bd71635c49 | ||
|
|
830bc3eac3 | ||
|
|
2d72bf4308 | ||
|
|
f4e694afa7 | ||
|
|
55610bae70 | ||
|
|
08351e0df2 | ||
|
|
7489771ad2 | ||
|
|
ea8023e51f | ||
|
|
8a9693a28e | ||
|
|
724fba4af9 | ||
|
|
beca1cbf96 | ||
|
|
b74d2ae9fa | ||
|
|
ec82a0154b | ||
|
|
0ea8dbea10 | ||
|
|
a131f44247 | ||
|
|
0423aedffc | ||
|
|
afc89cdd95 | ||
|
|
541606e00d |
@@ -1,4 +1,5 @@
|
|||||||
.*
|
.*
|
||||||
|
*~
|
||||||
Makefile
|
Makefile
|
||||||
*.o
|
*.o
|
||||||
lib*.a
|
lib*.a
|
||||||
|
|||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
if (NOT SYSTEM_EXPAT)
|
||||||
|
add_subdirectory(expat)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_subdirectory(utf8)
|
||||||
Vendored
+33
@@ -0,0 +1,33 @@
|
|||||||
|
|
||||||
|
configure_file (
|
||||||
|
"${PROJECT_SOURCE_DIR}/3rdparty/expat/expat_config_cmake.in"
|
||||||
|
"${PROJECT_BINARY_DIR}/3rdparty/expat/expat_config.h"
|
||||||
|
)
|
||||||
|
|
||||||
|
set(expat_sources
|
||||||
|
asciitab.h
|
||||||
|
hashtable.h
|
||||||
|
iasciitab.h
|
||||||
|
latin1tab.h
|
||||||
|
nametab.h
|
||||||
|
utf8tab.h
|
||||||
|
xmldef.h
|
||||||
|
xmlparse.h
|
||||||
|
xmlrole.h
|
||||||
|
xmltok.h
|
||||||
|
xmltok_impl.h
|
||||||
|
hashtable.c
|
||||||
|
xmlparse.c
|
||||||
|
xmlrole.c
|
||||||
|
xmltok.c
|
||||||
|
internal.h
|
||||||
|
ascii.h
|
||||||
|
sg_expat.h
|
||||||
|
sg_expat_external.h
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach(s ${expat_sources})
|
||||||
|
set_property(GLOBAL
|
||||||
|
APPEND PROPERTY LOCAL_EXPAT_SOURCES
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/${s}")
|
||||||
|
endforeach()
|
||||||
+1
-1
@@ -15,7 +15,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "expat_external.h"
|
#include "sg_expat_external.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
+1
-1
@@ -23,7 +23,7 @@
|
|||||||
#endif /* ndef COMPILED_FROM_DSP */
|
#endif /* ndef COMPILED_FROM_DSP */
|
||||||
|
|
||||||
#include "ascii.h"
|
#include "ascii.h"
|
||||||
#include "expat.h"
|
#include "sg_expat.h"
|
||||||
|
|
||||||
#ifdef XML_UNICODE
|
#ifdef XML_UNICODE
|
||||||
#define XML_ENCODE_MAX XML_UTF16_ENCODE_MAX
|
#define XML_ENCODE_MAX XML_UTF16_ENCODE_MAX
|
||||||
+1
-1
@@ -18,7 +18,7 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif /* ndef COMPILED_FROM_DSP */
|
#endif /* ndef COMPILED_FROM_DSP */
|
||||||
|
|
||||||
#include "expat_external.h"
|
#include "sg_expat_external.h"
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include "xmlrole.h"
|
#include "xmlrole.h"
|
||||||
#include "ascii.h"
|
#include "ascii.h"
|
||||||
+1
-1
@@ -18,7 +18,7 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif /* ndef COMPILED_FROM_DSP */
|
#endif /* ndef COMPILED_FROM_DSP */
|
||||||
|
|
||||||
#include "expat_external.h"
|
#include "sg_expat_external.h"
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include "xmltok.h"
|
#include "xmltok.h"
|
||||||
#include "nametab.h"
|
#include "nametab.h"
|
||||||
Vendored
+17
@@ -0,0 +1,17 @@
|
|||||||
|
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}")
|
||||||
Vendored
+12
@@ -0,0 +1,12 @@
|
|||||||
|
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
|
||||||
Vendored
+1789
File diff suppressed because it is too large
Load Diff
Vendored
+34
@@ -0,0 +1,34 @@
|
|||||||
|
// 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
|
||||||
Vendored
+327
@@ -0,0 +1,327 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
|
||||||
Vendored
+329
@@ -0,0 +1,329 @@
|
|||||||
|
// 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
@@ -0,0 +1,228 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
+98
-63
@@ -2,14 +2,19 @@ cmake_minimum_required (VERSION 2.6.4)
|
|||||||
include (CheckFunctionExists)
|
include (CheckFunctionExists)
|
||||||
include (CheckIncludeFile)
|
include (CheckIncludeFile)
|
||||||
include (CheckCXXSourceCompiles)
|
include (CheckCXXSourceCompiles)
|
||||||
|
include (CheckCXXCompilerFlag)
|
||||||
|
|
||||||
project(SimGear)
|
project(SimGear)
|
||||||
|
|
||||||
|
# using 10.7 because boost requires libc++ and 10.6 doesn't include it
|
||||||
|
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.7)
|
||||||
|
|
||||||
# read 'version' file into a variable (stripping any newlines or spaces)
|
# read 'version' file into a variable (stripping any newlines or spaces)
|
||||||
file(READ version versionFile)
|
file(READ version versionFile)
|
||||||
string(STRIP ${versionFile} SIMGEAR_VERSION)
|
string(STRIP ${versionFile} SIMGEAR_VERSION)
|
||||||
|
|
||||||
|
set(FIND_LIBRARY_USE_LIB64_PATHS ON)
|
||||||
|
|
||||||
# use simgear version also as the SO version (if building SOs)
|
# use simgear version also as the SO version (if building SOs)
|
||||||
SET(SIMGEAR_SOVERSION ${SIMGEAR_VERSION})
|
SET(SIMGEAR_SOVERSION ${SIMGEAR_VERSION})
|
||||||
|
|
||||||
@@ -32,7 +37,7 @@ SET(CPACK_INSTALL_CMAKE_PROJECTS ${CMAKE_CURRENT_BINARY_DIR};SimGear;ALL;/)
|
|||||||
|
|
||||||
# split version string into components, note CMAKE_MATCH_0 is the entire regexp match
|
# split version string into components, note CMAKE_MATCH_0 is the entire regexp match
|
||||||
string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" CPACK_PACKAGE_VERSION ${SIMGEAR_VERSION} )
|
string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" CPACK_PACKAGE_VERSION ${SIMGEAR_VERSION} )
|
||||||
set(CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1})
|
set(CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1})
|
||||||
set(CPACK_PACKAGE_VERSION_MINOR ${CMAKE_MATCH_2})
|
set(CPACK_PACKAGE_VERSION_MINOR ${CMAKE_MATCH_2})
|
||||||
set(CPACK_PACKAGE_VERSION_PATCH ${CMAKE_MATCH_3})
|
set(CPACK_PACKAGE_VERSION_PATCH ${CMAKE_MATCH_3})
|
||||||
|
|
||||||
@@ -85,13 +90,6 @@ if(NOT "${CMAKE_LIBRARY_ARCHITECTURE}" STREQUAL "")
|
|||||||
message(STATUS "additional library directories: ${ADDITIONAL_LIBRARY_PATHS}")
|
message(STATUS "additional library directories: ${ADDITIONAL_LIBRARY_PATHS}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT MSVC)
|
|
||||||
# TBD: are these really necessary? Aren't they considered by cmake automatically?
|
|
||||||
list(APPEND ADDITIONAL_LIBRARY_PATHS
|
|
||||||
/opt/local
|
|
||||||
/usr/local
|
|
||||||
/usr)
|
|
||||||
endif()
|
|
||||||
#####################################################################################
|
#####################################################################################
|
||||||
|
|
||||||
if (NOT MSVC)
|
if (NOT MSVC)
|
||||||
@@ -105,14 +103,13 @@ set(SYSTEM_EXPAT OFF)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
option(SIMGEAR_HEADLESS "Set to ON to build SimGear without GUI/graphics support" OFF)
|
option(SIMGEAR_HEADLESS "Set to ON to build SimGear without GUI/graphics support" OFF)
|
||||||
option(JPEG_FACTORY "Enable JPEG-factory support" OFF)
|
|
||||||
option(ENABLE_LIBSVN "Set to ON to build SimGear with libsvnclient support" ON)
|
|
||||||
option(ENABLE_RTI "Set to ON to build SimGear with RTI support" OFF)
|
option(ENABLE_RTI "Set to ON to build SimGear with RTI support" OFF)
|
||||||
option(ENABLE_TESTS "Set to OFF to disable building SimGear's test applications" ON)
|
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(ENABLE_SOUND "Set to OFF to disable building SimGear's sound support" ON)
|
||||||
|
option(ENABLE_PKGUTIL "Set to ON to build the sg_pkgutil application (default)" ON)
|
||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
GET_FILENAME_COMPONENT(PARENT_DIR ${PROJECT_SOURCE_DIR} PATH)
|
GET_FILENAME_COMPONENT(PARENT_DIR ${PROJECT_BINARY_DIR} PATH)
|
||||||
if (CMAKE_CL_64)
|
if (CMAKE_CL_64)
|
||||||
SET(TEST_3RDPARTY_DIR "${PARENT_DIR}/3rdparty.x64")
|
SET(TEST_3RDPARTY_DIR "${PARENT_DIR}/3rdparty.x64")
|
||||||
else (CMAKE_CL_64)
|
else (CMAKE_CL_64)
|
||||||
@@ -165,6 +162,13 @@ if (MSVC AND MSVC_3RDPARTY_ROOT)
|
|||||||
set (OPENAL_LIBRARY_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib)
|
set (OPENAL_LIBRARY_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib)
|
||||||
endif (MSVC AND MSVC_3RDPARTY_ROOT)
|
endif (MSVC AND MSVC_3RDPARTY_ROOT)
|
||||||
|
|
||||||
|
if(APPLE)
|
||||||
|
find_library(COCOA_LIBRARY Cocoa)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Somehow this only works if included before searching for Boost...
|
||||||
|
include(BoostTestTargets)
|
||||||
|
|
||||||
find_package(Boost REQUIRED)
|
find_package(Boost REQUIRED)
|
||||||
set (BOOST_CXX_FLAGS "-DBOOST_MULTI_INDEX_DISABLE_SERIALIZATION -DBOOST_BIMAP_DISABLE_SERIALIZATION")
|
set (BOOST_CXX_FLAGS "-DBOOST_MULTI_INDEX_DISABLE_SERIALIZATION -DBOOST_BIMAP_DISABLE_SERIALIZATION")
|
||||||
|
|
||||||
@@ -174,40 +178,15 @@ if(SIMGEAR_HEADLESS)
|
|||||||
else()
|
else()
|
||||||
message(STATUS "SimGear mode: NORMAL")
|
message(STATUS "SimGear mode: NORMAL")
|
||||||
find_package(OpenGL REQUIRED)
|
find_package(OpenGL REQUIRED)
|
||||||
|
|
||||||
if (ENABLE_SOUND)
|
if (ENABLE_SOUND)
|
||||||
find_package(OpenAL REQUIRED)
|
find_package(OpenAL REQUIRED)
|
||||||
message(STATUS "Sound support: ENABLED")
|
message(STATUS "Sound support: ENABLED")
|
||||||
endif(ENABLE_SOUND)
|
endif(ENABLE_SOUND)
|
||||||
|
|
||||||
find_package(OpenSceneGraph 3.0.0 REQUIRED osgText osgSim osgDB osgParticle osgGA osgUtil)
|
find_package(OpenSceneGraph 3.2.0 REQUIRED osgText osgSim osgDB osgParticle osgGA osgViewer osgUtil)
|
||||||
endif(SIMGEAR_HEADLESS)
|
endif(SIMGEAR_HEADLESS)
|
||||||
|
|
||||||
if(JPEG_FACTORY)
|
|
||||||
message(STATUS "JPEG-factory: ENABLED")
|
|
||||||
find_package(JPEG REQUIRED)
|
|
||||||
include_directories(${JPEG_INCLUDE_DIR})
|
|
||||||
else()
|
|
||||||
message(STATUS "JPEG-factory: DISABLED")
|
|
||||||
endif(JPEG_FACTORY)
|
|
||||||
|
|
||||||
if(ENABLE_LIBSVN)
|
|
||||||
find_package(SvnClient)
|
|
||||||
|
|
||||||
if(LIBSVN_FOUND)
|
|
||||||
message(STATUS "Subversion client support: ENABLED")
|
|
||||||
set(HAVE_SVN_CLIENT_H 1)
|
|
||||||
set(HAVE_LIBSVN_CLIENT_1 1)
|
|
||||||
else()
|
|
||||||
# Oops. ENABLE_LIBSVN is ON, but svn is still missing.
|
|
||||||
# Provide clearly visible warning/hint, so builders know what else they should install (or disable).
|
|
||||||
message(WARNING "Failed to enable subversion client support. Unable to find required subversion client library. Some features may not be available (scenery download).")
|
|
||||||
message(WARNING "Install 'libsvn' library/DLL (libsvn-devel/libsvnclient/...). Otherwise disable subversion support (set 'ENABLE_LIBSVN' to 'OFF').")
|
|
||||||
endif(LIBSVN_FOUND)
|
|
||||||
else()
|
|
||||||
message(STATUS "Subversion client support: DISABLED")
|
|
||||||
endif(ENABLE_LIBSVN)
|
|
||||||
|
|
||||||
find_package(ZLIB REQUIRED)
|
find_package(ZLIB REQUIRED)
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
@@ -215,12 +194,19 @@ if (SYSTEM_EXPAT)
|
|||||||
message(STATUS "Requested to use system Expat library, forcing SIMGEAR_SHARED to true")
|
message(STATUS "Requested to use system Expat library, forcing SIMGEAR_SHARED to true")
|
||||||
set(SIMGEAR_SHARED ON)
|
set(SIMGEAR_SHARED ON)
|
||||||
find_package(EXPAT REQUIRED)
|
find_package(EXPAT REQUIRED)
|
||||||
include_directories(${EXPAT_INCLUDE_DIRS})
|
|
||||||
else()
|
else()
|
||||||
message(STATUS "Using built-in expat code")
|
message(STATUS "Using built-in expat code")
|
||||||
add_definitions(-DHAVE_EXPAT_CONFIG_H)
|
# XML_STATIC is important to avoid sg_expat_external.h
|
||||||
|
# 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)
|
||||||
endif(SYSTEM_EXPAT)
|
endif(SYSTEM_EXPAT)
|
||||||
|
|
||||||
|
include_directories(${EXPAT_INCLUDE_DIRS})
|
||||||
|
|
||||||
check_include_file(inttypes.h HAVE_INTTYPES_H)
|
check_include_file(inttypes.h HAVE_INTTYPES_H)
|
||||||
check_include_file(sys/time.h HAVE_SYS_TIME_H)
|
check_include_file(sys/time.h HAVE_SYS_TIME_H)
|
||||||
check_include_file(sys/timeb.h HAVE_SYS_TIMEB_H)
|
check_include_file(sys/timeb.h HAVE_SYS_TIMEB_H)
|
||||||
@@ -269,6 +255,22 @@ if(HAVE_CLOCK_GETTIME)
|
|||||||
endif(HAVE_RT)
|
endif(HAVE_RT)
|
||||||
endif(HAVE_CLOCK_GETTIME)
|
endif(HAVE_CLOCK_GETTIME)
|
||||||
|
|
||||||
|
set(DL_LIBRARY "")
|
||||||
|
check_cxx_source_compiles(
|
||||||
|
"#include <dlfcn.h>
|
||||||
|
int main(void) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
HAVE_DLFCN_H)
|
||||||
|
|
||||||
|
if(HAVE_DLFCN_H)
|
||||||
|
check_library_exists(dl dlerror "" HAVE_DL)
|
||||||
|
if(HAVE_DL)
|
||||||
|
set(DL_LIBRARY "dl")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
SET(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "add a postfix, usually 'd' on windows")
|
SET(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "add a postfix, usually 'd' on windows")
|
||||||
SET(CMAKE_RELEASE_POSTFIX "" CACHE STRING "add a postfix, usually empty on windows")
|
SET(CMAKE_RELEASE_POSTFIX "" CACHE STRING "add a postfix, usually empty on windows")
|
||||||
SET(CMAKE_RELWITHDEBINFO_POSTFIX "" CACHE STRING "add a postfix, usually empty on windows")
|
SET(CMAKE_RELWITHDEBINFO_POSTFIX "" CACHE STRING "add a postfix, usually empty on windows")
|
||||||
@@ -276,13 +278,13 @@ SET(CMAKE_MINSIZEREL_POSTFIX "" CACHE STRING "add a postfix, usually empty on wi
|
|||||||
|
|
||||||
# isnan might not be real symbol, so can't check using function_exists
|
# isnan might not be real symbol, so can't check using function_exists
|
||||||
check_cxx_source_compiles(
|
check_cxx_source_compiles(
|
||||||
"#include <cmath>
|
"#include <cmath>
|
||||||
void f() { isnan(0.0);} "
|
int main() { return isnan(0.0);} "
|
||||||
HAVE_ISNAN)
|
HAVE_ISNAN)
|
||||||
|
|
||||||
check_cxx_source_compiles(
|
check_cxx_source_compiles(
|
||||||
"#include <cmath>
|
"#include <cmath>
|
||||||
void f() { std::isnan(0.0);} "
|
int main() { return std::isnan(0.0);} "
|
||||||
HAVE_STD_ISNAN)
|
HAVE_STD_ISNAN)
|
||||||
|
|
||||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||||
@@ -300,6 +302,17 @@ endif(CMAKE_COMPILER_IS_GNUCXX)
|
|||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||||
set(WARNING_FLAGS_CXX "-Wall -Wno-overloaded-virtual")
|
set(WARNING_FLAGS_CXX "-Wall -Wno-overloaded-virtual")
|
||||||
set(WARNING_FLAGS_C "-Wall")
|
set(WARNING_FLAGS_C "-Wall")
|
||||||
|
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++")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||||
|
# boost goes haywire wrt static asserts
|
||||||
|
check_cxx_compiler_flag(-Wno-unused-local-typedefs HAS_NOWARN_UNUSED_TYPEDEFS)
|
||||||
|
if(HAS_NOWARN_UNUSED_TYPEDEFS)
|
||||||
|
set(WARNING_FLAGS_CXX " ${WARNING_FLAGS_CXX} -Wno-unused-local-typedefs")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
@@ -313,28 +326,35 @@ if(WIN32)
|
|||||||
# foreach(warning 4244 4251 4267 4275 4290 4786 4305 4996)
|
# foreach(warning 4244 4251 4267 4275 4290 4786 4305 4996)
|
||||||
# SET(WARNING_FLAGS "${WARNING_FLAGS} /wd${warning}")
|
# SET(WARNING_FLAGS "${WARNING_FLAGS} /wd${warning}")
|
||||||
# endforeach(warning)
|
# endforeach(warning)
|
||||||
|
|
||||||
set(MSVC_FLAGS "-DWIN32 -DNOMINMAX -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS -D__CRT_NONSTDC_NO_WARNINGS /wd4996 /wd4250")
|
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)
|
||||||
|
set( MSVC_LD_FLAGS "/FORCE:MULTIPLE" )
|
||||||
|
endif (${MSVC_VERSION} GREATER 1599)
|
||||||
endif(MSVC)
|
endif(MSVC)
|
||||||
|
|
||||||
# assumed on Windows
|
# assumed on Windows
|
||||||
set(HAVE_GETLOCALTIME 1)
|
set(HAVE_GETLOCALTIME 1)
|
||||||
|
|
||||||
set( WINSOCK_LIBRARY "ws2_32.lib" )
|
set( WINSOCK_LIBRARY "ws2_32.lib" )
|
||||||
set( RT_LIBRARY "winmm" )
|
set( RT_LIBRARY "winmm" )
|
||||||
endif(WIN32)
|
endif(WIN32)
|
||||||
|
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS_C} ${MSVC_FLAGS}")
|
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_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_directories(${PROJECT_SOURCE_DIR})
|
# use BEFORE to ensure local directories are used first,
|
||||||
include_directories(${PROJECT_SOURCE_DIR}/simgear/canvas/ShivaVG/include)
|
# ahead of system-installed libs
|
||||||
include_directories(${PROJECT_BINARY_DIR}/simgear)
|
include_directories(BEFORE ${PROJECT_SOURCE_DIR})
|
||||||
include_directories(${PROJECT_BINARY_DIR}/simgear/xml)
|
include_directories(BEFORE ${PROJECT_SOURCE_DIR}/simgear/canvas/ShivaVG/include)
|
||||||
|
include_directories(BEFORE ${PROJECT_BINARY_DIR}/simgear)
|
||||||
|
|
||||||
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}
|
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}
|
||||||
${Boost_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIR}
|
${Boost_INCLUDE_DIRS}
|
||||||
${OPENAL_INCLUDE_DIR} )
|
${ZLIB_INCLUDE_DIR}
|
||||||
|
${OPENAL_INCLUDE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
add_definitions(-DHAVE_CONFIG_H)
|
add_definitions(-DHAVE_CONFIG_H)
|
||||||
|
|
||||||
@@ -345,22 +365,35 @@ configure_file (
|
|||||||
"${PROJECT_BINARY_DIR}/simgear/simgear_config.h"
|
"${PROJECT_BINARY_DIR}/simgear/simgear_config.h"
|
||||||
)
|
)
|
||||||
|
|
||||||
configure_file (
|
|
||||||
"${PROJECT_SOURCE_DIR}/simgear/xml/expat_config_cmake.in"
|
|
||||||
"${PROJECT_BINARY_DIR}/simgear/xml/expat_config.h"
|
|
||||||
)
|
|
||||||
|
|
||||||
if(ENABLE_TESTS)
|
if(ENABLE_TESTS)
|
||||||
# enable CTest / make test target
|
# enable CTest / make test target
|
||||||
message(STATUS "Tests: ENABLED")
|
message(STATUS "Tests: ENABLED")
|
||||||
|
|
||||||
include (Dart)
|
include (Dart)
|
||||||
enable_testing()
|
enable_testing()
|
||||||
else()
|
else()
|
||||||
message(STATUS "Tests: DISABLED")
|
message(STATUS "Tests: DISABLED")
|
||||||
endif(ENABLE_TESTS)
|
endif(ENABLE_TESTS)
|
||||||
|
|
||||||
|
# always set TEST_LIBS as it is also used by other tools/applications
|
||||||
|
set(TEST_LIBS_INTERNAL_CORE
|
||||||
|
${CMAKE_THREAD_LIBS_INIT}
|
||||||
|
${ZLIB_LIBRARY}
|
||||||
|
${WINSOCK_LIBRARY}
|
||||||
|
${RT_LIBRARY}
|
||||||
|
${DL_LIBRARY}
|
||||||
|
${COCOA_LIBRARY})
|
||||||
|
set(TEST_LIBS SimGearCore ${TEST_LIBS_INTERNAL_CORE})
|
||||||
|
|
||||||
|
if(NOT SIMGEAR_HEADLESS)
|
||||||
|
set(TEST_LIBS SimGearScene ${OPENGL_LIBRARIES} ${TEST_LIBS})
|
||||||
|
endif()
|
||||||
|
|
||||||
install (FILES ${PROJECT_BINARY_DIR}/simgear/simgear_config.h DESTINATION include/simgear/)
|
install (FILES ${PROJECT_BINARY_DIR}/simgear/simgear_config.h DESTINATION include/simgear/)
|
||||||
|
|
||||||
|
include_directories(3rdparty/utf8/source)
|
||||||
|
|
||||||
|
add_subdirectory(3rdparty)
|
||||||
add_subdirectory(simgear)
|
add_subdirectory(simgear)
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------
|
||||||
@@ -372,3 +405,5 @@ CONFIGURE_FILE(
|
|||||||
IMMEDIATE @ONLY)
|
IMMEDIATE @ONLY)
|
||||||
ADD_CUSTOM_TARGET(uninstall
|
ADD_CUSTOM_TARGET(uninstall
|
||||||
"${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
|
"${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,256 @@
|
|||||||
|
# - Add tests using boost::test
|
||||||
|
#
|
||||||
|
# Add this line to your test files in place of including a basic boost test header:
|
||||||
|
# #include <BoostTestTargetConfig.h>
|
||||||
|
#
|
||||||
|
# If you cannot do that and must use the included form for a given test,
|
||||||
|
# include the line
|
||||||
|
# // OVERRIDE_BOOST_TEST_INCLUDED_WARNING
|
||||||
|
# in the same file with the boost test include.
|
||||||
|
#
|
||||||
|
# include(BoostTestTargets)
|
||||||
|
# add_boost_test(<testdriver_name> SOURCES <source1> [<more sources...>]
|
||||||
|
# [FAIL_REGULAR_EXPRESSION <additional fail regex>]
|
||||||
|
# [LAUNCHER <generic launcher script>]
|
||||||
|
# [LIBRARIES <library> [<library>...]]
|
||||||
|
# [RESOURCES <resource> [<resource>...]]
|
||||||
|
# [TESTS <testcasename> [<testcasename>...]])
|
||||||
|
#
|
||||||
|
# If for some reason you need access to the executable target created,
|
||||||
|
# it can be found in ${${testdriver_name}_TARGET_NAME} as specified when
|
||||||
|
# you called add_boost_test
|
||||||
|
#
|
||||||
|
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||||
|
#
|
||||||
|
# Requires:
|
||||||
|
# GetForceIncludeDefinitions
|
||||||
|
# CopyResourcesToBuildTree
|
||||||
|
#
|
||||||
|
# Original Author:
|
||||||
|
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
|
||||||
|
# http://academic.cleardefinition.com
|
||||||
|
# Iowa State University HCI Graduate Program/VRAC
|
||||||
|
#
|
||||||
|
# Copyright Iowa State University 2009-2010.
|
||||||
|
# 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(__add_boost_test)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
set(__add_boost_test YES)
|
||||||
|
|
||||||
|
set(BOOST_TEST_TARGET_PREFIX "test")
|
||||||
|
|
||||||
|
if(NOT Boost_FOUND)
|
||||||
|
find_package(Boost 1.34.0 QUIET)
|
||||||
|
endif()
|
||||||
|
if("${Boost_VERSION}0" LESS "1034000")
|
||||||
|
set(_shared_msg
|
||||||
|
"NOTE: boost::test-based targets and tests cannot "
|
||||||
|
"be added: boost >= 1.34.0 required but not found. "
|
||||||
|
"(found: '${Boost_VERSION}'; want >=103400) ")
|
||||||
|
if(ENABLE_TESTS)
|
||||||
|
message(FATAL_ERROR
|
||||||
|
${_shared_msg}
|
||||||
|
"You may disable ENABLE_TESTS to continue without the "
|
||||||
|
"tests.")
|
||||||
|
else()
|
||||||
|
message(STATUS
|
||||||
|
${_shared_msg}
|
||||||
|
"ENABLE_TESTS disabled, so continuing anyway.")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(GetForceIncludeDefinitions)
|
||||||
|
include(CopyResourcesToBuildTree)
|
||||||
|
|
||||||
|
if(Boost_FOUND AND NOT "${Boost_VERSION}0" LESS "1034000")
|
||||||
|
set(_boosttesttargets_libs)
|
||||||
|
set(_boostConfig "BoostTestTargetsIncluded.h")
|
||||||
|
if(NOT Boost_UNIT_TEST_FRAMEWORK_LIBRARY)
|
||||||
|
find_package(Boost 1.34.0 QUIET COMPONENTS unit_test_framework)
|
||||||
|
endif()
|
||||||
|
if(Boost_UNIT_TEST_FRAMEWORK_LIBRARY)
|
||||||
|
set(_boosttesttargets_libs "${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}")
|
||||||
|
if(Boost_USE_STATIC_LIBS)
|
||||||
|
set(_boostConfig "BoostTestTargetsStatic.h")
|
||||||
|
else()
|
||||||
|
set(_boostConfig "BoostTestTargetsDynamic.h")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
get_filename_component(_moddir ${CMAKE_CURRENT_LIST_FILE} PATH)
|
||||||
|
configure_file("${_moddir}/${_boostConfig}"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/BoostTestTargetConfig.h"
|
||||||
|
COPYONLY)
|
||||||
|
include_directories("${CMAKE_CURRENT_BINARY_DIR}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
function(add_boost_test _name)
|
||||||
|
if(NOT ENABLE_TESTS)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# parse arguments
|
||||||
|
set(_nowhere)
|
||||||
|
set(_curdest _nowhere)
|
||||||
|
set(_val_args
|
||||||
|
SOURCES
|
||||||
|
FAIL_REGULAR_EXPRESSION
|
||||||
|
LAUNCHER
|
||||||
|
LIBRARIES
|
||||||
|
RESOURCES
|
||||||
|
TESTS)
|
||||||
|
set(_bool_args
|
||||||
|
USE_COMPILED_LIBRARY)
|
||||||
|
foreach(_arg ${_val_args} ${_bool_args})
|
||||||
|
set(${_arg})
|
||||||
|
endforeach()
|
||||||
|
foreach(_element ${ARGN})
|
||||||
|
list(FIND _val_args "${_element}" _val_arg_find)
|
||||||
|
list(FIND _bool_args "${_element}" _bool_arg_find)
|
||||||
|
if("${_val_arg_find}" GREATER "-1")
|
||||||
|
set(_curdest "${_element}")
|
||||||
|
elseif("${_bool_arg_find}" GREATER "-1")
|
||||||
|
set("${_element}" ON)
|
||||||
|
set(_curdest _nowhere)
|
||||||
|
else()
|
||||||
|
list(APPEND ${_curdest} "${_element}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
if(_nowhere)
|
||||||
|
message(FATAL_ERROR "Syntax error in use of add_boost_test!")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT SOURCES)
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"Syntax error in use of add_boost_test: at least one source file required!")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(Boost_FOUND AND NOT "${Boost_VERSION}0" LESS "1034000")
|
||||||
|
|
||||||
|
include_directories(${Boost_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
set(includeType)
|
||||||
|
foreach(src ${SOURCES})
|
||||||
|
file(READ ${src} thefile)
|
||||||
|
if("${thefile}" MATCHES ".*BoostTestTargetConfig.h.*")
|
||||||
|
set(includeType CONFIGURED)
|
||||||
|
set(includeFileLoc ${src})
|
||||||
|
break()
|
||||||
|
elseif("${thefile}" MATCHES ".*boost/test/included/unit_test.hpp.*")
|
||||||
|
set(includeType INCLUDED)
|
||||||
|
set(includeFileLoc ${src})
|
||||||
|
set(_boosttesttargets_libs) # clear this out - linking would be a bad idea
|
||||||
|
if(NOT
|
||||||
|
"${thefile}"
|
||||||
|
MATCHES
|
||||||
|
".*OVERRIDE_BOOST_TEST_INCLUDED_WARNING.*")
|
||||||
|
message("Please replace the include line in ${src} with this alternate include line instead:")
|
||||||
|
message(" \#include <BoostTestTargetConfig.h>")
|
||||||
|
message("Once you've saved your changes, re-run CMake. (See BoostTestTargets.cmake for more info)")
|
||||||
|
endif()
|
||||||
|
break()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
if(NOT _boostTestTargetsNagged${_name} STREQUAL "${includeType}")
|
||||||
|
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")
|
||||||
|
message("In test '${_name}': ${includeFileLoc} uses the 'included' form of the boost unit test framework.")
|
||||||
|
else()
|
||||||
|
message("In test '${_name}': Didn't detect the CMake-configurable boost test include.")
|
||||||
|
message("Please replace your existing boost test include in that test with the following:")
|
||||||
|
message(" \#include <BoostTestTargetConfig.h>")
|
||||||
|
message("Once you've saved your changes, re-run CMake. (See BoostTestTargets.cmake for more info)")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
set(_boostTestTargetsNagged${_name}
|
||||||
|
"${includeType}"
|
||||||
|
CACHE
|
||||||
|
INTERNAL
|
||||||
|
""
|
||||||
|
FORCE)
|
||||||
|
|
||||||
|
|
||||||
|
if(RESOURCES)
|
||||||
|
list(APPEND SOURCES ${RESOURCES})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Generate a unique target name, using the relative binary dir
|
||||||
|
# and provided name. (transform all / into _ and remove all other
|
||||||
|
# non-alphabet characters)
|
||||||
|
file(RELATIVE_PATH
|
||||||
|
targetpath
|
||||||
|
"${CMAKE_BINARY_DIR}"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}")
|
||||||
|
string(REGEX REPLACE "[^A-Za-z/_]" "" targetpath "${targetpath}")
|
||||||
|
string(REPLACE "/" "_" targetpath "${targetpath}")
|
||||||
|
|
||||||
|
set(_target_name ${BOOST_TEST_TARGET_PREFIX}-${targetpath}-${_name})
|
||||||
|
set(${_name}_TARGET_NAME "${_target_name}" PARENT_SCOPE)
|
||||||
|
|
||||||
|
# Build the test.
|
||||||
|
add_executable(${_target_name} ${SOURCES})
|
||||||
|
|
||||||
|
list(APPEND LIBRARIES ${_boosttesttargets_libs})
|
||||||
|
|
||||||
|
if(LIBRARIES)
|
||||||
|
target_link_libraries(${_target_name} ${LIBRARIES})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(RESOURCES)
|
||||||
|
set_property(TARGET ${_target_name} PROPERTY RESOURCE ${RESOURCES})
|
||||||
|
copy_resources_to_build_tree(${_target_name})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT Boost_TEST_FLAGS)
|
||||||
|
# set(Boost_TEST_FLAGS --catch_system_error=yes --output_format=XML)
|
||||||
|
set(Boost_TEST_FLAGS --catch_system_error=yes)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# TODO: Figure out why only recent boost handles individual test running properly
|
||||||
|
|
||||||
|
if(LAUNCHER)
|
||||||
|
set(_test_command ${LAUNCHER} "\$<TARGET_FILE:${_target_name}>")
|
||||||
|
else()
|
||||||
|
set(_test_command ${_target_name})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(TESTS AND ( "${Boost_VERSION}" VERSION_GREATER "103799" ))
|
||||||
|
foreach(_test ${TESTS})
|
||||||
|
add_test(
|
||||||
|
${_name}-${_test}
|
||||||
|
${_test_command} --run_test=${_test} ${Boost_TEST_FLAGS}
|
||||||
|
)
|
||||||
|
if(FAIL_REGULAR_EXPRESSION)
|
||||||
|
set_tests_properties(${_name}-${_test}
|
||||||
|
PROPERTIES
|
||||||
|
FAIL_REGULAR_EXPRESSION
|
||||||
|
"${FAIL_REGULAR_EXPRESSION}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
else()
|
||||||
|
add_test(
|
||||||
|
${_name}-boost_test
|
||||||
|
${_test_command} ${Boost_TEST_FLAGS}
|
||||||
|
)
|
||||||
|
if(FAIL_REGULAR_EXPRESSION)
|
||||||
|
set_tests_properties(${_name}-boost_test
|
||||||
|
PROPERTIES
|
||||||
|
FAIL_REGULAR_EXPRESSION
|
||||||
|
"${FAIL_REGULAR_EXPRESSION}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# CppCheck the test if we can.
|
||||||
|
if(COMMAND add_cppcheck)
|
||||||
|
add_cppcheck(${_target_name} STYLE UNUSED_FUNCTIONS)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// Small header computed by CMake to set up boost test.
|
||||||
|
// include AFTER #define BOOST_TEST_MODULE whatever
|
||||||
|
// but before any other boost test includes.
|
||||||
|
|
||||||
|
// Using the Boost UTF dynamic library
|
||||||
|
|
||||||
|
#define BOOST_TEST_DYN_LINK
|
||||||
|
#include <boost/test/unit_test.hpp>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// Small header computed by CMake to set up boost test.
|
||||||
|
// include AFTER #define BOOST_TEST_MODULE whatever
|
||||||
|
// but before any other boost test includes.
|
||||||
|
|
||||||
|
// Using the Boost UTF included framework
|
||||||
|
|
||||||
|
#include <boost/test/included/unit_test.hpp>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// Small header computed by CMake to set up boost test.
|
||||||
|
// include AFTER #define BOOST_TEST_MODULE whatever
|
||||||
|
// but before any other boost test includes.
|
||||||
|
|
||||||
|
// Using the Boost UTF static library
|
||||||
|
|
||||||
|
#include <boost/test/unit_test.hpp>
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
# - Copy the resources your app needs to the build tree.
|
||||||
|
#
|
||||||
|
# copy_resources_to_build_tree(<target_name>)
|
||||||
|
#
|
||||||
|
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||||
|
#
|
||||||
|
# Original Author:
|
||||||
|
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
|
||||||
|
# http://academic.cleardefinition.com
|
||||||
|
# Iowa State University HCI Graduate Program/VRAC
|
||||||
|
#
|
||||||
|
# Copyright Iowa State University 2009-2010.
|
||||||
|
# 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(__copy_resources_to_build_tree)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
set(__copy_resources_to_build_tree YES)
|
||||||
|
|
||||||
|
function(copy_resources_to_build_tree _target)
|
||||||
|
get_target_property(_resources ${_target} RESOURCE)
|
||||||
|
if(NOT _resources)
|
||||||
|
# Bail if no resources
|
||||||
|
message(STATUS
|
||||||
|
"Told to copy resources for target ${_target}, but "
|
||||||
|
"no resources are set!")
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
get_target_property(_path ${_target} LOCATION)
|
||||||
|
get_filename_component(_path "${_path}" PATH)
|
||||||
|
|
||||||
|
if(NOT MSVC AND NOT "${CMAKE_GENERATOR}" MATCHES "Makefiles")
|
||||||
|
foreach(_config ${CMAKE_CONFIGURATION_TYPES})
|
||||||
|
get_target_property(_path${_config} ${_target} LOCATION_${_config})
|
||||||
|
get_filename_component(_path${_config} "${_path${_config}}" PATH)
|
||||||
|
add_custom_command(TARGET ${_target}
|
||||||
|
POST_BUILD
|
||||||
|
COMMAND
|
||||||
|
${CMAKE_COMMAND}
|
||||||
|
ARGS -E make_directory "${_path${_config}}/"
|
||||||
|
COMMENT "Creating directory ${_path${_config}}/")
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
foreach(_res ${_resources})
|
||||||
|
if(NOT IS_ABSOLUTE "${_res}")
|
||||||
|
get_filename_component(_res "${_res}" ABSOLUTE)
|
||||||
|
endif()
|
||||||
|
get_filename_component(_name "${_res}" NAME)
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
# Working dir is solution file dir, not exe file dir.
|
||||||
|
add_custom_command(TARGET ${_target}
|
||||||
|
POST_BUILD
|
||||||
|
COMMAND
|
||||||
|
${CMAKE_COMMAND}
|
||||||
|
ARGS -E copy "${_res}" "${CMAKE_BINARY_DIR}/"
|
||||||
|
COMMENT "Copying ${_name} to ${CMAKE_BINARY_DIR}/ for MSVC")
|
||||||
|
else()
|
||||||
|
if("${CMAKE_GENERATOR}" MATCHES "Makefiles")
|
||||||
|
add_custom_command(TARGET ${_target}
|
||||||
|
POST_BUILD
|
||||||
|
COMMAND
|
||||||
|
${CMAKE_COMMAND}
|
||||||
|
ARGS -E copy "${_res}" "${_path}/"
|
||||||
|
COMMENT "Copying ${_name} to ${_path}/")
|
||||||
|
else()
|
||||||
|
foreach(_config ${CMAKE_CONFIGURATION_TYPES})
|
||||||
|
add_custom_command(TARGET ${_target}
|
||||||
|
POST_BUILD
|
||||||
|
COMMAND
|
||||||
|
${CMAKE_COMMAND}
|
||||||
|
ARGS -E copy "${_res}" "${_path${_config}}"
|
||||||
|
COMMENT "Copying ${_name} to ${_path${_config}}")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endfunction()
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
# Find Subversion client libraries, and dependencies
|
|
||||||
# including APR (Apache Portable Runtime)
|
|
||||||
|
|
||||||
include (CheckFunctionExists)
|
|
||||||
include (CheckIncludeFile)
|
|
||||||
include (CheckLibraryExists)
|
|
||||||
|
|
||||||
macro(find_static_component comp libs)
|
|
||||||
# account for alternative Windows svn distribution naming
|
|
||||||
if(MSVC)
|
|
||||||
set(compLib "lib${comp}")
|
|
||||||
else(MSVC)
|
|
||||||
set(compLib "${comp}")
|
|
||||||
endif(MSVC)
|
|
||||||
|
|
||||||
string(TOUPPER "${comp}" compLibBase)
|
|
||||||
set( compLibName ${compLibBase}_LIBRARY )
|
|
||||||
|
|
||||||
# NO_DEFAULT_PATH is important on Mac - we need to ensure subversion
|
|
||||||
# libraires in dist/ or Macports are picked over the Apple version
|
|
||||||
# in /usr, since that's what we will ship.
|
|
||||||
# On other platforms we do need default paths though, i.e. since Linux
|
|
||||||
# distros may use architecture-specific directories (like
|
|
||||||
# /usr/lib/x86_64-linux-gnu) which we cannot hardcode/guess here.
|
|
||||||
FIND_LIBRARY(${compLibName}
|
|
||||||
if(APPLE)
|
|
||||||
NO_DEFAULT_PATH
|
|
||||||
endif(APPLE)
|
|
||||||
NAMES ${compLib}
|
|
||||||
HINTS $ENV{LIBSVN_DIR} ${CMAKE_INSTALL_PREFIX} ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib
|
|
||||||
PATH_SUFFIXES lib64 lib libs64 libs libs/Win32 libs/Win64
|
|
||||||
PATHS ${ADDITIONAL_LIBRARY_PATHS}
|
|
||||||
)
|
|
||||||
|
|
||||||
list(APPEND ${libs} ${${compLibName}})
|
|
||||||
endmacro()
|
|
||||||
|
|
||||||
find_program(HAVE_APR_CONFIG apr-1-config)
|
|
||||||
if(HAVE_APR_CONFIG)
|
|
||||||
|
|
||||||
execute_process(COMMAND apr-1-config --cppflags --includes
|
|
||||||
OUTPUT_VARIABLE APR_CFLAGS
|
|
||||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
|
||||||
|
|
||||||
execute_process(COMMAND apr-1-config --link-ld
|
|
||||||
OUTPUT_VARIABLE RAW_APR_LIBS
|
|
||||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
|
||||||
|
|
||||||
# clean up some vars, or other CMake pieces complain
|
|
||||||
string(STRIP "${RAW_APR_LIBS}" APR_LIBS)
|
|
||||||
|
|
||||||
else(HAVE_APR_CONFIG)
|
|
||||||
message(STATUS "apr-1-config not found, implement manual search for APR")
|
|
||||||
endif(HAVE_APR_CONFIG)
|
|
||||||
|
|
||||||
if(HAVE_APR_CONFIG OR MSVC)
|
|
||||||
find_path(LIBSVN_INCLUDE_DIR svn_client.h
|
|
||||||
NO_DEFAULT_PATH
|
|
||||||
HINTS
|
|
||||||
$ENV{LIBSVN_DIR} ${CMAKE_INSTALL_PREFIX} ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include
|
|
||||||
PATH_SUFFIXES include/subversion-1
|
|
||||||
PATHS
|
|
||||||
/opt/local
|
|
||||||
/usr/local
|
|
||||||
/usr
|
|
||||||
)
|
|
||||||
|
|
||||||
set(LIBSVN_LIBRARIES "")
|
|
||||||
if (MSVC)
|
|
||||||
find_static_component("apr-1" LIBSVN_LIBRARIES)
|
|
||||||
else (MSVC)
|
|
||||||
list(APPEND LIBSVN_LIBRARIES ${APR_LIBS})
|
|
||||||
endif (MSVC)
|
|
||||||
find_static_component("svn_client-1" LIBSVN_LIBRARIES)
|
|
||||||
find_static_component("svn_subr-1" LIBSVN_LIBRARIES)
|
|
||||||
find_static_component("svn_ra-1" LIBSVN_LIBRARIES)
|
|
||||||
|
|
||||||
include(FindPackageHandleStandardArgs)
|
|
||||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBSVN DEFAULT_MSG LIBSVN_LIBRARIES LIBSVN_INCLUDE_DIR)
|
|
||||||
if(NOT LIBSVN_FOUND)
|
|
||||||
set(LIBSVN_LIBRARIES "")
|
|
||||||
endif(NOT LIBSVN_FOUND)
|
|
||||||
endif(HAVE_APR_CONFIG OR MSVC)
|
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# - Get the platform-appropriate flags to add to force inclusion of a file
|
||||||
|
#
|
||||||
|
# The most common use of this is to use a generated config.h-type file
|
||||||
|
# placed out of the source tree in all files.
|
||||||
|
#
|
||||||
|
# get_force_include_definitions(var forcedincludefiles...) -
|
||||||
|
# where var is the name of your desired output variable, and everything
|
||||||
|
# else is a source file to forcibly include.
|
||||||
|
# a list item to be filtered.
|
||||||
|
#
|
||||||
|
# Original Author:
|
||||||
|
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
|
||||||
|
# http://academic.cleardefinition.com
|
||||||
|
# Iowa State University HCI Graduate Program/VRAC
|
||||||
|
#
|
||||||
|
# Copyright Iowa State University 2009-2010.
|
||||||
|
# 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(__get_force_include_definitions)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
set(__get_force_include_definitions YES)
|
||||||
|
|
||||||
|
function(get_force_include_definitions var)
|
||||||
|
set(_flagprefix)
|
||||||
|
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||||
|
set(_flag "-include")
|
||||||
|
elseif(MSVC)
|
||||||
|
set(_flag "/FI")
|
||||||
|
else()
|
||||||
|
message(SEND_ERROR "You don't seem to be using MSVC or GCC, but")
|
||||||
|
message(SEND_ERROR "the project called get_force_include_definitions.")
|
||||||
|
message(SEND_ERROR "Contact this project with the name of your")
|
||||||
|
message(FATAL_ERROR "compiler and preferably the flag to force includes")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(_out)
|
||||||
|
foreach(_item ${ARGN})
|
||||||
|
list(APPEND _out "${_flag} \"${_item}\"")
|
||||||
|
endforeach()
|
||||||
|
set(${var} "${_out}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
+28
-32
@@ -18,6 +18,7 @@ foreach( mylibfolder
|
|||||||
threads
|
threads
|
||||||
timing
|
timing
|
||||||
xml
|
xml
|
||||||
|
package
|
||||||
)
|
)
|
||||||
|
|
||||||
add_subdirectory(${mylibfolder})
|
add_subdirectory(${mylibfolder})
|
||||||
@@ -44,52 +45,29 @@ install (FILES ${HEADERS} DESTINATION include/simgear/)
|
|||||||
get_property(coreSources GLOBAL PROPERTY CORE_SOURCES)
|
get_property(coreSources GLOBAL PROPERTY CORE_SOURCES)
|
||||||
get_property(sceneSources GLOBAL PROPERTY SCENE_SOURCES)
|
get_property(sceneSources GLOBAL PROPERTY SCENE_SOURCES)
|
||||||
get_property(publicHeaders GLOBAL PROPERTY PUBLIC_HEADERS)
|
get_property(publicHeaders GLOBAL PROPERTY PUBLIC_HEADERS)
|
||||||
|
get_property(localExpatSources GLOBAL PROPERTY LOCAL_EXPAT_SOURCES)
|
||||||
if(LIBSVN_FOUND)
|
|
||||||
add_definitions(${APR_CFLAGS})
|
|
||||||
include_directories(${LIBSVN_INCLUDE_DIR})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(SIMGEAR_SHARED)
|
if(SIMGEAR_SHARED)
|
||||||
message(STATUS "Library building mode: SHARED LIBRARIES")
|
message(STATUS "Library building mode: SHARED LIBRARIES")
|
||||||
add_library(SimGearCore SHARED ${coreSources})
|
add_library(SimGearCore SHARED ${coreSources} ${localExpatSources})
|
||||||
|
|
||||||
# set_property(TARGET SimGearCore PROPERTY FRAMEWORK 1)
|
|
||||||
# message(STATUS "public header: ${publicHeaders}")
|
|
||||||
# set_property(TARGET SimGearCore PROPERTY PUBLIC_HEADER "${publicHeaders}")
|
|
||||||
set_property(TARGET SimGearCore PROPERTY LINKER_LANGUAGE CXX)
|
set_property(TARGET SimGearCore PROPERTY LINKER_LANGUAGE CXX)
|
||||||
|
|
||||||
set_property(TARGET SimGearCore PROPERTY VERSION ${SIMGEAR_VERSION})
|
set_property(TARGET SimGearCore PROPERTY VERSION ${SIMGEAR_VERSION})
|
||||||
set_property(TARGET SimGearCore PROPERTY SOVERSION ${SIMGEAR_SOVERSION})
|
set_property(TARGET SimGearCore PROPERTY SOVERSION ${SIMGEAR_SOVERSION})
|
||||||
|
install(TARGETS SimGearCore EXPORT SimGearCoreConfig LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||||
target_link_libraries(SimGearCore ${ZLIB_LIBRARY} ${RT_LIBRARY}
|
install(EXPORT SimGearCoreConfig DESTINATION share/SimGearCore)
|
||||||
${EXPAT_LIBRARIES}
|
|
||||||
${CMAKE_THREAD_LIBS_INIT})
|
|
||||||
|
|
||||||
if(LIBSVN_FOUND)
|
|
||||||
target_link_libraries(SimGearCore ${LIBSVN_LIBRARIES})
|
|
||||||
endif(LIBSVN_FOUND)
|
|
||||||
|
|
||||||
if(NOT SIMGEAR_HEADLESS)
|
if(NOT SIMGEAR_HEADLESS)
|
||||||
add_library(SimGearScene SHARED ${sceneSources})
|
add_library(SimGearScene SHARED ${sceneSources})
|
||||||
# set_property(TARGET SimGearScene PROPERTY FRAMEWORK 1)
|
|
||||||
# set_property(TARGET SimGearScene PROPERTY PUBLIC_HEADER "${publicHeaders}")
|
|
||||||
set_property(TARGET SimGearScene PROPERTY LINKER_LANGUAGE CXX)
|
set_property(TARGET SimGearScene PROPERTY LINKER_LANGUAGE CXX)
|
||||||
set_property(TARGET SimGearScene PROPERTY VERSION ${SIMGEAR_VERSION})
|
set_property(TARGET SimGearScene PROPERTY VERSION ${SIMGEAR_VERSION})
|
||||||
set_property(TARGET SimGearScene PROPERTY SOVERSION ${SIMGEAR_SOVERSION})
|
set_property(TARGET SimGearScene PROPERTY SOVERSION ${SIMGEAR_SOVERSION})
|
||||||
|
|
||||||
target_link_libraries(SimGearScene
|
# EXPORT SimGearSceneConfig
|
||||||
SimGearCore
|
install(TARGETS SimGearScene LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} )
|
||||||
${ZLIB_LIBRARY}
|
# install(EXPORT SimGearSceneConfig DESTINATION share/SimGearScene)
|
||||||
${OPENSCENEGRAPH_LIBRARIES}
|
|
||||||
${OPENAL_LIBRARY}
|
|
||||||
${OPENGL_LIBRARY}
|
|
||||||
${JPEG_LIBRARY})
|
|
||||||
|
|
||||||
install(TARGETS SimGearScene LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
install(TARGETS SimGearCore LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
|
||||||
else()
|
else()
|
||||||
message(STATUS "Library building mode: STATIC LIBRARIES")
|
message(STATUS "Library building mode: STATIC LIBRARIES")
|
||||||
|
|
||||||
@@ -111,7 +89,7 @@ else()
|
|||||||
source_group("${name}\\Headers" FILES ${g2})
|
source_group("${name}\\Headers" FILES ${g2})
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
add_library(SimGearCore STATIC ${coreSources})
|
add_library(SimGearCore STATIC ${coreSources} ${localExpatSources})
|
||||||
install(TARGETS SimGearCore ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
install(TARGETS SimGearCore ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||||
|
|
||||||
if(NOT SIMGEAR_HEADLESS)
|
if(NOT SIMGEAR_HEADLESS)
|
||||||
@@ -138,6 +116,24 @@ else()
|
|||||||
endif(NOT SIMGEAR_HEADLESS)
|
endif(NOT SIMGEAR_HEADLESS)
|
||||||
endif(SIMGEAR_SHARED)
|
endif(SIMGEAR_SHARED)
|
||||||
|
|
||||||
|
target_link_libraries(SimGearCore
|
||||||
|
${ZLIB_LIBRARY}
|
||||||
|
${RT_LIBRARY}
|
||||||
|
${DL_LIBRARY}
|
||||||
|
${EXPAT_LIBRARIES}
|
||||||
|
${CMAKE_THREAD_LIBS_INIT}
|
||||||
|
${COCOA_LIBRARY})
|
||||||
|
|
||||||
|
if(NOT SIMGEAR_HEADLESS)
|
||||||
|
target_link_libraries(SimGearScene
|
||||||
|
SimGearCore
|
||||||
|
${ZLIB_LIBRARY}
|
||||||
|
${OPENSCENEGRAPH_LIBRARIES}
|
||||||
|
${OPENAL_LIBRARY}
|
||||||
|
${OPENGL_LIBRARY}
|
||||||
|
${JPEG_LIBRARY})
|
||||||
|
endif()
|
||||||
|
|
||||||
if(ENABLE_RTI)
|
if(ENABLE_RTI)
|
||||||
# Ugly first aid to make hla compile agian
|
# Ugly first aid to make hla compile agian
|
||||||
set_property(SOURCE hla/RTI13InteractionClass.cxx hla/RTI13ObjectClass.cxx
|
set_property(SOURCE hla/RTI13InteractionClass.cxx hla/RTI13ObjectClass.cxx
|
||||||
|
|||||||
@@ -4,4 +4,13 @@ include (SimGearComponent)
|
|||||||
set(HEADERS newbucket.hxx)
|
set(HEADERS newbucket.hxx)
|
||||||
set(SOURCES newbucket.cxx)
|
set(SOURCES newbucket.cxx)
|
||||||
|
|
||||||
simgear_component(bucket bucket "${SOURCES}" "${HEADERS}")
|
simgear_component(bucket bucket "${SOURCES}" "${HEADERS}")
|
||||||
|
|
||||||
|
|
||||||
|
if(ENABLE_TESTS)
|
||||||
|
|
||||||
|
add_executable(test_bucket test_bucket.cxx)
|
||||||
|
add_test(test_bucket ${EXECUTABLE_OUTPUT_PATH}/test_bucket)
|
||||||
|
target_link_libraries(test_bucket ${TEST_LIBS})
|
||||||
|
|
||||||
|
endif(ENABLE_TESTS)
|
||||||
+171
-101
@@ -27,36 +27,56 @@
|
|||||||
# include <simgear_config.h>
|
# include <simgear_config.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <math.h>
|
#include <cmath>
|
||||||
|
#include <cstdio> // some platforms need this for ::snprintf
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include <simgear/misc/sg_path.hxx>
|
#include <simgear/misc/sg_path.hxx>
|
||||||
|
#include <simgear/debug/logstream.hxx>
|
||||||
|
|
||||||
#include "newbucket.hxx"
|
#include "newbucket.hxx"
|
||||||
|
|
||||||
|
|
||||||
// default constructor
|
// default constructor
|
||||||
SGBucket::SGBucket() {
|
SGBucket::SGBucket() :
|
||||||
|
lon(-1000),
|
||||||
|
lat(-1000),
|
||||||
|
x(0),
|
||||||
|
y(0)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SGBucket::isValid() const
|
||||||
|
{
|
||||||
|
// The most northerly valid latitude is 89, not 90. There is no tile
|
||||||
|
// whose *bottom* latitude is 90. Similar there is no tile whose left egde
|
||||||
|
// is 180 longitude.
|
||||||
|
return (lon >= -180) &&
|
||||||
|
(lon < 180) &&
|
||||||
|
(lat >= -90) &&
|
||||||
|
(lat < 90) &&
|
||||||
|
(x < 8) && (y < 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SGBucket::make_bad()
|
||||||
|
{
|
||||||
|
lon = -1000;
|
||||||
|
lat = -1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef NO_DEPRECATED_API
|
||||||
|
|
||||||
// constructor for specified location
|
// constructor for specified location
|
||||||
SGBucket::SGBucket(const double dlon, const double dlat) {
|
SGBucket::SGBucket(const double dlon, const double dlat) {
|
||||||
set_bucket(dlon, dlat);
|
set_bucket(dlon, dlat);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
SGBucket::SGBucket(const SGGeod& geod) {
|
SGBucket::SGBucket(const SGGeod& geod) {
|
||||||
set_bucket(geod);
|
innerSet(geod.getLongitudeDeg(),
|
||||||
|
geod.getLatitudeDeg());
|
||||||
}
|
}
|
||||||
|
|
||||||
// create an impossible bucket if false
|
|
||||||
SGBucket::SGBucket(const bool is_good) {
|
|
||||||
set_bucket(0.0, 0.0);
|
|
||||||
if ( !is_good ) {
|
|
||||||
lon = -1000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Parse a unique scenery tile index and find the lon, lat, x, and y
|
// Parse a unique scenery tile index and find the lon, lat, x, and y
|
||||||
SGBucket::SGBucket(const long int bindex) {
|
SGBucket::SGBucket(const long int bindex) {
|
||||||
long int index = bindex;
|
long int index = bindex;
|
||||||
@@ -75,48 +95,59 @@ SGBucket::SGBucket(const long int bindex) {
|
|||||||
x = index;
|
x = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Calculate the greatest integral value less than
|
||||||
|
* or equal to the given value (floor(x)),
|
||||||
|
* but attribute coordinates close to the boundary to the next
|
||||||
|
* (increasing) integral
|
||||||
|
*/
|
||||||
|
static int floorWithEpsilon(double x)
|
||||||
|
{
|
||||||
|
return static_cast<int>(floor(x + SG_EPSILON));
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef NO_DEPRECATED_API
|
||||||
|
|
||||||
|
void SGBucket::set_bucket(double dlon, double dlat)
|
||||||
|
{
|
||||||
|
innerSet(dlon, dlat);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SGBucket::set_bucket(const SGGeod& geod)
|
||||||
|
{
|
||||||
|
innerSet(geod.getLongitudeDeg(), geod.getLatitudeDeg());
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
// Set the bucket params for the specified lat and lon
|
// Set the bucket params for the specified lat and lon
|
||||||
void SGBucket::set_bucket( double *lonlat ) {
|
void SGBucket::innerSet( double dlon, double dlat )
|
||||||
set_bucket( lonlat[0], lonlat[1] );
|
{
|
||||||
}
|
if ((dlon < -180.0) || (dlon >= 180.0)) {
|
||||||
|
SG_LOG(SG_TERRAIN, SG_WARN, "SGBucket::set_bucket: passed longitude:" << dlon);
|
||||||
|
dlon = SGMiscd::normalizePeriodic(-180.0, 180.0, dlon);
|
||||||
// Set the bucket params for the specified lat and lon
|
}
|
||||||
void SGBucket::set_bucket( double dlon, double dlat ) {
|
|
||||||
|
if ((dlat < -90.0) || (dlat > 90.0)) {
|
||||||
|
SG_LOG(SG_TERRAIN, SG_WARN, "SGBucket::set_bucket: passed latitude" << dlat);
|
||||||
|
dlat = SGMiscd::clip(dlat, -90.0, 90.0);
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// latitude first
|
// longitude first
|
||||||
//
|
//
|
||||||
double span = sg_bucket_span( dlat );
|
double span = sg_bucket_span( dlat );
|
||||||
double diff = dlon - (double)(int)dlon;
|
// we do NOT need to special case lon=180 here, since
|
||||||
|
// normalizePeriodic will never return 180; it will
|
||||||
// cout << "diff = " << diff << " span = " << span << endl;
|
// return -180, which is what we want.
|
||||||
|
lon = floorWithEpsilon(dlon);
|
||||||
/* Calculate the greatest integral longitude less than
|
|
||||||
* or equal to the given longitude (floor(dlon)),
|
|
||||||
* but attribute coordinates near the east border
|
|
||||||
* to the next tile.
|
|
||||||
*/
|
|
||||||
if ( (dlon >= 0) || (fabs(diff) < SG_EPSILON) ) {
|
|
||||||
lon = (int)dlon;
|
|
||||||
} else {
|
|
||||||
lon = (int)dlon - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find subdivision or super lon if needed
|
// find subdivision or super lon if needed
|
||||||
if ( span < SG_EPSILON ) {
|
if ( span <= 1.0 ) {
|
||||||
/* sg_bucket_span() never returns 0.0
|
|
||||||
* or anything near it, so this really
|
|
||||||
* should not occur at any time.
|
|
||||||
*/
|
|
||||||
// polar cap
|
|
||||||
lon = 0;
|
|
||||||
x = 0;
|
|
||||||
} else if ( span <= 1.0 ) {
|
|
||||||
/* We have more than one tile per degree of
|
/* We have more than one tile per degree of
|
||||||
* longitude, so we need an x offset.
|
* longitude, so we need an x offset.
|
||||||
*/
|
*/
|
||||||
x = (int)((dlon - lon) / span);
|
x = floorWithEpsilon((dlon - lon) / span);
|
||||||
} else {
|
} else {
|
||||||
/* We have one or more degrees per tile,
|
/* We have one or more degrees per tile,
|
||||||
* so we need to find the base longitude
|
* so we need to find the base longitude
|
||||||
@@ -129,48 +160,28 @@ void SGBucket::set_bucket( double dlon, double dlat ) {
|
|||||||
*
|
*
|
||||||
* That way, the Greenwich Meridian is always
|
* That way, the Greenwich Meridian is always
|
||||||
* a tile border.
|
* a tile border.
|
||||||
*
|
|
||||||
* This gets us into trouble with the polar caps,
|
|
||||||
* which have width 360 and thus either span
|
|
||||||
* the range from 0 to 360 or from -360 to 0
|
|
||||||
* degrees, depending on whether lon is positive
|
|
||||||
* or negative!
|
|
||||||
*
|
|
||||||
* We also get into trouble with the 8 degree tiles
|
|
||||||
* north of 88N and south of 88S, because the west-
|
|
||||||
* and east-most tiles in that range will cover 184W
|
|
||||||
* to 176W and 176E to 184E respectively, with their
|
|
||||||
* center at 180E/W!
|
|
||||||
*/
|
*/
|
||||||
lon=(int)floor(floor((lon+SG_EPSILON)/span)*span);
|
lon=static_cast<int>(floor(lon / span) * span);
|
||||||
/* Correct the polar cap issue */
|
x = 0;
|
||||||
if ( lon < -180 ) {
|
|
||||||
lon = -180;
|
|
||||||
}
|
|
||||||
x = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// then latitude
|
// then latitude
|
||||||
//
|
//
|
||||||
diff = dlat - (double)(int)dlat;
|
lat = floorWithEpsilon(dlat);
|
||||||
|
|
||||||
/* Again, a modified floor() function (see longitude) */
|
// special case when passing in the north pole point (possibly due to
|
||||||
if ( (dlat >= 0) || (fabs(diff) < SG_EPSILON) ) {
|
// clipping latitude above). Ensures we generate a valid bucket in this
|
||||||
lat = (int)dlat;
|
// scenario
|
||||||
|
if (lat == 90) {
|
||||||
|
lat = 89;
|
||||||
|
y = 7;
|
||||||
} else {
|
} else {
|
||||||
lat = (int)dlat - 1;
|
/* Latitude base and offset are easier, as
|
||||||
|
* tiles always are 1/8 degree of latitude wide.
|
||||||
|
*/
|
||||||
|
y = floorWithEpsilon((dlat - lat) * 8);
|
||||||
}
|
}
|
||||||
/* Latitude base and offset are easier, as
|
|
||||||
* tiles always are 1/8 degree of latitude wide.
|
|
||||||
*/
|
|
||||||
y = (int)((dlat - lat) * 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SGBucket::set_bucket(const SGGeod& geod)
|
|
||||||
{
|
|
||||||
set_bucket(geod.getLongitudeDeg(), geod.getLatitudeDeg());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the path name for this bucket
|
// Build the path name for this bucket
|
||||||
@@ -212,7 +223,7 @@ std::string SGBucket::gen_base_path() const {
|
|||||||
main_lat *= -1;
|
main_lat *= -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
sprintf(raw_path, "%c%03d%c%02d/%c%03d%c%02d",
|
::snprintf(raw_path, 256, "%c%03d%c%02d/%c%03d%c%02d",
|
||||||
hem, top_lon, pole, top_lat,
|
hem, top_lon, pole, top_lat,
|
||||||
hem, main_lon, pole, main_lat);
|
hem, main_lon, pole, main_lat);
|
||||||
|
|
||||||
@@ -224,17 +235,6 @@ std::string SGBucket::gen_base_path() const {
|
|||||||
|
|
||||||
// return width of the tile in degrees
|
// return width of the tile in degrees
|
||||||
double SGBucket::get_width() const {
|
double SGBucket::get_width() const {
|
||||||
if (lon==-180 && (lat==-89 || lat==88) ) {
|
|
||||||
/* Normally the tile at 180W in 88N and 89S
|
|
||||||
* would cover 184W to 176W and the next
|
|
||||||
* on the east side starts at 176W.
|
|
||||||
* To correct, make this a special tile
|
|
||||||
* from 180W to 176W with 4 degrees width
|
|
||||||
* instead of the normal 8 degrees at
|
|
||||||
* that latitude.
|
|
||||||
*/
|
|
||||||
return 4.0;
|
|
||||||
}
|
|
||||||
return sg_bucket_span( get_center_lat() );
|
return sg_bucket_span( get_center_lat() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,17 +244,32 @@ double SGBucket::get_height() const {
|
|||||||
return SG_BUCKET_SPAN;
|
return SG_BUCKET_SPAN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double SGBucket::get_highest_lat() const
|
||||||
// return width of the tile in meters
|
{
|
||||||
double SGBucket::get_width_m() const {
|
unsigned char adjustedY = y;
|
||||||
double clat = (int)get_center_lat();
|
if (lat >= 0) {
|
||||||
if ( clat > 0 ) {
|
// tile is north of the equator, so we want the top edge. Add one
|
||||||
clat = (int)clat + 0.5;
|
// to y to achieve this.
|
||||||
} else {
|
++adjustedY;
|
||||||
clat = (int)clat - 0.5;
|
|
||||||
}
|
}
|
||||||
double clat_rad = clat * SGD_DEGREES_TO_RADIANS;
|
|
||||||
|
return lat + (adjustedY / 8.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// return width of the tile in meters. This function is used by the
|
||||||
|
// tile-manager to estimate how many tiles are in the view distance, so
|
||||||
|
// we care about the smallest width, which occurs at the highest latitude.
|
||||||
|
double SGBucket::get_width_m() const
|
||||||
|
{
|
||||||
|
double clat_rad = get_highest_lat() * SGD_DEGREES_TO_RADIANS;
|
||||||
double cos_lat = cos( clat_rad );
|
double cos_lat = cos( clat_rad );
|
||||||
|
if (fabs(cos_lat) < SG_EPSILON) {
|
||||||
|
// happens for polar tiles, since we pass in a latitude of 90
|
||||||
|
// return an arbitrary small value so all tiles are loaded
|
||||||
|
return 10.0;
|
||||||
|
}
|
||||||
|
|
||||||
double local_radius = cos_lat * SG_EQUATORIAL_RADIUS_M;
|
double local_radius = cos_lat * SG_EQUATORIAL_RADIUS_M;
|
||||||
double local_perimeter = local_radius * SGD_2PI;
|
double local_perimeter = local_radius * SGD_2PI;
|
||||||
double degree_width = local_perimeter / 360.0;
|
double degree_width = local_perimeter / 360.0;
|
||||||
@@ -271,7 +286,41 @@ double SGBucket::get_height_m() const {
|
|||||||
return SG_BUCKET_SPAN * degree_height;
|
return SG_BUCKET_SPAN * degree_height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SGBucket SGBucket::sibling(int dx, int dy) const
|
||||||
|
{
|
||||||
|
if (!isValid()) {
|
||||||
|
SG_LOG(SG_TERRAIN, SG_WARN, "SGBucket::sibling: requesting sibling of invalid bucket");
|
||||||
|
return SGBucket();
|
||||||
|
}
|
||||||
|
|
||||||
|
double clat = get_center_lat() + dy * SG_BUCKET_SPAN;
|
||||||
|
// return invalid here instead of clipping, so callers can discard
|
||||||
|
// invalid buckets without having to check if it's an existing one
|
||||||
|
if ((clat < -90.0) || (clat > 90.0)) {
|
||||||
|
return SGBucket();
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the lon span for the new latitude
|
||||||
|
double span = sg_bucket_span( clat );
|
||||||
|
|
||||||
|
double tmp = get_center_lon() + dx * span;
|
||||||
|
tmp = SGMiscd::normalizePeriodic(-180.0, 180.0, tmp);
|
||||||
|
|
||||||
|
SGBucket b;
|
||||||
|
b.innerSet(tmp, clat);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SGBucket::gen_index_str() const
|
||||||
|
{
|
||||||
|
char tmp[20];
|
||||||
|
::snprintf(tmp, 20, "%ld",
|
||||||
|
(((long)lon + 180) << 14) + ((lat + 90) << 6)
|
||||||
|
+ (y << 3) + x);
|
||||||
|
return (std::string)tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef NO_DEPRECATED_API
|
||||||
// find the bucket which is offset by the specified tile units in the
|
// find the bucket which is offset by the specified tile units in the
|
||||||
// X & Y direction. We need the current lon and lat to resolve
|
// X & Y direction. We need the current lon and lat to resolve
|
||||||
// ambiguities when going from a wider tile to a narrower one above or
|
// ambiguities when going from a wider tile to a narrower one above or
|
||||||
@@ -298,7 +347,7 @@ SGBucket sgBucketOffset( double dlon, double dlat, int dx, int dy ) {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// calculate the offset between two buckets
|
// calculate the offset between two buckets
|
||||||
void sgBucketDiff( const SGBucket& b1, const SGBucket& b2, int *dx, int *dy ) {
|
void sgBucketDiff( const SGBucket& b1, const SGBucket& b2, int *dx, int *dy ) {
|
||||||
@@ -356,4 +405,25 @@ void sgBucketDiff( const SGBucket& b1, const SGBucket& b2, int *dx, int *dy ) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sgGetBuckets( const SGGeod& min, const SGGeod& max, std::vector<SGBucket>& list ) {
|
||||||
|
double lon, lat, span;
|
||||||
|
|
||||||
|
for (lat = min.getLatitudeDeg(); lat < max.getLatitudeDeg()+SG_BUCKET_SPAN; lat += SG_BUCKET_SPAN) {
|
||||||
|
span = sg_bucket_span( lat );
|
||||||
|
for (lon = min.getLongitudeDeg(); lon <= max.getLongitudeDeg(); lon += span)
|
||||||
|
{
|
||||||
|
SGBucket b(SGGeod::fromDeg(lon, lat));
|
||||||
|
if (!b.isValid()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.push_back(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<< ( std::ostream& out, const SGBucket& b )
|
||||||
|
{
|
||||||
|
return out << b.lon << ":" << (int)b.x << ", " << b.lat << ":" << (int)b.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,9 +39,11 @@
|
|||||||
#include <simgear/math/SGMath.hxx>
|
#include <simgear/math/SGMath.hxx>
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdio> // sprintf()
|
|
||||||
#include <ostream>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <iosfwd>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// #define NO_DEPRECATED_API
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* standard size of a bucket in degrees (1/8 of a degree)
|
* standard size of a bucket in degrees (1/8 of a degree)
|
||||||
@@ -57,9 +59,7 @@
|
|||||||
// return the horizontal tile span factor based on latitude
|
// return the horizontal tile span factor based on latitude
|
||||||
static double sg_bucket_span( double l ) {
|
static double sg_bucket_span( double l ) {
|
||||||
if ( l >= 89.0 ) {
|
if ( l >= 89.0 ) {
|
||||||
return 360.0;
|
return 12.0;
|
||||||
} else if ( l >= 88.0 ) {
|
|
||||||
return 8.0;
|
|
||||||
} else if ( l >= 86.0 ) {
|
} else if ( l >= 86.0 ) {
|
||||||
return 4.0;
|
return 4.0;
|
||||||
} else if ( l >= 83.0 ) {
|
} else if ( l >= 83.0 ) {
|
||||||
@@ -80,12 +80,10 @@ static double sg_bucket_span( double l ) {
|
|||||||
return 1.0;
|
return 1.0;
|
||||||
} else if ( l >= -86.0 ) {
|
} else if ( l >= -86.0 ) {
|
||||||
return 2.0;
|
return 2.0;
|
||||||
} else if ( l >= -88.0 ) {
|
|
||||||
return 4.0;
|
|
||||||
} else if ( l >= -89.0 ) {
|
} else if ( l >= -89.0 ) {
|
||||||
return 8.0;
|
return 4.0;
|
||||||
} else {
|
} else {
|
||||||
return 360.0;
|
return 12.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,75 +100,69 @@ class SGBucket {
|
|||||||
private:
|
private:
|
||||||
short lon; // longitude index (-180 to 179)
|
short lon; // longitude index (-180 to 179)
|
||||||
short lat; // latitude index (-90 to 89)
|
short lat; // latitude index (-90 to 89)
|
||||||
char x; // x subdivision (0 to 7)
|
unsigned char x; // x subdivision (0 to 7)
|
||||||
char y; // y subdivision (0 to 7)
|
unsigned char y; // y subdivision (0 to 7)
|
||||||
|
|
||||||
|
void innerSet( double dlon, double dlat );
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default constructor.
|
* Default constructor, creates an invalid SGBucket
|
||||||
*/
|
*/
|
||||||
SGBucket();
|
SGBucket();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this bucket refers to a valid tile, or not.
|
||||||
|
*/
|
||||||
|
bool isValid() const;
|
||||||
|
|
||||||
|
#ifndef NO_DEPRECATED_API
|
||||||
/**
|
/**
|
||||||
* Construct a bucket given a specific location.
|
* Construct a bucket given a specific location.
|
||||||
* @param dlon longitude specified in degrees
|
* @param dlon longitude specified in degrees
|
||||||
* @param dlat latitude specified in degrees
|
* @param dlat latitude specified in degrees
|
||||||
*/
|
*/
|
||||||
SGBucket(const double dlon, const double dlat);
|
SGBucket(const double dlon, const double dlat);
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a bucket given a specific location.
|
* Construct a bucket given a specific location.
|
||||||
* @param dlon longitude specified in degrees
|
*
|
||||||
* @param dlat latitude specified in degrees
|
* @param geod Geodetic location
|
||||||
*/
|
*/
|
||||||
SGBucket(const SGGeod& geod);
|
SGBucket(const SGGeod& geod);
|
||||||
|
|
||||||
/** Construct a bucket.
|
|
||||||
* @param is_good if false, create an invalid bucket. This is
|
|
||||||
* useful * if you are comparing cur_bucket to last_bucket and
|
|
||||||
* you want to * make sure last_bucket starts out as something
|
|
||||||
* impossible.
|
|
||||||
*/
|
|
||||||
SGBucket(const bool is_good);
|
|
||||||
|
|
||||||
/** Construct a bucket given a unique bucket index number.
|
/** Construct a bucket given a unique bucket index number.
|
||||||
|
*
|
||||||
* @param bindex unique bucket index
|
* @param bindex unique bucket index
|
||||||
*/
|
*/
|
||||||
SGBucket(const long int bindex);
|
SGBucket(const long int bindex);
|
||||||
|
|
||||||
|
#ifndef NO_DEPRECATED_API
|
||||||
|
/**
|
||||||
|
* Reset a bucket to represent a new location.
|
||||||
|
*
|
||||||
|
* @param geod New geodetic location
|
||||||
|
*/
|
||||||
|
void set_bucket(const SGGeod& geod);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset a bucket to represent a new lat and lon
|
* Reset a bucket to represent a new lat and lon
|
||||||
* @param dlon longitude specified in degrees
|
* @param dlon longitude specified in degrees
|
||||||
* @param dlat latitude specified in degrees
|
* @param dlat latitude specified in degrees
|
||||||
*/
|
*/
|
||||||
void set_bucket( double dlon, double dlat );
|
void set_bucket( double dlon, double dlat );
|
||||||
|
#endif
|
||||||
/**
|
|
||||||
* Reset a bucket to represent a new lat and lon
|
|
||||||
* @param lonlat an array of double[2] holding lon and lat
|
|
||||||
* (specified) in degrees
|
|
||||||
*/
|
|
||||||
void set_bucket( double *lonlat );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset a bucket to represent a new lat and lon
|
|
||||||
* @param dlon longitude specified in degrees
|
|
||||||
* @param dlat latitude specified in degrees
|
|
||||||
*/
|
|
||||||
void set_bucket(const SGGeod& geod);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an impossible bucket.
|
* Create an impossible bucket.
|
||||||
* This is useful if you are comparing cur_bucket to last_bucket
|
* This is useful if you are comparing cur_bucket to last_bucket
|
||||||
* and you want to make sure last_bucket starts out as something
|
* and you want to make sure last_bucket starts out as something
|
||||||
* impossible.
|
* impossible.
|
||||||
*/
|
*/
|
||||||
inline void make_bad() {
|
void make_bad();
|
||||||
set_bucket(0.0, 0.0);
|
|
||||||
lon = -1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate the unique scenery tile index for this bucket
|
* Generate the unique scenery tile index for this bucket
|
||||||
*
|
*
|
||||||
@@ -195,14 +187,8 @@ public:
|
|||||||
* string form.
|
* string form.
|
||||||
* @return tile index in string form
|
* @return tile index in string form
|
||||||
*/
|
*/
|
||||||
inline std::string gen_index_str() const {
|
std::string gen_index_str() const;
|
||||||
char tmp[20];
|
|
||||||
std::sprintf(tmp, "%ld",
|
|
||||||
(((long)lon + 180) << 14) + ((lat + 90) << 6)
|
|
||||||
+ (y << 3) + x);
|
|
||||||
return (std::string)tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build the base path name for this bucket.
|
* Build the base path name for this bucket.
|
||||||
* @return base path in string form
|
* @return base path in string form
|
||||||
@@ -229,6 +215,13 @@ public:
|
|||||||
return lat + y / 8.0 + SG_HALF_BUCKET_SPAN;
|
return lat + y / 8.0 + SG_HALF_BUCKET_SPAN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the highest (furthest from the equator) latitude of this
|
||||||
|
* tile. This is the top edge for tiles north of the equator, and
|
||||||
|
* the bottom edge for tiles south
|
||||||
|
*/
|
||||||
|
double get_highest_lat() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the width of the tile in degrees.
|
* @return the width of the tile in degrees.
|
||||||
*/
|
*/
|
||||||
@@ -290,6 +283,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
inline int get_y() const { return y; }
|
inline int get_y() const { return y; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bucket offset from this by dx,dy
|
||||||
|
*/
|
||||||
|
SGBucket sibling(int dx, int dy) const;
|
||||||
|
|
||||||
// friends
|
// friends
|
||||||
|
|
||||||
friend std::ostream& operator<< ( std::ostream&, const SGBucket& );
|
friend std::ostream& operator<< ( std::ostream&, const SGBucket& );
|
||||||
@@ -301,7 +299,7 @@ inline bool operator!= (const SGBucket& lhs, const SGBucket& rhs)
|
|||||||
return !(lhs == rhs);
|
return !(lhs == rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef NO_DEPRECATED_API
|
||||||
/**
|
/**
|
||||||
* \relates SGBucket
|
* \relates SGBucket
|
||||||
* Return the bucket which is offset from the specified dlon, dlat by
|
* Return the bucket which is offset from the specified dlon, dlat by
|
||||||
@@ -313,6 +311,7 @@ inline bool operator!= (const SGBucket& lhs, const SGBucket& rhs)
|
|||||||
* @return offset bucket
|
* @return offset bucket
|
||||||
*/
|
*/
|
||||||
SGBucket sgBucketOffset( double dlon, double dlat, int x, int y );
|
SGBucket sgBucketOffset( double dlon, double dlat, int x, int y );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -326,17 +325,21 @@ SGBucket sgBucketOffset( double dlon, double dlat, int x, int y );
|
|||||||
void sgBucketDiff( const SGBucket& b1, const SGBucket& b2, int *dx, int *dy );
|
void sgBucketDiff( const SGBucket& b1, const SGBucket& b2, int *dx, int *dy );
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \relates SGBucket
|
||||||
|
* retrieve a list of buckets in the given bounding box
|
||||||
|
* @param min min lon,lat of bounding box in degrees
|
||||||
|
* @param max max lon,lat of bounding box in degrees
|
||||||
|
* @param list standard vector of buckets within the bounding box
|
||||||
|
*/
|
||||||
|
void sgGetBuckets( const SGGeod& min, const SGGeod& max, std::vector<SGBucket>& list );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write the bucket lon, lat, x, and y to the output stream.
|
* Write the bucket lon, lat, x, and y to the output stream.
|
||||||
* @param out output stream
|
* @param out output stream
|
||||||
* @param b bucket
|
* @param b bucket
|
||||||
*/
|
*/
|
||||||
inline std::ostream&
|
std::ostream& operator<< ( std::ostream& out, const SGBucket& b );
|
||||||
operator<< ( std::ostream& out, const SGBucket& b )
|
|
||||||
{
|
|
||||||
return out << b.lon << ":" << (int)b.x << ", " << b.lat << ":" << (int)b.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compare two bucket structures for equality.
|
* Compare two bucket structures for equality.
|
||||||
|
|||||||
@@ -0,0 +1,283 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
* test_bucket.cxx -- unit-tests for SGBucket class
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 James Turner - <zakalawe@mac.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 <simgear/compiler.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
using std::cout;
|
||||||
|
using std::cerr;
|
||||||
|
using std::endl;
|
||||||
|
|
||||||
|
#include <simgear/bucket/newbucket.hxx>
|
||||||
|
#include <simgear/misc/test_macros.hxx>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
SGBucket defBuck;
|
||||||
|
VERIFY(!defBuck.isValid());
|
||||||
|
|
||||||
|
b3.make_bad();
|
||||||
|
VERIFY(!b3.isValid());
|
||||||
|
|
||||||
|
SGBucket atAntiMeridian(180.0, 12.3);
|
||||||
|
VERIFY(atAntiMeridian.isValid());
|
||||||
|
COMPARE(atAntiMeridian.get_chunk_lon(), -180);
|
||||||
|
COMPARE(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
|
||||||
|
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
|
||||||
|
SGBucket wrapMeridian(-200.0, 45.0);
|
||||||
|
COMPARE(wrapMeridian.get_chunk_lon(), 160);
|
||||||
|
|
||||||
|
SGBucket clipPole(48.9, 91);
|
||||||
|
COMPARE(clipPole.get_chunk_lat(), 89);
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
SGBucket b3(-2, 89.88);
|
||||||
|
SGBucket b4(-7, 89.88);
|
||||||
|
COMPARE(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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// test the tiles just below the pole (between 86 & 89 degrees N/S)
|
||||||
|
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());
|
||||||
|
|
||||||
|
SGBucket b3(176.1, 88.5);
|
||||||
|
COMPARE(b3.get_chunk_lon(), 176);
|
||||||
|
|
||||||
|
SGBucket b4(-178, 88.5);
|
||||||
|
COMPARE(b4.get_chunk_lon(), -180);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
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));
|
||||||
|
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
// offset vertically towards the pole
|
||||||
|
SGBucket b7(b1.sibling(0, -5));
|
||||||
|
VERIFY(!b7.isValid());
|
||||||
|
|
||||||
|
VERIFY(!SGBucket(0, 90).sibling(0, 1).isValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
// test behaviour of bucket-offset near the anti-meridian (180-meridian)
|
||||||
|
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);
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
testBucketSpans();
|
||||||
|
|
||||||
|
testBasic();
|
||||||
|
testPolar();
|
||||||
|
testNearPolar();
|
||||||
|
testOffset();
|
||||||
|
testOffsetWrap();
|
||||||
|
testPolarOffset();
|
||||||
|
|
||||||
|
cout << "all tests passed OK" << endl;
|
||||||
|
return 0; // passed
|
||||||
|
}
|
||||||
|
|
||||||
@@ -4,14 +4,14 @@ set(HEADERS
|
|||||||
canvas_fwd.hxx
|
canvas_fwd.hxx
|
||||||
Canvas.hxx
|
Canvas.hxx
|
||||||
CanvasEvent.hxx
|
CanvasEvent.hxx
|
||||||
CanvasEventListener.hxx
|
|
||||||
CanvasEventManager.hxx
|
CanvasEventManager.hxx
|
||||||
CanvasEventTypes.hxx
|
CanvasEventTypes.hxx
|
||||||
CanvasEventVisitor.hxx
|
CanvasEventVisitor.hxx
|
||||||
CanvasMgr.hxx
|
CanvasMgr.hxx
|
||||||
|
CanvasObjectPlacement.hxx
|
||||||
CanvasPlacement.hxx
|
CanvasPlacement.hxx
|
||||||
CanvasSystemAdapter.hxx
|
CanvasSystemAdapter.hxx
|
||||||
MouseEvent.hxx
|
CanvasWindow.hxx
|
||||||
ODGauge.hxx
|
ODGauge.hxx
|
||||||
VGInitOperation.hxx
|
VGInitOperation.hxx
|
||||||
)
|
)
|
||||||
@@ -19,16 +19,19 @@ set(HEADERS
|
|||||||
set(SOURCES
|
set(SOURCES
|
||||||
Canvas.cxx
|
Canvas.cxx
|
||||||
CanvasEvent.cxx
|
CanvasEvent.cxx
|
||||||
CanvasEventListener.cxx
|
|
||||||
CanvasEventManager.cxx
|
CanvasEventManager.cxx
|
||||||
CanvasEventVisitor.cxx
|
CanvasEventVisitor.cxx
|
||||||
CanvasMgr.cxx
|
CanvasMgr.cxx
|
||||||
|
CanvasObjectPlacement.cxx
|
||||||
CanvasPlacement.cxx
|
CanvasPlacement.cxx
|
||||||
|
CanvasWindow.cxx
|
||||||
ODGauge.cxx
|
ODGauge.cxx
|
||||||
VGInitOperation.cxx
|
VGInitOperation.cxx
|
||||||
)
|
)
|
||||||
|
|
||||||
add_subdirectory(ShivaVG/src)
|
add_subdirectory(ShivaVG/src)
|
||||||
add_subdirectory(elements)
|
add_subdirectory(elements)
|
||||||
|
add_subdirectory(events)
|
||||||
|
add_subdirectory(layout)
|
||||||
|
|
||||||
simgear_scene_component(canvas canvas "${SOURCES}" "${HEADERS}")
|
simgear_scene_component(canvas canvas "${SOURCES}" "${HEADERS}")
|
||||||
|
|||||||
+249
-83
@@ -19,8 +19,9 @@
|
|||||||
#include "Canvas.hxx"
|
#include "Canvas.hxx"
|
||||||
#include "CanvasEventManager.hxx"
|
#include "CanvasEventManager.hxx"
|
||||||
#include "CanvasEventVisitor.hxx"
|
#include "CanvasEventVisitor.hxx"
|
||||||
#include <simgear/canvas/MouseEvent.hxx>
|
#include "CanvasPlacement.hxx"
|
||||||
#include <simgear/canvas/CanvasPlacement.hxx>
|
#include <simgear/canvas/events/KeyboardEvent.hxx>
|
||||||
|
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||||
#include <simgear/scene/util/parse_color.hxx>
|
#include <simgear/scene/util/parse_color.hxx>
|
||||||
#include <simgear/scene/util/RenderConstants.hxx>
|
#include <simgear/scene/util/RenderConstants.hxx>
|
||||||
|
|
||||||
@@ -31,7 +32,6 @@
|
|||||||
|
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
namespace simgear
|
namespace simgear
|
||||||
{
|
{
|
||||||
@@ -49,8 +49,12 @@ namespace canvas
|
|||||||
void Canvas::CullCallback::operator()( osg::Node* node,
|
void Canvas::CullCallback::operator()( osg::Node* node,
|
||||||
osg::NodeVisitor* nv )
|
osg::NodeVisitor* nv )
|
||||||
{
|
{
|
||||||
if( (nv->getTraversalMask() & simgear::MODEL_BIT) && !_canvas.expired() )
|
if( (nv->getTraversalMask() & simgear::MODEL_BIT) )
|
||||||
_canvas.lock()->enableRendering();
|
{
|
||||||
|
CanvasPtr canvas = _canvas.lock();
|
||||||
|
if( canvas )
|
||||||
|
canvas->enableRendering();
|
||||||
|
}
|
||||||
|
|
||||||
traverse(node, nv);
|
traverse(node, nv);
|
||||||
}
|
}
|
||||||
@@ -73,6 +77,13 @@ namespace canvas
|
|||||||
{
|
{
|
||||||
_status = 0;
|
_status = 0;
|
||||||
setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
|
setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
|
||||||
|
|
||||||
|
_root_group.reset( new Group(this, _node) );
|
||||||
|
|
||||||
|
// Remove automatically created property listener as we forward them on our
|
||||||
|
// own
|
||||||
|
_root_group->removeListener();
|
||||||
|
_cull_callback = new CullCallback(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -82,16 +93,13 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Canvas::setSystemAdapter(const SystemAdapterPtr& system_adapter)
|
void Canvas::onDestroy()
|
||||||
{
|
{
|
||||||
_system_adapter = system_adapter;
|
if( _root_group )
|
||||||
_texture.setSystemAdapter(system_adapter);
|
{
|
||||||
}
|
_root_group->clearEventListener();
|
||||||
|
_root_group->onDestroy();
|
||||||
//----------------------------------------------------------------------------
|
}
|
||||||
SystemAdapterPtr Canvas::getSystemAdapter() const
|
|
||||||
{
|
|
||||||
return _system_adapter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -107,7 +115,13 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Canvas::addDependentCanvas(const CanvasWeakPtr& canvas)
|
bool Canvas::isInit() const
|
||||||
|
{
|
||||||
|
return _texture.serviceable();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Canvas::addParentCanvas(const CanvasWeakPtr& canvas)
|
||||||
{
|
{
|
||||||
if( canvas.expired() )
|
if( canvas.expired() )
|
||||||
{
|
{
|
||||||
@@ -115,28 +129,93 @@ namespace canvas
|
|||||||
(
|
(
|
||||||
SG_GENERAL,
|
SG_GENERAL,
|
||||||
SG_WARN,
|
SG_WARN,
|
||||||
"Canvas::addDependentCanvas: got an expired Canvas dependent on "
|
"Canvas::addParentCanvas(" << _node->getPath(true) << "): "
|
||||||
<< _node->getPath()
|
"got an expired parent!"
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_dependent_canvases.insert(canvas);
|
_parent_canvases.insert(canvas);
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Canvas::removeDependentCanvas(const CanvasWeakPtr& canvas)
|
void Canvas::addChildCanvas(const CanvasWeakPtr& canvas)
|
||||||
{
|
{
|
||||||
_dependent_canvases.erase(canvas);
|
if( canvas.expired() )
|
||||||
|
{
|
||||||
|
SG_LOG
|
||||||
|
(
|
||||||
|
SG_GENERAL,
|
||||||
|
SG_WARN,
|
||||||
|
"Canvas::addChildCanvas(" << _node->getPath(true) << "): "
|
||||||
|
" got an expired child!"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_child_canvases.insert(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Canvas::removeParentCanvas(const CanvasWeakPtr& canvas)
|
||||||
|
{
|
||||||
|
_parent_canvases.erase(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Canvas::removeChildCanvas(const CanvasWeakPtr& canvas)
|
||||||
|
{
|
||||||
|
_child_canvases.erase(canvas);
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
GroupPtr Canvas::createGroup(const std::string& name)
|
GroupPtr Canvas::createGroup(const std::string& name)
|
||||||
{
|
{
|
||||||
return boost::dynamic_pointer_cast<Group>
|
return _root_group->createChild<Group>(name);
|
||||||
(
|
}
|
||||||
_root_group->createChild("group", name)
|
|
||||||
);
|
//----------------------------------------------------------------------------
|
||||||
|
GroupPtr Canvas::getGroup(const std::string& name)
|
||||||
|
{
|
||||||
|
return _root_group->getChild<Group>(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
GroupPtr Canvas::getOrCreateGroup(const std::string& name)
|
||||||
|
{
|
||||||
|
return _root_group->getOrCreateChild<Group>(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
GroupPtr Canvas::getRootGroup()
|
||||||
|
{
|
||||||
|
return _root_group;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Canvas::setLayout(const LayoutRef& layout)
|
||||||
|
{
|
||||||
|
_layout = layout;
|
||||||
|
_layout->setCanvas(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Canvas::setFocusElement(const ElementPtr& el)
|
||||||
|
{
|
||||||
|
if( el && el->getCanvas().lock() != this )
|
||||||
|
{
|
||||||
|
SG_LOG(SG_GUI, SG_WARN, "setFocusElement: element not from this canvas");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO focus out/in events
|
||||||
|
_focus_element = el;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Canvas::clearFocusElement()
|
||||||
|
{
|
||||||
|
_focus_element.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -150,11 +229,10 @@ namespace canvas
|
|||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Canvas::update(double delta_time_sec)
|
void Canvas::update(double delta_time_sec)
|
||||||
{
|
{
|
||||||
if( (!_texture.serviceable() && _status != STATUS_DIRTY)
|
if( _status & (CREATE_FAILED | MISSING_SIZE) )
|
||||||
|| (_status & CREATE_FAILED) )
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if( _status == STATUS_DIRTY )
|
if( _status & STATUS_DIRTY )
|
||||||
{
|
{
|
||||||
_texture.setSize(_size_x, _size_y);
|
_texture.setSize(_size_x, _size_y);
|
||||||
|
|
||||||
@@ -168,24 +246,21 @@ namespace canvas
|
|||||||
{
|
{
|
||||||
// Resizing causes a new texture to be created so we need to reapply all
|
// Resizing causes a new texture to be created so we need to reapply all
|
||||||
// existing placements
|
// existing placements
|
||||||
for(size_t i = 0; i < _placements.size(); ++i)
|
reloadPlacements();
|
||||||
{
|
|
||||||
if( !_placements[i].empty() )
|
|
||||||
_dirty_placements.push_back( _placements[i].front()->getProps() );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::Camera* camera = _texture.getCamera();
|
osg::Camera* camera = _texture.getCamera();
|
||||||
|
|
||||||
|
// TODO Allow custom render order? For now just keep in order with
|
||||||
|
// property tree.
|
||||||
|
camera->setRenderOrder(osg::Camera::PRE_RENDER, _node->getIndex());
|
||||||
|
|
||||||
osg::Vec4 clear_color(0.0f, 0.0f, 0.0f , 1.0f);
|
osg::Vec4 clear_color(0.0f, 0.0f, 0.0f , 1.0f);
|
||||||
parseColor(_node->getStringValue("background"), clear_color);
|
parseColor(_node->getStringValue("background"), clear_color);
|
||||||
camera->setClearColor(clear_color);
|
camera->setClearColor(clear_color);
|
||||||
|
|
||||||
camera->addChild(_root_group->getMatrixTransform());
|
camera->addChild(_root_group->getMatrixTransform());
|
||||||
|
|
||||||
// Ensure objects are drawn in order of traversal
|
|
||||||
camera->getOrCreateStateSet()->setBinName("TraversalOrderBin");
|
|
||||||
|
|
||||||
if( _texture.serviceable() )
|
if( _texture.serviceable() )
|
||||||
{
|
{
|
||||||
setStatusFlags(STATUS_OK);
|
setStatusFlags(STATUS_OK);
|
||||||
@@ -199,15 +274,28 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( _layout )
|
||||||
|
_layout->setGeometry(SGRecti(0, 0, _view_width, _view_height));
|
||||||
|
|
||||||
if( _visible || _render_always )
|
if( _visible || _render_always )
|
||||||
{
|
{
|
||||||
|
BOOST_FOREACH(CanvasWeakPtr canvas_weak, _child_canvases)
|
||||||
|
{
|
||||||
|
// TODO should we check if the image the child canvas is displayed
|
||||||
|
// within is really visible?
|
||||||
|
CanvasPtr canvas = canvas_weak.lock();
|
||||||
|
if( canvas )
|
||||||
|
canvas->_visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
if( _render_dirty )
|
if( _render_dirty )
|
||||||
{
|
{
|
||||||
// Also mark all dependent (eg. recursively used) canvases as dirty
|
// Also mark all canvases this canvas is displayed within as dirty
|
||||||
BOOST_FOREACH(CanvasWeakPtr canvas, _dependent_canvases)
|
BOOST_FOREACH(CanvasWeakPtr canvas_weak, _parent_canvases)
|
||||||
{
|
{
|
||||||
if( !canvas.expired() )
|
CanvasPtr canvas = canvas_weak.lock();
|
||||||
canvas.lock()->_render_dirty = true;
|
if( canvas )
|
||||||
|
canvas->_render_dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,11 +338,7 @@ namespace canvas
|
|||||||
if( placement_factory != _placement_factories.end() )
|
if( placement_factory != _placement_factories.end() )
|
||||||
{
|
{
|
||||||
Placements& placements = _placements[ node->getIndex() ] =
|
Placements& placements = _placements[ node->getIndex() ] =
|
||||||
placement_factory->second
|
placement_factory->second(node, this);
|
||||||
(
|
|
||||||
node,
|
|
||||||
boost::static_pointer_cast<Canvas>(_self.lock())
|
|
||||||
);
|
|
||||||
node->setStringValue
|
node->setStringValue
|
||||||
(
|
(
|
||||||
"status-msg",
|
"status-msg",
|
||||||
@@ -267,12 +351,22 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
naRef Canvas::addEventListener(const nasal::CallContext& ctx)
|
bool Canvas::addEventListener( const std::string& type,
|
||||||
|
const EventListener& cb )
|
||||||
{
|
{
|
||||||
if( !_root_group.get() )
|
if( !_root_group.get() )
|
||||||
naRuntimeError(ctx.c, "Canvas: No root group!");
|
throw std::runtime_error("Canvas::addEventListener: no root group!");
|
||||||
|
|
||||||
return _root_group->addEventListener(ctx);
|
return _root_group->addEventListener(type, cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
bool Canvas::dispatchEvent(const EventPtr& event)
|
||||||
|
{
|
||||||
|
if( !_root_group.get() )
|
||||||
|
throw std::runtime_error("Canvas::dispatchEvent: no root group!");
|
||||||
|
|
||||||
|
return _root_group->dispatchEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -344,30 +438,55 @@ namespace canvas
|
|||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
int Canvas::getViewWidth() const
|
int Canvas::getViewWidth() const
|
||||||
{
|
{
|
||||||
return _view_width;
|
return _texture.getViewSize().x();
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
int Canvas::getViewHeight() const
|
int Canvas::getViewHeight() const
|
||||||
{
|
{
|
||||||
return _view_height;
|
return _texture.getViewSize().y();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
SGRect<int> Canvas::getViewport() const
|
||||||
|
{
|
||||||
|
return SGRect<int>(0, 0, getViewWidth(), getViewHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
bool Canvas::handleMouseEvent(const MouseEventPtr& event)
|
bool Canvas::handleMouseEvent(const MouseEventPtr& event)
|
||||||
{
|
{
|
||||||
if( !_root_group.get() )
|
if( !_root_group )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
EventVisitor visitor( EventVisitor::TRAVERSE_DOWN,
|
EventVisitor visitor( EventVisitor::TRAVERSE_DOWN,
|
||||||
event->getClientPos(),
|
event->getClientPos(),
|
||||||
event->getDelta() );
|
_root_group );
|
||||||
if( !_root_group->accept(visitor) )
|
if( !_root_group->accept(visitor) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return _event_manager->handleEvent(event, visitor.getPropagationPath());
|
return _event_manager->handleEvent(event, visitor.getPropagationPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
bool Canvas::handleKeyboardEvent(const KeyboardEventPtr& event)
|
||||||
|
{
|
||||||
|
ElementPtr target = _focus_element.lock();
|
||||||
|
if( !target )
|
||||||
|
target = _root_group;
|
||||||
|
if( !target )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return target->dispatchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
bool Canvas::propagateEvent( EventPtr const& event,
|
||||||
|
EventPropagationPath const& path )
|
||||||
|
{
|
||||||
|
return _event_manager->propagateEvent(event, path);
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Canvas::childAdded( SGPropertyNode * parent,
|
void Canvas::childAdded( SGPropertyNode * parent,
|
||||||
SGPropertyNode * child )
|
SGPropertyNode * child )
|
||||||
@@ -399,8 +518,10 @@ namespace canvas
|
|||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Canvas::valueChanged(SGPropertyNode* node)
|
void Canvas::valueChanged(SGPropertyNode* node)
|
||||||
{
|
{
|
||||||
if( boost::starts_with(node->getNameString(), "status")
|
const std::string& name = node->getNameString();
|
||||||
|| node->getParent()->getNameString() == "bounding-box" )
|
|
||||||
|
if( boost::starts_with(name, "status")
|
||||||
|
|| boost::starts_with(name, "data-") )
|
||||||
return;
|
return;
|
||||||
_render_dirty = true;
|
_render_dirty = true;
|
||||||
|
|
||||||
@@ -408,21 +529,26 @@ namespace canvas
|
|||||||
if( node->getParent()->getParent() == _node
|
if( node->getParent()->getParent() == _node
|
||||||
&& node->getParent()->getNameString() == "placement" )
|
&& node->getParent()->getNameString() == "placement" )
|
||||||
{
|
{
|
||||||
bool placement_dirty = false;
|
size_t index = node->getIndex();
|
||||||
BOOST_FOREACH(Placements& placements, _placements)
|
if( index < _placements.size() )
|
||||||
{
|
{
|
||||||
BOOST_FOREACH(PlacementPtr& placement, placements)
|
Placements& placements = _placements[index];
|
||||||
|
if( !placements.empty() )
|
||||||
{
|
{
|
||||||
// check if change can be directly handled by placement
|
bool placement_dirty = false;
|
||||||
if( placement->getProps() == node->getParent()
|
BOOST_FOREACH(PlacementPtr& placement, placements)
|
||||||
&& !placement->childChanged(node) )
|
{
|
||||||
placement_dirty = true;
|
// check if change can be directly handled by placement
|
||||||
|
if( placement->getProps() == node->getParent()
|
||||||
|
&& !placement->childChanged(node) )
|
||||||
|
placement_dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !placement_dirty )
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !placement_dirty )
|
|
||||||
return;
|
|
||||||
|
|
||||||
// prevent double updates...
|
// prevent double updates...
|
||||||
for( size_t i = 0; i < _dirty_placements.size(); ++i )
|
for( size_t i = 0; i < _dirty_placements.size(); ++i )
|
||||||
{
|
{
|
||||||
@@ -434,7 +560,7 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
else if( node->getParent() == _node )
|
else if( node->getParent() == _node )
|
||||||
{
|
{
|
||||||
if( node->getNameString() == "background" )
|
if( name == "background" )
|
||||||
{
|
{
|
||||||
osg::Vec4 color;
|
osg::Vec4 color;
|
||||||
if( _texture.getCamera() && parseColor(node->getStringValue(), color) )
|
if( _texture.getCamera() && parseColor(node->getStringValue(), color) )
|
||||||
@@ -443,31 +569,41 @@ namespace canvas
|
|||||||
_render_dirty = true;
|
_render_dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if( node->getNameString() == "mipmapping"
|
else if( name == "mipmapping"
|
||||||
|| node->getNameString() == "coverage-samples"
|
|| name == "coverage-samples"
|
||||||
|| node->getNameString() == "color-samples" )
|
|| name == "color-samples" )
|
||||||
{
|
{
|
||||||
_sampling_dirty = true;
|
_sampling_dirty = true;
|
||||||
}
|
}
|
||||||
else if( node->getNameString() == "render-always" )
|
else if( name == "additive-blend" )
|
||||||
|
{
|
||||||
|
_texture.useAdditiveBlend( node->getBoolValue() );
|
||||||
|
}
|
||||||
|
else if( name == "render-always" )
|
||||||
{
|
{
|
||||||
_render_always = node->getBoolValue();
|
_render_always = node->getBoolValue();
|
||||||
}
|
}
|
||||||
else if( node->getNameString() == "size" )
|
else if( name == "size" )
|
||||||
{
|
{
|
||||||
if( node->getIndex() == 0 )
|
if( node->getIndex() == 0 )
|
||||||
setSizeX( node->getIntValue() );
|
setSizeX( node->getIntValue() );
|
||||||
else if( node->getIndex() == 1 )
|
else if( node->getIndex() == 1 )
|
||||||
setSizeY( node->getIntValue() );
|
setSizeY( node->getIntValue() );
|
||||||
}
|
}
|
||||||
else if( node->getNameString() == "view" )
|
else if( name == "update" )
|
||||||
|
{
|
||||||
|
if( _root_group )
|
||||||
|
_root_group->update(0);
|
||||||
|
return update(0);
|
||||||
|
}
|
||||||
|
else if( name == "view" )
|
||||||
{
|
{
|
||||||
if( node->getIndex() == 0 )
|
if( node->getIndex() == 0 )
|
||||||
setViewWidth( node->getIntValue() );
|
setViewWidth( node->getIntValue() );
|
||||||
else if( node->getIndex() == 1 )
|
else if( node->getIndex() == 1 )
|
||||||
setViewHeight( node->getIntValue() );
|
setViewHeight( node->getIntValue() );
|
||||||
}
|
}
|
||||||
else if( node->getNameString() == "freeze" )
|
else if( name == "freeze" )
|
||||||
_texture.setRender( node->getBoolValue() );
|
_texture.setRender( node->getBoolValue() );
|
||||||
else
|
else
|
||||||
handled = false;
|
handled = false;
|
||||||
@@ -491,6 +627,24 @@ namespace canvas
|
|||||||
return _cull_callback;
|
return _cull_callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Canvas::reloadPlacements(const std::string& type)
|
||||||
|
{
|
||||||
|
for(size_t i = 0; i < _placements.size(); ++i)
|
||||||
|
{
|
||||||
|
if( _placements[i].empty() )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
SGPropertyNode* child = _placements[i].front()->getProps();
|
||||||
|
if( type.empty()
|
||||||
|
// reload if type matches or no type specified
|
||||||
|
|| child->getStringValue("type", type.c_str()) == type )
|
||||||
|
{
|
||||||
|
_dirty_placements.push_back(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Canvas::addPlacementFactory( const std::string& type,
|
void Canvas::addPlacementFactory( const std::string& type,
|
||||||
PlacementFactory factory )
|
PlacementFactory factory )
|
||||||
@@ -500,27 +654,38 @@ namespace canvas
|
|||||||
(
|
(
|
||||||
SG_GENERAL,
|
SG_GENERAL,
|
||||||
SG_WARN,
|
SG_WARN,
|
||||||
"Canvas::addPlacementFactory: replace existing factor for type " << type
|
"Canvas::addPlacementFactory: replace existing factory '" << type << "'"
|
||||||
);
|
);
|
||||||
|
|
||||||
_placement_factories[type] = factory;
|
_placement_factories[type] = factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Canvas::setSelf(const PropertyBasedElementPtr& self)
|
void Canvas::removePlacementFactory(const std::string& type)
|
||||||
{
|
{
|
||||||
PropertyBasedElement::setSelf(self);
|
PlacementFactoryMap::iterator it = _placement_factories.find(type);
|
||||||
|
if( it == _placement_factories.end() )
|
||||||
|
SG_LOG
|
||||||
|
(
|
||||||
|
SG_GENERAL,
|
||||||
|
SG_WARN,
|
||||||
|
"Canvas::removePlacementFactory: no such factory '" << type << "'"
|
||||||
|
);
|
||||||
|
else
|
||||||
|
_placement_factories.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
CanvasPtr canvas = boost::static_pointer_cast<Canvas>(self);
|
|
||||||
|
|
||||||
_root_group.reset( new Group(canvas, _node) );
|
//----------------------------------------------------------------------------
|
||||||
_root_group->setSelf(_root_group);
|
void Canvas::setSystemAdapter(const SystemAdapterPtr& system_adapter)
|
||||||
|
{
|
||||||
|
_system_adapter = system_adapter;
|
||||||
|
}
|
||||||
|
|
||||||
// Remove automatically created property listener as we forward them on our
|
//----------------------------------------------------------------------------
|
||||||
// own
|
SystemAdapterPtr Canvas::getSystemAdapter()
|
||||||
_root_group->removeListener();
|
{
|
||||||
|
return _system_adapter;
|
||||||
_cull_callback = new CullCallback(canvas);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -539,7 +704,7 @@ namespace canvas
|
|||||||
_status_msg = "Missing size-y";
|
_status_msg = "Missing size-y";
|
||||||
else if( _status & CREATE_FAILED )
|
else if( _status & CREATE_FAILED )
|
||||||
_status_msg = "Creating render target failed";
|
_status_msg = "Creating render target failed";
|
||||||
else if( _status == STATUS_DIRTY )
|
else if( _status & STATUS_DIRTY )
|
||||||
_status_msg = "Creation pending...";
|
_status_msg = "Creation pending...";
|
||||||
else
|
else
|
||||||
_status_msg = "Ok";
|
_status_msg = "Ok";
|
||||||
@@ -547,6 +712,7 @@ namespace canvas
|
|||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Canvas::PlacementFactoryMap Canvas::_placement_factories;
|
Canvas::PlacementFactoryMap Canvas::_placement_factories;
|
||||||
|
SystemAdapterPtr Canvas::_system_adapter;
|
||||||
|
|
||||||
} // namespace canvas
|
} // namespace canvas
|
||||||
} // namespace simgear
|
} // namespace simgear
|
||||||
|
|||||||
+99
-19
@@ -1,4 +1,5 @@
|
|||||||
// The canvas for rendering with the 2d API
|
///@file
|
||||||
|
/// The canvas for rendering with the 2d API
|
||||||
//
|
//
|
||||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||||
//
|
//
|
||||||
@@ -23,32 +24,42 @@
|
|||||||
#include "ODGauge.hxx"
|
#include "ODGauge.hxx"
|
||||||
|
|
||||||
#include <simgear/canvas/elements/CanvasGroup.hxx>
|
#include <simgear/canvas/elements/CanvasGroup.hxx>
|
||||||
|
#include <simgear/canvas/layout/Layout.hxx>
|
||||||
|
#include <simgear/math/SGRect.hxx>
|
||||||
|
#include <simgear/nasal/cppbind/NasalObject.hxx>
|
||||||
#include <simgear/props/PropertyBasedElement.hxx>
|
#include <simgear/props/PropertyBasedElement.hxx>
|
||||||
#include <simgear/props/propertyObject.hxx>
|
#include <simgear/props/propertyObject.hxx>
|
||||||
|
|
||||||
#include <osg/NodeCallback>
|
#include <osg/NodeCallback>
|
||||||
#include <osg/observer_ptr>
|
#include <osg/observer_ptr>
|
||||||
|
|
||||||
#include <memory>
|
#include <boost/scoped_ptr.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace simgear
|
namespace simgear
|
||||||
{
|
{
|
||||||
|
/// Canvas 2D drawing API
|
||||||
namespace canvas
|
namespace canvas
|
||||||
{
|
{
|
||||||
class CanvasMgr;
|
class CanvasMgr;
|
||||||
class MouseEvent;
|
class MouseEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Canvas to draw onto (to an off-screen render target).
|
||||||
|
*/
|
||||||
class Canvas:
|
class Canvas:
|
||||||
public PropertyBasedElement
|
public PropertyBasedElement,
|
||||||
|
public nasal::Object
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
enum StatusFlags
|
enum StatusFlags
|
||||||
{
|
{
|
||||||
STATUS_OK,
|
STATUS_OK,
|
||||||
STATUS_DIRTY = 1,
|
STATUS_DIRTY = 1,
|
||||||
MISSING_SIZE_X = STATUS_DIRTY << 1,
|
MISSING_SIZE_X = STATUS_DIRTY << 1,
|
||||||
MISSING_SIZE_Y = MISSING_SIZE_X << 1,
|
MISSING_SIZE_Y = MISSING_SIZE_X << 1,
|
||||||
|
MISSING_SIZE = MISSING_SIZE_X | MISSING_SIZE_Y,
|
||||||
CREATE_FAILED = MISSING_SIZE_Y << 1
|
CREATE_FAILED = MISSING_SIZE_Y << 1
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -71,29 +82,76 @@ namespace canvas
|
|||||||
|
|
||||||
Canvas(SGPropertyNode* node);
|
Canvas(SGPropertyNode* node);
|
||||||
virtual ~Canvas();
|
virtual ~Canvas();
|
||||||
|
virtual void onDestroy();
|
||||||
void setSystemAdapter(const SystemAdapterPtr& system_adapter);
|
|
||||||
SystemAdapterPtr getSystemAdapter() const;
|
|
||||||
|
|
||||||
void setCanvasMgr(CanvasMgr* canvas_mgr);
|
void setCanvasMgr(CanvasMgr* canvas_mgr);
|
||||||
CanvasMgr* getCanvasMgr() const;
|
CanvasMgr* getCanvasMgr() const;
|
||||||
|
|
||||||
|
bool isInit() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a canvas which should be mared as dirty upon any change to this
|
* Add a canvas which should be marked as dirty upon any change to this
|
||||||
* canvas.
|
* canvas.
|
||||||
*
|
*
|
||||||
* This mechanism is used to eg. redraw a canvas if it's displaying
|
* This mechanism is used to eg. redraw a canvas if it's displaying
|
||||||
* another canvas (recursive canvases)
|
* another canvas (recursive canvases)
|
||||||
*/
|
*/
|
||||||
void addDependentCanvas(const CanvasWeakPtr& canvas);
|
void addParentCanvas(const CanvasWeakPtr& canvas);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a canvas which should be marked visible if this canvas is visible.
|
||||||
|
*/
|
||||||
|
void addChildCanvas(const CanvasWeakPtr& canvas);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop notifying the given canvas upon changes
|
* Stop notifying the given canvas upon changes
|
||||||
*/
|
*/
|
||||||
void removeDependentCanvas(const CanvasWeakPtr& canvas);
|
void removeParentCanvas(const CanvasWeakPtr& canvas);
|
||||||
|
void removeChildCanvas(const CanvasWeakPtr& canvas);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new group
|
||||||
|
*/
|
||||||
GroupPtr createGroup(const std::string& name = "");
|
GroupPtr createGroup(const std::string& name = "");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an existing group with the given name
|
||||||
|
*/
|
||||||
|
GroupPtr getGroup(const std::string& name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an existing group with the given name or otherwise create a new
|
||||||
|
* group
|
||||||
|
*/
|
||||||
|
GroupPtr getOrCreateGroup(const std::string& name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the root group of the canvas
|
||||||
|
*/
|
||||||
|
GroupPtr getRootGroup();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the layout of the canvas (the layout will automatically update with
|
||||||
|
* the viewport size of the canvas)
|
||||||
|
*/
|
||||||
|
void setLayout(const LayoutRef& layout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the focus to the given element.
|
||||||
|
*
|
||||||
|
* The focus element will receive all keyboard events propagated to this
|
||||||
|
* canvas. If there is no valid focus element the root group will receive
|
||||||
|
* the events instead.
|
||||||
|
*/
|
||||||
|
void setFocusElement(const ElementPtr& el);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the focus element.
|
||||||
|
*
|
||||||
|
* @see setFocusElement()
|
||||||
|
*/
|
||||||
|
void clearFocusElement();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable rendering for the next frame
|
* Enable rendering for the next frame
|
||||||
*
|
*
|
||||||
@@ -104,7 +162,8 @@ namespace canvas
|
|||||||
|
|
||||||
void update(double delta_time_sec);
|
void update(double delta_time_sec);
|
||||||
|
|
||||||
naRef addEventListener(const nasal::CallContext& ctx);
|
bool addEventListener(const std::string& type, const EventListener& cb);
|
||||||
|
bool dispatchEvent(const EventPtr& event);
|
||||||
|
|
||||||
void setSizeX(int sx);
|
void setSizeX(int sx);
|
||||||
void setSizeY(int sy);
|
void setSizeY(int sy);
|
||||||
@@ -117,8 +176,13 @@ namespace canvas
|
|||||||
|
|
||||||
int getViewWidth() const;
|
int getViewWidth() const;
|
||||||
int getViewHeight() const;
|
int getViewHeight() const;
|
||||||
|
SGRect<int> getViewport() const;
|
||||||
|
|
||||||
bool handleMouseEvent(const MouseEventPtr& event);
|
bool handleMouseEvent(const MouseEventPtr& event);
|
||||||
|
bool handleKeyboardEvent(const KeyboardEventPtr& event);
|
||||||
|
|
||||||
|
bool propagateEvent( EventPtr const& event,
|
||||||
|
EventPropagationPath const& path );
|
||||||
|
|
||||||
virtual void childAdded( SGPropertyNode * parent,
|
virtual void childAdded( SGPropertyNode * parent,
|
||||||
SGPropertyNode * child );
|
SGPropertyNode * child );
|
||||||
@@ -130,15 +194,26 @@ namespace canvas
|
|||||||
|
|
||||||
CullCallbackPtr getCullCallback() const;
|
CullCallbackPtr getCullCallback() const;
|
||||||
|
|
||||||
|
void reloadPlacements( const std::string& type = std::string() );
|
||||||
static void addPlacementFactory( const std::string& type,
|
static void addPlacementFactory( const std::string& type,
|
||||||
PlacementFactory factory );
|
PlacementFactory factory );
|
||||||
|
static void removePlacementFactory(const std::string& type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set global SystemAdapter for all Canvas/ODGauge instances.
|
||||||
|
*
|
||||||
|
* The SystemAdapter is responsible for application specific operations
|
||||||
|
* like loading images/fonts and adding/removing cameras to the scene
|
||||||
|
* graph.
|
||||||
|
*/
|
||||||
|
static void setSystemAdapter(const SystemAdapterPtr& system_adapter);
|
||||||
|
static SystemAdapterPtr getSystemAdapter();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
SystemAdapterPtr _system_adapter;
|
|
||||||
CanvasMgr *_canvas_mgr;
|
CanvasMgr *_canvas_mgr;
|
||||||
|
|
||||||
std::auto_ptr<EventManager> _event_manager;
|
boost::scoped_ptr<EventManager> _event_manager;
|
||||||
|
|
||||||
int _size_x,
|
int _size_x,
|
||||||
_size_y,
|
_size_y,
|
||||||
@@ -153,25 +228,30 @@ namespace canvas
|
|||||||
_visible;
|
_visible;
|
||||||
|
|
||||||
ODGauge _texture;
|
ODGauge _texture;
|
||||||
GroupPtr _root_group;
|
|
||||||
|
GroupPtr _root_group;
|
||||||
|
LayoutRef _layout;
|
||||||
|
|
||||||
|
ElementWeakPtr _focus_element;
|
||||||
|
|
||||||
CullCallbackPtr _cull_callback;
|
CullCallbackPtr _cull_callback;
|
||||||
bool _render_always; //<! Used to disable automatic lazy rendering (culling)
|
bool _render_always; //!< Used to disable automatic lazy rendering (culling)
|
||||||
|
|
||||||
std::vector<SGPropertyNode*> _dirty_placements;
|
std::vector<SGPropertyNode*> _dirty_placements;
|
||||||
std::vector<Placements> _placements;
|
std::vector<Placements> _placements;
|
||||||
std::set<CanvasWeakPtr> _dependent_canvases; //<! Canvases which use this
|
std::set<CanvasWeakPtr> _parent_canvases, //!< Canvases showing this canvas
|
||||||
// canvas and should be
|
_child_canvases; //!< Canvases displayed within
|
||||||
// notified about changes
|
// this canvas
|
||||||
|
|
||||||
typedef std::map<std::string, PlacementFactory> PlacementFactoryMap;
|
typedef std::map<std::string, PlacementFactory> PlacementFactoryMap;
|
||||||
static PlacementFactoryMap _placement_factories;
|
static PlacementFactoryMap _placement_factories;
|
||||||
|
|
||||||
virtual void setSelf(const PropertyBasedElementPtr& self);
|
|
||||||
void setStatusFlags(unsigned int flags, bool set = true);
|
void setStatusFlags(unsigned int flags, bool set = true);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
static SystemAdapterPtr _system_adapter;
|
||||||
|
|
||||||
Canvas(const Canvas&); // = delete;
|
Canvas(const Canvas&); // = delete;
|
||||||
Canvas& operator=(const Canvas&); // = delete;
|
Canvas& operator=(const Canvas&); // = delete;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -26,7 +26,9 @@ namespace canvas
|
|||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Event::Event():
|
Event::Event():
|
||||||
type(UNKNOWN),
|
type(UNKNOWN),
|
||||||
propagation_stopped(false)
|
time(-1),
|
||||||
|
propagation_stopped(false),
|
||||||
|
default_prevented(false)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -38,7 +40,13 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Event::Type Event::getType() const
|
bool Event::canBubble() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
int Event::getType() const
|
||||||
{
|
{
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
@@ -46,14 +54,7 @@ namespace canvas
|
|||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
std::string Event::getTypeString() const
|
std::string Event::getTypeString() const
|
||||||
{
|
{
|
||||||
switch( type )
|
return typeToStr(type);
|
||||||
{
|
|
||||||
# define ENUM_MAPPING(name, str) case name: return str;
|
|
||||||
# include "CanvasEventTypes.hxx"
|
|
||||||
# undef ENUM_MAPPING
|
|
||||||
default:
|
|
||||||
return "unknown";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -62,6 +63,12 @@ namespace canvas
|
|||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
ElementWeakPtr Event::getCurrentTarget() const
|
||||||
|
{
|
||||||
|
return current_target;
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
double Event::getTime() const
|
double Event::getTime() const
|
||||||
{
|
{
|
||||||
@@ -75,23 +82,69 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Event::Type Event::strToType(const std::string& str)
|
void Event::preventDefault()
|
||||||
|
{
|
||||||
|
default_prevented = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
bool Event::defaultPrevented() const
|
||||||
|
{
|
||||||
|
return default_prevented;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
int Event::getOrRegisterType(const std::string& type_str)
|
||||||
|
{
|
||||||
|
int type = strToType(type_str);
|
||||||
|
|
||||||
|
if( type == UNKNOWN )
|
||||||
|
{
|
||||||
|
// Register new type
|
||||||
|
TypeMap& type_map = getTypeMap();
|
||||||
|
type = type_map.size() + 1; // ids start with 1 (after UNKNOWN)
|
||||||
|
type_map.insert(TypeMap::value_type(type_str, type));
|
||||||
|
}
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
int Event::strToType(const std::string& str)
|
||||||
|
{
|
||||||
|
TypeMap const& type_map = getTypeMap();
|
||||||
|
|
||||||
|
TypeMap::map_by<name>::const_iterator it = type_map.by<name>().find(str);
|
||||||
|
if( it == type_map.by<name>().end() )
|
||||||
|
return UNKNOWN;
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
std::string Event::typeToStr(int type)
|
||||||
|
{
|
||||||
|
TypeMap const& type_map = getTypeMap();
|
||||||
|
|
||||||
|
TypeMap::map_by<id>::const_iterator it = type_map.by<id>().find(type);
|
||||||
|
if( it == type_map.by<id>().end() )
|
||||||
|
return "unknown";
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
Event::TypeMap& Event::getTypeMap()
|
||||||
{
|
{
|
||||||
typedef std::map<std::string, Type> TypeMap;
|
|
||||||
static TypeMap type_map;
|
static TypeMap type_map;
|
||||||
|
|
||||||
if( type_map.empty() )
|
if( type_map.empty() )
|
||||||
{
|
{
|
||||||
# define ENUM_MAPPING(type, str) type_map[ str ] = type;
|
# define ENUM_MAPPING(type, str, class_name)\
|
||||||
# include "CanvasEventTypes.hxx"
|
type_map.insert(TypeMap::value_type(str, type));
|
||||||
# undef ENUM_MAPPING
|
# include "CanvasEventTypes.hxx"
|
||||||
|
# undef ENUM_MAPPING
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeMap::const_iterator it = type_map.find(str);
|
return type_map;
|
||||||
if( it == type_map.end() )
|
|
||||||
return UNKNOWN;
|
|
||||||
|
|
||||||
return it->second;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace canvas
|
} // namespace canvas
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// Canvas Event for event model similar to DOM Level 3 Event Model
|
/// @file
|
||||||
|
/// Canvas Event for event model similar to DOM Level 3 Event Model
|
||||||
//
|
//
|
||||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||||
//
|
//
|
||||||
@@ -20,30 +21,43 @@
|
|||||||
#define CANVAS_EVENT_HXX_
|
#define CANVAS_EVENT_HXX_
|
||||||
|
|
||||||
#include "canvas_fwd.hxx"
|
#include "canvas_fwd.hxx"
|
||||||
|
#include <boost/bimap.hpp>
|
||||||
|
|
||||||
namespace simgear
|
namespace simgear
|
||||||
{
|
{
|
||||||
namespace canvas
|
namespace canvas
|
||||||
{
|
{
|
||||||
|
|
||||||
class Event
|
/**
|
||||||
|
* Base class for all Canvas events.
|
||||||
|
*
|
||||||
|
* The event system is closely following the specification of the DOM Level 3
|
||||||
|
* Event Model.
|
||||||
|
*/
|
||||||
|
class Event:
|
||||||
|
public SGReferenced
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
/// Event type identifier
|
||||||
enum Type
|
enum Type
|
||||||
{
|
{
|
||||||
UNKNOWN,
|
UNKNOWN,
|
||||||
# define ENUM_MAPPING(name, str) name,
|
# define ENUM_MAPPING(name, str, class_name)\
|
||||||
|
name, /*!< class_name (type=str) */
|
||||||
# include "CanvasEventTypes.hxx"
|
# include "CanvasEventTypes.hxx"
|
||||||
# undef ENUM_MAPPING
|
# undef ENUM_MAPPING
|
||||||
USER_TYPE ///<! first unused id to be used for user defined types (not
|
CUSTOM_EVENT ///< First event type id available for user defined event
|
||||||
/// implemented yet)
|
/// type.
|
||||||
|
/// @see CustomEvent
|
||||||
};
|
};
|
||||||
|
|
||||||
Type type;
|
int type;
|
||||||
ElementWeakPtr target;
|
ElementWeakPtr target,
|
||||||
|
current_target;
|
||||||
double time;
|
double time;
|
||||||
bool propagation_stopped;
|
bool propagation_stopped,
|
||||||
|
default_prevented;
|
||||||
|
|
||||||
Event();
|
Event();
|
||||||
|
|
||||||
@@ -51,16 +65,64 @@ namespace canvas
|
|||||||
// of the actual event instances.
|
// of the actual event instances.
|
||||||
virtual ~Event();
|
virtual ~Event();
|
||||||
|
|
||||||
Type getType() const;
|
/**
|
||||||
|
* Get whether this events support bubbling
|
||||||
|
*/
|
||||||
|
virtual bool canBubble() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set type of event.
|
||||||
|
*
|
||||||
|
* If no such type exists it is registered.
|
||||||
|
*/
|
||||||
|
void setType(const std::string& type);
|
||||||
|
|
||||||
|
int getType() const;
|
||||||
std::string getTypeString() const;
|
std::string getTypeString() const;
|
||||||
|
|
||||||
ElementWeakPtr getTarget() const;
|
ElementWeakPtr getTarget() const;
|
||||||
|
ElementWeakPtr getCurrentTarget() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get time at which the event was generated.
|
||||||
|
*/
|
||||||
double getTime() const;
|
double getTime() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent further propagation of the event during event flow.
|
||||||
|
*
|
||||||
|
* @note This does not prevent calling further event handlers registered
|
||||||
|
* on the current event target.
|
||||||
|
*/
|
||||||
void stopPropagation();
|
void stopPropagation();
|
||||||
|
|
||||||
static Type strToType(const std::string& str);
|
/**
|
||||||
|
* Cancel any default action normally taken as result of this event.
|
||||||
|
*
|
||||||
|
* @note For event handlers registered on the DesktopGroup (Nasal:
|
||||||
|
* canvas.getDesktop()) this stops the event from being further
|
||||||
|
* propagated to the normal FlightGear input event handling code.
|
||||||
|
*/
|
||||||
|
void preventDefault();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get if preventDefault() has been called.
|
||||||
|
*/
|
||||||
|
bool defaultPrevented() const;
|
||||||
|
|
||||||
|
static int getOrRegisterType(const std::string& type);
|
||||||
|
static int strToType(const std::string& type);
|
||||||
|
static std::string typeToStr(int type);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
struct name {};
|
||||||
|
struct id {};
|
||||||
|
typedef boost::bimaps::bimap<
|
||||||
|
boost::bimaps::tagged<std::string, name>,
|
||||||
|
boost::bimaps::tagged<int, id>
|
||||||
|
> TypeMap;
|
||||||
|
|
||||||
|
static TypeMap& getTypeMap();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,9 @@
|
|||||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
|
||||||
#include "CanvasEventManager.hxx"
|
#include "CanvasEventManager.hxx"
|
||||||
#include "MouseEvent.hxx"
|
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||||
#include <simgear/canvas/elements/CanvasElement.hxx>
|
#include <simgear/canvas/elements/CanvasElement.hxx>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
namespace simgear
|
namespace simgear
|
||||||
{
|
{
|
||||||
@@ -60,6 +61,16 @@ namespace canvas
|
|||||||
return !path.empty() && time > 0;
|
return !path.empty() && time > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void EventManager::MouseEventInfo::set( const MouseEventPtr& event,
|
||||||
|
const EventPropagationPath& p )
|
||||||
|
{
|
||||||
|
path = p;
|
||||||
|
time = event->time;
|
||||||
|
button = event->button;
|
||||||
|
pos = event->screen_pos;
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
EventManager::EventManager():
|
EventManager::EventManager():
|
||||||
_current_click_count(0)
|
_current_click_count(0)
|
||||||
@@ -71,39 +82,58 @@ namespace canvas
|
|||||||
bool EventManager::handleEvent( const MouseEventPtr& event,
|
bool EventManager::handleEvent( const MouseEventPtr& event,
|
||||||
const EventPropagationPath& path )
|
const EventPropagationPath& path )
|
||||||
{
|
{
|
||||||
|
bool handled = false;
|
||||||
switch( event->type )
|
switch( event->type )
|
||||||
{
|
{
|
||||||
case Event::MOUSE_DOWN:
|
case Event::MOUSE_DOWN:
|
||||||
_last_mouse_down = StampedPropagationPath(path, event->getTime());
|
_last_mouse_down.set(event, path);
|
||||||
break;
|
break;
|
||||||
case Event::MOUSE_UP:
|
case Event::MOUSE_UP:
|
||||||
{
|
{
|
||||||
if( _last_mouse_down.path.empty() )
|
// If the mouse has moved while a button was down (aka. dragging) we
|
||||||
// Ignore mouse up without any previous mouse down
|
// need to notify the original element that the mouse has left it, and
|
||||||
return false;
|
// the new element that it has been entered
|
||||||
|
if( _last_mouse_down.path != path )
|
||||||
|
handled |= handleMove(event, path);
|
||||||
|
|
||||||
// normal mouseup
|
// normal mouseup
|
||||||
propagateEvent(event, path);
|
handled |= propagateEvent(event, path);
|
||||||
|
|
||||||
|
if( !_last_mouse_down.valid() )
|
||||||
|
// Ignore mouse up without any previous mouse down
|
||||||
|
return handled;
|
||||||
|
|
||||||
// now handle click/dblclick
|
// now handle click/dblclick
|
||||||
if( checkClickDistance(path, _last_mouse_down.path) )
|
if( checkClickDistance(event->screen_pos, _last_mouse_down.pos) )
|
||||||
handleClick(event, getCommonAncestor(_last_mouse_down.path, path));
|
handled |=
|
||||||
|
handleClick(event, getCommonAncestor(_last_mouse_down.path, path));
|
||||||
|
|
||||||
_last_mouse_down.clear();
|
_last_mouse_down.clear();
|
||||||
|
|
||||||
return true;
|
return handled;
|
||||||
}
|
}
|
||||||
case Event::DRAG:
|
case Event::DRAG:
|
||||||
if( !_last_mouse_down.valid() )
|
if( !_last_mouse_down.valid() )
|
||||||
return false;
|
return false;
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
// OSG does not set button for drag events.
|
||||||
|
event->button = _last_mouse_down.button;
|
||||||
return propagateEvent(event, _last_mouse_down.path);
|
return propagateEvent(event, _last_mouse_down.path);
|
||||||
|
}
|
||||||
case Event::MOUSE_MOVE:
|
case Event::MOUSE_MOVE:
|
||||||
handleMove(event, path);
|
handled |= handleMove(event, path);
|
||||||
break;
|
break;
|
||||||
case Event::MOUSE_LEAVE:
|
case Event::MOUSE_LEAVE:
|
||||||
// Mouse leaves window and therefore also current mouseover element
|
// Mouse leaves window and therefore also current mouseover element
|
||||||
handleMove(event, EventPropagationPath());
|
handleMove(event, EventPropagationPath());
|
||||||
|
|
||||||
|
// Event is only send if mouse is moved outside the window or dragging
|
||||||
|
// has ended somewhere outside the window. In both cases a mouse button
|
||||||
|
// has been released, so no more mouse down or click...
|
||||||
|
_last_mouse_down.clear();
|
||||||
|
_last_click.clear();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
case Event::WHEEL:
|
case Event::WHEEL:
|
||||||
break;
|
break;
|
||||||
@@ -111,11 +141,76 @@ namespace canvas
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return propagateEvent(event, path);
|
return handled | propagateEvent(event, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void EventManager::handleClick( const MouseEventPtr& event,
|
bool EventManager::propagateEvent( const EventPtr& event,
|
||||||
|
const EventPropagationPath& path )
|
||||||
|
{
|
||||||
|
event->target = path.back().element;
|
||||||
|
MouseEventPtr mouse_event = dynamic_cast<MouseEvent*>(event.get());
|
||||||
|
|
||||||
|
// Event propagation similar to DOM Level 3 event flow:
|
||||||
|
// http://www.w3.org/TR/DOM-Level-3-Events/#event-flow
|
||||||
|
|
||||||
|
// Position update only needed for drag event (as event needs to be
|
||||||
|
// delivered to element of initial mousedown, but with update positions)
|
||||||
|
if( mouse_event && mouse_event->type == MouseEvent::DRAG )
|
||||||
|
{
|
||||||
|
osg::Vec2f local_pos = mouse_event->client_pos;
|
||||||
|
|
||||||
|
// Capturing phase (currently just update position)
|
||||||
|
for( EventPropagationPath::const_iterator it = path.begin();
|
||||||
|
it != path.end();
|
||||||
|
++it )
|
||||||
|
{
|
||||||
|
ElementPtr el = it->element.lock();
|
||||||
|
if( !el )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
it->local_pos = local_pos = el->posToLocal(local_pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool const do_bubble = event->canBubble();
|
||||||
|
|
||||||
|
// Bubbling phase
|
||||||
|
for( EventPropagationPath::const_reverse_iterator
|
||||||
|
it = path.rbegin();
|
||||||
|
it != path.rend();
|
||||||
|
++it )
|
||||||
|
{
|
||||||
|
ElementPtr el = it->element.lock();
|
||||||
|
|
||||||
|
if( !el )
|
||||||
|
{
|
||||||
|
// Ignore element if it has been destroyed while traversing the event
|
||||||
|
// (eg. removed by another event handler)
|
||||||
|
if( do_bubble )
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO provide functions to convert delta to local coordinates on demand.
|
||||||
|
// Maybe also provide a clone method for events as local coordinates
|
||||||
|
// might differ between different elements receiving the same event.
|
||||||
|
if( mouse_event )
|
||||||
|
mouse_event->local_pos = it->local_pos;
|
||||||
|
|
||||||
|
event->current_target = el;
|
||||||
|
el->handleEvent(event);
|
||||||
|
|
||||||
|
if( event->propagation_stopped || !do_bubble )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
bool EventManager::handleClick( const MouseEventPtr& event,
|
||||||
const EventPropagationPath& path )
|
const EventPropagationPath& path )
|
||||||
{
|
{
|
||||||
MouseEventPtr click(new MouseEvent(*event));
|
MouseEventPtr click(new MouseEvent(*event));
|
||||||
@@ -130,8 +225,10 @@ namespace canvas
|
|||||||
|
|
||||||
if( _current_click_count > 1 )
|
if( _current_click_count > 1 )
|
||||||
{
|
{
|
||||||
// Reset current click count if moved too far
|
// Reset current click count if moved too far or different button has
|
||||||
if( !checkClickDistance(path, _last_click.path) )
|
// been clicked
|
||||||
|
if( !checkClickDistance(event->screen_pos, _last_click.pos)
|
||||||
|
|| _last_click.button != event->button )
|
||||||
_current_click_count = 1;
|
_current_click_count = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,104 +242,86 @@ namespace canvas
|
|||||||
dbl_click->type = Event::DBL_CLICK;
|
dbl_click->type = Event::DBL_CLICK;
|
||||||
}
|
}
|
||||||
|
|
||||||
propagateEvent(click, path);
|
bool handled = propagateEvent(click, path);
|
||||||
|
|
||||||
if( dbl_click )
|
if( dbl_click )
|
||||||
propagateEvent(dbl_click, getCommonAncestor(_last_click.path, path));
|
handled |= propagateEvent( dbl_click,
|
||||||
|
getCommonAncestor(_last_click.path, path) );
|
||||||
|
|
||||||
_last_click = StampedPropagationPath(path, event->getTime());
|
_last_click.set(event, path);
|
||||||
|
|
||||||
|
return handled;
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void EventManager::handleMove( const MouseEventPtr& event,
|
bool EventManager::handleMove( const MouseEventPtr& event,
|
||||||
const EventPropagationPath& path )
|
const EventPropagationPath& path )
|
||||||
{
|
{
|
||||||
if( _last_mouse_over.path == path )
|
EventPropagationPath& last_path = _last_mouse_over.path;
|
||||||
return;
|
if( last_path == path )
|
||||||
|
return false;
|
||||||
|
|
||||||
if( !_last_mouse_over.path.empty() )
|
bool handled = false;
|
||||||
|
|
||||||
|
// Leave old element
|
||||||
|
if( !last_path.empty() )
|
||||||
{
|
{
|
||||||
MouseEventPtr mouseout(new MouseEvent(*event));
|
MouseEventPtr mouseout(new MouseEvent(*event));
|
||||||
mouseout->type = Event::MOUSE_OUT;
|
mouseout->type = Event::MOUSE_OUT;
|
||||||
propagateEvent(mouseout, _last_mouse_over.path);
|
handled |= propagateEvent(mouseout, last_path);
|
||||||
|
|
||||||
|
// Send a mouseleave event to all ancestors of the currently left element
|
||||||
|
// which are not ancestor of the new element currently entered
|
||||||
|
EventPropagationPath path_leave = last_path;
|
||||||
|
for(size_t i = path_leave.size() - 1; i > 0; --i)
|
||||||
|
{
|
||||||
|
if( i < path.size() && path[i] == path_leave[i] )
|
||||||
|
break;
|
||||||
|
|
||||||
|
MouseEventPtr mouseleave(new MouseEvent(*event));
|
||||||
|
mouseleave->type = Event::MOUSE_LEAVE;
|
||||||
|
handled |= propagateEvent(mouseleave, path_leave);
|
||||||
|
|
||||||
|
path_leave.pop_back();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enter new element
|
||||||
if( !path.empty() )
|
if( !path.empty() )
|
||||||
{
|
{
|
||||||
MouseEventPtr mouseover(new MouseEvent(*event));
|
MouseEventPtr mouseover(new MouseEvent(*event));
|
||||||
mouseover->type = Event::MOUSE_OVER;
|
mouseover->type = Event::MOUSE_OVER;
|
||||||
propagateEvent(mouseover, path);
|
handled |= propagateEvent(mouseover, path);
|
||||||
|
|
||||||
|
// Send a mouseenter event to all ancestors of the currently entered
|
||||||
|
// element which are not ancestor of the old element currently being
|
||||||
|
// left
|
||||||
|
EventPropagationPath path_enter;
|
||||||
|
for(size_t i = 0; i < path.size(); ++i)
|
||||||
|
{
|
||||||
|
path_enter.push_back(path[i]);
|
||||||
|
|
||||||
|
if( i < last_path.size() && path[i] == last_path[i] )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
MouseEventPtr mouseenter(new MouseEvent(*event));
|
||||||
|
mouseenter->type = Event::MOUSE_ENTER;
|
||||||
|
handled |= propagateEvent(mouseenter, path_enter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_last_mouse_over.path = path;
|
_last_mouse_over.path = path;
|
||||||
}
|
return handled;
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
bool EventManager::propagateEvent( const EventPtr& event,
|
|
||||||
const EventPropagationPath& path )
|
|
||||||
{
|
|
||||||
event->target = path.back().element;
|
|
||||||
MouseEventPtr mouse_event = boost::dynamic_pointer_cast<MouseEvent>(event);
|
|
||||||
|
|
||||||
// Event propagation similar to DOM Level 3 event flow:
|
|
||||||
// http://www.w3.org/TR/DOM-Level-3-Events/#event-flow
|
|
||||||
|
|
||||||
// Capturing phase
|
|
||||||
// for( EventTargets::iterator it = _target_path.begin();
|
|
||||||
// it != _target_path.end();
|
|
||||||
// ++it )
|
|
||||||
// {
|
|
||||||
// if( it->element )
|
|
||||||
// std::cout << it->element->getProps()->getPath() << " "
|
|
||||||
// << "(" << it->local_pos.x() << "|" << it->local_pos.y() << ")\n";
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Bubbling phase
|
|
||||||
for( EventPropagationPath::const_reverse_iterator
|
|
||||||
it = path.rbegin();
|
|
||||||
it != path.rend();
|
|
||||||
++it )
|
|
||||||
{
|
|
||||||
ElementPtr el = it->element.lock();
|
|
||||||
|
|
||||||
if( !el )
|
|
||||||
// Ignore element if it has been destroyed while traversing the event
|
|
||||||
// (eg. removed by another event handler)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// TODO provide functions to convert position and delta to local
|
|
||||||
// coordinates on demand. Events shouldn't contain informations in
|
|
||||||
// local coordinates as they might differe between different elements
|
|
||||||
// receiving the same event.
|
|
||||||
// if( mouse_event && event->type != Event::DRAG )
|
|
||||||
// {
|
|
||||||
// // TODO transform pos and delta for drag events. Maybe we should just
|
|
||||||
// // store the global coordinates and convert to local coordinates
|
|
||||||
// // on demand.
|
|
||||||
//
|
|
||||||
// // Position and delta are specified in local coordinate system of
|
|
||||||
// // current element
|
|
||||||
// mouse_event->pos = it->local_pos;
|
|
||||||
// mouse_event->delta = it->local_delta;
|
|
||||||
// }
|
|
||||||
|
|
||||||
el->callListeners(event);
|
|
||||||
|
|
||||||
if( event->propagation_stopped )
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
bool
|
bool
|
||||||
EventManager::checkClickDistance( const EventPropagationPath& path1,
|
EventManager::checkClickDistance( const osg::Vec2f& pos1,
|
||||||
const EventPropagationPath& path2 ) const
|
const osg::Vec2f& pos2 ) const
|
||||||
{
|
{
|
||||||
osg::Vec2 delta = path1.front().local_pos - path2.front().local_pos;
|
osg::Vec2 delta = pos1 - pos2;
|
||||||
return delta.x() < drag_threshold
|
return std::fabs(delta.x()) < drag_threshold
|
||||||
&& delta.y() < drag_threshold;
|
&& std::fabs(delta.y()) < drag_threshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -250,6 +329,9 @@ namespace canvas
|
|||||||
EventManager::getCommonAncestor( const EventPropagationPath& path1,
|
EventManager::getCommonAncestor( const EventPropagationPath& path1,
|
||||||
const EventPropagationPath& path2 ) const
|
const EventPropagationPath& path2 ) const
|
||||||
{
|
{
|
||||||
|
if( path1.empty() || path2.empty() )
|
||||||
|
return EventPropagationPath();
|
||||||
|
|
||||||
if( path1.back().element.lock() == path2.back().element.lock() )
|
if( path1.back().element.lock() == path2.back().element.lock() )
|
||||||
return path2;
|
return path2;
|
||||||
|
|
||||||
|
|||||||
@@ -29,11 +29,18 @@ namespace canvas
|
|||||||
|
|
||||||
struct EventTarget
|
struct EventTarget
|
||||||
{
|
{
|
||||||
ElementWeakPtr element;
|
ElementWeakPtr element;
|
||||||
osg::Vec2f local_pos,
|
|
||||||
local_delta;
|
// Used as storage by EventManager during event propagation
|
||||||
|
mutable osg::Vec2f local_pos;
|
||||||
|
|
||||||
|
EventTarget( Element* el,
|
||||||
|
const osg::Vec2f pos = osg::Vec2f() ):
|
||||||
|
element(el),
|
||||||
|
local_pos(pos)
|
||||||
|
{}
|
||||||
};
|
};
|
||||||
typedef std::deque<EventTarget> EventPropagationPath;
|
|
||||||
inline bool operator==(const EventTarget& t1, const EventTarget& t2)
|
inline bool operator==(const EventTarget& t1, const EventTarget& t2)
|
||||||
{
|
{
|
||||||
return t1.element.lock() == t2.element.lock();
|
return t1.element.lock() == t2.element.lock();
|
||||||
@@ -47,6 +54,9 @@ namespace canvas
|
|||||||
bool handleEvent( const MouseEventPtr& event,
|
bool handleEvent( const MouseEventPtr& event,
|
||||||
const EventPropagationPath& path );
|
const EventPropagationPath& path );
|
||||||
|
|
||||||
|
bool propagateEvent( const EventPtr& event,
|
||||||
|
const EventPropagationPath& path );
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
struct StampedPropagationPath
|
struct StampedPropagationPath
|
||||||
{
|
{
|
||||||
@@ -62,33 +72,39 @@ namespace canvas
|
|||||||
|
|
||||||
// TODO if we really need the paths modify to not copy around the paths
|
// TODO if we really need the paths modify to not copy around the paths
|
||||||
// that much.
|
// that much.
|
||||||
StampedPropagationPath _last_mouse_down,
|
StampedPropagationPath _last_mouse_over;
|
||||||
_last_click,
|
|
||||||
_last_mouse_over;
|
|
||||||
size_t _current_click_count;
|
size_t _current_click_count;
|
||||||
|
|
||||||
|
struct MouseEventInfo:
|
||||||
|
public StampedPropagationPath
|
||||||
|
{
|
||||||
|
int button;
|
||||||
|
osg::Vec2f pos;
|
||||||
|
|
||||||
|
void set( const MouseEventPtr& event,
|
||||||
|
const EventPropagationPath& path );
|
||||||
|
} _last_mouse_down,
|
||||||
|
_last_click;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Propagate click event and handle multi-click (eg. create dblclick)
|
* Propagate click event and handle multi-click (eg. create dblclick)
|
||||||
*/
|
*/
|
||||||
void handleClick( const MouseEventPtr& event,
|
bool handleClick( const MouseEventPtr& event,
|
||||||
const EventPropagationPath& path );
|
const EventPropagationPath& path );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle mouseover/enter/out/leave
|
* Handle mouseover/enter/out/leave
|
||||||
*/
|
*/
|
||||||
void handleMove( const MouseEventPtr& event,
|
bool handleMove( const MouseEventPtr& event,
|
||||||
const EventPropagationPath& path );
|
const EventPropagationPath& path );
|
||||||
|
|
||||||
bool propagateEvent( const EventPtr& event,
|
|
||||||
const EventPropagationPath& path );
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if two click events (either mousedown/up or two consecutive
|
* Check if two click events (either mousedown/up or two consecutive
|
||||||
* clicks) are inside a maximum distance to still create a click or
|
* clicks) are inside a maximum distance to still create a click or
|
||||||
* dblclick event respectively.
|
* dblclick event respectively.
|
||||||
*/
|
*/
|
||||||
bool checkClickDistance( const EventPropagationPath& path1,
|
bool checkClickDistance( const osg::Vec2f& pos1,
|
||||||
const EventPropagationPath& path2 ) const;
|
const osg::Vec2f& pos2 ) const;
|
||||||
EventPropagationPath
|
EventPropagationPath
|
||||||
getCommonAncestor( const EventPropagationPath& path1,
|
getCommonAncestor( const EventPropagationPath& path1,
|
||||||
const EventPropagationPath& path2 ) const;
|
const EventPropagationPath& path2 ) const;
|
||||||
|
|||||||
@@ -20,14 +20,17 @@
|
|||||||
# error "Don't include this file directly!"
|
# error "Don't include this file directly!"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ENUM_MAPPING(MOUSE_DOWN, "mousedown")
|
ENUM_MAPPING(MOUSE_DOWN, "mousedown", MouseEvent)
|
||||||
ENUM_MAPPING(MOUSE_UP, "mouseup")
|
ENUM_MAPPING(MOUSE_UP, "mouseup", MouseEvent)
|
||||||
ENUM_MAPPING(CLICK, "click")
|
ENUM_MAPPING(CLICK, "click", MouseEvent)
|
||||||
ENUM_MAPPING(DBL_CLICK, "dblclick")
|
ENUM_MAPPING(DBL_CLICK, "dblclick", MouseEvent)
|
||||||
ENUM_MAPPING(DRAG, "drag")
|
ENUM_MAPPING(DRAG, "drag", MouseEvent)
|
||||||
ENUM_MAPPING(WHEEL, "wheel")
|
ENUM_MAPPING(WHEEL, "wheel", MouseEvent)
|
||||||
ENUM_MAPPING(MOUSE_MOVE, "mousemove")
|
ENUM_MAPPING(MOUSE_MOVE, "mousemove", MouseEvent)
|
||||||
ENUM_MAPPING(MOUSE_OVER, "mouseover")
|
ENUM_MAPPING(MOUSE_OVER, "mouseover", MouseEvent)
|
||||||
ENUM_MAPPING(MOUSE_OUT, "mouseout")
|
ENUM_MAPPING(MOUSE_OUT, "mouseout", MouseEvent)
|
||||||
ENUM_MAPPING(MOUSE_ENTER, "mouseenter")
|
ENUM_MAPPING(MOUSE_ENTER, "mouseenter", MouseEvent)
|
||||||
ENUM_MAPPING(MOUSE_LEAVE, "mouseleave")
|
ENUM_MAPPING(MOUSE_LEAVE, "mouseleave", MouseEvent)
|
||||||
|
ENUM_MAPPING(KEY_DOWN, "keydown", KeyboardEvent)
|
||||||
|
ENUM_MAPPING(KEY_UP, "keyup", KeyboardEvent)
|
||||||
|
ENUM_MAPPING(KEY_PRESS, "keypress", KeyboardEvent)
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
#include "CanvasEvent.hxx"
|
#include "CanvasEvent.hxx"
|
||||||
#include "CanvasEventVisitor.hxx"
|
#include "CanvasEventVisitor.hxx"
|
||||||
#include <simgear/canvas/elements/CanvasElement.hxx>
|
#include <simgear/canvas/elements/CanvasElement.hxx>
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
namespace simgear
|
namespace simgear
|
||||||
{
|
{
|
||||||
@@ -30,14 +29,12 @@ namespace canvas
|
|||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
EventVisitor::EventVisitor( TraverseMode mode,
|
EventVisitor::EventVisitor( TraverseMode mode,
|
||||||
const osg::Vec2f& pos,
|
const osg::Vec2f& pos,
|
||||||
const osg::Vec2f& delta ):
|
const ElementPtr& root ):
|
||||||
_traverse_mode( mode )
|
_traverse_mode( mode ),
|
||||||
|
_root(root)
|
||||||
{
|
{
|
||||||
if( mode == TRAVERSE_DOWN )
|
if( mode == TRAVERSE_DOWN )
|
||||||
{
|
_target_path.push_back( EventTarget(NULL, pos) );
|
||||||
EventTarget target = {ElementWeakPtr(), pos, delta};
|
|
||||||
_target_path.push_back(target);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -61,32 +58,20 @@ namespace canvas
|
|||||||
// We only need to check for hits while traversing down
|
// We only need to check for hits while traversing down
|
||||||
if( _traverse_mode == TRAVERSE_DOWN )
|
if( _traverse_mode == TRAVERSE_DOWN )
|
||||||
{
|
{
|
||||||
// Transform event to local coordinates
|
|
||||||
const osg::Matrix& m = el.getMatrixTransform()->getInverseMatrix();
|
|
||||||
const osg::Vec2f& pos = _target_path.back().local_pos;
|
const osg::Vec2f& pos = _target_path.back().local_pos;
|
||||||
const osg::Vec2f local_pos
|
const osg::Vec2f local_pos = el.posToLocal(pos);
|
||||||
(
|
|
||||||
m(0, 0) * pos[0] + m(1, 0) * pos[1] + m(3, 0),
|
|
||||||
m(0, 1) * pos[0] + m(1, 1) * pos[1] + m(3, 1)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Don't check collision with root element (2nd element in _target_path)
|
// Don't check specified root element for collision, as its purpose is to
|
||||||
// do event listeners attached to the canvas itself (its root group)
|
// catch all events which have no target. This allows for example calling
|
||||||
// always get called even if no element has been hit.
|
// event listeners attached to the canvas itself (its root group) even if
|
||||||
if( _target_path.size() > 2 && !el.hitBound(local_pos) )
|
// no element has been hit.
|
||||||
|
if( _root.get() != &el
|
||||||
|
&& !el.hitBound(_target_path.front().local_pos, pos, local_pos) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const osg::Vec2f& delta = _target_path.back().local_delta;
|
_target_path.push_back( EventTarget(&el, local_pos) );
|
||||||
const osg::Vec2f local_delta
|
|
||||||
(
|
|
||||||
m(0, 0) * delta[0] + m(1, 0) * delta[1],
|
|
||||||
m(0, 1) * delta[0] + m(1, 1) * delta[1]
|
|
||||||
);
|
|
||||||
|
|
||||||
EventTarget target = {el.getWeakPtr(), local_pos, local_delta};
|
if( el.traverse(*this) || &el == _root.get() )
|
||||||
_target_path.push_back(target);
|
|
||||||
|
|
||||||
if( el.traverse(*this) || _target_path.size() <= 2 )
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
_target_path.pop_back();
|
_target_path.pop_back();
|
||||||
|
|||||||
@@ -38,9 +38,15 @@ namespace canvas
|
|||||||
TRAVERSE_DOWN
|
TRAVERSE_DOWN
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param mode
|
||||||
|
* @param pos Mouse position
|
||||||
|
* @param root Element to dispatch events to if no element is hit
|
||||||
|
*/
|
||||||
EventVisitor( TraverseMode mode,
|
EventVisitor( TraverseMode mode,
|
||||||
const osg::Vec2f& pos,
|
const osg::Vec2f& pos,
|
||||||
const osg::Vec2f& delta );
|
const ElementPtr& root = ElementPtr() );
|
||||||
virtual ~EventVisitor();
|
virtual ~EventVisitor();
|
||||||
virtual bool traverse(Element& el);
|
virtual bool traverse(Element& el);
|
||||||
virtual bool apply(Element& el);
|
virtual bool apply(Element& el);
|
||||||
@@ -51,6 +57,7 @@ namespace canvas
|
|||||||
|
|
||||||
TraverseMode _traverse_mode;
|
TraverseMode _traverse_mode;
|
||||||
EventPropagationPath _target_path;
|
EventPropagationPath _target_path;
|
||||||
|
ElementPtr _root;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include "CanvasMgr.hxx"
|
#include "CanvasMgr.hxx"
|
||||||
#include "Canvas.hxx"
|
#include "Canvas.hxx"
|
||||||
|
#include "CanvasEventManager.hxx"
|
||||||
|
|
||||||
#include <boost/bind.hpp>
|
#include <boost/bind.hpp>
|
||||||
|
|
||||||
@@ -35,10 +36,8 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
CanvasMgr::CanvasMgr( SGPropertyNode_ptr node,
|
CanvasMgr::CanvasMgr(SGPropertyNode_ptr node):
|
||||||
SystemAdapterPtr system_adapter ):
|
PropertyBasedMgr(node, "texture", &canvasFactory)
|
||||||
PropertyBasedMgr(node, "texture", &canvasFactory),
|
|
||||||
_system_adapter(system_adapter)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -46,26 +45,25 @@ namespace canvas
|
|||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
CanvasPtr CanvasMgr::createCanvas(const std::string& name)
|
CanvasPtr CanvasMgr::createCanvas(const std::string& name)
|
||||||
{
|
{
|
||||||
return boost::static_pointer_cast<Canvas>( createElement(name) );
|
return static_cast<Canvas*>( createElement(name).get() );
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
CanvasPtr CanvasMgr::getCanvas(size_t index) const
|
CanvasPtr CanvasMgr::getCanvas(size_t index) const
|
||||||
{
|
{
|
||||||
return boost::static_pointer_cast<Canvas>( getElement(index) );
|
return static_cast<Canvas*>( getElement(index).get() );
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
CanvasPtr CanvasMgr::getCanvas(const std::string& name) const
|
CanvasPtr CanvasMgr::getCanvas(const std::string& name) const
|
||||||
{
|
{
|
||||||
return boost::static_pointer_cast<Canvas>( getElement(name) );
|
return static_cast<Canvas*>( getElement(name).get() );
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void CanvasMgr::elementCreated(PropertyBasedElementPtr element)
|
void CanvasMgr::elementCreated(PropertyBasedElementPtr element)
|
||||||
{
|
{
|
||||||
CanvasPtr canvas = boost::static_pointer_cast<Canvas>(element);
|
CanvasPtr canvas = static_cast<Canvas*>(element.get());
|
||||||
canvas->setSystemAdapter(_system_adapter);
|
|
||||||
canvas->setCanvasMgr(this);
|
canvas->setCanvasMgr(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,13 +33,9 @@ namespace canvas
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param node Root node of branch used to control canvasses
|
* @param node Root node of branch used to control canvasses
|
||||||
* @param system_adapter Adapter for connecting between canvas and
|
|
||||||
* application framework
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
CanvasMgr( SGPropertyNode_ptr node,
|
CanvasMgr(SGPropertyNode_ptr node);
|
||||||
SystemAdapterPtr system_adapter );
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new canvas
|
* Create a new canvas
|
||||||
@@ -65,8 +61,6 @@ namespace canvas
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
SystemAdapterPtr _system_adapter;
|
|
||||||
|
|
||||||
virtual void elementCreated(PropertyBasedElementPtr element);
|
virtual void elementCreated(PropertyBasedElementPtr element);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,244 @@
|
|||||||
|
// Canvas placement for placing a canvas texture onto osg objects.
|
||||||
|
//
|
||||||
|
// It also provides a SGPickCallback for passing mouse events to the canvas and
|
||||||
|
// manages emissive lighting of the placed canvas.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2013 Thomas Geymayer <tomgey@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 Library General Public
|
||||||
|
// 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 "Canvas.hxx"
|
||||||
|
#include "CanvasObjectPlacement.hxx"
|
||||||
|
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||||
|
|
||||||
|
#include <simgear/props/props.hxx>
|
||||||
|
#include <simgear/scene/util/SGPickCallback.hxx>
|
||||||
|
|
||||||
|
#include <osgGA/GUIEventAdapter>
|
||||||
|
|
||||||
|
namespace simgear
|
||||||
|
{
|
||||||
|
namespace canvas
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle picking events on object with a canvas placed onto
|
||||||
|
*/
|
||||||
|
class ObjectPickCallback:
|
||||||
|
public SGPickCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
ObjectPickCallback(const CanvasWeakPtr& canvas):
|
||||||
|
_canvas(canvas)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual bool needsUV() const { return true; }
|
||||||
|
|
||||||
|
virtual bool buttonPressed( int,
|
||||||
|
const osgGA::GUIEventAdapter& ea,
|
||||||
|
const Info& info )
|
||||||
|
{
|
||||||
|
MouseEventPtr event(new MouseEvent(ea));
|
||||||
|
updatePosFromUV(event, info.uv);
|
||||||
|
|
||||||
|
if( ea.getEventType() == osgGA::GUIEventAdapter::SCROLL )
|
||||||
|
{
|
||||||
|
event->type = Event::WHEEL;
|
||||||
|
event->delta.set(0,0);
|
||||||
|
switch( ea.getScrollingMotion() )
|
||||||
|
{
|
||||||
|
case osgGA::GUIEventAdapter::SCROLL_UP:
|
||||||
|
event->delta.y() = 1;
|
||||||
|
break;
|
||||||
|
case osgGA::GUIEventAdapter::SCROLL_DOWN:
|
||||||
|
event->delta.y() = -1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
event->type = Event::MOUSE_DOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return handleEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void buttonReleased( int,
|
||||||
|
const osgGA::GUIEventAdapter& ea,
|
||||||
|
const Info* info )
|
||||||
|
{
|
||||||
|
if( ea.getEventType() != osgGA::GUIEventAdapter::RELEASE )
|
||||||
|
return;
|
||||||
|
|
||||||
|
MouseEventPtr event(new MouseEvent(ea));
|
||||||
|
event->type = Event::MOUSE_UP;
|
||||||
|
updatePosFromUV(event, info ? info->uv : SGVec2d(-1,-1));
|
||||||
|
|
||||||
|
handleEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void mouseMoved( const osgGA::GUIEventAdapter& ea,
|
||||||
|
const Info* info )
|
||||||
|
{
|
||||||
|
// drag (currently only with LMB)
|
||||||
|
if( ea.getEventType() != osgGA::GUIEventAdapter::DRAG )
|
||||||
|
return;
|
||||||
|
|
||||||
|
MouseEventPtr event(new MouseEvent(ea));
|
||||||
|
event->type = Event::DRAG;
|
||||||
|
updatePosFromUV(event, info ? info->uv : SGVec2d(-1,-1));
|
||||||
|
|
||||||
|
handleEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool hover( const osg::Vec2d& windowPos,
|
||||||
|
const Info& info )
|
||||||
|
{
|
||||||
|
// TODO somehow get more info about event (time, modifiers, pressed
|
||||||
|
// buttons, ...)
|
||||||
|
MouseEventPtr event(new MouseEvent);
|
||||||
|
event->type = Event::MOUSE_MOVE;
|
||||||
|
event->screen_pos = windowPos;
|
||||||
|
updatePosFromUV(event, info.uv);
|
||||||
|
|
||||||
|
return handleEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void mouseLeave( const osg::Vec2d& windowPos )
|
||||||
|
{
|
||||||
|
MouseEventPtr event(new MouseEvent);
|
||||||
|
event->type = Event::MOUSE_LEAVE;
|
||||||
|
event->screen_pos = windowPos;
|
||||||
|
|
||||||
|
handleEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
CanvasWeakPtr _canvas;
|
||||||
|
osg::Vec2f _last_pos,
|
||||||
|
_last_delta;
|
||||||
|
|
||||||
|
void updatePosFromUV(const MouseEventPtr& event, const SGVec2d& uv)
|
||||||
|
{
|
||||||
|
CanvasPtr canvas = _canvas.lock();
|
||||||
|
if( !canvas )
|
||||||
|
return;
|
||||||
|
|
||||||
|
osg::Vec2d pos( uv.x() * canvas->getViewWidth(),
|
||||||
|
(1 - uv.y()) * canvas->getViewHeight() );
|
||||||
|
|
||||||
|
_last_delta = pos - _last_pos;
|
||||||
|
_last_pos = pos;
|
||||||
|
|
||||||
|
event->client_pos = pos;
|
||||||
|
event->delta = _last_delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool handleEvent(const MouseEventPtr& event)
|
||||||
|
{
|
||||||
|
CanvasPtr canvas = _canvas.lock();
|
||||||
|
if( !canvas )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return canvas->handleMouseEvent(event);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
ObjectPlacement::ObjectPlacement( SGPropertyNode* node,
|
||||||
|
const GroupPtr& group,
|
||||||
|
const CanvasWeakPtr& canvas ):
|
||||||
|
Placement(node),
|
||||||
|
_group(group),
|
||||||
|
_canvas(canvas)
|
||||||
|
{
|
||||||
|
// TODO make more generic and extendable for more properties
|
||||||
|
if( node->hasValue("emission") )
|
||||||
|
setEmission( node->getFloatValue("emission") );
|
||||||
|
if( node->hasValue("capture-events") )
|
||||||
|
setCaptureEvents( node->getBoolValue("capture-events") );
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
ObjectPlacement::~ObjectPlacement()
|
||||||
|
{
|
||||||
|
assert( _group->getNumChildren() == 1 );
|
||||||
|
osg::Node *child = _group->getChild(0);
|
||||||
|
|
||||||
|
if( _group->getNumParents() )
|
||||||
|
{
|
||||||
|
osg::Group *parent = _group->getParent(0);
|
||||||
|
parent->addChild(child);
|
||||||
|
parent->removeChild(_group);
|
||||||
|
}
|
||||||
|
|
||||||
|
_group->removeChild(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void ObjectPlacement::setEmission(float emit)
|
||||||
|
{
|
||||||
|
emit = SGMiscf::clip(emit, 0, 1);
|
||||||
|
|
||||||
|
if( !_material )
|
||||||
|
{
|
||||||
|
_material = new osg::Material;
|
||||||
|
_material->setColorMode(osg::Material::OFF);
|
||||||
|
_material->setDataVariance(osg::Object::DYNAMIC);
|
||||||
|
_group->getOrCreateStateSet()
|
||||||
|
->setAttribute(_material, ( osg::StateAttribute::ON
|
||||||
|
| osg::StateAttribute::OVERRIDE ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
_material->setEmission(
|
||||||
|
osg::Material::FRONT_AND_BACK,
|
||||||
|
osg::Vec4(emit, emit, emit, emit)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void ObjectPlacement::setCaptureEvents(bool enable)
|
||||||
|
{
|
||||||
|
if( !enable && _scene_user_data )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( enable && !_pick_cb )
|
||||||
|
_pick_cb = new ObjectPickCallback(_canvas);
|
||||||
|
|
||||||
|
_scene_user_data = SGSceneUserData::getOrCreateSceneUserData(_group);
|
||||||
|
_scene_user_data->setPickCallback(enable ? _pick_cb.get() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
bool ObjectPlacement::childChanged(SGPropertyNode* node)
|
||||||
|
{
|
||||||
|
if( node->getParent() != _node )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if( node->getNameString() == "emission" )
|
||||||
|
setEmission( node->getFloatValue() );
|
||||||
|
else if( node->getNameString() == "capture-events" )
|
||||||
|
setCaptureEvents( node->getBoolValue() );
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace canvas
|
||||||
|
} // namespace simgear
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
///@file
|
||||||
|
/// Placement for putting a canvas texture onto OpenSceneGraph objects.
|
||||||
|
///
|
||||||
|
/// It also provides a SGPickCallback for passing mouse events to the canvas and
|
||||||
|
/// manages emissive lighting of the placed canvas.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2013 Thomas Geymayer <tomgey@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 Library General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
|
||||||
|
#ifndef CANVAS_OBJECT_PLACEMENT_HXX_
|
||||||
|
#define CANVAS_OBJECT_PLACEMENT_HXX_
|
||||||
|
|
||||||
|
#include "CanvasPlacement.hxx"
|
||||||
|
#include "canvas_fwd.hxx"
|
||||||
|
|
||||||
|
#include <simgear/scene/util/SGSceneUserData.hxx>
|
||||||
|
#include <osg/Material>
|
||||||
|
|
||||||
|
namespace simgear
|
||||||
|
{
|
||||||
|
namespace canvas
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Place a Canvas onto an osg object (as texture).
|
||||||
|
*/
|
||||||
|
class ObjectPlacement:
|
||||||
|
public Placement
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
typedef osg::ref_ptr<osg::Group> GroupPtr;
|
||||||
|
typedef osg::ref_ptr<osg::Material> MaterialPtr;
|
||||||
|
|
||||||
|
ObjectPlacement( SGPropertyNode* node,
|
||||||
|
const GroupPtr& group,
|
||||||
|
const CanvasWeakPtr& canvas );
|
||||||
|
virtual ~ObjectPlacement();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set emissive lighting of the object the canvas is placed on.
|
||||||
|
*/
|
||||||
|
void setEmission(float emit);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether pick events should be captured.
|
||||||
|
*/
|
||||||
|
void setCaptureEvents(bool enable);
|
||||||
|
|
||||||
|
virtual bool childChanged(SGPropertyNode* child);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
typedef SGSharedPtr<SGPickCallback> PickCallbackPtr;
|
||||||
|
typedef osg::ref_ptr<SGSceneUserData> SGSceneUserDataPtr;
|
||||||
|
|
||||||
|
GroupPtr _group;
|
||||||
|
MaterialPtr _material;
|
||||||
|
CanvasWeakPtr _canvas;
|
||||||
|
PickCallbackPtr _pick_cb;
|
||||||
|
SGSceneUserDataPtr _scene_user_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace canvas
|
||||||
|
} // namespace simgear
|
||||||
|
|
||||||
|
#endif /* CANVAS_OBJECT_PLACEMENT_HXX_ */
|
||||||
@@ -20,10 +20,12 @@
|
|||||||
#define SG_CANVAS_SYSTEM_ADAPTER_HXX_
|
#define SG_CANVAS_SYSTEM_ADAPTER_HXX_
|
||||||
|
|
||||||
#include "canvas_fwd.hxx"
|
#include "canvas_fwd.hxx"
|
||||||
#include <simgear/nasal/nasal.h>
|
|
||||||
|
class SGSubsystem;
|
||||||
|
|
||||||
namespace simgear
|
namespace simgear
|
||||||
{
|
{
|
||||||
|
namespace HTTP { class Client; }
|
||||||
namespace canvas
|
namespace canvas
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -36,29 +38,8 @@ namespace canvas
|
|||||||
virtual void addCamera(osg::Camera* camera) const = 0;
|
virtual void addCamera(osg::Camera* camera) const = 0;
|
||||||
virtual void removeCamera(osg::Camera* camera) const = 0;
|
virtual void removeCamera(osg::Camera* camera) const = 0;
|
||||||
virtual osg::Image* getImage(const std::string& path) const = 0;
|
virtual osg::Image* getImage(const std::string& path) const = 0;
|
||||||
|
virtual SGSubsystem* getSubsystem(const std::string& name) const = 0;
|
||||||
virtual naContext getNasalContext() const = 0;
|
virtual HTTP::Client* getHTTPClient() const = 0;
|
||||||
|
|
||||||
/**
|
|
||||||
* Save passed reference to Nasal object from being deleted by the
|
|
||||||
* garbage collector.
|
|
||||||
*/
|
|
||||||
virtual int gcSave(naRef r) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Release an object previously passed to ::gcSave to allow it being
|
|
||||||
* cleaned up by the garbage collector.
|
|
||||||
*/
|
|
||||||
virtual void gcRelease(int key) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call a Nasal function with the given environment and arguments.
|
|
||||||
*/
|
|
||||||
virtual naRef callMethod( naRef code,
|
|
||||||
naRef self,
|
|
||||||
int argc,
|
|
||||||
naRef* args,
|
|
||||||
naRef locals ) = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace canvas
|
} // namespace canvas
|
||||||
|
|||||||
@@ -0,0 +1,346 @@
|
|||||||
|
// Window for placing a Canvas onto it (for dialogs, menus, etc.)
|
||||||
|
//
|
||||||
|
// Copyright (C) 2012 Thomas Geymayer <tomgey@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 Library General Public
|
||||||
|
// 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 "CanvasMgr.hxx"
|
||||||
|
#include "CanvasSystemAdapter.hxx"
|
||||||
|
#include "CanvasWindow.hxx"
|
||||||
|
|
||||||
|
#include <simgear/canvas/Canvas.hxx>
|
||||||
|
#include <simgear/scene/util/OsgMath.hxx>
|
||||||
|
|
||||||
|
#include <osgGA/GUIEventHandler>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
|
#include <boost/foreach.hpp>
|
||||||
|
|
||||||
|
namespace simgear
|
||||||
|
{
|
||||||
|
namespace canvas
|
||||||
|
{
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
const std::string Window::TYPE_NAME = "window";
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
Window::Window( const CanvasWeakPtr& canvas,
|
||||||
|
const SGPropertyNode_ptr& node,
|
||||||
|
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"),
|
||||||
|
_resize_left(node, "resize-left"),
|
||||||
|
_resize_status(node, "resize-status")
|
||||||
|
{
|
||||||
|
node->setFloatValue("source/right", 1);
|
||||||
|
node->setFloatValue("source/bottom", 1);
|
||||||
|
node->setBoolValue("source/normalized", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
Window::~Window()
|
||||||
|
{
|
||||||
|
if( _canvas_decoration )
|
||||||
|
_canvas_decoration->destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Window::update(double delta_time_sec)
|
||||||
|
{
|
||||||
|
if( _attributes_dirty & DECORATION )
|
||||||
|
{
|
||||||
|
updateDecoration();
|
||||||
|
_attributes_dirty &= ~DECORATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
Image::update(delta_time_sec);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Window::valueChanged(SGPropertyNode * node)
|
||||||
|
{
|
||||||
|
bool handled = false;
|
||||||
|
if( node->getParent() == _node )
|
||||||
|
{
|
||||||
|
handled = true;
|
||||||
|
const std::string& name = node->getNameString();
|
||||||
|
if( name == "resize" )
|
||||||
|
_resizable = node->getBoolValue();
|
||||||
|
else if( name == "update" )
|
||||||
|
update(0);
|
||||||
|
else if( name == "capture-events" )
|
||||||
|
_capture_events = node->getBoolValue();
|
||||||
|
else if( name == "decoration-border" )
|
||||||
|
parseDecorationBorder(node->getStringValue());
|
||||||
|
else if( boost::starts_with(name, "shadow-")
|
||||||
|
|| name == "content-size" )
|
||||||
|
_attributes_dirty |= DECORATION;
|
||||||
|
else
|
||||||
|
handled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !handled )
|
||||||
|
Image::valueChanged(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
osg::Group* Window::getGroup()
|
||||||
|
{
|
||||||
|
return getMatrixTransform();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
const SGVec2<float> Window::getPosition() const
|
||||||
|
{
|
||||||
|
const osg::Matrix& m = getMatrixTransform()->getMatrix();
|
||||||
|
return SGVec2<float>( m(3, 0), m(3, 1) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
const SGRect<float> Window::getScreenRegion() const
|
||||||
|
{
|
||||||
|
return getPosition() + getRegion();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Window::setCanvasContent(CanvasPtr canvas)
|
||||||
|
{
|
||||||
|
_canvas_content = canvas;
|
||||||
|
if( _layout )
|
||||||
|
{
|
||||||
|
canvas->setLayout(_layout);
|
||||||
|
_layout.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if( _image_content )
|
||||||
|
// Placement within decoration canvas
|
||||||
|
_image_content->setSrcCanvas(canvas);
|
||||||
|
else
|
||||||
|
setSrcCanvas(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
CanvasWeakPtr Window::getCanvasContent() const
|
||||||
|
{
|
||||||
|
return _canvas_content;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Window::setLayout(const LayoutRef& layout)
|
||||||
|
{
|
||||||
|
CanvasPtr canvas = _canvas_content.lock();
|
||||||
|
if( canvas )
|
||||||
|
canvas->setLayout(layout);
|
||||||
|
else
|
||||||
|
_layout = layout; // keep layout until content canvas is set
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
CanvasPtr Window::getCanvasDecoration() const
|
||||||
|
{
|
||||||
|
return _canvas_decoration;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
bool Window::isResizable() const
|
||||||
|
{
|
||||||
|
return _resizable;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
bool Window::isCapturingEvents() const
|
||||||
|
{
|
||||||
|
return _capture_events;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Window::setVisible(bool visible)
|
||||||
|
{
|
||||||
|
LayoutItem::setVisible(visible);
|
||||||
|
Element::setVisible(LayoutItem::isVisible());
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
bool Window::isVisible() const
|
||||||
|
{
|
||||||
|
return Element::isVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Window::raise()
|
||||||
|
{
|
||||||
|
// on writing the z-index the window always is moved to the top of all other
|
||||||
|
// windows with the same z-index.
|
||||||
|
set<int>("z-index", get<int>("z-index", 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Window::handleResize( uint8_t mode,
|
||||||
|
const osg::Vec2f& offset )
|
||||||
|
{
|
||||||
|
if( mode == NONE )
|
||||||
|
{
|
||||||
|
_resize_status = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if( mode & INIT )
|
||||||
|
{
|
||||||
|
_resize_top = getRegion().t();
|
||||||
|
_resize_right = getRegion().r();
|
||||||
|
_resize_bottom = getRegion().b();
|
||||||
|
_resize_left = getRegion().l();
|
||||||
|
_resize_status = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( mode & BOTTOM )
|
||||||
|
_resize_bottom = getRegion().b() + offset.y();
|
||||||
|
else if( mode & TOP )
|
||||||
|
_resize_top = getRegion().t() + offset.y();
|
||||||
|
|
||||||
|
if( mode & canvas::Window::RIGHT )
|
||||||
|
_resize_right = getRegion().r() + offset.x();
|
||||||
|
else if( mode & canvas::Window::LEFT )
|
||||||
|
_resize_left = getRegion().l() + offset.x();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Window::parseDecorationBorder(const std::string& str)
|
||||||
|
{
|
||||||
|
_decoration_border = simgear::CSSBorder::parse(str);
|
||||||
|
_attributes_dirty |= DECORATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Window::updateDecoration()
|
||||||
|
{
|
||||||
|
int shadow_radius = get<float>("shadow-radius") + 0.5;
|
||||||
|
if( shadow_radius < 2 )
|
||||||
|
shadow_radius = 0;
|
||||||
|
|
||||||
|
CanvasPtr content = _canvas_content.lock();
|
||||||
|
SGRect<int> content_view
|
||||||
|
(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
get<int>("content-size[0]", content ? content->getViewWidth() : 400),
|
||||||
|
get<int>("content-size[1]", content ? content->getViewHeight() : 300)
|
||||||
|
);
|
||||||
|
|
||||||
|
if( _decoration_border.isNone() && !shadow_radius )
|
||||||
|
{
|
||||||
|
setSrcCanvas(content);
|
||||||
|
set<int>("size[0]", content_view.width());
|
||||||
|
set<int>("size[1]", content_view.height());
|
||||||
|
|
||||||
|
_image_content.reset();
|
||||||
|
_image_shadow.reset();
|
||||||
|
if( _canvas_decoration )
|
||||||
|
_canvas_decoration->destroy();
|
||||||
|
_canvas_decoration.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !_canvas_decoration )
|
||||||
|
{
|
||||||
|
CanvasMgr* mgr = dynamic_cast<CanvasMgr*>(
|
||||||
|
Canvas::getSystemAdapter()->getSubsystem("Canvas")
|
||||||
|
);
|
||||||
|
|
||||||
|
if( !mgr )
|
||||||
|
{
|
||||||
|
SG_LOG(SG_GENERAL, SG_WARN, "canvas::Window: no canvas manager!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_canvas_decoration = mgr->createCanvas("window-decoration");
|
||||||
|
_canvas_decoration->getProps()
|
||||||
|
->setStringValue("background", "rgba(0,0,0,0)");
|
||||||
|
setSrcCanvas(_canvas_decoration);
|
||||||
|
|
||||||
|
_image_content = _canvas_decoration->getRootGroup()
|
||||||
|
->createChild<Image>("content");
|
||||||
|
_image_content->setSrcCanvas(content);
|
||||||
|
|
||||||
|
// Forward keyboard events to content
|
||||||
|
_image_content->setFocus();
|
||||||
|
|
||||||
|
// Draw content on top of decoration
|
||||||
|
_image_content->set<int>("z-index", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupPtr group_decoration =
|
||||||
|
_canvas_decoration->getOrCreateGroup("decoration");
|
||||||
|
group_decoration->set<int>("tf/t[0]", shadow_radius);
|
||||||
|
group_decoration->set<int>("tf/t[1]", shadow_radius);
|
||||||
|
// TODO do we need clipping or shall we trust the decorator not to draw over
|
||||||
|
// the shadow?
|
||||||
|
|
||||||
|
CSSBorder::Offsets const border =
|
||||||
|
_decoration_border.getAbsOffsets(content_view);
|
||||||
|
|
||||||
|
int shad2 = 2 * shadow_radius,
|
||||||
|
outer_width = border.l + content_view.width() + border.r + shad2,
|
||||||
|
outer_height = border.t + content_view.height() + border.b + shad2;
|
||||||
|
|
||||||
|
_canvas_decoration->setSizeX( outer_width );
|
||||||
|
_canvas_decoration->setSizeY( outer_height );
|
||||||
|
_canvas_decoration->setViewWidth( outer_width );
|
||||||
|
_canvas_decoration->setViewHeight( outer_height );
|
||||||
|
|
||||||
|
set<int>("size[0]", outer_width - shad2);
|
||||||
|
set<int>("size[1]", outer_height - shad2);
|
||||||
|
set<int>("outset", shadow_radius);
|
||||||
|
|
||||||
|
assert(_image_content);
|
||||||
|
_image_content->set<int>("x", shadow_radius + border.l);
|
||||||
|
_image_content->set<int>("y", shadow_radius + border.t);
|
||||||
|
_image_content->set<int>("size[0]", content_view.width());
|
||||||
|
_image_content->set<int>("size[1]", content_view.height());
|
||||||
|
|
||||||
|
if( !shadow_radius )
|
||||||
|
{
|
||||||
|
if( _image_shadow )
|
||||||
|
{
|
||||||
|
_image_shadow->destroy();
|
||||||
|
_image_shadow.reset();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int shadow_inset = std::max<int>(get<float>("shadow-inset") + 0.5, 0),
|
||||||
|
slice_width = shadow_radius + shadow_inset;
|
||||||
|
|
||||||
|
_image_shadow = _canvas_decoration->getRootGroup()
|
||||||
|
->getOrCreateChild<Image>("shadow");
|
||||||
|
_image_shadow->set<std::string>("src", "gui/images/shadow.png");
|
||||||
|
_image_shadow->set<float>("slice", 7);
|
||||||
|
_image_shadow->set<std::string>("fill", "#000000");
|
||||||
|
_image_shadow->set<float>("slice-width", slice_width);
|
||||||
|
_image_shadow->set<int>("size[0]", outer_width);
|
||||||
|
_image_shadow->set<int>("size[1]", outer_height);
|
||||||
|
|
||||||
|
// Draw shadow below decoration
|
||||||
|
_image_shadow->set<int>("z-index", -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace canvas
|
||||||
|
} // namespace simgear
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
// Window for placing a Canvas onto it (for dialogs, menus, etc.)
|
||||||
|
//
|
||||||
|
// Copyright (C) 2012 Thomas Geymayer <tomgey@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 Library General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
|
||||||
|
#ifndef CANVAS_WINDOW_HXX_
|
||||||
|
#define CANVAS_WINDOW_HXX_
|
||||||
|
|
||||||
|
#include <simgear/canvas/elements/CanvasImage.hxx>
|
||||||
|
#include <simgear/canvas/layout/Layout.hxx>
|
||||||
|
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||||
|
#include <simgear/props/PropertyBasedElement.hxx>
|
||||||
|
#include <simgear/props/propertyObject.hxx>
|
||||||
|
#include <simgear/misc/CSSBorder.hxx>
|
||||||
|
|
||||||
|
#include <osg/Geode>
|
||||||
|
#include <osg/Geometry>
|
||||||
|
|
||||||
|
namespace simgear
|
||||||
|
{
|
||||||
|
namespace canvas
|
||||||
|
{
|
||||||
|
|
||||||
|
class Window:
|
||||||
|
public Image,
|
||||||
|
public LayoutItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const std::string TYPE_NAME;
|
||||||
|
|
||||||
|
enum Resize
|
||||||
|
{
|
||||||
|
NONE = 0,
|
||||||
|
LEFT = 1,
|
||||||
|
RIGHT = LEFT << 1,
|
||||||
|
TOP = RIGHT << 1,
|
||||||
|
BOTTOM = TOP << 1,
|
||||||
|
INIT = BOTTOM << 1
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param node Property node containing settings for this window:
|
||||||
|
* capture-events Disable/Enable event capturing
|
||||||
|
* content-size[0-1] Size of content area (excluding
|
||||||
|
* decoration border)
|
||||||
|
* decoration-border Size of decoration border
|
||||||
|
* resize Enable resize cursor and properties
|
||||||
|
* shadow-inset Inset of shadow image
|
||||||
|
* shadow-radius Radius/outset of shadow image
|
||||||
|
*/
|
||||||
|
Window( const CanvasWeakPtr& canvas,
|
||||||
|
const SGPropertyNode_ptr& node,
|
||||||
|
const Style& parent_style = Style(),
|
||||||
|
Element* parent = 0 );
|
||||||
|
virtual ~Window();
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
void setCanvasContent(CanvasPtr canvas);
|
||||||
|
simgear::canvas::CanvasWeakPtr getCanvasContent() const;
|
||||||
|
|
||||||
|
void setLayout(const LayoutRef& layout);
|
||||||
|
|
||||||
|
CanvasPtr getCanvasDecoration() const;
|
||||||
|
|
||||||
|
bool isResizable() const;
|
||||||
|
bool isCapturingEvents() const;
|
||||||
|
|
||||||
|
virtual void setVisible(bool visible);
|
||||||
|
virtual bool isVisible() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves window on top of all other windows with the same z-index.
|
||||||
|
*
|
||||||
|
* @note If no z-index is set it defaults to 0.
|
||||||
|
*/
|
||||||
|
void raise();
|
||||||
|
|
||||||
|
void handleResize( uint8_t mode,
|
||||||
|
const osg::Vec2f& offset = osg::Vec2f() );
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
enum Attributes
|
||||||
|
{
|
||||||
|
DECORATION = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t _attributes_dirty;
|
||||||
|
|
||||||
|
CanvasPtr _canvas_decoration;
|
||||||
|
CanvasWeakPtr _canvas_content;
|
||||||
|
LayoutRef _layout;
|
||||||
|
|
||||||
|
ImagePtr _image_content,
|
||||||
|
_image_shadow;
|
||||||
|
|
||||||
|
bool _resizable,
|
||||||
|
_capture_events;
|
||||||
|
|
||||||
|
PropertyObject<int> _resize_top,
|
||||||
|
_resize_right,
|
||||||
|
_resize_bottom,
|
||||||
|
_resize_left,
|
||||||
|
_resize_status;
|
||||||
|
|
||||||
|
CSSBorder _decoration_border;
|
||||||
|
|
||||||
|
void parseDecorationBorder(const std::string& str);
|
||||||
|
void updateDecoration();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace canvas
|
||||||
|
} // namespace simgear
|
||||||
|
|
||||||
|
#endif /* CANVAS_WINDOW_HXX_ */
|
||||||
+130
-48
@@ -29,6 +29,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "ODGauge.hxx"
|
#include "ODGauge.hxx"
|
||||||
|
#include "Canvas.hxx"
|
||||||
#include "CanvasSystemAdapter.hxx"
|
#include "CanvasSystemAdapter.hxx"
|
||||||
|
|
||||||
#include <simgear/debug/logstream.hxx>
|
#include <simgear/debug/logstream.hxx>
|
||||||
@@ -43,6 +44,8 @@
|
|||||||
#include <osg/ShadeModel>
|
#include <osg/ShadeModel>
|
||||||
#include <osg/StateSet>
|
#include <osg/StateSet>
|
||||||
#include <osg/FrameBufferObject> // for GL_DEPTH_STENCIL_EXT on Windows
|
#include <osg/FrameBufferObject> // for GL_DEPTH_STENCIL_EXT on Windows
|
||||||
|
#include <osg/Version>
|
||||||
|
#include <osgUtil/RenderBin>
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
@@ -51,18 +54,67 @@ namespace simgear
|
|||||||
namespace canvas
|
namespace canvas
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class PreOrderBin:
|
||||||
|
public osgUtil::RenderBin
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
PreOrderBin()
|
||||||
|
{}
|
||||||
|
PreOrderBin( const RenderBin& rhs,
|
||||||
|
const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY ):
|
||||||
|
RenderBin(rhs, copyop)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual osg::Object* cloneType() const
|
||||||
|
{
|
||||||
|
return new PreOrderBin();
|
||||||
|
}
|
||||||
|
virtual osg::Object* clone(const osg::CopyOp& copyop) const
|
||||||
|
{
|
||||||
|
return new PreOrderBin(*this,copyop);
|
||||||
|
}
|
||||||
|
virtual bool isSameKindAs(const osg::Object* obj) const
|
||||||
|
{
|
||||||
|
return dynamic_cast<const PreOrderBin*>(obj) != 0L;
|
||||||
|
}
|
||||||
|
virtual const char* className() const
|
||||||
|
{
|
||||||
|
return "PreOrderBin";
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void sort()
|
||||||
|
{
|
||||||
|
// Do not sort to keep traversal order...
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef OSG_INIT_SINGLETON_PROXY
|
||||||
|
/**
|
||||||
|
* http://svn.openscenegraph.org/osg/OpenSceneGraph/trunk/include/osg/Object
|
||||||
|
*
|
||||||
|
* Helper macro that creates a static proxy object to call singleton function
|
||||||
|
* on it's construction, ensuring that the singleton gets initialized at
|
||||||
|
* startup.
|
||||||
|
*/
|
||||||
|
# define OSG_INIT_SINGLETON_PROXY(ProxyName, Func)\
|
||||||
|
static struct ProxyName{ ProxyName() { Func; } } s_##ProxyName;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
OSG_INIT_SINGLETON_PROXY(
|
||||||
|
PreOrderBinProxy,
|
||||||
|
(osgUtil::RenderBin::addRenderBinPrototype("PreOrderBin", new PreOrderBin))
|
||||||
|
);
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
ODGauge::ODGauge():
|
ODGauge::ODGauge():
|
||||||
_size_x( -1 ),
|
_size_x( -1 ),
|
||||||
_size_y( -1 ),
|
_size_y( -1 ),
|
||||||
_view_width( -1 ),
|
_view_width( -1 ),
|
||||||
_view_height( -1 ),
|
_view_height( -1 ),
|
||||||
_use_image_coords( false ),
|
_flags(0),
|
||||||
_use_stencil( false ),
|
|
||||||
_use_mipmapping( false ),
|
|
||||||
_coverage_samples( 0 ),
|
_coverage_samples( 0 ),
|
||||||
_color_samples( 0 ),
|
_color_samples( 0 )
|
||||||
rtAvailable( false )
|
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -73,12 +125,6 @@ namespace canvas
|
|||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
void ODGauge::setSystemAdapter(const SystemAdapterPtr& system_adapter)
|
|
||||||
{
|
|
||||||
_system_adapter = system_adapter;
|
|
||||||
}
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void ODGauge::setSize(int size_x, int size_y)
|
void ODGauge::setSize(int size_x, int size_y)
|
||||||
{
|
{
|
||||||
@@ -101,42 +147,43 @@ namespace canvas
|
|||||||
updateCoordinateFrame();
|
updateCoordinateFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
osg::Vec2s ODGauge::getViewSize() const
|
||||||
|
{
|
||||||
|
return osg::Vec2s(_view_width, _view_height);
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void ODGauge::useImageCoords(bool use)
|
void ODGauge::useImageCoords(bool use)
|
||||||
{
|
{
|
||||||
if( use == _use_image_coords )
|
if( updateFlag(USE_IMAGE_COORDS, use) && texture )
|
||||||
return;
|
|
||||||
|
|
||||||
_use_image_coords = use;
|
|
||||||
|
|
||||||
if( texture )
|
|
||||||
updateCoordinateFrame();
|
updateCoordinateFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void ODGauge::useStencil(bool use)
|
void ODGauge::useStencil(bool use)
|
||||||
{
|
{
|
||||||
if( use == _use_stencil )
|
if( updateFlag(USE_STENCIL, use) && texture )
|
||||||
return;
|
|
||||||
|
|
||||||
_use_stencil = use;
|
|
||||||
|
|
||||||
if( texture )
|
|
||||||
updateStencil();
|
updateStencil();
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void ODGauge::setSampling( bool mipmapping,
|
void ODGauge::useAdditiveBlend(bool use)
|
||||||
int coverage_samples,
|
|
||||||
int color_samples )
|
|
||||||
{
|
{
|
||||||
if( _use_mipmapping == mipmapping
|
if( updateFlag(USE_ADDITIVE_BLEND, use) && camera )
|
||||||
|
updateBlendMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void ODGauge::setSampling( bool mipmapping,
|
||||||
|
int coverage_samples,
|
||||||
|
int color_samples )
|
||||||
|
{
|
||||||
|
if( !updateFlag(USE_MIPMAPPING, mipmapping)
|
||||||
&& _coverage_samples == coverage_samples
|
&& _coverage_samples == coverage_samples
|
||||||
&& _color_samples == color_samples )
|
&& _color_samples == color_samples )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_use_mipmapping = mipmapping;
|
|
||||||
|
|
||||||
if( color_samples > coverage_samples )
|
if( color_samples > coverage_samples )
|
||||||
{
|
{
|
||||||
SG_LOG
|
SG_LOG
|
||||||
@@ -162,9 +209,9 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
bool ODGauge::serviceable(void)
|
bool ODGauge::serviceable() const
|
||||||
{
|
{
|
||||||
return rtAvailable;
|
return _flags & AVAILABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -196,32 +243,39 @@ namespace canvas
|
|||||||
osg::PolygonMode::FILL ),
|
osg::PolygonMode::FILL ),
|
||||||
osg::StateAttribute::ON );
|
osg::StateAttribute::ON );
|
||||||
stateSet->setAttributeAndModes(
|
stateSet->setAttributeAndModes(
|
||||||
new osg::AlphaFunc(osg::AlphaFunc::GREATER, 0.0f),
|
new osg::AlphaFunc(osg::AlphaFunc::GREATER, 0.001f),
|
||||||
osg::StateAttribute::ON );
|
osg::StateAttribute::ON );
|
||||||
stateSet->setAttribute(new osg::ShadeModel(osg::ShadeModel::FLAT));
|
stateSet->setAttribute(new osg::ShadeModel(osg::ShadeModel::FLAT));
|
||||||
stateSet->setAttributeAndModes(
|
|
||||||
new osg::BlendFunc( osg::BlendFunc::SRC_ALPHA,
|
|
||||||
osg::BlendFunc::ONE_MINUS_SRC_ALPHA),
|
|
||||||
osg::StateAttribute::ON );
|
|
||||||
if( !texture )
|
if( !texture )
|
||||||
{
|
{
|
||||||
texture = new osg::Texture2D;
|
texture = new osg::Texture2D;
|
||||||
|
texture->setResizeNonPowerOfTwoHint(false);
|
||||||
texture->setTextureSize(_size_x, _size_y);
|
texture->setTextureSize(_size_x, _size_y);
|
||||||
texture->setInternalFormat(GL_RGBA);
|
texture->setInternalFormat(GL_RGBA);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSampling();
|
updateSampling();
|
||||||
|
updateBlendMode();
|
||||||
|
|
||||||
if( _system_adapter )
|
if( Canvas::getSystemAdapter() )
|
||||||
_system_adapter->addCamera(camera.get());
|
Canvas::getSystemAdapter()->addCamera(camera.get());
|
||||||
|
|
||||||
rtAvailable = true;
|
_flags |= AVAILABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void ODGauge::reinit()
|
void ODGauge::reinit()
|
||||||
{
|
{
|
||||||
osg::NodeCallback* cull_callback = camera ? camera->getCullCallback() : 0;
|
osg::NodeCallback* cull_callback =
|
||||||
|
camera
|
||||||
|
#if OSG_VERSION_LESS_THAN(3,3,2)
|
||||||
|
? camera->getCullCallback()
|
||||||
|
#else
|
||||||
|
? dynamic_cast<osg::NodeCallback*>(camera->getCullCallback())
|
||||||
|
#endif
|
||||||
|
: 0;
|
||||||
|
|
||||||
clear();
|
clear();
|
||||||
allocRT(cull_callback);
|
allocRT(cull_callback);
|
||||||
}
|
}
|
||||||
@@ -229,12 +283,22 @@ namespace canvas
|
|||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void ODGauge::clear()
|
void ODGauge::clear()
|
||||||
{
|
{
|
||||||
if( camera.valid() && _system_adapter )
|
if( camera.valid() && Canvas::getSystemAdapter() )
|
||||||
_system_adapter->removeCamera(camera.get());
|
Canvas::getSystemAdapter()->removeCamera(camera.get());
|
||||||
camera.release();
|
camera.release();
|
||||||
texture.release();
|
texture.release();
|
||||||
|
|
||||||
rtAvailable = false;
|
_flags &= ~AVAILABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
bool ODGauge::updateFlag(Flags flag, bool enable)
|
||||||
|
{
|
||||||
|
if( bool(_flags & flag) == enable )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_flags ^= flag;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -249,7 +313,7 @@ namespace canvas
|
|||||||
|
|
||||||
camera->setViewport(0, 0, _size_x, _size_y);
|
camera->setViewport(0, 0, _size_x, _size_y);
|
||||||
|
|
||||||
if( _use_image_coords )
|
if( _flags & USE_IMAGE_COORDS )
|
||||||
camera->setProjectionMatrix(
|
camera->setProjectionMatrix(
|
||||||
osg::Matrix::ortho2D(0, _view_width, _view_height, 0)
|
osg::Matrix::ortho2D(0, _view_width, _view_height, 0)
|
||||||
);
|
);
|
||||||
@@ -267,7 +331,7 @@ namespace canvas
|
|||||||
|
|
||||||
GLbitfield mask = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT;
|
GLbitfield mask = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT;
|
||||||
|
|
||||||
if( _use_stencil)
|
if( _flags & USE_STENCIL )
|
||||||
{
|
{
|
||||||
camera->attach( osg::Camera::PACKED_DEPTH_STENCIL_BUFFER,
|
camera->attach( osg::Camera::PACKED_DEPTH_STENCIL_BUFFER,
|
||||||
GL_DEPTH_STENCIL_EXT );
|
GL_DEPTH_STENCIL_EXT );
|
||||||
@@ -289,18 +353,36 @@ namespace canvas
|
|||||||
|
|
||||||
texture->setFilter(
|
texture->setFilter(
|
||||||
osg::Texture2D::MIN_FILTER,
|
osg::Texture2D::MIN_FILTER,
|
||||||
_use_mipmapping ? osg::Texture2D::LINEAR_MIPMAP_LINEAR
|
(_flags & USE_MIPMAPPING) ? osg::Texture2D::LINEAR_MIPMAP_LINEAR
|
||||||
: osg::Texture2D::LINEAR
|
: osg::Texture2D::LINEAR
|
||||||
);
|
);
|
||||||
camera->attach(
|
camera->attach(
|
||||||
osg::Camera::COLOR_BUFFER,
|
osg::Camera::COLOR_BUFFER,
|
||||||
texture.get(),
|
texture.get(),
|
||||||
0, 0,
|
0, 0,
|
||||||
_use_mipmapping,
|
_flags & USE_MIPMAPPING,
|
||||||
_coverage_samples,
|
_coverage_samples,
|
||||||
_color_samples
|
_color_samples
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void ODGauge::updateBlendMode()
|
||||||
|
{
|
||||||
|
assert( camera );
|
||||||
|
|
||||||
|
camera->getOrCreateStateSet()
|
||||||
|
->setAttributeAndModes
|
||||||
|
(
|
||||||
|
(_flags & USE_ADDITIVE_BLEND)
|
||||||
|
? new osg::BlendFunc( osg::BlendFunc::SRC_ALPHA,
|
||||||
|
osg::BlendFunc::ONE_MINUS_SRC_ALPHA,
|
||||||
|
osg::BlendFunc::ONE,
|
||||||
|
osg::BlendFunc::ONE )
|
||||||
|
: new osg::BlendFunc( osg::BlendFunc::SRC_ALPHA,
|
||||||
|
osg::BlendFunc::ONE_MINUS_SRC_ALPHA )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace canvas
|
} // namespace canvas
|
||||||
} // namespace simgear
|
} // namespace simgear
|
||||||
|
|||||||
@@ -53,8 +53,6 @@ namespace canvas
|
|||||||
ODGauge();
|
ODGauge();
|
||||||
virtual ~ODGauge();
|
virtual ~ODGauge();
|
||||||
|
|
||||||
void setSystemAdapter(const SystemAdapterPtr& system_adapter);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the size of the render target.
|
* Set the size of the render target.
|
||||||
*
|
*
|
||||||
@@ -71,6 +69,8 @@ namespace canvas
|
|||||||
*/
|
*/
|
||||||
void setViewSize(int width, int height = -1);
|
void setViewSize(int width, int height = -1);
|
||||||
|
|
||||||
|
osg::Vec2s getViewSize() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DEPRECATED
|
* DEPRECATED
|
||||||
*
|
*
|
||||||
@@ -91,6 +91,12 @@ namespace canvas
|
|||||||
*/
|
*/
|
||||||
void useStencil(bool use = true);
|
void useStencil(bool use = true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable/Disable additive alpha blending (Can improve results with
|
||||||
|
* transparent background)
|
||||||
|
*/
|
||||||
|
void useAdditiveBlend(bool use = true);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set sampling parameters for mipmapping and coverage sampling
|
* Set sampling parameters for mipmapping and coverage sampling
|
||||||
* antialiasing.
|
* antialiasing.
|
||||||
@@ -112,7 +118,7 @@ namespace canvas
|
|||||||
* Say if we can render to a texture.
|
* Say if we can render to a texture.
|
||||||
* @return true if rtt is available
|
* @return true if rtt is available
|
||||||
*/
|
*/
|
||||||
bool serviceable(void);
|
bool serviceable() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the OSG camera for drawing this gauge.
|
* Get the OSG camera for drawing this gauge.
|
||||||
@@ -128,27 +134,35 @@ namespace canvas
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
SystemAdapterPtr _system_adapter;
|
|
||||||
|
|
||||||
int _size_x,
|
int _size_x,
|
||||||
_size_y,
|
_size_y,
|
||||||
_view_width,
|
_view_width,
|
||||||
_view_height;
|
_view_height;
|
||||||
bool _use_image_coords,
|
|
||||||
_use_stencil,
|
enum Flags
|
||||||
_use_mipmapping;
|
{
|
||||||
|
AVAILABLE = 1,
|
||||||
|
USE_IMAGE_COORDS = AVAILABLE << 1,
|
||||||
|
USE_STENCIL = USE_IMAGE_COORDS << 1,
|
||||||
|
USE_MIPMAPPING = USE_STENCIL << 1,
|
||||||
|
USE_ADDITIVE_BLEND = USE_MIPMAPPING << 1
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t _flags;
|
||||||
|
|
||||||
// Multisampling parameters
|
// Multisampling parameters
|
||||||
int _coverage_samples,
|
int _coverage_samples,
|
||||||
_color_samples;
|
_color_samples;
|
||||||
|
|
||||||
bool rtAvailable;
|
|
||||||
osg::ref_ptr<osg::Camera> camera;
|
osg::ref_ptr<osg::Camera> camera;
|
||||||
osg::ref_ptr<osg::Texture2D> texture;
|
osg::ref_ptr<osg::Texture2D> texture;
|
||||||
|
|
||||||
|
bool updateFlag(Flags flag, bool enable);
|
||||||
|
|
||||||
void updateCoordinateFrame();
|
void updateCoordinateFrame();
|
||||||
void updateStencil();
|
void updateStencil();
|
||||||
void updateSampling();
|
void updateSampling();
|
||||||
|
void updateBlendMode();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -627,6 +627,7 @@ VG_API_CALL const VGubyte * vgGetString(VGStringID name);
|
|||||||
#define OVG_SH_blend_dst_atop 1
|
#define OVG_SH_blend_dst_atop 1
|
||||||
|
|
||||||
VG_API_CALL VGboolean vgCreateContextSH(VGint width, VGint height);
|
VG_API_CALL VGboolean vgCreateContextSH(VGint width, VGint height);
|
||||||
|
VG_API_CALL VGboolean vgHasContextSH();
|
||||||
VG_API_CALL void vgResizeSurfaceSH(VGint width, VGint height);
|
VG_API_CALL void vgResizeSurfaceSH(VGint width, VGint height);
|
||||||
VG_API_CALL void vgDestroyContextSH(void);
|
VG_API_CALL void vgDestroyContextSH(void);
|
||||||
|
|
||||||
|
|||||||
@@ -38,4 +38,9 @@
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// We currently do not support using images (inside paths). If we were going to
|
||||||
|
// use it loading and unloading needs to happen within OpenSceneGraph to handle
|
||||||
|
// synchronization correctly in multithreading mode.
|
||||||
|
#define SH_NO_IMAGE
|
||||||
|
|
||||||
#endif // __SHCONFIG_H
|
#endif // __SHCONFIG_H
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ VG_API_CALL VGboolean vgCreateContextSH(VGint width, VGint height)
|
|||||||
|
|
||||||
glMatrixMode(GL_PROJECTION);
|
glMatrixMode(GL_PROJECTION);
|
||||||
glLoadIdentity();
|
glLoadIdentity();
|
||||||
gluOrtho2D(0,width,0,height);
|
glOrtho(0, width, 0, height, -1, 1);
|
||||||
|
|
||||||
glMatrixMode(GL_MODELVIEW);
|
glMatrixMode(GL_MODELVIEW);
|
||||||
glLoadIdentity();*/
|
glLoadIdentity();*/
|
||||||
@@ -61,6 +61,11 @@ VG_API_CALL VGboolean vgCreateContextSH(VGint width, VGint height)
|
|||||||
return VG_TRUE;
|
return VG_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VG_API_CALL VGboolean vgHasContextSH()
|
||||||
|
{
|
||||||
|
return g_context != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
VG_API_CALL void vgResizeSurfaceSH(VGint width, VGint height)
|
VG_API_CALL void vgResizeSurfaceSH(VGint width, VGint height)
|
||||||
{
|
{
|
||||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||||
@@ -74,7 +79,7 @@ VG_API_CALL void vgResizeSurfaceSH(VGint width, VGint height)
|
|||||||
|
|
||||||
glMatrixMode(GL_PROJECTION);
|
glMatrixMode(GL_PROJECTION);
|
||||||
glLoadIdentity();
|
glLoadIdentity();
|
||||||
gluOrtho2D(0,width,0,height);
|
glOrtho(0, width, 0, height, -1, 1);
|
||||||
|
|
||||||
glMatrixMode(GL_MODELVIEW);
|
glMatrixMode(GL_MODELVIEW);
|
||||||
glLoadIdentity();
|
glLoadIdentity();
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
|
|
||||||
#ifndef VG_API_MACOSX
|
#if !defined(VG_API_MACOSX) && !defined(__FreeBSD__)
|
||||||
# include <malloc.h>
|
# include <malloc.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -156,20 +156,16 @@ SHfloat getMaxFloat();
|
|||||||
|
|
||||||
/* OpenGL headers */
|
/* OpenGL headers */
|
||||||
|
|
||||||
#if defined(VG_API_LINUX)
|
#if defined(VG_API_LINUX) || defined(VG_API_FREEBSD)
|
||||||
#include <GL/gl.h>
|
#include <GL/gl.h>
|
||||||
#include <GL/glu.h>
|
|
||||||
#include <GL/glx.h>
|
#include <GL/glx.h>
|
||||||
#elif defined(VG_API_MACOSX)
|
#elif defined(VG_API_MACOSX)
|
||||||
#include <OpenGL/gl.h>
|
#include <OpenGL/gl.h>
|
||||||
#include <OpenGL/glu.h>
|
|
||||||
#elif defined(VG_API_WINDOWS)
|
#elif defined(VG_API_WINDOWS)
|
||||||
#include <GL/gl.h>
|
#include <GL/gl.h>
|
||||||
#include <GL/glu.h>
|
|
||||||
#else
|
#else
|
||||||
#define GL_GLEXT_LEGACY /* don't include glext.h */
|
#define GL_GLEXT_LEGACY /* don't include glext.h */
|
||||||
#include <GL/gl.h>
|
#include <GL/gl.h>
|
||||||
#include <GL/glu.h>
|
|
||||||
#include <GL/glx.h>
|
#include <GL/glx.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
#define _ARRAY_DEFINE
|
#define _ARRAY_DEFINE
|
||||||
#include "shArrayBase.h"
|
#include "shArrayBase.h"
|
||||||
|
|
||||||
|
#ifndef SH_NO_IMAGE
|
||||||
|
|
||||||
/*-----------------------------------------------------------
|
/*-----------------------------------------------------------
|
||||||
* Prepares the proper pixel pack/unpack info for the given
|
* Prepares the proper pixel pack/unpack info for the given
|
||||||
@@ -440,6 +441,7 @@ void shLoadColor(SHColor *c, const void *data, SHImageFormatDesc *f)
|
|||||||
if (f->rmask == 0x0) { c->r = 1.0f; c->g = 1.0f; c->b = 1.0f; }
|
if (f->rmask == 0x0) { c->r = 1.0f; c->g = 1.0f; c->b = 1.0f; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif // SH_NO_IMAGE
|
||||||
|
|
||||||
/*----------------------------------------------
|
/*----------------------------------------------
|
||||||
* Color and Image constructors and destructors
|
* Color and Image constructors and destructors
|
||||||
@@ -461,18 +463,28 @@ void SHImage_ctor(SHImage *i)
|
|||||||
i->data = NULL;
|
i->data = NULL;
|
||||||
i->width = 0;
|
i->width = 0;
|
||||||
i->height = 0;
|
i->height = 0;
|
||||||
|
|
||||||
|
#ifdef SH_NO_IMAGE
|
||||||
|
printf("ShivaVG: images not supported!");
|
||||||
|
#else
|
||||||
glGenTextures(1, &i->texture);
|
glGenTextures(1, &i->texture);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void SHImage_dtor(SHImage *i)
|
void SHImage_dtor(SHImage *i)
|
||||||
{
|
{
|
||||||
if (i->data != NULL)
|
if (i->data != NULL)
|
||||||
free(i->data);
|
free(i->data);
|
||||||
|
|
||||||
|
#ifdef SH_NO_IMAGE
|
||||||
|
printf("ShivaVG: images not supported!");
|
||||||
|
#else
|
||||||
if (glIsTexture(i->texture))
|
if (glIsTexture(i->texture))
|
||||||
glDeleteTextures(1, &i->texture);
|
glDeleteTextures(1, &i->texture);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef SH_NO_IMAGE
|
||||||
/*--------------------------------------------------------
|
/*--------------------------------------------------------
|
||||||
* Finds appropriate OpenGL texture size for the size of
|
* Finds appropriate OpenGL texture size for the size of
|
||||||
* the given image
|
* the given image
|
||||||
@@ -553,6 +565,7 @@ void shUpdateImageTexture(SHImage *i, VGContext *c)
|
|||||||
i->texwidth, i->texheight, 0,
|
i->texwidth, i->texheight, 0,
|
||||||
i->fd.glformat, i->fd.gltype, i->data);
|
i->fd.glformat, i->fd.gltype, i->data);
|
||||||
}
|
}
|
||||||
|
#endif // SH_NO_IMAGE
|
||||||
|
|
||||||
/*----------------------------------------------------------
|
/*----------------------------------------------------------
|
||||||
* Creates a new image object and returns the handle to it
|
* Creates a new image object and returns the handle to it
|
||||||
@@ -562,6 +575,10 @@ VG_API_CALL VGImage vgCreateImage(VGImageFormat format,
|
|||||||
VGint width, VGint height,
|
VGint width, VGint height,
|
||||||
VGbitfield allowedQuality)
|
VGbitfield allowedQuality)
|
||||||
{
|
{
|
||||||
|
#ifdef SH_NO_IMAGE
|
||||||
|
printf("ShivaVG: images not supported!");
|
||||||
|
return VG_INVALID_HANDLE;
|
||||||
|
#else
|
||||||
SHImage *i = NULL;
|
SHImage *i = NULL;
|
||||||
SHImageFormatDesc fd;
|
SHImageFormatDesc fd;
|
||||||
VG_GETCONTEXT(VG_INVALID_HANDLE);
|
VG_GETCONTEXT(VG_INVALID_HANDLE);
|
||||||
@@ -614,12 +631,16 @@ VG_API_CALL VGImage vgCreateImage(VGImageFormat format,
|
|||||||
|
|
||||||
/* Add to resource list */
|
/* Add to resource list */
|
||||||
shImageArrayPushBack(&context->images, i);
|
shImageArrayPushBack(&context->images, i);
|
||||||
|
|
||||||
VG_RETURN((VGImage)i);
|
VG_RETURN((VGImage)i);
|
||||||
|
#endif // SH_NO_IMAGE
|
||||||
}
|
}
|
||||||
|
|
||||||
VG_API_CALL void vgDestroyImage(VGImage image)
|
VG_API_CALL void vgDestroyImage(VGImage image)
|
||||||
{
|
{
|
||||||
|
#ifdef SH_NO_IMAGE
|
||||||
|
printf("ShivaVG: images not supported!");
|
||||||
|
#else
|
||||||
SHint index;
|
SHint index;
|
||||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||||
|
|
||||||
@@ -630,8 +651,9 @@ VG_API_CALL void vgDestroyImage(VGImage image)
|
|||||||
/* Delete object and remove resource */
|
/* Delete object and remove resource */
|
||||||
SH_DELETEOBJ(SHImage, (SHImage*)image);
|
SH_DELETEOBJ(SHImage, (SHImage*)image);
|
||||||
shImageArrayRemoveAt(&context->images, index);
|
shImageArrayRemoveAt(&context->images, index);
|
||||||
|
|
||||||
VG_RETURN(VG_NO_RETVAL);
|
VG_RETURN(VG_NO_RETVAL);
|
||||||
|
#endif // SH_NO_IMAGE
|
||||||
}
|
}
|
||||||
|
|
||||||
/*---------------------------------------------------
|
/*---------------------------------------------------
|
||||||
@@ -642,6 +664,9 @@ VG_API_CALL void vgDestroyImage(VGImage image)
|
|||||||
VG_API_CALL void vgClearImage(VGImage image,
|
VG_API_CALL void vgClearImage(VGImage image,
|
||||||
VGint x, VGint y, VGint width, VGint height)
|
VGint x, VGint y, VGint width, VGint height)
|
||||||
{
|
{
|
||||||
|
#ifdef SH_NO_IMAGE
|
||||||
|
printf("ShivaVG: images not supported!");
|
||||||
|
#else
|
||||||
SHImage *i;
|
SHImage *i;
|
||||||
SHColor clear;
|
SHColor clear;
|
||||||
SHuint8 *data;
|
SHuint8 *data;
|
||||||
@@ -682,9 +707,13 @@ VG_API_CALL void vgClearImage(VGImage image,
|
|||||||
}}
|
}}
|
||||||
|
|
||||||
shUpdateImageTexture(i, context);
|
shUpdateImageTexture(i, context);
|
||||||
|
|
||||||
VG_RETURN(VG_NO_RETVAL);
|
VG_RETURN(VG_NO_RETVAL);
|
||||||
|
#endif // SH_NO_IMAGE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef SH_NO_IMAGE
|
||||||
|
|
||||||
/*------------------------------------------------------------
|
/*------------------------------------------------------------
|
||||||
* Generic function for copying a rectangle area of pixels
|
* Generic function for copying a rectangle area of pixels
|
||||||
* of size (width,height) among two data buffers. The size of
|
* of size (width,height) among two data buffers. The size of
|
||||||
@@ -795,6 +824,8 @@ void shCopyPixels(SHuint8 *dst, VGImageFormat dstFormat, SHint dstStride,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif // SH_NO_IMAGE
|
||||||
|
|
||||||
/*---------------------------------------------------------
|
/*---------------------------------------------------------
|
||||||
* Copies a rectangle area of pixels of size (width,height)
|
* Copies a rectangle area of pixels of size (width,height)
|
||||||
* from given data buffer to image surface at destination
|
* from given data buffer to image surface at destination
|
||||||
@@ -806,6 +837,9 @@ VG_API_CALL void vgImageSubData(VGImage image,
|
|||||||
VGImageFormat dataFormat,
|
VGImageFormat dataFormat,
|
||||||
VGint x, VGint y, VGint width, VGint height)
|
VGint x, VGint y, VGint width, VGint height)
|
||||||
{
|
{
|
||||||
|
#ifdef SH_NO_IMAGE
|
||||||
|
printf("ShivaVG: images not supported!");
|
||||||
|
#else
|
||||||
SHImage *i;
|
SHImage *i;
|
||||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||||
|
|
||||||
@@ -836,7 +870,9 @@ VG_API_CALL void vgImageSubData(VGImage image,
|
|||||||
x, y, 0, 0, width, height);
|
x, y, 0, 0, width, height);
|
||||||
|
|
||||||
shUpdateImageTexture(i, context);
|
shUpdateImageTexture(i, context);
|
||||||
|
|
||||||
VG_RETURN(VG_NO_RETVAL);
|
VG_RETURN(VG_NO_RETVAL);
|
||||||
|
#endif // SH_NO_IMAGE
|
||||||
}
|
}
|
||||||
|
|
||||||
/*---------------------------------------------------------
|
/*---------------------------------------------------------
|
||||||
@@ -851,6 +887,9 @@ VG_API_CALL void vgGetImageSubData(VGImage image,
|
|||||||
VGint x, VGint y,
|
VGint x, VGint y,
|
||||||
VGint width, VGint height)
|
VGint width, VGint height)
|
||||||
{
|
{
|
||||||
|
#ifdef SH_NO_IMAGE
|
||||||
|
printf("ShivaVG: images not supported!");
|
||||||
|
#else
|
||||||
SHImage *i;
|
SHImage *i;
|
||||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||||
|
|
||||||
@@ -878,9 +917,10 @@ VG_API_CALL void vgGetImageSubData(VGImage image,
|
|||||||
shCopyPixels(data, dataFormat, dataStride,
|
shCopyPixels(data, dataFormat, dataStride,
|
||||||
i->data, i->fd.vgformat, i->texwidth * i->fd.bytes,
|
i->data, i->fd.vgformat, i->texwidth * i->fd.bytes,
|
||||||
width, height, i->width, i->height,
|
width, height, i->width, i->height,
|
||||||
0,0,x,x,width,height);
|
0,0,x,y,width,height);
|
||||||
|
|
||||||
VG_RETURN(VG_NO_RETVAL);
|
VG_RETURN(VG_NO_RETVAL);
|
||||||
|
#endif // SH_NO_IMAGE
|
||||||
}
|
}
|
||||||
|
|
||||||
/*----------------------------------------------------------
|
/*----------------------------------------------------------
|
||||||
@@ -894,6 +934,9 @@ VG_API_CALL void vgCopyImage(VGImage dst, VGint dx, VGint dy,
|
|||||||
VGint width, VGint height,
|
VGint width, VGint height,
|
||||||
VGboolean dither)
|
VGboolean dither)
|
||||||
{
|
{
|
||||||
|
#ifdef SH_NO_IMAGE
|
||||||
|
printf("ShivaVG: images not supported!");
|
||||||
|
#else
|
||||||
SHImage *s, *d;
|
SHImage *s, *d;
|
||||||
SHuint8 *pixels;
|
SHuint8 *pixels;
|
||||||
|
|
||||||
@@ -933,7 +976,9 @@ VG_API_CALL void vgCopyImage(VGImage dst, VGint dx, VGint dy,
|
|||||||
free(pixels);
|
free(pixels);
|
||||||
|
|
||||||
shUpdateImageTexture(d, context);
|
shUpdateImageTexture(d, context);
|
||||||
|
|
||||||
VG_RETURN(VG_NO_RETVAL);
|
VG_RETURN(VG_NO_RETVAL);
|
||||||
|
#endif // SH_NO_IMAGE
|
||||||
}
|
}
|
||||||
|
|
||||||
/*---------------------------------------------------------
|
/*---------------------------------------------------------
|
||||||
@@ -946,6 +991,9 @@ VG_API_CALL void vgSetPixels(VGint dx, VGint dy,
|
|||||||
VGImage src, VGint sx, VGint sy,
|
VGImage src, VGint sx, VGint sy,
|
||||||
VGint width, VGint height)
|
VGint width, VGint height)
|
||||||
{
|
{
|
||||||
|
#ifdef SH_NO_IMAGE
|
||||||
|
printf("ShivaVG: images not supported!");
|
||||||
|
#else
|
||||||
SHImage *i;
|
SHImage *i;
|
||||||
SHuint8 *pixels;
|
SHuint8 *pixels;
|
||||||
SHImageFormatDesc winfd;
|
SHImageFormatDesc winfd;
|
||||||
@@ -986,6 +1034,7 @@ VG_API_CALL void vgSetPixels(VGint dx, VGint dy,
|
|||||||
free(pixels);
|
free(pixels);
|
||||||
|
|
||||||
VG_RETURN(VG_NO_RETVAL);
|
VG_RETURN(VG_NO_RETVAL);
|
||||||
|
#endif // SH_NO_IMAGE
|
||||||
}
|
}
|
||||||
|
|
||||||
/*---------------------------------------------------------
|
/*---------------------------------------------------------
|
||||||
@@ -999,6 +1048,9 @@ VG_API_CALL void vgWritePixels(const void * data, VGint dataStride,
|
|||||||
VGint dx, VGint dy,
|
VGint dx, VGint dy,
|
||||||
VGint width, VGint height)
|
VGint width, VGint height)
|
||||||
{
|
{
|
||||||
|
#ifdef SH_NO_IMAGE
|
||||||
|
printf("ShivaVG: images not supported!");
|
||||||
|
#else
|
||||||
SHuint8 *pixels;
|
SHuint8 *pixels;
|
||||||
SHImageFormatDesc winfd;
|
SHImageFormatDesc winfd;
|
||||||
|
|
||||||
@@ -1043,7 +1095,8 @@ VG_API_CALL void vgWritePixels(const void * data, VGint dataStride,
|
|||||||
|
|
||||||
free(pixels);
|
free(pixels);
|
||||||
|
|
||||||
VG_RETURN(VG_NO_RETVAL);
|
VG_RETURN(VG_NO_RETVAL);
|
||||||
|
#endif // SH_NO_IMAGE
|
||||||
}
|
}
|
||||||
|
|
||||||
/*-----------------------------------------------------------
|
/*-----------------------------------------------------------
|
||||||
@@ -1056,6 +1109,9 @@ VG_API_CALL void vgGetPixels(VGImage dst, VGint dx, VGint dy,
|
|||||||
VGint sx, VGint sy,
|
VGint sx, VGint sy,
|
||||||
VGint width, VGint height)
|
VGint width, VGint height)
|
||||||
{
|
{
|
||||||
|
#ifdef SH_NO_IMAGE
|
||||||
|
printf("ShivaVG: images not supported!");
|
||||||
|
#else
|
||||||
SHImage *i;
|
SHImage *i;
|
||||||
SHuint8 *pixels;
|
SHuint8 *pixels;
|
||||||
SHImageFormatDesc winfd;
|
SHImageFormatDesc winfd;
|
||||||
@@ -1093,7 +1149,9 @@ VG_API_CALL void vgGetPixels(VGImage dst, VGint dx, VGint dy,
|
|||||||
free(pixels);
|
free(pixels);
|
||||||
|
|
||||||
shUpdateImageTexture(i, context);
|
shUpdateImageTexture(i, context);
|
||||||
|
|
||||||
VG_RETURN(VG_NO_RETVAL);
|
VG_RETURN(VG_NO_RETVAL);
|
||||||
|
#endif // SH_NO_IMAGE
|
||||||
}
|
}
|
||||||
|
|
||||||
/*-----------------------------------------------------------
|
/*-----------------------------------------------------------
|
||||||
@@ -1107,6 +1165,9 @@ VG_API_CALL void vgReadPixels(void * data, VGint dataStride,
|
|||||||
VGint sx, VGint sy,
|
VGint sx, VGint sy,
|
||||||
VGint width, VGint height)
|
VGint width, VGint height)
|
||||||
{
|
{
|
||||||
|
#ifdef SH_NO_IMAGE
|
||||||
|
printf("ShivaVG: images not supported!");
|
||||||
|
#else
|
||||||
SHuint8 *pixels;
|
SHuint8 *pixels;
|
||||||
SHImageFormatDesc winfd;
|
SHImageFormatDesc winfd;
|
||||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||||
@@ -1146,8 +1207,9 @@ VG_API_CALL void vgReadPixels(void * data, VGint dataStride,
|
|||||||
0, 0, 0, 0, width, height);
|
0, 0, 0, 0, width, height);
|
||||||
|
|
||||||
free(pixels);
|
free(pixels);
|
||||||
|
|
||||||
VG_RETURN(VG_NO_RETVAL);
|
VG_RETURN(VG_NO_RETVAL);
|
||||||
|
#endif // SH_NO_IMAGE
|
||||||
}
|
}
|
||||||
|
|
||||||
/*----------------------------------------------------------
|
/*----------------------------------------------------------
|
||||||
@@ -1160,6 +1222,9 @@ VG_API_CALL void vgCopyPixels(VGint dx, VGint dy,
|
|||||||
VGint sx, VGint sy,
|
VGint sx, VGint sy,
|
||||||
VGint width, VGint height)
|
VGint width, VGint height)
|
||||||
{
|
{
|
||||||
|
#ifdef SH_NO_IMAGE
|
||||||
|
printf("ShivaVG: images not supported!");
|
||||||
|
#else
|
||||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||||
|
|
||||||
VG_RETURN_ERR_IF(width <= 0 || height <= 0,
|
VG_RETURN_ERR_IF(width <= 0 || height <= 0,
|
||||||
@@ -1170,8 +1235,9 @@ VG_API_CALL void vgCopyPixels(VGint dx, VGint dy,
|
|||||||
glRasterPos2i(dx, dy);
|
glRasterPos2i(dx, dy);
|
||||||
glCopyPixels(sx, sy, width, height, GL_COLOR);
|
glCopyPixels(sx, sy, width, height, GL_COLOR);
|
||||||
glRasterPos2i(0, 0);
|
glRasterPos2i(0, 0);
|
||||||
|
|
||||||
VG_RETURN(VG_NO_RETVAL);
|
VG_RETURN(VG_NO_RETVAL);
|
||||||
|
#endif // SH_NO_IMAGE
|
||||||
}
|
}
|
||||||
|
|
||||||
VG_API_CALL VGImage vgChildImage(VGImage parent,
|
VG_API_CALL VGImage vgChildImage(VGImage parent,
|
||||||
|
|||||||
@@ -36,6 +36,9 @@
|
|||||||
#define _ARRAY_DEFINE
|
#define _ARRAY_DEFINE
|
||||||
#include "shArrayBase.h"
|
#include "shArrayBase.h"
|
||||||
|
|
||||||
|
// We currently do not use gradients which need textures, so disable them to
|
||||||
|
// prevent freeing resources outside the correct OpenGL thread/context.
|
||||||
|
#define SH_NO_PAINT_TEXTURE
|
||||||
|
|
||||||
void SHPaint_ctor(SHPaint *p)
|
void SHPaint_ctor(SHPaint *p)
|
||||||
{
|
{
|
||||||
@@ -51,20 +54,26 @@ void SHPaint_ctor(SHPaint *p)
|
|||||||
for (i=0; i<4; ++i) p->linearGradient[i] = 0.0f;
|
for (i=0; i<4; ++i) p->linearGradient[i] = 0.0f;
|
||||||
for (i=0; i<5; ++i) p->radialGradient[i] = 0.0f;
|
for (i=0; i<5; ++i) p->radialGradient[i] = 0.0f;
|
||||||
p->pattern = VG_INVALID_HANDLE;
|
p->pattern = VG_INVALID_HANDLE;
|
||||||
|
|
||||||
|
#ifndef SH_NO_PAINT_TEXTURE
|
||||||
glGenTextures(1, &p->texture);
|
glGenTextures(1, &p->texture);
|
||||||
glBindTexture(GL_TEXTURE_1D, p->texture);
|
glBindTexture(GL_TEXTURE_1D, p->texture);
|
||||||
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, SH_GRADIENT_TEX_SIZE, 0,
|
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, SH_GRADIENT_TEX_SIZE, 0,
|
||||||
GL_RGBA, GL_FLOAT, NULL);
|
GL_RGBA, GL_FLOAT, NULL);
|
||||||
|
#else
|
||||||
|
p->texture = 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void SHPaint_dtor(SHPaint *p)
|
void SHPaint_dtor(SHPaint *p)
|
||||||
{
|
{
|
||||||
SH_DEINITOBJ(SHStopArray, p->instops);
|
SH_DEINITOBJ(SHStopArray, p->instops);
|
||||||
SH_DEINITOBJ(SHStopArray, p->stops);
|
SH_DEINITOBJ(SHStopArray, p->stops);
|
||||||
|
|
||||||
|
#ifndef SH_NO_PAINT_TEXTURE
|
||||||
if (glIsTexture(p->texture))
|
if (glIsTexture(p->texture))
|
||||||
glDeleteTextures(1, &p->texture);
|
glDeleteTextures(1, &p->texture);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
VG_API_CALL VGPaint vgCreatePaint(void)
|
VG_API_CALL VGPaint vgCreatePaint(void)
|
||||||
@@ -143,6 +152,7 @@ VG_API_CALL void vgPaintPattern(VGPaint paint, VGImage pattern)
|
|||||||
|
|
||||||
void shUpdateColorRampTexture(SHPaint *p)
|
void shUpdateColorRampTexture(SHPaint *p)
|
||||||
{
|
{
|
||||||
|
#ifndef SH_NO_PAINT_TEXTURE
|
||||||
SHint s=0;
|
SHint s=0;
|
||||||
SHStop *stop1, *stop2;
|
SHStop *stop1, *stop2;
|
||||||
SHfloat rgba[SH_GRADIENT_TEX_COORDSIZE];
|
SHfloat rgba[SH_GRADIENT_TEX_COORDSIZE];
|
||||||
@@ -177,12 +187,15 @@ void shUpdateColorRampTexture(SHPaint *p)
|
|||||||
CSTORE_RGBA1D_F(c, rgba, x);
|
CSTORE_RGBA1D_F(c, rgba, x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update texture image */
|
/* Update texture image */
|
||||||
glBindTexture(GL_TEXTURE_1D, p->texture);
|
glBindTexture(GL_TEXTURE_1D, p->texture);
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||||
glTexSubImage1D(GL_TEXTURE_1D, 0, 0, SH_GRADIENT_TEX_SIZE,
|
glTexSubImage1D(GL_TEXTURE_1D, 0, 0, SH_GRADIENT_TEX_SIZE,
|
||||||
GL_RGBA, GL_FLOAT, rgba);
|
GL_RGBA, GL_FLOAT, rgba);
|
||||||
|
#else
|
||||||
|
printf("ShivaVG: gradients not supported!");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void shValidateInputStops(SHPaint *p)
|
void shValidateInputStops(SHPaint *p)
|
||||||
@@ -344,6 +357,7 @@ void shGenerateStops(SHPaint *p, SHfloat minOffset, SHfloat maxOffset,
|
|||||||
|
|
||||||
void shSetGradientTexGLState(SHPaint *p)
|
void shSetGradientTexGLState(SHPaint *p)
|
||||||
{
|
{
|
||||||
|
#ifndef SH_NO_PAINT_TEXTURE
|
||||||
glBindTexture(GL_TEXTURE_1D, p->texture);
|
glBindTexture(GL_TEXTURE_1D, p->texture);
|
||||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
@@ -359,6 +373,9 @@ void shSetGradientTexGLState(SHPaint *p)
|
|||||||
|
|
||||||
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
||||||
glColor4f(1,1,1,1);
|
glColor4f(1,1,1,1);
|
||||||
|
#else
|
||||||
|
printf("ShivaVG: gradients not supported!");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void shSetPatternTexGLState(SHPaint *p, VGContext *c)
|
void shSetPatternTexGLState(SHPaint *p, VGContext *c)
|
||||||
|
|||||||
@@ -314,9 +314,9 @@ VG_API_CALL void vgDrawPath(VGPath path, VGbitfield paintModes)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: Turn antialiasing on/off */
|
/* TODO: Turn antialiasing on/off */
|
||||||
glDisable(GL_LINE_SMOOTH);
|
// glDisable(GL_LINE_SMOOTH);
|
||||||
glDisable(GL_POLYGON_SMOOTH);
|
// glDisable(GL_POLYGON_SMOOTH);
|
||||||
glEnable(GL_MULTISAMPLE);
|
// glEnable(GL_MULTISAMPLE);
|
||||||
|
|
||||||
/* Pick paint if available or default*/
|
/* Pick paint if available or default*/
|
||||||
fill = (context->fillPaint ? context->fillPaint : &context->defaultPaint);
|
fill = (context->fillPaint ? context->fillPaint : &context->defaultPaint);
|
||||||
@@ -364,26 +364,27 @@ VG_API_CALL void vgDrawPath(VGPath path, VGbitfield paintModes)
|
|||||||
/* TODO: Is there any way to do this safely along
|
/* TODO: Is there any way to do this safely along
|
||||||
with the paint generation pass?? */
|
with the paint generation pass?? */
|
||||||
glDisable(GL_BLEND);
|
glDisable(GL_BLEND);
|
||||||
glDisable(GL_MULTISAMPLE);
|
// glDisable(GL_MULTISAMPLE);
|
||||||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
||||||
shDrawBoundBox(context, p, VG_FILL_PATH);
|
shDrawBoundBox(context, p, VG_FILL_PATH);
|
||||||
|
|
||||||
/* Reset state */
|
/* Reset state */
|
||||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||||
glDisable(GL_STENCIL_TEST);
|
glDisable(GL_STENCIL_TEST);
|
||||||
glDisable(GL_BLEND);
|
// glDisable(GL_BLEND);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: Turn antialiasing on/off */
|
/* TODO: Turn antialiasing on/off */
|
||||||
glDisable(GL_LINE_SMOOTH);
|
// glDisable(GL_LINE_SMOOTH);
|
||||||
glDisable(GL_POLYGON_SMOOTH);
|
// glDisable(GL_POLYGON_SMOOTH);
|
||||||
glEnable(GL_MULTISAMPLE);
|
// glEnable(GL_MULTISAMPLE);
|
||||||
|
|
||||||
if ((paintModes & VG_STROKE_PATH) &&
|
if ((paintModes & VG_STROKE_PATH) &&
|
||||||
context->strokeLineWidth > 0.0f) {
|
context->strokeLineWidth > 0.0f) {
|
||||||
|
|
||||||
if (1) {/*context->strokeLineWidth > 1.0f) {*/
|
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if (1) {/*context->strokeLineWidth > 1.0f) {*/
|
||||||
|
#endif
|
||||||
if (shIsStrokeCacheValid( context, p ) == VG_FALSE)
|
if (shIsStrokeCacheValid( context, p ) == VG_FALSE)
|
||||||
{
|
{
|
||||||
/* Generate stroke triangles in user space */
|
/* Generate stroke triangles in user space */
|
||||||
@@ -411,15 +412,15 @@ VG_API_CALL void vgDrawPath(VGPath path, VGbitfield paintModes)
|
|||||||
|
|
||||||
/* Clear stencil for sure */
|
/* Clear stencil for sure */
|
||||||
glDisable(GL_BLEND);
|
glDisable(GL_BLEND);
|
||||||
glDisable(GL_MULTISAMPLE);
|
// glDisable(GL_MULTISAMPLE);
|
||||||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
||||||
shDrawBoundBox(context, p, VG_STROKE_PATH);
|
shDrawBoundBox(context, p, VG_STROKE_PATH);
|
||||||
|
|
||||||
/* Reset state */
|
/* Reset state */
|
||||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||||
glDisable(GL_STENCIL_TEST);
|
glDisable(GL_STENCIL_TEST);
|
||||||
glDisable(GL_BLEND);
|
// glDisable(GL_BLEND);
|
||||||
|
#if 0
|
||||||
}else{
|
}else{
|
||||||
|
|
||||||
/* Simulate thin stroke by alpha */
|
/* Simulate thin stroke by alpha */
|
||||||
@@ -438,6 +439,7 @@ VG_API_CALL void vgDrawPath(VGPath path, VGbitfield paintModes)
|
|||||||
glDisable(GL_BLEND);
|
glDisable(GL_BLEND);
|
||||||
glDisable(GL_LINE_SMOOTH);
|
glDisable(GL_LINE_SMOOTH);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -124,11 +124,13 @@ int shLineLineXsection(SHVector2 *o1, SHVector2 *v1,
|
|||||||
SHfloat DX = rightU * (-v2->y) - rightD * (-v2->x);
|
SHfloat DX = rightU * (-v2->y) - rightD * (-v2->x);
|
||||||
/*SHfloat DY = v1.x * rightD - v1.y * rightU;*/
|
/*SHfloat DY = v1.x * rightD - v1.y * rightU;*/
|
||||||
|
|
||||||
SHfloat t1 = DX / D;
|
SHfloat t1;
|
||||||
|
|
||||||
if (D == 0.0f)
|
if (D == 0.0f)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
t1 = DX / D;
|
||||||
|
|
||||||
xsection->x = o1->x + t1*v1->x;
|
xsection->x = o1->x + t1*v1->x;
|
||||||
xsection->y = o1->y + t1*v1->y;
|
xsection->y = o1->y + t1*v1->y;
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ namespace canvas
|
|||||||
|
|
||||||
#define SG_FWD_DECL(name)\
|
#define SG_FWD_DECL(name)\
|
||||||
class name;\
|
class name;\
|
||||||
typedef boost::shared_ptr<name> name##Ptr;\
|
typedef SGSharedPtr<name> name##Ptr;\
|
||||||
typedef boost::weak_ptr<name> name##WeakPtr;
|
typedef SGWeakPtr<name> name##WeakPtr;
|
||||||
|
|
||||||
SG_FWD_DECL(Canvas)
|
SG_FWD_DECL(Canvas)
|
||||||
SG_FWD_DECL(Element)
|
SG_FWD_DECL(Element)
|
||||||
@@ -49,10 +49,21 @@ namespace canvas
|
|||||||
SG_FWD_DECL(Map)
|
SG_FWD_DECL(Map)
|
||||||
SG_FWD_DECL(Path)
|
SG_FWD_DECL(Path)
|
||||||
SG_FWD_DECL(Text)
|
SG_FWD_DECL(Text)
|
||||||
|
SG_FWD_DECL(Window)
|
||||||
|
|
||||||
SG_FWD_DECL(Event)
|
SG_FWD_DECL(Event)
|
||||||
SG_FWD_DECL(EventListener)
|
SG_FWD_DECL(CustomEvent)
|
||||||
|
SG_FWD_DECL(DeviceEvent)
|
||||||
|
SG_FWD_DECL(KeyboardEvent)
|
||||||
SG_FWD_DECL(MouseEvent)
|
SG_FWD_DECL(MouseEvent)
|
||||||
|
|
||||||
|
#undef SG_FWD_DECL
|
||||||
|
|
||||||
|
#define SG_FWD_DECL(name)\
|
||||||
|
class name;\
|
||||||
|
typedef boost::shared_ptr<name> name##Ptr;\
|
||||||
|
typedef boost::weak_ptr<name> name##WeakPtr;
|
||||||
|
|
||||||
SG_FWD_DECL(Placement)
|
SG_FWD_DECL(Placement)
|
||||||
SG_FWD_DECL(SystemAdapter)
|
SG_FWD_DECL(SystemAdapter)
|
||||||
|
|
||||||
@@ -61,6 +72,9 @@ namespace canvas
|
|||||||
class EventManager;
|
class EventManager;
|
||||||
class EventVisitor;
|
class EventVisitor;
|
||||||
|
|
||||||
|
struct EventTarget;
|
||||||
|
typedef std::deque<EventTarget> EventPropagationPath;
|
||||||
|
|
||||||
typedef std::map<std::string, const SGPropertyNode*> Style;
|
typedef std::map<std::string, const SGPropertyNode*> Style;
|
||||||
typedef ElementPtr (*ElementFactory)( const CanvasWeakPtr&,
|
typedef ElementPtr (*ElementFactory)( const CanvasWeakPtr&,
|
||||||
const SGPropertyNode_ptr&,
|
const SGPropertyNode_ptr&,
|
||||||
@@ -73,6 +87,8 @@ namespace canvas
|
|||||||
typedef boost::function<Placements( SGPropertyNode*,
|
typedef boost::function<Placements( SGPropertyNode*,
|
||||||
CanvasPtr )> PlacementFactory;
|
CanvasPtr )> PlacementFactory;
|
||||||
|
|
||||||
|
typedef boost::function<void(const EventPtr&)> EventListener;
|
||||||
|
|
||||||
} // namespace canvas
|
} // namespace canvas
|
||||||
} // namespace simgear
|
} // namespace simgear
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,11 @@ set(HEADERS
|
|||||||
CanvasImage.hxx
|
CanvasImage.hxx
|
||||||
CanvasMap.hxx
|
CanvasMap.hxx
|
||||||
CanvasPath.hxx
|
CanvasPath.hxx
|
||||||
CanvasText.hxx
|
CanvasText.hxx
|
||||||
|
)
|
||||||
|
|
||||||
|
set(DETAIL_HEADERS
|
||||||
|
detail/add_segment_variadic.hxx
|
||||||
)
|
)
|
||||||
|
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
@@ -18,4 +22,10 @@ set(SOURCES
|
|||||||
CanvasText.cxx
|
CanvasText.cxx
|
||||||
)
|
)
|
||||||
|
|
||||||
simgear_scene_component(canvas-elements canvas/elements "${SOURCES}" "${HEADERS}")
|
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
|
||||||
|
LIBRARIES ${TEST_LIBS}
|
||||||
|
)
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,5 @@
|
|||||||
// Interface for 2D Canvas elements
|
///@file
|
||||||
|
/// Interface for 2D Canvas elements
|
||||||
//
|
//
|
||||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||||
//
|
//
|
||||||
@@ -23,13 +24,13 @@
|
|||||||
#include <simgear/canvas/CanvasEvent.hxx>
|
#include <simgear/canvas/CanvasEvent.hxx>
|
||||||
#include <simgear/props/PropertyBasedElement.hxx>
|
#include <simgear/props/PropertyBasedElement.hxx>
|
||||||
#include <simgear/misc/stdint.hxx> // for uint32_t
|
#include <simgear/misc/stdint.hxx> // for uint32_t
|
||||||
#include <simgear/nasal/cppbind/Ghost.hxx>
|
|
||||||
|
|
||||||
#include <osg/BoundingBox>
|
#include <osg/BoundingBox>
|
||||||
#include <osg/MatrixTransform>
|
#include <osg/MatrixTransform>
|
||||||
|
|
||||||
#include <boost/bind.hpp>
|
#include <boost/bind.hpp>
|
||||||
#include <boost/function.hpp>
|
#include <boost/function.hpp>
|
||||||
|
#include <boost/type_traits/is_base_of.hpp>
|
||||||
|
|
||||||
namespace osg
|
namespace osg
|
||||||
{
|
{
|
||||||
@@ -41,28 +42,62 @@ namespace simgear
|
|||||||
namespace canvas
|
namespace canvas
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for Elements displayed inside a Canvas.
|
||||||
|
*/
|
||||||
class Element:
|
class Element:
|
||||||
public PropertyBasedElement
|
public PropertyBasedElement
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef boost::function<void(const SGPropertyNode*)> StyleSetter;
|
|
||||||
typedef std::map<std::string, StyleSetter> StyleSetters;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the property listener of the element.
|
* Store pointer to window as user data
|
||||||
*
|
|
||||||
* You will need to call the appropriate methods (#childAdded,
|
|
||||||
* #childRemoved, #valueChanged) yourself to ensure the element still
|
|
||||||
* receives the needed events.
|
|
||||||
*/
|
*/
|
||||||
void removeListener();
|
class OSGUserData:
|
||||||
|
public osg::Referenced
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ElementPtr element;
|
||||||
|
OSGUserData(ElementPtr element);
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef boost::function<bool(Element&, const SGPropertyNode*)>
|
||||||
|
StyleSetterFunc;
|
||||||
|
typedef boost::function<void(Element&, const SGPropertyNode*)>
|
||||||
|
StyleSetterFuncUnchecked;
|
||||||
|
struct StyleSetter:
|
||||||
|
public SGReferenced
|
||||||
|
{
|
||||||
|
StyleSetterFunc func;
|
||||||
|
SGSharedPtr<StyleSetter> next;
|
||||||
|
};
|
||||||
|
struct StyleInfo
|
||||||
|
{
|
||||||
|
StyleSetter setter; ///< Function(s) for setting this style
|
||||||
|
std::string type; ///< Interpolation type
|
||||||
|
bool inheritable; ///< Whether children can inherit this style from
|
||||||
|
/// their parents
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Coordinate reference frame (eg. "clip" property)
|
||||||
|
*/
|
||||||
|
enum ReferenceFrame
|
||||||
|
{
|
||||||
|
GLOBAL, ///< Global coordinates
|
||||||
|
PARENT, ///< Coordinates relative to parent coordinate frame
|
||||||
|
LOCAL ///< Coordinates relative to local coordinates (parent
|
||||||
|
/// coordinates with local transformations applied)
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
virtual ~Element() = 0;
|
virtual ~Element() = 0;
|
||||||
|
virtual void onDestroy();
|
||||||
|
|
||||||
ElementWeakPtr getWeakPtr() const;
|
ElementPtr getParent() const;
|
||||||
|
CanvasWeakPtr getCanvas() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called every frame to update internal state
|
* Called every frame to update internal state
|
||||||
@@ -71,18 +106,49 @@ namespace canvas
|
|||||||
*/
|
*/
|
||||||
virtual void update(double dt);
|
virtual void update(double dt);
|
||||||
|
|
||||||
naRef addEventListener(const nasal::CallContext& ctx);
|
bool addEventListener(const std::string& type, const EventListener& cb);
|
||||||
|
virtual void clearEventListener();
|
||||||
|
|
||||||
|
/// Get (keyboard) input focus.
|
||||||
|
void setFocus();
|
||||||
|
|
||||||
virtual bool accept(EventVisitor& visitor);
|
virtual bool accept(EventVisitor& visitor);
|
||||||
virtual bool ascend(EventVisitor& visitor);
|
virtual bool ascend(EventVisitor& visitor);
|
||||||
virtual bool traverse(EventVisitor& visitor);
|
virtual bool traverse(EventVisitor& visitor);
|
||||||
|
|
||||||
void callListeners(const canvas::EventPtr& event);
|
/// Get the number of event handlers for the given type
|
||||||
|
size_t numEventHandler(int type) const;
|
||||||
|
|
||||||
virtual bool hitBound(const osg::Vec2f& pos) const;
|
virtual bool handleEvent(const EventPtr& event);
|
||||||
|
bool dispatchEvent(const EventPtr& event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param global_pos Position in global (canvas) coordinate frame
|
||||||
|
* @param parent_pos Position in parent coordinate frame
|
||||||
|
* @param local_pos Position in local (element) coordinate frame
|
||||||
|
*/
|
||||||
|
virtual bool hitBound( const osg::Vec2f& global_pos,
|
||||||
|
const osg::Vec2f& parent_pos,
|
||||||
|
const osg::Vec2f& local_pos ) const;
|
||||||
|
|
||||||
osg::ref_ptr<osg::MatrixTransform> getMatrixTransform();
|
/**
|
||||||
|
* Set visibility of the element.
|
||||||
|
*/
|
||||||
|
virtual void setVisible(bool visible);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get whether the element is visible or hidden.
|
||||||
|
*/
|
||||||
|
virtual bool isVisible() const;
|
||||||
|
|
||||||
|
osg::MatrixTransform* getMatrixTransform();
|
||||||
|
osg::MatrixTransform const* getMatrixTransform() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform position to local coordinages.
|
||||||
|
*/
|
||||||
|
osg::Vec2f posToLocal(const osg::Vec2f& pos) const;
|
||||||
|
|
||||||
virtual void childAdded( SGPropertyNode * parent,
|
virtual void childAdded( SGPropertyNode * parent,
|
||||||
SGPropertyNode * child );
|
SGPropertyNode * child );
|
||||||
@@ -90,7 +156,8 @@ namespace canvas
|
|||||||
SGPropertyNode * child );
|
SGPropertyNode * child );
|
||||||
virtual void valueChanged(SGPropertyNode * child);
|
virtual void valueChanged(SGPropertyNode * child);
|
||||||
|
|
||||||
virtual bool setStyle(const SGPropertyNode* child);
|
virtual bool setStyle( const SGPropertyNode* child,
|
||||||
|
const StyleInfo* style_info = 0 );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set clipping shape
|
* Set clipping shape
|
||||||
@@ -101,20 +168,57 @@ namespace canvas
|
|||||||
void setClip(const std::string& clip);
|
void setClip(const std::string& clip);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write the given bounding box to the property tree
|
* Clipping coordinates reference frame
|
||||||
*/
|
*/
|
||||||
void setBoundingBox(const osg::BoundingBox& bb);
|
void setClipFrame(ReferenceFrame rf);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get bounding box (may not be as tight as bounding box returned by
|
||||||
|
* #getTightBoundingBox)
|
||||||
|
*/
|
||||||
|
osg::BoundingBox getBoundingBox() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get tight bounding box (child points are transformed to elements
|
||||||
|
* coordinate space before calculating the bounding box).
|
||||||
|
*/
|
||||||
|
osg::BoundingBox getTightBoundingBox() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get bounding box with children/drawables transformed by passed matrix
|
* Get bounding box with children/drawables transformed by passed matrix
|
||||||
*/
|
*/
|
||||||
virtual osg::BoundingBox getTransformedBounds(const osg::Matrix& m) const;
|
virtual osg::BoundingBox getTransformedBounds(const osg::Matrix& m) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the transformation matrix (product of all transforms)
|
||||||
|
*/
|
||||||
|
osg::Matrix getMatrix() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an canvas Element
|
||||||
|
*
|
||||||
|
* @tparam Derived Type of element (needs to derive from Element)
|
||||||
|
*/
|
||||||
|
template<typename Derived>
|
||||||
|
static
|
||||||
|
typename boost::enable_if<
|
||||||
|
boost::is_base_of<Element, Derived>,
|
||||||
|
ElementPtr
|
||||||
|
>::type create( const CanvasWeakPtr& canvas,
|
||||||
|
const SGPropertyNode_ptr& node,
|
||||||
|
const Style& style = Style(),
|
||||||
|
Element* parent = NULL )
|
||||||
|
{
|
||||||
|
return ElementPtr( new Derived(canvas, node, style, parent) );
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
enum Attributes
|
enum Attributes
|
||||||
{
|
{
|
||||||
LAST_ATTRIBUTE = 0x0001
|
TRANSFORM = 1,
|
||||||
|
BLEND_FUNC = TRANSFORM << 1,
|
||||||
|
LAST_ATTRIBUTE = BLEND_FUNC << 1
|
||||||
};
|
};
|
||||||
|
|
||||||
enum TransformType
|
enum TransformType
|
||||||
@@ -126,64 +230,331 @@ namespace canvas
|
|||||||
TT_SCALE
|
TT_SCALE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class RelativeScissor;
|
||||||
|
|
||||||
CanvasWeakPtr _canvas;
|
CanvasWeakPtr _canvas;
|
||||||
Element *_parent;
|
Element *_parent;
|
||||||
|
|
||||||
uint32_t _attributes_dirty;
|
mutable uint32_t _attributes_dirty;
|
||||||
|
|
||||||
bool _transform_dirty;
|
osg::observer_ptr<osg::MatrixTransform> _transform;
|
||||||
osg::ref_ptr<osg::MatrixTransform> _transform;
|
std::vector<TransformType> _transform_types;
|
||||||
std::vector<TransformType> _transform_types;
|
|
||||||
|
|
||||||
Style _style;
|
Style _style;
|
||||||
StyleSetters _style_setters;
|
RelativeScissor *_scissor;
|
||||||
std::vector<SGPropertyNode_ptr> _bounding_box;
|
|
||||||
|
|
||||||
typedef std::vector<EventListenerPtr> Listener;
|
typedef std::vector<EventListener> Listener;
|
||||||
typedef std::map<Event::Type, Listener> ListenerMap;
|
typedef std::map<int, Listener> ListenerMap;
|
||||||
|
|
||||||
ListenerMap _listener;
|
ListenerMap _listener;
|
||||||
|
|
||||||
|
typedef std::map<std::string, StyleInfo> StyleSetters;
|
||||||
|
static StyleSetters _style_setters;
|
||||||
|
|
||||||
|
static void staticInit();
|
||||||
|
|
||||||
Element( const CanvasWeakPtr& canvas,
|
Element( const CanvasWeakPtr& canvas,
|
||||||
const SGPropertyNode_ptr& node,
|
const SGPropertyNode_ptr& node,
|
||||||
const Style& parent_style,
|
const Style& parent_style,
|
||||||
Element* parent );
|
Element* parent );
|
||||||
|
|
||||||
template<typename T, class C1, class C2>
|
/**
|
||||||
Element::StyleSetter
|
* Returns false on first call and true on any successive call. Use to
|
||||||
addStyle(const std::string& name, void (C1::*setter)(T), C2 instance)
|
* perform initialization tasks which are only required once per element
|
||||||
|
* type.
|
||||||
|
*
|
||||||
|
* @tparam Derived (Derived) class type
|
||||||
|
*/
|
||||||
|
template<class Derived>
|
||||||
|
static bool isInit()
|
||||||
{
|
{
|
||||||
return _style_setters[ name ] =
|
static bool is_init = false;
|
||||||
bindStyleSetter<T>(name, setter, instance);
|
if( is_init )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
is_init = true;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T1, typename T2, class C1, class C2>
|
/**
|
||||||
Element::StyleSetter
|
* Register a function for setting a style specified by the given property
|
||||||
addStyle(const std::string& name, void (C1::*setter)(T2), C2 instance)
|
*
|
||||||
{
|
* @param name Property name
|
||||||
return _style_setters[ name ] =
|
* @param type Interpolation type
|
||||||
bindStyleSetter<T1>(name, setter, instance);
|
* @param setter Setter function
|
||||||
}
|
* @param inheritable If this style propagates to child elements
|
||||||
|
*
|
||||||
template<class C1, class C2>
|
* @tparam T1 Type of value used to retrieve value from property
|
||||||
Element::StyleSetter
|
* node
|
||||||
|
* @tparam T2 Type of value the setter function expects
|
||||||
|
* @tparam Derived Type of class the setter can be applied to
|
||||||
|
*
|
||||||
|
* @note T1 needs to be convertible to T2
|
||||||
|
*/
|
||||||
|
template<
|
||||||
|
typename T1,
|
||||||
|
typename T2,
|
||||||
|
class Derived
|
||||||
|
>
|
||||||
|
static
|
||||||
|
StyleSetter
|
||||||
addStyle( const std::string& name,
|
addStyle( const std::string& name,
|
||||||
void (C1::*setter)(const std::string&),
|
const std::string& type,
|
||||||
C2 instance )
|
const boost::function<void (Derived&, T2)>& setter,
|
||||||
|
bool inheritable = true )
|
||||||
{
|
{
|
||||||
return _style_setters[ name ] =
|
StyleInfo& style_info = _style_setters[ name ];
|
||||||
bindStyleSetter<const char*>(name, setter, instance);
|
if( !type.empty() )
|
||||||
|
{
|
||||||
|
if( !style_info.type.empty() && type != style_info.type )
|
||||||
|
SG_LOG
|
||||||
|
(
|
||||||
|
SG_GENERAL,
|
||||||
|
SG_WARN,
|
||||||
|
"changing animation type for '" << name << "': "
|
||||||
|
<< style_info.type << " -> " << type
|
||||||
|
);
|
||||||
|
|
||||||
|
style_info.type = type;
|
||||||
|
}
|
||||||
|
// TODO check if changed?
|
||||||
|
style_info.inheritable = inheritable;
|
||||||
|
|
||||||
|
StyleSetter* style = &style_info.setter;
|
||||||
|
while( style->next )
|
||||||
|
style = style->next;
|
||||||
|
if( style->func )
|
||||||
|
style = style->next = new StyleSetter;
|
||||||
|
|
||||||
|
style->func = boost::bind
|
||||||
|
(
|
||||||
|
&type_match<Derived>::call,
|
||||||
|
_1,
|
||||||
|
_2,
|
||||||
|
bindStyleSetter<T1>(name, setter)
|
||||||
|
);
|
||||||
|
return *style;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T1, typename T2, class C1, class C2>
|
template<
|
||||||
Element::StyleSetter
|
typename T,
|
||||||
bindStyleSetter( const std::string& name,
|
class Derived
|
||||||
void (C1::*setter)(T2),
|
>
|
||||||
C2 instance )
|
static
|
||||||
|
StyleSetter
|
||||||
|
addStyle( const std::string& name,
|
||||||
|
const std::string& type,
|
||||||
|
const boost::function<void (Derived&, T)>& setter,
|
||||||
|
bool inheritable = true )
|
||||||
{
|
{
|
||||||
return boost::bind(setter, instance, boost::bind(&getValue<T1>, _1));
|
return addStyle<T, T>(name, type, setter, inheritable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<
|
||||||
|
typename T,
|
||||||
|
class Derived
|
||||||
|
>
|
||||||
|
static
|
||||||
|
StyleSetter
|
||||||
|
addStyle( const std::string& name,
|
||||||
|
const std::string& type,
|
||||||
|
void (Derived::*setter)(T),
|
||||||
|
bool inheritable = true )
|
||||||
|
{
|
||||||
|
return addStyle<T, T>
|
||||||
|
(
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
boost::function<void (Derived&, T)>(setter),
|
||||||
|
inheritable
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<
|
||||||
|
typename T1,
|
||||||
|
typename T2,
|
||||||
|
class Derived
|
||||||
|
>
|
||||||
|
static
|
||||||
|
StyleSetterFunc
|
||||||
|
addStyle( const std::string& name,
|
||||||
|
const std::string& type,
|
||||||
|
void (Derived::*setter)(T2),
|
||||||
|
bool inheritable = true )
|
||||||
|
{
|
||||||
|
return addStyle<T1>
|
||||||
|
(
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
boost::function<void (Derived&, T2)>(setter),
|
||||||
|
inheritable
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<
|
||||||
|
class Derived
|
||||||
|
>
|
||||||
|
static
|
||||||
|
StyleSetter
|
||||||
|
addStyle( const std::string& name,
|
||||||
|
const std::string& type,
|
||||||
|
void (Derived::*setter)(const std::string&),
|
||||||
|
bool inheritable = true )
|
||||||
|
{
|
||||||
|
return addStyle<const char*, const std::string&>
|
||||||
|
(
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
boost::function<void (Derived&, const std::string&)>(setter),
|
||||||
|
inheritable
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<
|
||||||
|
typename T,
|
||||||
|
class Derived,
|
||||||
|
class Other,
|
||||||
|
class OtherRef
|
||||||
|
>
|
||||||
|
static
|
||||||
|
StyleSetter
|
||||||
|
addStyle( const std::string& name,
|
||||||
|
const std::string& type,
|
||||||
|
void (Other::*setter)(T),
|
||||||
|
OtherRef Derived::*instance_ref,
|
||||||
|
bool inheritable = true )
|
||||||
|
{
|
||||||
|
return addStyle<T, T>
|
||||||
|
(
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
bindOther(setter, instance_ref),
|
||||||
|
inheritable
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<
|
||||||
|
typename T1,
|
||||||
|
typename T2,
|
||||||
|
class Derived,
|
||||||
|
class Other,
|
||||||
|
class OtherRef
|
||||||
|
>
|
||||||
|
static
|
||||||
|
StyleSetter
|
||||||
|
addStyle( const std::string& name,
|
||||||
|
const std::string& type,
|
||||||
|
void (Other::*setter)(T2),
|
||||||
|
OtherRef Derived::*instance_ref,
|
||||||
|
bool inheritable = true )
|
||||||
|
{
|
||||||
|
return addStyle<T1>
|
||||||
|
(
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
bindOther(setter, instance_ref),
|
||||||
|
inheritable
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<
|
||||||
|
typename T1,
|
||||||
|
typename T2,
|
||||||
|
class Derived,
|
||||||
|
class Other,
|
||||||
|
class OtherRef
|
||||||
|
>
|
||||||
|
static
|
||||||
|
StyleSetter
|
||||||
|
addStyle( const std::string& name,
|
||||||
|
const std::string& type,
|
||||||
|
const boost::function<void (Other&, T2)>& setter,
|
||||||
|
OtherRef Derived::*instance_ref,
|
||||||
|
bool inheritable = true )
|
||||||
|
{
|
||||||
|
return addStyle<T1>
|
||||||
|
(
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
bindOther(setter, instance_ref),
|
||||||
|
inheritable
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<
|
||||||
|
class Derived,
|
||||||
|
class Other,
|
||||||
|
class OtherRef
|
||||||
|
>
|
||||||
|
static
|
||||||
|
StyleSetter
|
||||||
|
addStyle( const std::string& name,
|
||||||
|
const std::string& type,
|
||||||
|
void (Other::*setter)(const std::string&),
|
||||||
|
OtherRef Derived::*instance_ref,
|
||||||
|
bool inheritable = true )
|
||||||
|
{
|
||||||
|
return addStyle<const char*, const std::string&>
|
||||||
|
(
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
boost::function<void (Other&, const std::string&)>(setter),
|
||||||
|
instance_ref,
|
||||||
|
inheritable
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, class Derived, class Other, class OtherRef>
|
||||||
|
static
|
||||||
|
boost::function<void (Derived&, T)>
|
||||||
|
bindOther( void (Other::*setter)(T), OtherRef Derived::*instance_ref )
|
||||||
|
{
|
||||||
|
return boost::bind(setter, boost::bind(instance_ref, _1), _2);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, class Derived, class Other, class OtherRef>
|
||||||
|
static
|
||||||
|
boost::function<void (Derived&, T)>
|
||||||
|
bindOther( const boost::function<void (Other&, T)>& setter,
|
||||||
|
OtherRef Derived::*instance_ref )
|
||||||
|
{
|
||||||
|
return boost::bind
|
||||||
|
(
|
||||||
|
setter,
|
||||||
|
boost::bind
|
||||||
|
(
|
||||||
|
&reference_from_pointer<Other, OtherRef>,
|
||||||
|
boost::bind(instance_ref, _1)
|
||||||
|
),
|
||||||
|
_2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T1, typename T2, class Derived>
|
||||||
|
static
|
||||||
|
StyleSetterFuncUnchecked
|
||||||
|
bindStyleSetter( const std::string& name,
|
||||||
|
const boost::function<void (Derived&, T2)>& setter )
|
||||||
|
{
|
||||||
|
return boost::bind
|
||||||
|
(
|
||||||
|
setter,
|
||||||
|
// We will only call setters with Derived instances, so we can safely
|
||||||
|
// cast here.
|
||||||
|
boost::bind(&derived_cast<Derived>, _1),
|
||||||
|
boost::bind(&getValue<T1>, _2)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isStyleEmpty(const SGPropertyNode* child) const;
|
||||||
|
bool canApplyStyle(const SGPropertyNode* child) const;
|
||||||
|
bool setStyleImpl( const SGPropertyNode* child,
|
||||||
|
const StyleInfo* style_info = 0 );
|
||||||
|
|
||||||
|
const StyleInfo* getStyleInfo(const std::string& name) const;
|
||||||
|
const StyleSetter* getStyleSetter(const std::string& name) const;
|
||||||
|
const SGPropertyNode* getParentStyle(const SGPropertyNode* child) const;
|
||||||
|
|
||||||
virtual void childAdded(SGPropertyNode * child) {}
|
virtual void childAdded(SGPropertyNode * child) {}
|
||||||
virtual void childRemoved(SGPropertyNode * child){}
|
virtual void childRemoved(SGPropertyNode * child){}
|
||||||
virtual void childChanged(SGPropertyNode * child){}
|
virtual void childChanged(SGPropertyNode * child){}
|
||||||
@@ -193,7 +564,7 @@ namespace canvas
|
|||||||
/**
|
/**
|
||||||
* Get stateset of drawable if available or use transform otherwise
|
* Get stateset of drawable if available or use transform otherwise
|
||||||
*/
|
*/
|
||||||
osg::StateSet* getOrCreateStateSet();
|
virtual osg::StateSet* getOrCreateStateSet();
|
||||||
|
|
||||||
void setupStyle();
|
void setupStyle();
|
||||||
|
|
||||||
@@ -202,9 +573,63 @@ namespace canvas
|
|||||||
osg::ref_ptr<osg::Drawable> _drawable;
|
osg::ref_ptr<osg::Drawable> _drawable;
|
||||||
|
|
||||||
Element(const Element&);// = delete
|
Element(const Element&);// = delete
|
||||||
|
|
||||||
|
template<class Derived>
|
||||||
|
static Derived& derived_cast(Element& el)
|
||||||
|
{
|
||||||
|
return static_cast<Derived&>(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T, class SharedPtr>
|
||||||
|
static T& reference_from_pointer(const SharedPtr& p)
|
||||||
|
{
|
||||||
|
return *get_pointer(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to call a function only of the element type can be converted to
|
||||||
|
* the required type.
|
||||||
|
*
|
||||||
|
* @return Whether the function has been called
|
||||||
|
*/
|
||||||
|
template<class Derived>
|
||||||
|
struct type_match
|
||||||
|
{
|
||||||
|
static bool call( Element& el,
|
||||||
|
const SGPropertyNode* prop,
|
||||||
|
const StyleSetterFuncUnchecked& func )
|
||||||
|
{
|
||||||
|
Derived* d = dynamic_cast<Derived*>(&el);
|
||||||
|
if( !d )
|
||||||
|
return false;
|
||||||
|
func(*d, prop);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace canvas
|
} // namespace canvas
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct enum_traits<canvas::Element::ReferenceFrame>
|
||||||
|
{
|
||||||
|
static const char* name()
|
||||||
|
{
|
||||||
|
return "canvas::Element::ReferenceFrame";
|
||||||
|
}
|
||||||
|
|
||||||
|
static canvas::Element::ReferenceFrame defVal()
|
||||||
|
{
|
||||||
|
return canvas::Element::GLOBAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool validate(int frame)
|
||||||
|
{
|
||||||
|
return frame >= canvas::Element::GLOBAL
|
||||||
|
&& frame <= canvas::Element::LOCAL;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace simgear
|
} // namespace simgear
|
||||||
|
|
||||||
#endif /* CANVAS_ELEMENT_HXX_ */
|
#endif /* CANVAS_ELEMENT_HXX_ */
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
#include "CanvasPath.hxx"
|
#include "CanvasPath.hxx"
|
||||||
#include "CanvasText.hxx"
|
#include "CanvasText.hxx"
|
||||||
#include <simgear/canvas/CanvasEventVisitor.hxx>
|
#include <simgear/canvas/CanvasEventVisitor.hxx>
|
||||||
#include <simgear/canvas/MouseEvent.hxx>
|
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||||
|
|
||||||
#include <boost/bind.hpp>
|
#include <boost/bind.hpp>
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
@@ -33,17 +33,37 @@ namespace simgear
|
|||||||
namespace canvas
|
namespace canvas
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Create an canvas Element of type T
|
* Add canvas Element type to factory map
|
||||||
*/
|
*/
|
||||||
template<typename T>
|
template<typename ElementType>
|
||||||
ElementPtr createElement( const CanvasWeakPtr& canvas,
|
void add(ElementFactories& factories)
|
||||||
const SGPropertyNode_ptr& node,
|
|
||||||
const Style& style,
|
|
||||||
Element* parent )
|
|
||||||
{
|
{
|
||||||
ElementPtr el( new T(canvas, node, style, parent) );
|
ElementType::staticInit();
|
||||||
el->setSelf(el);
|
factories[ElementType::TYPE_NAME] = &Element::create<ElementType>;
|
||||||
return el;
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
ElementFactories Group::_child_factories;
|
||||||
|
const std::string Group::TYPE_NAME = "group";
|
||||||
|
|
||||||
|
void warnTransformExpired(const char* member_name)
|
||||||
|
{
|
||||||
|
SG_LOG( SG_GENERAL,
|
||||||
|
SG_WARN,
|
||||||
|
"canvas::Group::" << member_name << ": Group has expired." );
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Group::staticInit()
|
||||||
|
{
|
||||||
|
if( isInit<Group>() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
add<Group>(_child_factories);
|
||||||
|
add<Image>(_child_factories);
|
||||||
|
add<Map >(_child_factories);
|
||||||
|
add<Path >(_child_factories);
|
||||||
|
add<Text >(_child_factories);
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -53,11 +73,7 @@ namespace canvas
|
|||||||
Element* parent ):
|
Element* parent ):
|
||||||
Element(canvas, node, parent_style, parent)
|
Element(canvas, node, parent_style, parent)
|
||||||
{
|
{
|
||||||
_child_factories["group"] = &createElement<Group>;
|
staticInit();
|
||||||
_child_factories["image"] = &createElement<Image>;
|
|
||||||
_child_factories["map" ] = &createElement<Map >;
|
|
||||||
_child_factories["path" ] = &createElement<Path >;
|
|
||||||
_child_factories["text" ] = &createElement<Text >;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -80,25 +96,57 @@ namespace canvas
|
|||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
ElementPtr Group::getChild(const SGPropertyNode* node)
|
ElementPtr Group::getChild(const SGPropertyNode* node)
|
||||||
{
|
{
|
||||||
ChildList::iterator child = findChild(node);
|
return findChild(node, "");
|
||||||
if( child == _children.end() )
|
}
|
||||||
return ElementPtr();
|
|
||||||
|
|
||||||
return child->second;
|
//----------------------------------------------------------------------------
|
||||||
|
ElementPtr Group::getChild(const std::string& id)
|
||||||
|
{
|
||||||
|
return findChild(0, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
ElementPtr Group::getOrCreateChild( const std::string& type,
|
||||||
|
const std::string& id )
|
||||||
|
{
|
||||||
|
ElementPtr child = getChild(id);
|
||||||
|
if( child )
|
||||||
|
{
|
||||||
|
if( child->getProps()->getNameString() == type )
|
||||||
|
return child;
|
||||||
|
|
||||||
|
SG_LOG
|
||||||
|
(
|
||||||
|
SG_GENERAL,
|
||||||
|
SG_WARN,
|
||||||
|
"Group::getOrCreateChild: type missmatch! "
|
||||||
|
"('" << type << "' != '" << child->getProps()->getName() << "', "
|
||||||
|
"id = '" << id << "')"
|
||||||
|
);
|
||||||
|
|
||||||
|
return ElementPtr();
|
||||||
|
}
|
||||||
|
|
||||||
|
return createChild(type, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
ElementPtr Group::getElementById(const std::string& id)
|
ElementPtr Group::getElementById(const std::string& id)
|
||||||
{
|
{
|
||||||
std::vector<GroupPtr> groups;
|
if( !_transform.valid() )
|
||||||
|
|
||||||
BOOST_FOREACH( ChildList::value_type child, _children )
|
|
||||||
{
|
{
|
||||||
const ElementPtr& el = child.second;
|
warnTransformExpired("getElementById");
|
||||||
if( el->getProps()->getStringValue("id") == id )
|
return ElementPtr();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<GroupPtr> groups;
|
||||||
|
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
||||||
|
{
|
||||||
|
const ElementPtr& el = getChildByIndex(i);
|
||||||
|
if( el->get<std::string>("id") == id )
|
||||||
return el;
|
return el;
|
||||||
|
|
||||||
GroupPtr group = boost::dynamic_pointer_cast<Group>(el);
|
Group* group = dynamic_cast<Group*>(el.get());
|
||||||
if( group )
|
if( group )
|
||||||
groups.push_back(group);
|
groups.push_back(group);
|
||||||
}
|
}
|
||||||
@@ -113,11 +161,23 @@ namespace canvas
|
|||||||
return ElementPtr();
|
return ElementPtr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
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)
|
void Group::update(double dt)
|
||||||
{
|
{
|
||||||
BOOST_FOREACH( ChildList::value_type child, _children )
|
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
||||||
child.second->update(dt);
|
getChildByIndex(i)->update(dt);
|
||||||
|
|
||||||
Element::update(dt);
|
Element::update(dt);
|
||||||
}
|
}
|
||||||
@@ -126,30 +186,32 @@ namespace canvas
|
|||||||
bool Group::traverse(EventVisitor& visitor)
|
bool Group::traverse(EventVisitor& visitor)
|
||||||
{
|
{
|
||||||
// Iterate in reverse order as last child is displayed on top
|
// Iterate in reverse order as last child is displayed on top
|
||||||
BOOST_REVERSE_FOREACH( ChildList::value_type child, _children )
|
for(size_t i = _transform->getNumChildren(); i --> 0;)
|
||||||
{
|
{
|
||||||
if( child.second->accept(visitor) )
|
if( getChildByIndex(i)->accept(visitor) )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
bool Group::setStyle(const SGPropertyNode* style)
|
bool Group::setStyle( const SGPropertyNode* style,
|
||||||
|
const StyleInfo* style_info )
|
||||||
{
|
{
|
||||||
// Don't propagate styles directly applicable to this group
|
if( !canApplyStyle(style) )
|
||||||
if( Element::setStyle(style) )
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if( style->getParent() != _node
|
|
||||||
&& _style.find(style->getNameString()) != _style.end() )
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
bool handled = false;
|
bool handled = setStyleImpl(style, style_info);
|
||||||
BOOST_FOREACH( ChildList::value_type child, _children )
|
if( style_info->inheritable )
|
||||||
{
|
{
|
||||||
if( child.second->setStyle(style) )
|
if( !_transform.valid() )
|
||||||
handled = true;
|
{
|
||||||
|
warnTransformExpired("setStyle");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
||||||
|
handled |= getChildByIndex(i)->setStyle(style, style_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
return handled;
|
return handled;
|
||||||
@@ -159,17 +221,23 @@ namespace canvas
|
|||||||
osg::BoundingBox Group::getTransformedBounds(const osg::Matrix& m) const
|
osg::BoundingBox Group::getTransformedBounds(const osg::Matrix& m) const
|
||||||
{
|
{
|
||||||
osg::BoundingBox bb;
|
osg::BoundingBox bb;
|
||||||
|
if( !_transform.valid() )
|
||||||
BOOST_FOREACH( ChildList::value_type child, _children )
|
|
||||||
{
|
{
|
||||||
if( !child.second->getMatrixTransform()->getNodeMask() )
|
warnTransformExpired("getTransformedBounds");
|
||||||
|
return bb;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
||||||
|
{
|
||||||
|
const ElementPtr& child = getChildByIndex(i);
|
||||||
|
if( !child->getMatrixTransform()->getNodeMask() )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
bb.expandBy
|
bb.expandBy
|
||||||
(
|
(
|
||||||
child.second->getTransformedBounds
|
child->getTransformedBounds
|
||||||
(
|
(
|
||||||
child.second->getMatrixTransform()->getMatrix() * m
|
child->getMatrixTransform()->getMatrix() * m
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -177,31 +245,42 @@ namespace canvas
|
|||||||
return bb;
|
return bb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
ElementFactory Group::getChildFactory(const std::string& type) const
|
||||||
|
{
|
||||||
|
ElementFactories::iterator child_factory = _child_factories.find(type);
|
||||||
|
if( child_factory != _child_factories.end() )
|
||||||
|
return child_factory->second;
|
||||||
|
|
||||||
|
return ElementFactory();
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Group::childAdded(SGPropertyNode* child)
|
void Group::childAdded(SGPropertyNode* child)
|
||||||
{
|
{
|
||||||
if( child->getParent() != _node )
|
if( child->getParent() != _node )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ChildFactories::iterator child_factory =
|
ElementFactory child_factory = getChildFactory( child->getNameString() );
|
||||||
_child_factories.find( child->getNameString() );
|
if( child_factory )
|
||||||
if( child_factory != _child_factories.end() )
|
|
||||||
{
|
{
|
||||||
ElementPtr element = child_factory->second(_canvas, child, _style, this);
|
if( !_transform.valid() )
|
||||||
|
return warnTransformExpired("childAdded");
|
||||||
|
|
||||||
|
ElementPtr element = child_factory(_canvas, child, _style, this);
|
||||||
|
|
||||||
// Add to osg scene graph...
|
// Add to osg scene graph...
|
||||||
_transform->addChild( element->getMatrixTransform() );
|
_transform->addChild( element->getMatrixTransform() );
|
||||||
_children.push_back( ChildList::value_type(child, element) );
|
|
||||||
|
// ...and ensure correct ordering
|
||||||
|
handleZIndexChanged(element);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !Element::setStyle(child) )
|
StyleInfo const* style = getStyleInfo(child->getNameString());
|
||||||
{
|
if( style && style->inheritable )
|
||||||
// Only add style if not applicable to group itself
|
|
||||||
_style[ child->getNameString() ] = child;
|
_style[ child->getNameString() ] = child;
|
||||||
setStyle(child);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -210,10 +289,15 @@ namespace canvas
|
|||||||
if( node->getParent() != _node )
|
if( node->getParent() != _node )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if( _child_factories.find(node->getNameString()) != _child_factories.end() )
|
if( getChildFactory(node->getNameString()) )
|
||||||
{
|
{
|
||||||
ChildList::iterator child = findChild(node);
|
if( !_transform.valid() )
|
||||||
if( child == _children.end() )
|
// If transform is destroyed also all children are destroyed, so we can
|
||||||
|
// not do anything here.
|
||||||
|
return;
|
||||||
|
|
||||||
|
ElementPtr child = getChild(node);
|
||||||
|
if( !child )
|
||||||
SG_LOG
|
SG_LOG
|
||||||
(
|
(
|
||||||
SG_GL,
|
SG_GL,
|
||||||
@@ -222,71 +306,70 @@ namespace canvas
|
|||||||
);
|
);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_transform->removeChild( child->second->getMatrixTransform() );
|
// Remove child from the scenegraph (this automatically invalidates the
|
||||||
_children.erase(child);
|
// reference on the element hold by the MatrixTransform)
|
||||||
|
child->onDestroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Style::iterator style = _style.find(node->getNameString());
|
_style.erase( node->getNameString() );
|
||||||
if( style != _style.end() )
|
|
||||||
_style.erase(style);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Group::childChanged(SGPropertyNode* node)
|
void Group::childChanged(SGPropertyNode* node)
|
||||||
{
|
{
|
||||||
if( node->getParent()->getParent() == _node
|
SGPropertyNode* parent = node->getParent();
|
||||||
|
SGPropertyNode* grand_parent = parent ? parent->getParent() : NULL;
|
||||||
|
|
||||||
|
if( grand_parent == _node
|
||||||
&& node->getNameString() == "z-index" )
|
&& node->getNameString() == "z-index" )
|
||||||
return handleZIndexChanged(node->getParent(), node->getIntValue());
|
return handleZIndexChanged(getChild(parent), node->getIntValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Group::handleZIndexChanged(SGPropertyNode* node, int z_index)
|
void Group::handleZIndexChanged(ElementPtr child, int z_index)
|
||||||
{
|
{
|
||||||
ChildList::iterator child = findChild(node);
|
if( !child || !_transform.valid() )
|
||||||
if( child == _children.end() )
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
osg::Node* tf = child->second->getMatrixTransform();
|
osg::ref_ptr<osg::MatrixTransform> tf = child->getMatrixTransform();
|
||||||
int index = _transform->getChildIndex(tf),
|
size_t index = _transform->getChildIndex(tf),
|
||||||
index_new = index;
|
index_new = index;
|
||||||
|
|
||||||
ChildList::iterator next = child;
|
for(;; ++index_new)
|
||||||
++next;
|
|
||||||
|
|
||||||
while( next != _children.end()
|
|
||||||
&& next->first->getIntValue("z-index", 0) <= z_index )
|
|
||||||
{
|
{
|
||||||
++index_new;
|
if( index_new + 1 == _transform->getNumChildren() )
|
||||||
++next;
|
break;
|
||||||
|
|
||||||
|
// Move to end of block with same index (= move upwards until the next
|
||||||
|
// element has a higher index)
|
||||||
|
if( getChildByIndex(index_new + 1)->get<int>("z-index", 0) > z_index )
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( index_new != index )
|
if( index_new == index )
|
||||||
{
|
{
|
||||||
_children.insert(next, *child);
|
// We were not able to move upwards so now try downwards
|
||||||
}
|
for(;; --index_new)
|
||||||
else
|
|
||||||
{
|
|
||||||
ChildList::iterator prev = child;
|
|
||||||
while( prev != _children.begin()
|
|
||||||
&& (--prev)->first->getIntValue("z-index", 0) > z_index)
|
|
||||||
{
|
{
|
||||||
--index_new;
|
if( index_new == 0 )
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Move to end of block with same index (= move downwards until the
|
||||||
|
// previous element has the same or a lower index)
|
||||||
|
if( getChildByIndex(index_new - 1)->get<int>("z-index", 0) <= z_index )
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( index == index_new )
|
if( index == index_new )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_children.insert(prev, *child);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_transform->removeChild(index);
|
_transform->removeChild(index);
|
||||||
_transform->insertChild(index_new, tf);
|
_transform->insertChild(index_new, tf);
|
||||||
|
|
||||||
_children.erase(child);
|
|
||||||
|
|
||||||
SG_LOG
|
SG_LOG
|
||||||
(
|
(
|
||||||
SG_GENERAL,
|
SG_GENERAL,
|
||||||
@@ -296,14 +379,42 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Group::ChildList::iterator Group::findChild(const SGPropertyNode* node)
|
ElementPtr Group::getChildByIndex(size_t index) const
|
||||||
{
|
{
|
||||||
return std::find_if
|
assert(_transform.valid());
|
||||||
(
|
OSGUserData* ud =
|
||||||
_children.begin(),
|
static_cast<OSGUserData*>(_transform->getChild(index)->getUserData());
|
||||||
_children.end(),
|
assert(ud);
|
||||||
boost::bind(&ChildList::value_type::first, _1) == node
|
return ud->element;
|
||||||
);
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
ElementPtr Group::findChild( const SGPropertyNode* node,
|
||||||
|
const std::string& id ) const
|
||||||
|
{
|
||||||
|
if( !_transform.valid() )
|
||||||
|
{
|
||||||
|
warnTransformExpired("findChild");
|
||||||
|
return ElementPtr();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
||||||
|
{
|
||||||
|
ElementPtr el = getChildByIndex(i);
|
||||||
|
|
||||||
|
if( node )
|
||||||
|
{
|
||||||
|
if( el->getProps() == node )
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( el->get<std::string>("id") == id )
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ElementPtr();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace canvas
|
} // namespace canvas
|
||||||
|
|||||||
@@ -28,10 +28,15 @@ namespace simgear
|
|||||||
namespace canvas
|
namespace canvas
|
||||||
{
|
{
|
||||||
|
|
||||||
|
typedef std::map<std::string, ElementFactory> ElementFactories;
|
||||||
|
|
||||||
class Group:
|
class Group:
|
||||||
public Element
|
public Element
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static const std::string TYPE_NAME;
|
||||||
|
static void staticInit();
|
||||||
|
|
||||||
typedef std::list< std::pair< const SGPropertyNode*,
|
typedef std::list< std::pair< const SGPropertyNode*,
|
||||||
ElementPtr
|
ElementPtr
|
||||||
>
|
>
|
||||||
@@ -46,6 +51,33 @@ namespace canvas
|
|||||||
ElementPtr createChild( const std::string& type,
|
ElementPtr createChild( const std::string& type,
|
||||||
const std::string& id = "" );
|
const std::string& id = "" );
|
||||||
ElementPtr getChild(const SGPropertyNode* node);
|
ElementPtr getChild(const SGPropertyNode* node);
|
||||||
|
ElementPtr getChild(const std::string& id);
|
||||||
|
ElementPtr getOrCreateChild( const std::string& type,
|
||||||
|
const std::string& id );
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
SGSharedPtr<T> createChild(const std::string& id = "")
|
||||||
|
{
|
||||||
|
return dynamic_cast<T*>( createChild(T::TYPE_NAME, id).get() );
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
SGSharedPtr<T> getChild(const SGPropertyNode* node)
|
||||||
|
{
|
||||||
|
return dynamic_cast<T*>( getChild(node).get() );
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
SGSharedPtr<T> getChild(const std::string& id)
|
||||||
|
{
|
||||||
|
return dynamic_cast<T*>( getChild(id).get() );
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
SGSharedPtr<T> getOrCreateChild(const std::string& id)
|
||||||
|
{
|
||||||
|
return dynamic_cast<T*>( getOrCreateChild(T::TYPE_NAME, id).get() );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get first child with given id (breadth-first search)
|
* Get first child with given id (breadth-first search)
|
||||||
@@ -54,28 +86,36 @@ namespace canvas
|
|||||||
*/
|
*/
|
||||||
ElementPtr getElementById(const std::string& id);
|
ElementPtr getElementById(const std::string& id);
|
||||||
|
|
||||||
|
virtual void clearEventListener();
|
||||||
|
|
||||||
virtual void update(double dt);
|
virtual void update(double dt);
|
||||||
|
|
||||||
virtual bool traverse(EventVisitor& visitor);
|
virtual bool traverse(EventVisitor& visitor);
|
||||||
|
|
||||||
virtual bool setStyle(const SGPropertyNode* child);
|
virtual bool setStyle( const SGPropertyNode* child,
|
||||||
|
const StyleInfo* style_info = 0 );
|
||||||
|
|
||||||
virtual osg::BoundingBox getTransformedBounds(const osg::Matrix& m) const;
|
virtual osg::BoundingBox getTransformedBounds(const osg::Matrix& m) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
typedef std::map<std::string, ElementFactory> ChildFactories;
|
static ElementFactories _child_factories;
|
||||||
|
|
||||||
ChildFactories _child_factories;
|
/**
|
||||||
ChildList _children;
|
* Overload in derived classes to allow for more/other types of elements
|
||||||
|
* to be managed.
|
||||||
|
*/
|
||||||
|
virtual ElementFactory getChildFactory(const std::string& type) const;
|
||||||
|
|
||||||
virtual void childAdded(SGPropertyNode * child);
|
virtual void childAdded(SGPropertyNode * child);
|
||||||
virtual void childRemoved(SGPropertyNode * child);
|
virtual void childRemoved(SGPropertyNode * child);
|
||||||
virtual void childChanged(SGPropertyNode * child);
|
virtual void childChanged(SGPropertyNode * child);
|
||||||
|
|
||||||
void handleZIndexChanged(SGPropertyNode* node, int z_index);
|
void handleZIndexChanged(ElementPtr child, int z_index = 0);
|
||||||
|
|
||||||
ChildList::iterator findChild(const SGPropertyNode* node);
|
ElementPtr getChildByIndex(size_t index) const;
|
||||||
|
ElementPtr findChild( const SGPropertyNode* node,
|
||||||
|
const std::string& id ) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace canvas
|
} // namespace canvas
|
||||||
|
|||||||
@@ -21,14 +21,17 @@
|
|||||||
#include <simgear/canvas/Canvas.hxx>
|
#include <simgear/canvas/Canvas.hxx>
|
||||||
#include <simgear/canvas/CanvasMgr.hxx>
|
#include <simgear/canvas/CanvasMgr.hxx>
|
||||||
#include <simgear/canvas/CanvasSystemAdapter.hxx>
|
#include <simgear/canvas/CanvasSystemAdapter.hxx>
|
||||||
|
#include <simgear/canvas/events/KeyboardEvent.hxx>
|
||||||
|
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||||
|
#include <simgear/scene/util/OsgMath.hxx>
|
||||||
#include <simgear/scene/util/parse_color.hxx>
|
#include <simgear/scene/util/parse_color.hxx>
|
||||||
#include <simgear/misc/sg_path.hxx>
|
#include <simgear/misc/sg_path.hxx>
|
||||||
|
|
||||||
#include <osg/Array>
|
#include <osg/Array>
|
||||||
#include <osg/Geometry>
|
#include <osg/Geometry>
|
||||||
#include <osg/PrimitiveSet>
|
#include <osg/PrimitiveSet>
|
||||||
|
#include <osgDB/Registry>
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <osg/Version>
|
||||||
|
|
||||||
namespace simgear
|
namespace simgear
|
||||||
{
|
{
|
||||||
@@ -43,9 +46,11 @@ namespace canvas
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CullCallback(const CanvasWeakPtr& canvas);
|
CullCallback(const CanvasWeakPtr& canvas);
|
||||||
|
void cullNextFrame();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CanvasWeakPtr _canvas;
|
CanvasWeakPtr _canvas;
|
||||||
|
mutable bool _cull_next_frame;
|
||||||
|
|
||||||
virtual bool cull( osg::NodeVisitor* nv,
|
virtual bool cull( osg::NodeVisitor* nv,
|
||||||
osg::Drawable* drawable,
|
osg::Drawable* drawable,
|
||||||
@@ -54,21 +59,53 @@ namespace canvas
|
|||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
CullCallback::CullCallback(const CanvasWeakPtr& canvas):
|
CullCallback::CullCallback(const CanvasWeakPtr& canvas):
|
||||||
_canvas( canvas )
|
_canvas( canvas ),
|
||||||
|
_cull_next_frame( false )
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void CullCallback::cullNextFrame()
|
||||||
|
{
|
||||||
|
_cull_next_frame = true;
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
bool CullCallback::cull( osg::NodeVisitor* nv,
|
bool CullCallback::cull( osg::NodeVisitor* nv,
|
||||||
osg::Drawable* drawable,
|
osg::Drawable* drawable,
|
||||||
osg::RenderInfo* renderInfo ) const
|
osg::RenderInfo* renderInfo ) const
|
||||||
{
|
{
|
||||||
if( !_canvas.expired() )
|
CanvasPtr canvas = _canvas.lock();
|
||||||
_canvas.lock()->enableRendering();
|
if( canvas )
|
||||||
|
canvas->enableRendering();
|
||||||
|
|
||||||
// TODO check if window/image should be culled
|
if( !_cull_next_frame )
|
||||||
return false;
|
// TODO check if window/image should be culled
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_cull_next_frame = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
const std::string Image::TYPE_NAME = "image";
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Image::staticInit()
|
||||||
|
{
|
||||||
|
if( isInit<Image>() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
addStyle("fill", "color", &Image::setFill);
|
||||||
|
addStyle("outset", "", &Image::setOutset);
|
||||||
|
addStyle("preserveAspectRatio", "", &Image::setPreserveAspectRatio);
|
||||||
|
addStyle("slice", "", &Image::setSlice);
|
||||||
|
addStyle("slice-width", "", &Image::setSliceWidth);
|
||||||
|
|
||||||
|
osgDB::Registry* reg = osgDB::Registry::instance();
|
||||||
|
if( !reg->getReaderWriterForExtension("png") )
|
||||||
|
SG_LOG(SG_GL, SG_ALERT, "canvas::Image: Missing 'png' image reader");
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -82,6 +119,8 @@ namespace canvas
|
|||||||
_src_rect(0,0),
|
_src_rect(0,0),
|
||||||
_region(0,0)
|
_region(0,0)
|
||||||
{
|
{
|
||||||
|
staticInit();
|
||||||
|
|
||||||
_geom = new osg::Geometry;
|
_geom = new osg::Geometry;
|
||||||
_geom->setUseDisplayList(false);
|
_geom->setUseDisplayList(false);
|
||||||
|
|
||||||
@@ -91,35 +130,33 @@ namespace canvas
|
|||||||
|
|
||||||
// allocate arrays for the image
|
// allocate arrays for the image
|
||||||
_vertices = new osg::Vec3Array(4);
|
_vertices = new osg::Vec3Array(4);
|
||||||
_vertices->setDataVariance(osg::Object::STATIC);
|
_vertices->setDataVariance(osg::Object::DYNAMIC);
|
||||||
_geom->setVertexArray(_vertices);
|
_geom->setVertexArray(_vertices);
|
||||||
|
|
||||||
_texCoords = new osg::Vec2Array(4);
|
_texCoords = new osg::Vec2Array(4);
|
||||||
_texCoords->setDataVariance(osg::Object::STATIC);
|
_texCoords->setDataVariance(osg::Object::DYNAMIC);
|
||||||
_geom->setTexCoordArray(0, _texCoords);
|
_geom->setTexCoordArray(0, _texCoords, osg::Array::BIND_PER_VERTEX);
|
||||||
|
|
||||||
_colors = new osg::Vec4Array(4);
|
_colors = new osg::Vec4Array(1);
|
||||||
_colors->setDataVariance(osg::Object::STATIC);
|
_colors->setDataVariance(osg::Object::DYNAMIC);
|
||||||
_geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
|
_geom->setColorArray(_colors, osg::Array::BIND_OVERALL);
|
||||||
_geom->setColorArray(_colors);
|
|
||||||
|
|
||||||
osg::DrawArrays* prim = new osg::DrawArrays(osg::PrimitiveSet::QUADS);
|
_prim = new osg::DrawArrays(osg::PrimitiveSet::QUADS);
|
||||||
prim->set(osg::PrimitiveSet::QUADS, 0, 4);
|
_prim->set(osg::PrimitiveSet::QUADS, 0, 4);
|
||||||
prim->setDataVariance(osg::Object::STATIC);
|
_prim->setDataVariance(osg::Object::DYNAMIC);
|
||||||
_geom->addPrimitiveSet(prim);
|
_geom->addPrimitiveSet(_prim);
|
||||||
|
|
||||||
setDrawable(_geom);
|
setDrawable(_geom);
|
||||||
|
|
||||||
addStyle("fill", &Image::setFill, this);
|
|
||||||
setFill("#ffffff"); // TODO how should we handle default values?
|
setFill("#ffffff"); // TODO how should we handle default values?
|
||||||
|
|
||||||
setupStyle();
|
setupStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Image::~Image()
|
Image::~Image()
|
||||||
{
|
{
|
||||||
|
if( _http_request )
|
||||||
|
_http_request->abort("image destroyed");
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -127,61 +164,295 @@ namespace canvas
|
|||||||
{
|
{
|
||||||
Element::update(dt);
|
Element::update(dt);
|
||||||
|
|
||||||
|
osg::Texture2D* texture = dynamic_cast<osg::Texture2D*>
|
||||||
|
(
|
||||||
|
_geom->getOrCreateStateSet()
|
||||||
|
->getTextureAttribute(0, osg::StateAttribute::TEXTURE)
|
||||||
|
);
|
||||||
|
simgear::canvas::CanvasPtr canvas = _src_canvas.lock();
|
||||||
|
|
||||||
|
if( (_attributes_dirty & SRC_CANVAS)
|
||||||
|
// check if texture has changed (eg. due to resizing)
|
||||||
|
|| (canvas && texture != canvas->getTexture()) )
|
||||||
|
{
|
||||||
|
_geom->getOrCreateStateSet()
|
||||||
|
->setTextureAttribute(0, canvas ? canvas->getTexture() : 0);
|
||||||
|
|
||||||
|
if( !canvas || canvas->isInit() )
|
||||||
|
_attributes_dirty &= ~SRC_CANVAS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !_attributes_dirty )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const SGRect<int>& tex_dim = getTextureDimensions();
|
||||||
|
|
||||||
|
// http://www.w3.org/TR/css3-background/#border-image-slice
|
||||||
|
|
||||||
|
// The ‘fill’ keyword, if present, causes the middle part of the image to be
|
||||||
|
// preserved. (By default it is discarded, i.e., treated as empty.)
|
||||||
|
bool fill = (_slice.getKeyword() == "fill");
|
||||||
|
|
||||||
if( _attributes_dirty & DEST_SIZE )
|
if( _attributes_dirty & DEST_SIZE )
|
||||||
{
|
{
|
||||||
(*_vertices)[0].set(_region.l(), _region.t(), 0);
|
size_t num_vertices = (_slice.isValid() ? (fill ? 9 : 8) : 1) * 4;
|
||||||
(*_vertices)[1].set(_region.r(), _region.t(), 0);
|
|
||||||
(*_vertices)[2].set(_region.r(), _region.b(), 0);
|
|
||||||
(*_vertices)[3].set(_region.l(), _region.b(), 0);
|
|
||||||
_vertices->dirty();
|
|
||||||
|
|
||||||
|
if( num_vertices != _prim->getNumPrimitives() )
|
||||||
|
{
|
||||||
|
_vertices->resize(num_vertices);
|
||||||
|
_texCoords->resize(num_vertices);
|
||||||
|
_prim->setCount(num_vertices);
|
||||||
|
_prim->dirty();
|
||||||
|
|
||||||
|
_attributes_dirty |= SRC_RECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://www.w3.org/TR/css3-background/#border-image-outset
|
||||||
|
SGRect<float> region = _region;
|
||||||
|
if( _outset.isValid() )
|
||||||
|
{
|
||||||
|
const CSSBorder::Offsets& outset = _outset.getAbsOffsets(tex_dim);
|
||||||
|
region.t() -= outset.t;
|
||||||
|
region.r() += outset.r;
|
||||||
|
region.b() += outset.b;
|
||||||
|
region.l() -= outset.l;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !_slice.isValid() )
|
||||||
|
{
|
||||||
|
setQuad(0, region.getMin(), region.getMax());
|
||||||
|
|
||||||
|
if( !_preserve_aspect_ratio.scaleToFill() )
|
||||||
|
// We need to update texture coordinates to keep the aspect ratio
|
||||||
|
_attributes_dirty |= SRC_RECT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Image slice, 9-scale, whatever it is called. The four corner images
|
||||||
|
stay unscaled (tl, tr, bl, br) whereas the other parts are scaled to
|
||||||
|
fill the remaining space up to the specified size.
|
||||||
|
|
||||||
|
x[0] x[1] x[2] x[3]
|
||||||
|
| | | |
|
||||||
|
-------------------- - y[0]
|
||||||
|
| tl | top | tr |
|
||||||
|
-------------------- - y[1]
|
||||||
|
| | | |
|
||||||
|
| l | | r |
|
||||||
|
| e | center | i |
|
||||||
|
| f | | g |
|
||||||
|
| t | | h |
|
||||||
|
| | | t |
|
||||||
|
-------------------- - y[2]
|
||||||
|
| bl | bottom | br |
|
||||||
|
-------------------- - y[3]
|
||||||
|
*/
|
||||||
|
|
||||||
|
const CSSBorder::Offsets& slice =
|
||||||
|
(_slice_width.isValid() ? _slice_width : _slice)
|
||||||
|
.getAbsOffsets(tex_dim);
|
||||||
|
float x[4] = {
|
||||||
|
region.l(),
|
||||||
|
region.l() + slice.l,
|
||||||
|
region.r() - slice.r,
|
||||||
|
region.r()
|
||||||
|
};
|
||||||
|
float y[4] = {
|
||||||
|
region.t(),
|
||||||
|
region.t() + slice.t,
|
||||||
|
region.b() - slice.b,
|
||||||
|
region.b()
|
||||||
|
};
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for(int ix = 0; ix < 3; ++ix)
|
||||||
|
for(int iy = 0; iy < 3; ++iy)
|
||||||
|
{
|
||||||
|
if( ix == 1 && iy == 1 && !fill )
|
||||||
|
// The ‘fill’ keyword, if present, causes the middle part of the
|
||||||
|
// image to be filled.
|
||||||
|
continue;
|
||||||
|
|
||||||
|
setQuad( i++,
|
||||||
|
SGVec2f(x[ix ], y[iy ]),
|
||||||
|
SGVec2f(x[ix + 1], y[iy + 1]) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_vertices->dirty();
|
||||||
_attributes_dirty &= ~DEST_SIZE;
|
_attributes_dirty &= ~DEST_SIZE;
|
||||||
_geom->dirtyBound();
|
_geom->dirtyBound();
|
||||||
setBoundingBox(_geom->getBound());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if( _attributes_dirty & SRC_RECT )
|
if( _attributes_dirty & SRC_RECT )
|
||||||
{
|
{
|
||||||
double u0 = _src_rect.l(),
|
SGRect<float> src_rect = _src_rect;
|
||||||
u1 = _src_rect.r(),
|
|
||||||
v0 = _src_rect.b(),
|
|
||||||
v1 = _src_rect.t();
|
|
||||||
|
|
||||||
if( !_node_src_rect->getBoolValue("normalized", true) )
|
if( !_node_src_rect->getBoolValue("normalized", true) )
|
||||||
{
|
{
|
||||||
const SGRect<int>& tex_dim = getTextureDimensions();
|
src_rect.t() /= tex_dim.height();
|
||||||
|
src_rect.r() /= tex_dim.width();
|
||||||
u0 /= tex_dim.width();
|
src_rect.b() /= tex_dim.height();
|
||||||
u1 /= tex_dim.width();
|
src_rect.l() /= tex_dim.width();
|
||||||
v0 /= tex_dim.height();
|
|
||||||
v1 /= tex_dim.height();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
(*_texCoords)[0].set(u0, v0);
|
// Image coordinate systems y-axis is flipped
|
||||||
(*_texCoords)[1].set(u1, v0);
|
std::swap(src_rect.t(), src_rect.b());
|
||||||
(*_texCoords)[2].set(u1, v1);
|
|
||||||
(*_texCoords)[3].set(u0, v1);
|
|
||||||
_texCoords->dirty();
|
|
||||||
|
|
||||||
|
if( !_slice.isValid() )
|
||||||
|
{
|
||||||
|
// Image scaling preserving aspect ratio. Change texture coordinates to
|
||||||
|
// scale image accordingly.
|
||||||
|
//
|
||||||
|
// TODO allow to specify what happens to not filled space (eg. color,
|
||||||
|
// or texture repeat/mirror)
|
||||||
|
//
|
||||||
|
// http://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute
|
||||||
|
if( !_preserve_aspect_ratio.scaleToFill() )
|
||||||
|
{
|
||||||
|
osg::BoundingBox const& bb = getBoundingBox();
|
||||||
|
float dst_width = bb._max.x() - bb._min.x(),
|
||||||
|
dst_height = bb._max.y() - bb._min.y();
|
||||||
|
float scale_x = dst_width / tex_dim.width(),
|
||||||
|
scale_y = dst_height / tex_dim.height();
|
||||||
|
|
||||||
|
float scale = _preserve_aspect_ratio.scaleToFit()
|
||||||
|
? std::min(scale_x, scale_y)
|
||||||
|
: std::max(scale_x, scale_y);
|
||||||
|
|
||||||
|
if( scale_x != scale )
|
||||||
|
{
|
||||||
|
float d = scale_x / scale - 1;
|
||||||
|
if( _preserve_aspect_ratio.alignX()
|
||||||
|
== SVGpreserveAspectRatio::ALIGN_MIN )
|
||||||
|
{
|
||||||
|
src_rect.r() += d;
|
||||||
|
}
|
||||||
|
else if( _preserve_aspect_ratio.alignX()
|
||||||
|
== SVGpreserveAspectRatio::ALIGN_MAX )
|
||||||
|
{
|
||||||
|
src_rect.l() -= d;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
src_rect.l() -= d / 2;
|
||||||
|
src_rect.r() += d / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( scale_y != scale )
|
||||||
|
{
|
||||||
|
float d = scale_y / scale - 1;
|
||||||
|
if( _preserve_aspect_ratio.alignY()
|
||||||
|
== SVGpreserveAspectRatio::ALIGN_MIN )
|
||||||
|
{
|
||||||
|
src_rect.b() -= d;
|
||||||
|
}
|
||||||
|
else if( _preserve_aspect_ratio.alignY()
|
||||||
|
== SVGpreserveAspectRatio::ALIGN_MAX )
|
||||||
|
{
|
||||||
|
src_rect.t() += d;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
src_rect.t() += d / 2;
|
||||||
|
src_rect.b() -= d / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setQuadUV(0, src_rect.getMin(), src_rect.getMax());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const CSSBorder::Offsets& slice = _slice.getRelOffsets(tex_dim);
|
||||||
|
float x[4] = {
|
||||||
|
src_rect.l(),
|
||||||
|
src_rect.l() + slice.l,
|
||||||
|
src_rect.r() - slice.r,
|
||||||
|
src_rect.r()
|
||||||
|
};
|
||||||
|
float y[4] = {
|
||||||
|
src_rect.t(),
|
||||||
|
src_rect.t() - slice.t,
|
||||||
|
src_rect.b() + slice.b,
|
||||||
|
src_rect.b()
|
||||||
|
};
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for(int ix = 0; ix < 3; ++ix)
|
||||||
|
for(int iy = 0; iy < 3; ++iy)
|
||||||
|
{
|
||||||
|
if( ix == 1 && iy == 1 && !fill )
|
||||||
|
// The ‘fill’ keyword, if present, causes the middle part of the
|
||||||
|
// image to be filled.
|
||||||
|
continue;
|
||||||
|
|
||||||
|
setQuadUV( i++,
|
||||||
|
SGVec2f(x[ix ], y[iy ]),
|
||||||
|
SGVec2f(x[ix + 1], y[iy + 1]) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_texCoords->dirty();
|
||||||
_attributes_dirty &= ~SRC_RECT;
|
_attributes_dirty &= ~SRC_RECT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
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)
|
void Image::setSrcCanvas(CanvasPtr canvas)
|
||||||
{
|
{
|
||||||
if( !_src_canvas.expired() )
|
CanvasPtr src_canvas = _src_canvas.lock(),
|
||||||
_src_canvas.lock()->removeDependentCanvas(_canvas);
|
self_canvas = _canvas.lock();
|
||||||
|
|
||||||
_src_canvas = canvas;
|
if( src_canvas )
|
||||||
_geom->getOrCreateStateSet()
|
src_canvas->removeParentCanvas(self_canvas);
|
||||||
->setTextureAttribute(0, canvas ? canvas->getTexture() : 0);
|
if( self_canvas )
|
||||||
|
self_canvas->removeChildCanvas(src_canvas);
|
||||||
|
|
||||||
|
_src_canvas = src_canvas = canvas;
|
||||||
|
_attributes_dirty |= SRC_CANVAS;
|
||||||
_geom->setCullCallback(canvas ? new CullCallback(canvas) : 0);
|
_geom->setCullCallback(canvas ? new CullCallback(canvas) : 0);
|
||||||
|
|
||||||
if( !_src_canvas.expired() )
|
if( src_canvas )
|
||||||
{
|
{
|
||||||
setupDefaultDimensions();
|
setupDefaultDimensions();
|
||||||
_src_canvas.lock()->addDependentCanvas(_canvas);
|
|
||||||
|
if( self_canvas )
|
||||||
|
{
|
||||||
|
self_canvas->addChildCanvas(src_canvas);
|
||||||
|
src_canvas->addParentCanvas(self_canvas);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,7 +468,10 @@ namespace canvas
|
|||||||
// remove canvas...
|
// remove canvas...
|
||||||
setSrcCanvas( CanvasPtr() );
|
setSrcCanvas( CanvasPtr() );
|
||||||
|
|
||||||
|
_texture->setResizeNonPowerOfTwoHint(false);
|
||||||
_texture->setImage(img);
|
_texture->setImage(img);
|
||||||
|
_texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
|
||||||
|
_texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
|
||||||
_geom->getOrCreateStateSet()
|
_geom->getOrCreateStateSet()
|
||||||
->setTextureAttributeAndModes(0, _texture);
|
->setTextureAttributeAndModes(0, _texture);
|
||||||
|
|
||||||
@@ -208,21 +482,92 @@ namespace canvas
|
|||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Image::setFill(const std::string& fill)
|
void Image::setFill(const std::string& fill)
|
||||||
{
|
{
|
||||||
osg::Vec4 color;
|
osg::Vec4 color(1,1,1,1);
|
||||||
if( !parseColor(fill, color) )
|
if( !fill.empty() // If no color is given default to white
|
||||||
|
&& !parseColor(fill, color) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for( int i = 0; i < 4; ++i )
|
_colors->front() = color;
|
||||||
(*_colors)[i] = color;
|
|
||||||
_colors->dirty();
|
_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
|
const SGRect<float>& Image::getRegion() const
|
||||||
{
|
{
|
||||||
return _region;
|
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)
|
void Image::childChanged(SGPropertyNode* child)
|
||||||
{
|
{
|
||||||
@@ -265,20 +610,41 @@ namespace canvas
|
|||||||
|
|
||||||
_attributes_dirty |= DEST_SIZE;
|
_attributes_dirty |= DEST_SIZE;
|
||||||
}
|
}
|
||||||
else if( name == "file" )
|
else if( name == "src" || name == "file" )
|
||||||
{
|
{
|
||||||
static const std::string CANVAS_PROTOCOL = "canvas://";
|
if( name == "file" )
|
||||||
const std::string& path = child->getStringValue();
|
SG_LOG(SG_GL, SG_WARN, "'file' is deprecated. Use 'src' instead");
|
||||||
|
|
||||||
CanvasPtr canvas = _canvas.lock();
|
// Abort pending request
|
||||||
if( !canvas )
|
if( _http_request )
|
||||||
{
|
{
|
||||||
SG_LOG(SG_GL, SG_ALERT, "canvas::Image: No canvas available");
|
_http_request->abort("setting new image");
|
||||||
return;
|
_http_request.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
if( boost::starts_with(path, CANVAS_PROTOCOL) )
|
static const std::string PROTOCOL_SEP = "://";
|
||||||
|
|
||||||
|
std::string url = child->getStringValue(),
|
||||||
|
protocol, path;
|
||||||
|
|
||||||
|
size_t sep_pos = url.find(PROTOCOL_SEP);
|
||||||
|
if( sep_pos != std::string::npos )
|
||||||
{
|
{
|
||||||
|
protocol = url.substr(0, sep_pos);
|
||||||
|
path = url.substr(sep_pos + PROTOCOL_SEP.length());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
path = url;
|
||||||
|
|
||||||
|
if( protocol == "canvas" )
|
||||||
|
{
|
||||||
|
CanvasPtr canvas = _canvas.lock();
|
||||||
|
if( !canvas )
|
||||||
|
{
|
||||||
|
SG_LOG(SG_GL, SG_ALERT, "canvas::Image: No canvas available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
CanvasMgr* canvas_mgr = canvas->getCanvasMgr();
|
CanvasMgr* canvas_mgr = canvas->getCanvasMgr();
|
||||||
if( !canvas_mgr )
|
if( !canvas_mgr )
|
||||||
{
|
{
|
||||||
@@ -289,7 +655,7 @@ namespace canvas
|
|||||||
const SGPropertyNode* canvas_node =
|
const SGPropertyNode* canvas_node =
|
||||||
canvas_mgr->getPropertyRoot()
|
canvas_mgr->getPropertyRoot()
|
||||||
->getParent()
|
->getParent()
|
||||||
->getNode( path.substr(CANVAS_PROTOCOL.size()) );
|
->getNode( path );
|
||||||
if( !canvas_node )
|
if( !canvas_node )
|
||||||
{
|
{
|
||||||
SG_LOG(SG_GL, SG_ALERT, "canvas::Image: No such canvas: " << path);
|
SG_LOG(SG_GL, SG_ALERT, "canvas::Image: No such canvas: " << path);
|
||||||
@@ -307,9 +673,19 @@ namespace canvas
|
|||||||
|
|
||||||
setSrcCanvas(src_canvas);
|
setSrcCanvas(src_canvas);
|
||||||
}
|
}
|
||||||
|
else if( protocol == "http" || protocol == "https" )
|
||||||
|
// TODO check https
|
||||||
|
{
|
||||||
|
_http_request =
|
||||||
|
Canvas::getSystemAdapter()
|
||||||
|
->getHTTPClient()
|
||||||
|
->load(url)
|
||||||
|
// TODO handle capture of 'this'
|
||||||
|
->done(this, &Image::handleImageLoadDone);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
setImage( canvas->getSystemAdapter()->getImage(path) );
|
setImage( Canvas::getSystemAdapter()->getImage(path) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -319,36 +695,127 @@ namespace canvas
|
|||||||
{
|
{
|
||||||
if( !_src_rect.width() || !_src_rect.height() )
|
if( !_src_rect.width() || !_src_rect.height() )
|
||||||
{
|
{
|
||||||
const SGRect<int>& tex_dim = getTextureDimensions();
|
// Show whole image by default
|
||||||
|
_node_src_rect->setBoolValue("normalized", true);
|
||||||
_node_src_rect->setBoolValue("normalized", false);
|
_node_src_rect->setFloatValue("right", 1);
|
||||||
_node_src_rect->setFloatValue("right", tex_dim.width());
|
_node_src_rect->setFloatValue("bottom", 1);
|
||||||
_node_src_rect->setFloatValue("bottom", tex_dim.height());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !_region.width() || !_region.height() )
|
if( !_region.width() || !_region.height() )
|
||||||
{
|
{
|
||||||
_node->setFloatValue("size[0]", _src_rect.width());
|
// Default to image size.
|
||||||
_node->setFloatValue("size[1]", _src_rect.height());
|
// TODO handle showing only part of image?
|
||||||
|
const SGRect<int>& dim = getTextureDimensions();
|
||||||
|
_node->setFloatValue("size[0]", dim.width());
|
||||||
|
_node->setFloatValue("size[1]", dim.height());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
SGRect<int> Image::getTextureDimensions() const
|
SGRect<int> Image::getTextureDimensions() const
|
||||||
{
|
{
|
||||||
osg::Texture2D *texture = !_src_canvas.expired()
|
CanvasPtr canvas = _src_canvas.lock();
|
||||||
? _src_canvas.lock()->getTexture()
|
SGRect<int> dim(0,0);
|
||||||
: _texture.get();
|
|
||||||
|
|
||||||
if( !texture )
|
// Use canvas/image dimensions rather than texture dimensions, as they could
|
||||||
return SGRect<int>();
|
// be resized internally by OpenSceneGraph (eg. to nearest power of two).
|
||||||
|
if( canvas )
|
||||||
|
{
|
||||||
|
dim.setRight( canvas->getViewWidth() );
|
||||||
|
dim.setBottom( canvas->getViewHeight() );
|
||||||
|
}
|
||||||
|
else if( _texture )
|
||||||
|
{
|
||||||
|
osg::Image* img = _texture->getImage();
|
||||||
|
|
||||||
return SGRect<int>
|
if( img )
|
||||||
(
|
{
|
||||||
0,0,
|
dim.setRight( img->s() );
|
||||||
texture->getTextureWidth(),
|
dim.setBottom( img->t() );
|
||||||
texture->getTextureHeight()
|
}
|
||||||
);
|
}
|
||||||
|
|
||||||
|
return dim;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Image::setQuad(size_t index, const SGVec2f& tl, const SGVec2f& br)
|
||||||
|
{
|
||||||
|
int i = index * 4;
|
||||||
|
(*_vertices)[i + 0].set(tl.x(), tl.y(), 0);
|
||||||
|
(*_vertices)[i + 1].set(br.x(), tl.y(), 0);
|
||||||
|
(*_vertices)[i + 2].set(br.x(), br.y(), 0);
|
||||||
|
(*_vertices)[i + 3].set(tl.x(), br.y(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Image::setQuadUV(size_t index, const SGVec2f& tl, const SGVec2f& br)
|
||||||
|
{
|
||||||
|
int i = index * 4;
|
||||||
|
(*_texCoords)[i + 0].set(tl.x(), tl.y());
|
||||||
|
(*_texCoords)[i + 1].set(br.x(), tl.y());
|
||||||
|
(*_texCoords)[i + 2].set(br.x(), br.y());
|
||||||
|
(*_texCoords)[i + 3].set(tl.x(), br.y());
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Image::handleImageLoadDone(HTTP::Request* req)
|
||||||
|
{
|
||||||
|
// Ignore stale/expired requests
|
||||||
|
if( _http_request != req )
|
||||||
|
return;
|
||||||
|
_http_request.reset();
|
||||||
|
|
||||||
|
if( req->responseCode() != 200 )
|
||||||
|
{
|
||||||
|
SG_LOG(SG_IO, SG_WARN, "failed to download '" << req->url() << "': "
|
||||||
|
<< req->responseReason());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string ext = SGPath(req->path()).extension(),
|
||||||
|
mime = req->responseMime();
|
||||||
|
|
||||||
|
SG_LOG(SG_IO, SG_INFO, "received " << req->url() <<
|
||||||
|
" (ext=" << ext << ", MIME=" << mime << ")");
|
||||||
|
|
||||||
|
const std::string& img_data =
|
||||||
|
static_cast<HTTP::MemoryRequest*>(req)->responseBody();
|
||||||
|
osgDB::Registry* reg = osgDB::Registry::instance();
|
||||||
|
|
||||||
|
// First try to detect image type by extension
|
||||||
|
osgDB::ReaderWriter* rw = reg->getReaderWriterForExtension(ext);
|
||||||
|
if( rw && loadImage(*rw, img_data, *req, "extension") )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Now try with MIME type
|
||||||
|
rw = reg->getReaderWriterForMimeType(mime);
|
||||||
|
if( rw && loadImage(*rw, img_data, *req, "MIME type") )
|
||||||
|
return;
|
||||||
|
|
||||||
|
SG_LOG(SG_IO, SG_WARN, "unable to read image '" << req->url() << "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
bool Image::loadImage( osgDB::ReaderWriter& reader,
|
||||||
|
const std::string& data,
|
||||||
|
HTTP::Request& request,
|
||||||
|
const std::string& type )
|
||||||
|
{
|
||||||
|
SG_LOG(SG_IO, SG_DEBUG, "use image reader detected by " << type);
|
||||||
|
|
||||||
|
std::istringstream data_strm(data);
|
||||||
|
osgDB::ReaderWriter::ReadResult result = reader.readImage(data_strm);
|
||||||
|
if( result.success() )
|
||||||
|
{
|
||||||
|
setImage( result.takeImage() );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SG_LOG(SG_IO, SG_WARN, "failed to read image '" << request.url() << "': "
|
||||||
|
<< result.message());
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace canvas
|
} // namespace canvas
|
||||||
|
|||||||
@@ -22,11 +22,14 @@
|
|||||||
#include "CanvasElement.hxx"
|
#include "CanvasElement.hxx"
|
||||||
|
|
||||||
#include <simgear/canvas/canvas_fwd.hxx>
|
#include <simgear/canvas/canvas_fwd.hxx>
|
||||||
#include <simgear/math/SGRect.hxx>
|
#include <simgear/io/HTTPClient.hxx>
|
||||||
|
#include <simgear/misc/CSSBorder.hxx>
|
||||||
|
#include <simgear/misc/SVGpreserveAspectRatio.hxx>
|
||||||
#include <osg/Texture2D>
|
#include <osg/Texture2D>
|
||||||
|
|
||||||
namespace simgear
|
namespace simgear
|
||||||
{
|
{
|
||||||
|
namespace HTTP { class Request; }
|
||||||
namespace canvas
|
namespace canvas
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -34,6 +37,9 @@ namespace canvas
|
|||||||
public Element
|
public Element
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static const std::string TYPE_NAME;
|
||||||
|
static void staticInit();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param node Property node containing settings for this image:
|
* @param node Property node containing settings for this image:
|
||||||
* rect/[left/right/top/bottom] Dimensions of source
|
* rect/[left/right/top/bottom] Dimensions of source
|
||||||
@@ -43,11 +49,12 @@ namespace canvas
|
|||||||
*/
|
*/
|
||||||
Image( const CanvasWeakPtr& canvas,
|
Image( const CanvasWeakPtr& canvas,
|
||||||
const SGPropertyNode_ptr& node,
|
const SGPropertyNode_ptr& node,
|
||||||
const Style& parent_style,
|
const Style& parent_style = Style(),
|
||||||
Element* parent = 0 );
|
Element* parent = 0 );
|
||||||
virtual ~Image();
|
virtual ~Image();
|
||||||
|
|
||||||
virtual void update(double dt);
|
virtual void update(double dt);
|
||||||
|
virtual void valueChanged(SGPropertyNode* child);
|
||||||
|
|
||||||
void setSrcCanvas(CanvasPtr canvas);
|
void setSrcCanvas(CanvasPtr canvas);
|
||||||
CanvasWeakPtr getSrcCanvas() const;
|
CanvasWeakPtr getSrcCanvas() const;
|
||||||
@@ -55,14 +62,46 @@ namespace canvas
|
|||||||
void setImage(osg::Image *img);
|
void setImage(osg::Image *img);
|
||||||
void setFill(const std::string& fill);
|
void setFill(const std::string& fill);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see http://www.w3.org/TR/css3-background/#border-image-outset
|
||||||
|
*/
|
||||||
|
void setOutset(const std::string& outset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see
|
||||||
|
* http://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute
|
||||||
|
*/
|
||||||
|
void setPreserveAspectRatio(const std::string& scale);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set image slice (aka. 9-scale)
|
||||||
|
*
|
||||||
|
* @see http://www.w3.org/TR/css3-background/#border-image-slice
|
||||||
|
*/
|
||||||
|
void setSlice(const std::string& slice);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set image slice width.
|
||||||
|
*
|
||||||
|
* By default the size of the 9-scale grid is the same as specified
|
||||||
|
* with setSlice/"slice". Using this method allows setting values
|
||||||
|
* different to the size in the source image.
|
||||||
|
*
|
||||||
|
* @see http://www.w3.org/TR/css3-background/#border-image-width
|
||||||
|
*/
|
||||||
|
void setSliceWidth(const std::string& width);
|
||||||
|
|
||||||
const SGRect<float>& getRegion() const;
|
const SGRect<float>& getRegion() const;
|
||||||
|
|
||||||
|
bool handleEvent(const EventPtr& event);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
enum ImageAttributes
|
enum ImageAttributes
|
||||||
{
|
{
|
||||||
SRC_RECT = LAST_ATTRIBUTE << 1, // Source image rectangle
|
SRC_RECT = LAST_ATTRIBUTE << 1, // Source image rectangle
|
||||||
DEST_SIZE = SRC_RECT << 1 // Element size
|
DEST_SIZE = SRC_RECT << 1, // Element size
|
||||||
|
SRC_CANVAS = DEST_SIZE << 1
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual void childChanged(SGPropertyNode * child);
|
virtual void childChanged(SGPropertyNode * child);
|
||||||
@@ -70,11 +109,22 @@ namespace canvas
|
|||||||
void setupDefaultDimensions();
|
void setupDefaultDimensions();
|
||||||
SGRect<int> getTextureDimensions() const;
|
SGRect<int> getTextureDimensions() const;
|
||||||
|
|
||||||
|
void setQuad(size_t index, const SGVec2f& tl, const SGVec2f& br);
|
||||||
|
void setQuadUV(size_t index, const SGVec2f& tl, const SGVec2f& br);
|
||||||
|
|
||||||
|
void handleImageLoadDone(HTTP::Request*);
|
||||||
|
bool loadImage( osgDB::ReaderWriter& reader,
|
||||||
|
const std::string& data,
|
||||||
|
HTTP::Request& request,
|
||||||
|
const std::string& type );
|
||||||
|
|
||||||
osg::ref_ptr<osg::Texture2D> _texture;
|
osg::ref_ptr<osg::Texture2D> _texture;
|
||||||
// TODO optionally forward events to canvas
|
// TODO optionally forward events to canvas
|
||||||
CanvasWeakPtr _src_canvas;
|
CanvasWeakPtr _src_canvas;
|
||||||
|
HTTP::Request_ptr _http_request;
|
||||||
|
|
||||||
osg::ref_ptr<osg::Geometry> _geom;
|
osg::ref_ptr<osg::Geometry> _geom;
|
||||||
|
osg::ref_ptr<osg::DrawArrays>_prim;
|
||||||
osg::ref_ptr<osg::Vec3Array> _vertices;
|
osg::ref_ptr<osg::Vec3Array> _vertices;
|
||||||
osg::ref_ptr<osg::Vec2Array> _texCoords;
|
osg::ref_ptr<osg::Vec2Array> _texCoords;
|
||||||
osg::ref_ptr<osg::Vec4Array> _colors;
|
osg::ref_ptr<osg::Vec4Array> _colors;
|
||||||
@@ -82,6 +132,12 @@ namespace canvas
|
|||||||
SGPropertyNode *_node_src_rect;
|
SGPropertyNode *_node_src_rect;
|
||||||
SGRect<float> _src_rect,
|
SGRect<float> _src_rect,
|
||||||
_region;
|
_region;
|
||||||
|
|
||||||
|
SVGpreserveAspectRatio _preserve_aspect_ratio;
|
||||||
|
|
||||||
|
CSSBorder _outset,
|
||||||
|
_slice,
|
||||||
|
_slice_width;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace canvas
|
} // namespace canvas
|
||||||
|
|||||||
@@ -42,7 +42,20 @@ namespace simgear
|
|||||||
namespace canvas
|
namespace canvas
|
||||||
{
|
{
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
const std::string GEO = "-geo";
|
const std::string GEO = "-geo";
|
||||||
|
const std::string Map::TYPE_NAME = "map";
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Map::staticInit()
|
||||||
|
{
|
||||||
|
Group::staticInit();
|
||||||
|
|
||||||
|
if( isInit<Map>() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Do some initialization if needed...
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Map::Map( const CanvasWeakPtr& canvas,
|
Map::Map( const CanvasWeakPtr& canvas,
|
||||||
@@ -54,7 +67,7 @@ namespace canvas
|
|||||||
_projection(new SansonFlamsteedProjection),
|
_projection(new SansonFlamsteedProjection),
|
||||||
_projection_dirty(true)
|
_projection_dirty(true)
|
||||||
{
|
{
|
||||||
|
staticInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -192,6 +205,8 @@ namespace canvas
|
|||||||
_projection->setOrientation(child->getFloatValue());
|
_projection->setOrientation(child->getFloatValue());
|
||||||
else if( child->getNameString() == "range" )
|
else if( child->getNameString() == "range" )
|
||||||
_projection->setRange(child->getDoubleValue());
|
_projection->setRange(child->getDoubleValue());
|
||||||
|
else if( child->getNameString() == "screen-range" )
|
||||||
|
_projection->setScreenRange(child->getDoubleValue());
|
||||||
else
|
else
|
||||||
return Group::childChanged(child);
|
return Group::childChanged(child);
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ namespace canvas
|
|||||||
public Group
|
public Group
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static const std::string TYPE_NAME;
|
||||||
|
static void staticInit();
|
||||||
|
|
||||||
Map( const CanvasWeakPtr& canvas,
|
Map( const CanvasWeakPtr& canvas,
|
||||||
const SGPropertyNode_ptr& node,
|
const SGPropertyNode_ptr& node,
|
||||||
const Style& parent_style,
|
const Style& parent_style,
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
#include <simgear/scene/util/parse_color.hxx>
|
#include <simgear/scene/util/parse_color.hxx>
|
||||||
|
|
||||||
#include <osg/Drawable>
|
#include <osg/Drawable>
|
||||||
#include <osg/BlendFunc>
|
#include <osg/Version>
|
||||||
|
|
||||||
#include <vg/openvg.h>
|
#include <vg/openvg.h>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
@@ -70,7 +70,8 @@ namespace canvas
|
|||||||
_mode(0),
|
_mode(0),
|
||||||
_fill_rule(VG_EVEN_ODD),
|
_fill_rule(VG_EVEN_ODD),
|
||||||
_stroke_width(1),
|
_stroke_width(1),
|
||||||
_stroke_linecap(VG_CAP_BUTT)
|
_stroke_linecap(VG_CAP_BUTT),
|
||||||
|
_stroke_linejoin(VG_JOIN_MITER)
|
||||||
{
|
{
|
||||||
setSupportsDisplayList(false);
|
setSupportsDisplayList(false);
|
||||||
setDataVariance(Object::DYNAMIC);
|
setDataVariance(Object::DYNAMIC);
|
||||||
@@ -111,7 +112,7 @@ namespace canvas
|
|||||||
*/
|
*/
|
||||||
void setFill(const std::string& fill)
|
void setFill(const std::string& fill)
|
||||||
{
|
{
|
||||||
if( fill == "none" )
|
if( fill.empty() || fill == "none" )
|
||||||
{
|
{
|
||||||
_mode &= ~VG_FILL_PATH;
|
_mode &= ~VG_FILL_PATH;
|
||||||
}
|
}
|
||||||
@@ -151,7 +152,7 @@ namespace canvas
|
|||||||
*/
|
*/
|
||||||
void setStroke(const std::string& stroke)
|
void setStroke(const std::string& stroke)
|
||||||
{
|
{
|
||||||
if( stroke == "none" )
|
if( stroke.empty() || stroke == "none" )
|
||||||
{
|
{
|
||||||
_mode &= ~VG_STROKE_PATH;
|
_mode &= ~VG_STROKE_PATH;
|
||||||
}
|
}
|
||||||
@@ -203,6 +204,21 @@ namespace canvas
|
|||||||
_stroke_linecap = VG_CAP_BUTT;
|
_stroke_linecap = VG_CAP_BUTT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set stroke-linejoin
|
||||||
|
*
|
||||||
|
* @see http://www.w3.org/TR/SVG/painting.html#StrokeLinejoinProperty
|
||||||
|
*/
|
||||||
|
void setStrokeLinejoin(const std::string& linejoin)
|
||||||
|
{
|
||||||
|
if( linejoin == "round" )
|
||||||
|
_stroke_linejoin = VG_JOIN_ROUND;
|
||||||
|
else if( linejoin == "bevel" )
|
||||||
|
_stroke_linejoin = VG_JOIN_BEVEL;
|
||||||
|
else
|
||||||
|
_stroke_linejoin = VG_JOIN_MITER;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draw callback
|
* Draw callback
|
||||||
*/
|
*/
|
||||||
@@ -218,9 +234,10 @@ namespace canvas
|
|||||||
state->setClientActiveTextureUnit(0);
|
state->setClientActiveTextureUnit(0);
|
||||||
state->disableAllVertexArrays();
|
state->disableAllVertexArrays();
|
||||||
|
|
||||||
glPushAttrib(~0u); // Don't use GL_ALL_ATTRIB_BITS as on my machine it
|
bool was_blend_enabled = state->getLastAppliedMode(GL_BLEND);
|
||||||
// eg. doesn't include GL_MULTISAMPLE_BIT
|
bool was_stencil_enabled = state->getLastAppliedMode(GL_STENCIL_TEST);
|
||||||
glPushClientAttrib(~0u);
|
osg::StateAttribute const* blend_func =
|
||||||
|
state->getLastAppliedAttribute(osg::StateAttribute::BLENDFUNC);
|
||||||
|
|
||||||
// Initialize/Update the paint
|
// Initialize/Update the paint
|
||||||
if( _attributes_dirty & STROKE_COLOR )
|
if( _attributes_dirty & STROKE_COLOR )
|
||||||
@@ -251,6 +268,7 @@ namespace canvas
|
|||||||
|
|
||||||
vgSetf(VG_STROKE_LINE_WIDTH, _stroke_width);
|
vgSetf(VG_STROKE_LINE_WIDTH, _stroke_width);
|
||||||
vgSeti(VG_STROKE_CAP_STYLE, _stroke_linecap);
|
vgSeti(VG_STROKE_CAP_STYLE, _stroke_linecap);
|
||||||
|
vgSeti(VG_STROKE_JOIN_STYLE, _stroke_linejoin);
|
||||||
vgSetfv( VG_STROKE_DASH_PATTERN,
|
vgSetfv( VG_STROKE_DASH_PATTERN,
|
||||||
_stroke_dash.size(),
|
_stroke_dash.size(),
|
||||||
_stroke_dash.empty() ? 0 : &_stroke_dash[0] );
|
_stroke_dash.empty() ? 0 : &_stroke_dash[0] );
|
||||||
@@ -270,15 +288,19 @@ namespace canvas
|
|||||||
if( err != VG_NO_ERROR )
|
if( err != VG_NO_ERROR )
|
||||||
SG_LOG(SG_GL, SG_ALERT, "vgError: " << err);
|
SG_LOG(SG_GL, SG_ALERT, "vgError: " << err);
|
||||||
|
|
||||||
glPopAttrib();
|
// Restore OpenGL state (TODO check if more is needed or integrate
|
||||||
glPopClientAttrib();
|
// better with OpenSceneGraph)
|
||||||
|
if( was_blend_enabled ) glEnable(GL_BLEND);
|
||||||
|
if( was_stencil_enabled ) glEnable(GL_STENCIL_TEST);
|
||||||
|
if( blend_func ) blend_func->apply(*state);
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::BoundingBox getTransformedBounds(const osg::Matrix& mat) const
|
osg::BoundingBox getTransformedBounds(const osg::Matrix& mat) const
|
||||||
{
|
{
|
||||||
osg::BoundingBox bb;
|
osg::BoundingBox bb;
|
||||||
|
|
||||||
osg::Vec2f cur; // VG "Current point" (in local coordinates)
|
osg::Vec2f cur(0, 0), // VG "Current point" (in local coordinates)
|
||||||
|
sub(0, 0); // beginning of current sub path
|
||||||
VGubyte cmd_index = 0;
|
VGubyte cmd_index = 0;
|
||||||
for( size_t i = 0, ci = 0;
|
for( size_t i = 0, ci = 0;
|
||||||
i < _cmds.size() && ci < _coords.size();
|
i < _cmds.size() && ci < _coords.size();
|
||||||
@@ -298,6 +320,7 @@ namespace canvas
|
|||||||
switch( cmd )
|
switch( cmd )
|
||||||
{
|
{
|
||||||
case VG_CLOSE_PATH:
|
case VG_CLOSE_PATH:
|
||||||
|
cur = sub;
|
||||||
break;
|
break;
|
||||||
case VG_MOVE_TO:
|
case VG_MOVE_TO:
|
||||||
case VG_LINE_TO:
|
case VG_LINE_TO:
|
||||||
@@ -353,7 +376,12 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
|
|
||||||
if( num_coords > 0 )
|
if( num_coords > 0 )
|
||||||
|
{
|
||||||
cur = points[ num_coords - 1 ];
|
cur = points[ num_coords - 1 ];
|
||||||
|
|
||||||
|
if( cmd == VG_MOVE_TO )
|
||||||
|
sub = cur;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return bb;
|
return bb;
|
||||||
@@ -362,7 +390,13 @@ namespace canvas
|
|||||||
/**
|
/**
|
||||||
* Compute the bounding box
|
* Compute the bounding box
|
||||||
*/
|
*/
|
||||||
virtual osg::BoundingBox computeBound() const
|
virtual osg::BoundingBox
|
||||||
|
#if OSG_VERSION_LESS_THAN(3,3,2)
|
||||||
|
computeBound()
|
||||||
|
#else
|
||||||
|
computeBoundingBox()
|
||||||
|
#endif
|
||||||
|
const
|
||||||
{
|
{
|
||||||
if( _path == VG_INVALID_HANDLE || (_attributes_dirty & PATH) )
|
if( _path == VG_INVALID_HANDLE || (_attributes_dirty & PATH) )
|
||||||
return osg::BoundingBox();
|
return osg::BoundingBox();
|
||||||
@@ -375,14 +409,11 @@ namespace canvas
|
|||||||
// vgPathBounds doesn't take stroke width into account
|
// vgPathBounds doesn't take stroke width into account
|
||||||
float ext = 0.5 * _stroke_width;
|
float ext = 0.5 * _stroke_width;
|
||||||
|
|
||||||
osg::BoundingBox bb
|
return osg::BoundingBox
|
||||||
(
|
(
|
||||||
min[0] - ext, min[1] - ext, -0.1,
|
min[0] - ext, min[1] - ext, -0.1,
|
||||||
min[0] + size[0] + ext, min[1] + size[1] + ext, 0.1
|
min[0] + size[0] + ext, min[1] + size[1] + ext, 0.1
|
||||||
);
|
);
|
||||||
_path_element->setBoundingBox(bb);
|
|
||||||
|
|
||||||
return bb;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -412,6 +443,7 @@ namespace canvas
|
|||||||
VGfloat _stroke_width;
|
VGfloat _stroke_width;
|
||||||
std::vector<VGfloat> _stroke_dash;
|
std::vector<VGfloat> _stroke_dash;
|
||||||
VGCapStyle _stroke_linecap;
|
VGCapStyle _stroke_linecap;
|
||||||
|
VGJoinStyle _stroke_linejoin;
|
||||||
|
|
||||||
osg::Vec3f transformPoint( const osg::Matrix& m,
|
osg::Vec3f transformPoint( const osg::Matrix& m,
|
||||||
osg::Vec2f pos ) const
|
osg::Vec2f pos ) const
|
||||||
@@ -429,6 +461,9 @@ namespace canvas
|
|||||||
*/
|
*/
|
||||||
void update()
|
void update()
|
||||||
{
|
{
|
||||||
|
if( !vgHasContextSH() )
|
||||||
|
return;
|
||||||
|
|
||||||
if( _attributes_dirty & PATH )
|
if( _attributes_dirty & PATH )
|
||||||
{
|
{
|
||||||
const VGbitfield caps = VG_PATH_CAPABILITY_APPEND_TO
|
const VGbitfield caps = VG_PATH_CAPABILITY_APPEND_TO
|
||||||
@@ -454,7 +489,12 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
|
|
||||||
if( _attributes_dirty & BOUNDING_BOX )
|
if( _attributes_dirty & BOUNDING_BOX )
|
||||||
|
{
|
||||||
dirtyBound();
|
dirtyBound();
|
||||||
|
|
||||||
|
// Recalculate bounding box now (prevent race condition)
|
||||||
|
getBound();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PathUpdateCallback:
|
struct PathUpdateCallback:
|
||||||
@@ -467,6 +507,26 @@ namespace canvas
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
const std::string Path::TYPE_NAME = "path";
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Path::staticInit()
|
||||||
|
{
|
||||||
|
if( isInit<Path>() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
PathDrawableRef Path::*path = &Path::_path;
|
||||||
|
|
||||||
|
addStyle("fill", "color", &PathDrawable::setFill, path);
|
||||||
|
addStyle("fill-rule", "", &PathDrawable::setFillRule, path);
|
||||||
|
addStyle("stroke", "color", &PathDrawable::setStroke, path);
|
||||||
|
addStyle("stroke-width", "numeric", &PathDrawable::setStrokeWidth, path);
|
||||||
|
addStyle("stroke-dasharray", "", &PathDrawable::setStrokeDashArray, path);
|
||||||
|
addStyle("stroke-linecap", "", &PathDrawable::setStrokeLinecap, path);
|
||||||
|
addStyle("stroke-linejoin", "", &PathDrawable::setStrokeLinejoin, path);
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Path::Path( const CanvasWeakPtr& canvas,
|
Path::Path( const CanvasWeakPtr& canvas,
|
||||||
const SGPropertyNode_ptr& node,
|
const SGPropertyNode_ptr& node,
|
||||||
@@ -475,16 +535,9 @@ namespace canvas
|
|||||||
Element(canvas, node, parent_style, parent),
|
Element(canvas, node, parent_style, parent),
|
||||||
_path( new PathDrawable(this) )
|
_path( new PathDrawable(this) )
|
||||||
{
|
{
|
||||||
|
staticInit();
|
||||||
|
|
||||||
setDrawable(_path);
|
setDrawable(_path);
|
||||||
PathDrawable *path = _path.get();
|
|
||||||
|
|
||||||
addStyle("fill", &PathDrawable::setFill, path);
|
|
||||||
addStyle("fill-rule", &PathDrawable::setFillRule, path);
|
|
||||||
addStyle("stroke", &PathDrawable::setStroke, path);
|
|
||||||
addStyle("stroke-width", &PathDrawable::setStrokeWidth, path);
|
|
||||||
addStyle("stroke-dasharray", &PathDrawable::setStrokeDashArray, path);
|
|
||||||
addStyle("stroke-linecap", &PathDrawable::setStrokeLinecap, path);
|
|
||||||
|
|
||||||
setupStyle();
|
setupStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -517,6 +570,60 @@ namespace canvas
|
|||||||
return _path->getTransformedBounds(m);
|
return _path->getTransformedBounds(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
Path& Path::moveTo(float x_abs, float 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
Path& Path::lineTo(float x_abs, float 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
Path& Path::horizTo(float x_abs)
|
||||||
|
{
|
||||||
|
return addSegment(VG_HLINE_TO_ABS, x_abs);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
Path& Path::horiz(float x_rel)
|
||||||
|
{
|
||||||
|
return addSegment(VG_HLINE_TO_REL, x_rel);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
Path& Path::vertTo(float y_abs)
|
||||||
|
{
|
||||||
|
return addSegment(VG_VLINE_TO_ABS, y_abs);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
Path& Path::vert(float y_rel)
|
||||||
|
{
|
||||||
|
return addSegment(VG_VLINE_TO_REL, y_rel);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
Path& Path::close()
|
||||||
|
{
|
||||||
|
return addSegment(VG_CLOSE_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Path::childRemoved(SGPropertyNode* child)
|
void Path::childRemoved(SGPropertyNode* child)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#define CANVAS_PATH_HXX_
|
#define CANVAS_PATH_HXX_
|
||||||
|
|
||||||
#include "CanvasElement.hxx"
|
#include "CanvasElement.hxx"
|
||||||
|
#include <boost/preprocessor/iteration/iterate.hpp>
|
||||||
|
|
||||||
namespace simgear
|
namespace simgear
|
||||||
{
|
{
|
||||||
@@ -29,6 +30,9 @@ namespace canvas
|
|||||||
public Element
|
public Element
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static const std::string TYPE_NAME;
|
||||||
|
static void staticInit();
|
||||||
|
|
||||||
Path( const CanvasWeakPtr& canvas,
|
Path( const CanvasWeakPtr& canvas,
|
||||||
const SGPropertyNode_ptr& node,
|
const SGPropertyNode_ptr& node,
|
||||||
const Style& parent_style,
|
const Style& parent_style,
|
||||||
@@ -39,6 +43,30 @@ namespace canvas
|
|||||||
|
|
||||||
virtual osg::BoundingBox getTransformedBounds(const osg::Matrix& m) const;
|
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()
|
||||||
|
|
||||||
|
/** Move path cursor */
|
||||||
|
Path& moveTo(float x_abs, float y_abs);
|
||||||
|
Path& move(float x_rel, float y_rel);
|
||||||
|
|
||||||
|
/** Add a line */
|
||||||
|
Path& lineTo(float x_abs, float y_abs);
|
||||||
|
Path& line(float x_rel, float y_rel);
|
||||||
|
|
||||||
|
/** Add a horizontal line */
|
||||||
|
Path& horizTo(float x_abs);
|
||||||
|
Path& horiz(float x_rel);
|
||||||
|
|
||||||
|
/** Add a vertical line */
|
||||||
|
Path& vertTo(float y_abs);
|
||||||
|
Path& vert(float y_rel);
|
||||||
|
|
||||||
|
/** Close the path (implicit lineTo to first point of path) */
|
||||||
|
Path& close();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
enum PathAttributes
|
enum PathAttributes
|
||||||
@@ -48,7 +76,8 @@ namespace canvas
|
|||||||
};
|
};
|
||||||
|
|
||||||
class PathDrawable;
|
class PathDrawable;
|
||||||
osg::ref_ptr<PathDrawable> _path;
|
typedef osg::ref_ptr<PathDrawable> PathDrawableRef;
|
||||||
|
PathDrawableRef _path;
|
||||||
|
|
||||||
virtual void childRemoved(SGPropertyNode * child);
|
virtual void childRemoved(SGPropertyNode * child);
|
||||||
virtual void childChanged(SGPropertyNode * child);
|
virtual void childChanged(SGPropertyNode * child);
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
#include <simgear/canvas/Canvas.hxx>
|
#include <simgear/canvas/Canvas.hxx>
|
||||||
#include <simgear/canvas/CanvasSystemAdapter.hxx>
|
#include <simgear/canvas/CanvasSystemAdapter.hxx>
|
||||||
#include <simgear/scene/util/parse_color.hxx>
|
#include <simgear/scene/util/parse_color.hxx>
|
||||||
|
#include <osg/Version>
|
||||||
|
#include <osgDB/Registry>
|
||||||
#include <osgText/Text>
|
#include <osgText/Text>
|
||||||
|
|
||||||
namespace simgear
|
namespace simgear
|
||||||
@@ -30,27 +32,201 @@ namespace canvas
|
|||||||
public osgText::Text
|
public osgText::Text
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
TextOSG(canvas::Text* text);
|
TextOSG(canvas::Text* text);
|
||||||
|
|
||||||
|
void setFontResolution(int res);
|
||||||
void setCharacterAspect(float aspect);
|
void setCharacterAspect(float aspect);
|
||||||
|
void setLineHeight(float factor);
|
||||||
void setFill(const std::string& fill);
|
void setFill(const std::string& fill);
|
||||||
|
void setStroke(const std::string& color);
|
||||||
void setBackgroundColor(const std::string& fill);
|
void setBackgroundColor(const std::string& fill);
|
||||||
|
|
||||||
osg::Vec2 handleHit(const osg::Vec2f& pos);
|
float lineHeight() const;
|
||||||
|
|
||||||
virtual osg::BoundingBox computeBound() const;
|
/// Get the number of lines
|
||||||
|
size_t lineCount() const;
|
||||||
|
|
||||||
|
/// Get line @a i
|
||||||
|
TextLine lineAt(size_t i) const;
|
||||||
|
|
||||||
|
/// Get nearest line to given y-coordinate
|
||||||
|
TextLine nearestLine(float pos_y) const;
|
||||||
|
|
||||||
|
|
||||||
|
SGVec2i sizeForWidth(int w) const;
|
||||||
|
|
||||||
|
virtual osg::BoundingBox
|
||||||
|
#if OSG_VERSION_LESS_THAN(3,3,2)
|
||||||
|
computeBound()
|
||||||
|
#else
|
||||||
|
computeBoundingBox()
|
||||||
|
#endif
|
||||||
|
const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
friend class TextLine;
|
||||||
|
|
||||||
canvas::Text *_text_element;
|
canvas::Text *_text_element;
|
||||||
|
|
||||||
|
virtual void computePositions(unsigned int contextID) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class TextLine
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TextLine();
|
||||||
|
TextLine(size_t line, Text::TextOSG const* text);
|
||||||
|
|
||||||
|
/// Number of characters on this line
|
||||||
|
size_t size() const;
|
||||||
|
bool empty() const;
|
||||||
|
|
||||||
|
osg::Vec2 cursorPos(size_t i) const;
|
||||||
|
osg::Vec2 nearestCursor(float x) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
typedef Text::TextOSG::GlyphQuads GlyphQuads;
|
||||||
|
|
||||||
|
Text::TextOSG const *_text;
|
||||||
|
GlyphQuads const *_quads;
|
||||||
|
|
||||||
|
size_t _line,
|
||||||
|
_begin,
|
||||||
|
_end;
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
TextLine::TextLine():
|
||||||
|
_text(NULL),
|
||||||
|
_quads(NULL),
|
||||||
|
_line(0),
|
||||||
|
_begin(-1),
|
||||||
|
_end(-1)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
TextLine::TextLine(size_t line, Text::TextOSG const* text):
|
||||||
|
_text(text),
|
||||||
|
_quads(NULL),
|
||||||
|
_line(line),
|
||||||
|
_begin(-1),
|
||||||
|
_end(-1)
|
||||||
|
{
|
||||||
|
if( !text || text->_textureGlyphQuadMap.empty() || !_text->lineCount() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
_quads = &text->_textureGlyphQuadMap.begin()->second;
|
||||||
|
|
||||||
|
GlyphQuads::LineNumbers const& line_numbers = _quads->_lineNumbers;
|
||||||
|
GlyphQuads::LineNumbers::const_iterator begin_it =
|
||||||
|
std::lower_bound(line_numbers.begin(), line_numbers.end(), _line);
|
||||||
|
|
||||||
|
if( begin_it == line_numbers.end() || *begin_it != _line )
|
||||||
|
// empty line or past last line
|
||||||
|
return;
|
||||||
|
|
||||||
|
_begin = begin_it - line_numbers.begin();
|
||||||
|
_end = std::upper_bound(begin_it, line_numbers.end(), _line)
|
||||||
|
- line_numbers.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
size_t TextLine::size() const
|
||||||
|
{
|
||||||
|
return _end - _begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
bool TextLine::empty() const
|
||||||
|
{
|
||||||
|
return _end == _begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
osg::Vec2 TextLine::cursorPos(size_t i) const
|
||||||
|
{
|
||||||
|
if( !_quads )
|
||||||
|
return osg::Vec2(0, 0);
|
||||||
|
|
||||||
|
if( i > size() )
|
||||||
|
// Position after last character if out of range (TODO better exception?)
|
||||||
|
i = size();
|
||||||
|
|
||||||
|
osg::Vec2 pos(0, _text->_offset.y() + _line * _text->lineHeight());
|
||||||
|
|
||||||
|
if( empty() )
|
||||||
|
return pos;
|
||||||
|
|
||||||
|
GlyphQuads::Coords2 const& coords = _quads->_coords;
|
||||||
|
size_t global_i = _begin + i;
|
||||||
|
|
||||||
|
if( global_i == _begin )
|
||||||
|
// before first character of line
|
||||||
|
pos.x() = coords[_begin * 4].x();
|
||||||
|
else if( global_i == _end )
|
||||||
|
// After Last character of line
|
||||||
|
pos.x() = coords[(_end - 1) * 4 + 2].x();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float prev_l = coords[(global_i - 1) * 4].x(),
|
||||||
|
prev_r = coords[(global_i - 1) * 4 + 2].x(),
|
||||||
|
cur_l = coords[global_i * 4].x();
|
||||||
|
|
||||||
|
if( prev_l == prev_r )
|
||||||
|
// If previous character width is zero set to begin of next character
|
||||||
|
// (Happens eg. with spaces)
|
||||||
|
pos.x() = cur_l;
|
||||||
|
else
|
||||||
|
// position at center between characters
|
||||||
|
pos.x() = 0.5 * (prev_r + cur_l);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
osg::Vec2 TextLine::nearestCursor(float x) const
|
||||||
|
{
|
||||||
|
if( empty() )
|
||||||
|
return cursorPos(0);
|
||||||
|
|
||||||
|
GlyphQuads::Glyphs const& glyphs = _quads->_glyphs;
|
||||||
|
GlyphQuads::Coords2 const& coords = _quads->_coords;
|
||||||
|
|
||||||
|
float const HIT_FRACTION = 0.6;
|
||||||
|
float const character_width = _text->getCharacterHeight()
|
||||||
|
* _text->getCharacterAspectRatio();
|
||||||
|
|
||||||
|
size_t i = _begin;
|
||||||
|
for(; i < _end; ++i)
|
||||||
|
{
|
||||||
|
// Get threshold for mouse x position for setting cursor before or after
|
||||||
|
// current character
|
||||||
|
float threshold = coords[i * 4].x()
|
||||||
|
+ HIT_FRACTION * glyphs[i]->getHorizontalAdvance()
|
||||||
|
* character_width;
|
||||||
|
|
||||||
|
if( x <= threshold )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cursorPos(i - _begin);
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Text::TextOSG::TextOSG(canvas::Text* text):
|
Text::TextOSG::TextOSG(canvas::Text* text):
|
||||||
_text_element(text)
|
_text_element(text)
|
||||||
{
|
{
|
||||||
|
setBackdropImplementation(NO_DEPTH_BUFFER);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Text::TextOSG::setFontResolution(int res)
|
||||||
|
{
|
||||||
|
TextBase::setFontResolution(res, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -59,6 +235,12 @@ namespace canvas
|
|||||||
setCharacterSize(getCharacterHeight(), aspect);
|
setCharacterSize(getCharacterHeight(), aspect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Text::TextOSG::setLineHeight(float factor)
|
||||||
|
{
|
||||||
|
setLineSpacing(factor - 1);
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Text::TextOSG::setFill(const std::string& fill)
|
void Text::TextOSG::setFill(const std::string& fill)
|
||||||
{
|
{
|
||||||
@@ -70,6 +252,19 @@ namespace canvas
|
|||||||
setColor( color );
|
setColor( color );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Text::TextOSG::setStroke(const std::string& stroke)
|
||||||
|
{
|
||||||
|
osg::Vec4 color;
|
||||||
|
if( stroke == "none" || !parseColor(stroke, color) )
|
||||||
|
setBackdropType(NONE);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setBackdropType(OUTLINE);
|
||||||
|
setBackdropColor(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Text::TextOSG::setBackgroundColor(const std::string& fill)
|
void Text::TextOSG::setBackgroundColor(const std::string& fill)
|
||||||
{
|
{
|
||||||
@@ -79,100 +274,451 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
osg::Vec2 Text::TextOSG::handleHit(const osg::Vec2f& pos)
|
float Text::TextOSG::lineHeight() const
|
||||||
{
|
{
|
||||||
float line_height = _characterHeight + _lineSpacing;
|
return (1 + _lineSpacing) * _characterHeight;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO check with align other than TOP
|
//----------------------------------------------------------------------------
|
||||||
float first_line_y = -0.5 * _lineSpacing;//_offset.y() - _characterHeight;
|
size_t Text::TextOSG::lineCount() const
|
||||||
size_t line = std::max<int>(0, (pos.y() - first_line_y) / line_height);
|
{
|
||||||
|
return _lineCount;
|
||||||
|
}
|
||||||
|
|
||||||
if( _textureGlyphQuadMap.empty() )
|
//----------------------------------------------------------------------------
|
||||||
return osg::Vec2(-1, -1);
|
TextLine Text::TextOSG::lineAt(size_t i) const
|
||||||
|
{
|
||||||
|
return TextLine(i, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
TextLine Text::TextOSG::nearestLine(float pos_y) const
|
||||||
|
{
|
||||||
|
osgText::Font const* font = getActiveFont();
|
||||||
|
if( !font || lineCount() <= 0 )
|
||||||
|
return TextLine(0, this);
|
||||||
|
|
||||||
|
float asc = .9f, desc = -.2f;
|
||||||
|
font->getVerticalSize(asc, desc);
|
||||||
|
|
||||||
|
float first_line_y = _offset.y()
|
||||||
|
- (1 + _lineSpacing / 2 + desc) * _characterHeight;
|
||||||
|
|
||||||
|
size_t line_num = std::min<size_t>(
|
||||||
|
std::max<size_t>(0, (pos_y - first_line_y) / lineHeight()),
|
||||||
|
lineCount() - 1
|
||||||
|
);
|
||||||
|
|
||||||
|
return TextLine(line_num, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// simplified version of osgText::Text::computeGlyphRepresentation() to
|
||||||
|
// just calculate the size for a given weight. Glpyh calculations/creating
|
||||||
|
// is not necessary for this...
|
||||||
|
SGVec2i Text::TextOSG::sizeForWidth(int w) const
|
||||||
|
{
|
||||||
|
if( _text.empty() )
|
||||||
|
return SGVec2i(0, 0);
|
||||||
|
|
||||||
|
osgText::Font* activefont = const_cast<osgText::Font*>(getActiveFont());
|
||||||
|
if( !activefont )
|
||||||
|
return SGVec2i(-1, -1);
|
||||||
|
|
||||||
|
float max_width_safe = _maximumWidth;
|
||||||
|
const_cast<TextOSG*>(this)->_maximumWidth = w;
|
||||||
|
|
||||||
|
SGRecti bb;
|
||||||
|
|
||||||
|
osg::Vec2 startOfLine_coords(0.0f,0.0f);
|
||||||
|
osg::Vec2 cursor(startOfLine_coords);
|
||||||
|
osg::Vec2 local(0.0f,0.0f);
|
||||||
|
|
||||||
|
unsigned int previous_charcode = 0;
|
||||||
|
unsigned int line_length = 0;
|
||||||
|
bool horizontal = _layout != VERTICAL;
|
||||||
|
bool kerning = true;
|
||||||
|
|
||||||
|
float hr = _characterHeight;
|
||||||
|
float wr = hr / getCharacterAspectRatio();
|
||||||
|
|
||||||
|
// osg should really care more about const :-/
|
||||||
|
osgText::String& text = const_cast<osgText::String&>(_text);
|
||||||
|
typedef osgText::String::iterator TextIterator;
|
||||||
|
|
||||||
|
for( TextIterator itr = text.begin(); itr != text.end(); )
|
||||||
|
{
|
||||||
|
// record the start of the current line
|
||||||
|
TextIterator startOfLine_itr = itr;
|
||||||
|
|
||||||
|
// find the end of the current line.
|
||||||
|
osg::Vec2 endOfLine_coords(cursor);
|
||||||
|
TextIterator endOfLine_itr =
|
||||||
|
const_cast<TextOSG*>(this)->computeLastCharacterOnLine(
|
||||||
|
endOfLine_coords, itr, text.end()
|
||||||
|
);
|
||||||
|
|
||||||
|
line_length = endOfLine_itr - startOfLine_itr;
|
||||||
|
|
||||||
|
// Set line position to correct alignment.
|
||||||
|
switch( _layout )
|
||||||
|
{
|
||||||
|
case LEFT_TO_RIGHT:
|
||||||
|
{
|
||||||
|
switch( _alignment )
|
||||||
|
{
|
||||||
|
// nothing to be done for these
|
||||||
|
//case LEFT_TOP:
|
||||||
|
//case LEFT_CENTER:
|
||||||
|
//case LEFT_BOTTOM:
|
||||||
|
//case LEFT_BASE_LINE:
|
||||||
|
//case LEFT_BOTTOM_BASE_LINE:
|
||||||
|
// break;
|
||||||
|
case CENTER_TOP:
|
||||||
|
case CENTER_CENTER:
|
||||||
|
case CENTER_BOTTOM:
|
||||||
|
case CENTER_BASE_LINE:
|
||||||
|
case CENTER_BOTTOM_BASE_LINE:
|
||||||
|
cursor.x() = (cursor.x() - endOfLine_coords.x()) * 0.5f;
|
||||||
|
break;
|
||||||
|
case RIGHT_TOP:
|
||||||
|
case RIGHT_CENTER:
|
||||||
|
case RIGHT_BOTTOM:
|
||||||
|
case RIGHT_BASE_LINE:
|
||||||
|
case RIGHT_BOTTOM_BASE_LINE:
|
||||||
|
cursor.x() = cursor.x() - endOfLine_coords.x();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case RIGHT_TO_LEFT:
|
||||||
|
{
|
||||||
|
switch( _alignment )
|
||||||
|
{
|
||||||
|
case LEFT_TOP:
|
||||||
|
case LEFT_CENTER:
|
||||||
|
case LEFT_BOTTOM:
|
||||||
|
case LEFT_BASE_LINE:
|
||||||
|
case LEFT_BOTTOM_BASE_LINE:
|
||||||
|
cursor.x() = 2 * cursor.x() - endOfLine_coords.x();
|
||||||
|
break;
|
||||||
|
case CENTER_TOP:
|
||||||
|
case CENTER_CENTER:
|
||||||
|
case CENTER_BOTTOM:
|
||||||
|
case CENTER_BASE_LINE:
|
||||||
|
case CENTER_BOTTOM_BASE_LINE:
|
||||||
|
cursor.x() = cursor.x()
|
||||||
|
+ (cursor.x() - endOfLine_coords.x()) * 0.5f;
|
||||||
|
break;
|
||||||
|
// nothing to be done for these
|
||||||
|
//case RIGHT_TOP:
|
||||||
|
//case RIGHT_CENTER:
|
||||||
|
//case RIGHT_BOTTOM:
|
||||||
|
//case RIGHT_BASE_LINE:
|
||||||
|
//case RIGHT_BOTTOM_BASE_LINE:
|
||||||
|
// break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case VERTICAL:
|
||||||
|
{
|
||||||
|
switch( _alignment )
|
||||||
|
{
|
||||||
|
// TODO: current behaviour top baselines lined up in both cases - need to implement
|
||||||
|
// top of characters alignment - Question is this necessary?
|
||||||
|
// ... otherwise, nothing to be done for these 6 cases
|
||||||
|
//case LEFT_TOP:
|
||||||
|
//case CENTER_TOP:
|
||||||
|
//case RIGHT_TOP:
|
||||||
|
// break;
|
||||||
|
//case LEFT_BASE_LINE:
|
||||||
|
//case CENTER_BASE_LINE:
|
||||||
|
//case RIGHT_BASE_LINE:
|
||||||
|
// break;
|
||||||
|
case LEFT_CENTER:
|
||||||
|
case CENTER_CENTER:
|
||||||
|
case RIGHT_CENTER:
|
||||||
|
cursor.y() = cursor.y()
|
||||||
|
+ (cursor.y() - endOfLine_coords.y()) * 0.5f;
|
||||||
|
break;
|
||||||
|
case LEFT_BOTTOM_BASE_LINE:
|
||||||
|
case CENTER_BOTTOM_BASE_LINE:
|
||||||
|
case RIGHT_BOTTOM_BASE_LINE:
|
||||||
|
cursor.y() = cursor.y() - (line_length * _characterHeight);
|
||||||
|
break;
|
||||||
|
case LEFT_BOTTOM:
|
||||||
|
case CENTER_BOTTOM:
|
||||||
|
case RIGHT_BOTTOM:
|
||||||
|
cursor.y() = 2 * cursor.y() - endOfLine_coords.y();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( itr != endOfLine_itr )
|
||||||
|
{
|
||||||
|
|
||||||
|
for(;itr != endOfLine_itr;++itr)
|
||||||
|
{
|
||||||
|
unsigned int charcode = *itr;
|
||||||
|
|
||||||
|
osgText::Glyph* glyph = activefont->getGlyph(_fontSize, charcode);
|
||||||
|
if( glyph )
|
||||||
|
{
|
||||||
|
float width = (float) (glyph->getWidth()) * wr;
|
||||||
|
float height = (float) (glyph->getHeight()) * hr;
|
||||||
|
|
||||||
|
if( _layout == RIGHT_TO_LEFT )
|
||||||
|
{
|
||||||
|
cursor.x() -= glyph->getHorizontalAdvance() * wr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjust cursor position w.r.t any kerning.
|
||||||
|
if( kerning && previous_charcode )
|
||||||
|
{
|
||||||
|
switch( _layout )
|
||||||
|
{
|
||||||
|
case LEFT_TO_RIGHT:
|
||||||
|
{
|
||||||
|
osg::Vec2 delta( activefont->getKerning( previous_charcode,
|
||||||
|
charcode,
|
||||||
|
_kerningType ) );
|
||||||
|
cursor.x() += delta.x() * wr;
|
||||||
|
cursor.y() += delta.y() * hr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case RIGHT_TO_LEFT:
|
||||||
|
{
|
||||||
|
osg::Vec2 delta( activefont->getKerning( charcode,
|
||||||
|
previous_charcode,
|
||||||
|
_kerningType ) );
|
||||||
|
cursor.x() -= delta.x() * wr;
|
||||||
|
cursor.y() -= delta.y() * hr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case VERTICAL:
|
||||||
|
break; // no kerning when vertical.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
local = cursor;
|
||||||
|
osg::Vec2 bearing( horizontal ? glyph->getHorizontalBearing()
|
||||||
|
: glyph->getVerticalBearing() );
|
||||||
|
local.x() += bearing.x() * wr;
|
||||||
|
local.y() += bearing.y() * hr;
|
||||||
|
|
||||||
|
// set up the coords of the quad
|
||||||
|
osg::Vec2 upLeft = local + osg::Vec2(0.f, height);
|
||||||
|
osg::Vec2 lowLeft = local;
|
||||||
|
osg::Vec2 lowRight = local + osg::Vec2(width, 0.f);
|
||||||
|
osg::Vec2 upRight = local + osg::Vec2(width, height);
|
||||||
|
|
||||||
|
// move the cursor onto the next character.
|
||||||
|
// also expand bounding box
|
||||||
|
switch( _layout )
|
||||||
|
{
|
||||||
|
case LEFT_TO_RIGHT:
|
||||||
|
cursor.x() += glyph->getHorizontalAdvance() * wr;
|
||||||
|
bb.expandBy(lowLeft.x(), lowLeft.y());
|
||||||
|
bb.expandBy(upRight.x(), upRight.y());
|
||||||
|
break;
|
||||||
|
case VERTICAL:
|
||||||
|
cursor.y() -= glyph->getVerticalAdvance() * hr;
|
||||||
|
bb.expandBy(upLeft.x(), upLeft.y());
|
||||||
|
bb.expandBy(lowRight.x(), lowRight.y());
|
||||||
|
break;
|
||||||
|
case RIGHT_TO_LEFT:
|
||||||
|
bb.expandBy(lowRight.x(), lowRight.y());
|
||||||
|
bb.expandBy(upLeft.x(), upLeft.y());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
previous_charcode = charcode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip over spaces and return.
|
||||||
|
while( itr != text.end() && *itr == ' ' )
|
||||||
|
++itr;
|
||||||
|
if( itr != text.end() && *itr == '\n' )
|
||||||
|
++itr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++itr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// move to new line.
|
||||||
|
switch( _layout )
|
||||||
|
{
|
||||||
|
case LEFT_TO_RIGHT:
|
||||||
|
{
|
||||||
|
startOfLine_coords.y() -= _characterHeight * (1.0 + _lineSpacing);
|
||||||
|
cursor = startOfLine_coords;
|
||||||
|
previous_charcode = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case RIGHT_TO_LEFT:
|
||||||
|
{
|
||||||
|
startOfLine_coords.y() -= _characterHeight * (1.0 + _lineSpacing);
|
||||||
|
cursor = startOfLine_coords;
|
||||||
|
previous_charcode = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case VERTICAL:
|
||||||
|
{
|
||||||
|
startOfLine_coords.x() += _characterHeight * (1.0 + _lineSpacing)
|
||||||
|
/ getCharacterAspectRatio();
|
||||||
|
cursor = startOfLine_coords;
|
||||||
|
previous_charcode = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const_cast<TextOSG*>(this)->_maximumWidth = max_width_safe;
|
||||||
|
|
||||||
|
return bb.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
osg::BoundingBox
|
||||||
|
#if OSG_VERSION_LESS_THAN(3,3,2)
|
||||||
|
Text::TextOSG::computeBound()
|
||||||
|
#else
|
||||||
|
Text::TextOSG::computeBoundingBox()
|
||||||
|
#endif
|
||||||
|
const
|
||||||
|
{
|
||||||
|
osg::BoundingBox bb =
|
||||||
|
#if OSG_VERSION_LESS_THAN(3,3,2)
|
||||||
|
osgText::Text::computeBound();
|
||||||
|
#else
|
||||||
|
osgText::Text::computeBoundingBox();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if OSG_VERSION_LESS_THAN(3,1,0)
|
||||||
|
if( bb.valid() )
|
||||||
|
{
|
||||||
|
// TODO bounding box still doesn't seem always right (eg. with center
|
||||||
|
// horizontal alignment not completely accurate)
|
||||||
|
bb._min.y() += _offset.y();
|
||||||
|
bb._max.y() += _offset.y();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return bb;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Text::TextOSG::computePositions(unsigned int contextID) const
|
||||||
|
{
|
||||||
|
if( _textureGlyphQuadMap.empty() || _layout == VERTICAL )
|
||||||
|
return osgText::Text::computePositions(contextID);
|
||||||
|
|
||||||
// TODO check when it can be larger
|
// TODO check when it can be larger
|
||||||
assert( _textureGlyphQuadMap.size() == 1 );
|
assert( _textureGlyphQuadMap.size() == 1 );
|
||||||
|
|
||||||
const GlyphQuads& glyphquad = _textureGlyphQuadMap.begin()->second;
|
const GlyphQuads& quads = _textureGlyphQuadMap.begin()->second;
|
||||||
const GlyphQuads::Glyphs& glyphs = glyphquad._glyphs;
|
const GlyphQuads::Glyphs& glyphs = quads._glyphs;
|
||||||
const GlyphQuads::Coords2& coords = glyphquad._coords;
|
const GlyphQuads::Coords2& coords = quads._coords;
|
||||||
const GlyphQuads::LineNumbers& line_numbers = glyphquad._lineNumbers;
|
const GlyphQuads::LineNumbers& line_numbers = quads._lineNumbers;
|
||||||
|
|
||||||
const float HIT_FRACTION = 0.6;
|
float wr = _characterHeight / getCharacterAspectRatio();
|
||||||
const float character_width = getCharacterHeight()
|
|
||||||
* getCharacterAspectRatio();
|
|
||||||
|
|
||||||
float y = (line + 0.5) * line_height;
|
size_t cur_line = static_cast<size_t>(-1);
|
||||||
|
for(size_t i = 0; i < glyphs.size(); ++i)
|
||||||
bool line_found = false;
|
|
||||||
for(size_t i = 0; i < line_numbers.size(); ++i)
|
|
||||||
{
|
{
|
||||||
if( line_numbers[i] != line )
|
// Check horizontal offsets
|
||||||
{
|
|
||||||
if( !line_found )
|
|
||||||
{
|
|
||||||
if( line_numbers[i] < line )
|
|
||||||
// Wait for the correct line...
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// We have already passed the correct line -> It's empty...
|
bool first_char = cur_line != line_numbers[i];
|
||||||
return osg::Vec2(0, y);
|
cur_line = line_numbers[i];
|
||||||
|
|
||||||
|
bool last_char = (i + 1 == glyphs.size())
|
||||||
|
|| (cur_line != line_numbers[i + 1]);
|
||||||
|
|
||||||
|
if( first_char || last_char )
|
||||||
|
{
|
||||||
|
// From osg/src/osgText/Text.cpp:
|
||||||
|
//
|
||||||
|
// osg::Vec2 upLeft = local+osg::Vec2(0.0f-fHorizQuadMargin, ...);
|
||||||
|
// osg::Vec2 lowLeft = local+osg::Vec2(0.0f-fHorizQuadMargin, ...);
|
||||||
|
// osg::Vec2 lowRight = local+osg::Vec2(width+fHorizQuadMargin, ...);
|
||||||
|
// osg::Vec2 upRight = local+osg::Vec2(width+fHorizQuadMargin, ...);
|
||||||
|
|
||||||
|
float left = coords[i * 4].x(),
|
||||||
|
right = coords[i * 4 + 2].x(),
|
||||||
|
width = glyphs[i]->getWidth() * wr;
|
||||||
|
|
||||||
|
// (local + width + fHoriz) - (local - fHoriz) = width + 2*fHoriz | -width
|
||||||
|
float margin = 0.5f * (right - left - width),
|
||||||
|
cursor_x = left + margin
|
||||||
|
- glyphs[i]->getHorizontalBearing().x() * wr;
|
||||||
|
|
||||||
|
if( first_char )
|
||||||
|
{
|
||||||
|
if( cur_line == 0 || cursor_x < _textBB._min.x() )
|
||||||
|
_textBB._min.x() = cursor_x;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next line and not returned -> not before any character
|
if( last_char )
|
||||||
// -> return position after last character of line
|
{
|
||||||
return osg::Vec2(coords[(i - 1) * 4 + 2].x(), y);
|
float cursor_w = cursor_x + glyphs[i]->getHorizontalAdvance() * wr;
|
||||||
}
|
|
||||||
|
|
||||||
line_found = true;
|
if( cur_line == 0 || cursor_w > _textBB._max.x() )
|
||||||
|
_textBB._max.x() = cursor_w;
|
||||||
// Get threshold for mouse x position for setting cursor before or after
|
}
|
||||||
// current character
|
|
||||||
float threshold = coords[i * 4].x()
|
|
||||||
+ HIT_FRACTION * glyphs[i]->getHorizontalAdvance()
|
|
||||||
* character_width;
|
|
||||||
|
|
||||||
if( pos.x() <= threshold )
|
|
||||||
{
|
|
||||||
osg::Vec2 hit(0, y);
|
|
||||||
if( i == 0 || line_numbers[i - 1] != line )
|
|
||||||
// first character of line
|
|
||||||
hit.x() = coords[i * 4].x();
|
|
||||||
else if( coords[(i - 1) * 4].x() == coords[(i - 1) * 4 + 2].x() )
|
|
||||||
// If previous character width is zero set to begin of next character
|
|
||||||
// (Happens eg. with spaces)
|
|
||||||
hit.x() = coords[i * 4].x();
|
|
||||||
else
|
|
||||||
// position at center between characters
|
|
||||||
hit.x() = 0.5 * (coords[(i - 1) * 4 + 2].x() + coords[i * 4].x());
|
|
||||||
|
|
||||||
return hit;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nothing found -> return position after last character
|
return osgText::Text::computePositions(contextID);
|
||||||
return osg::Vec2
|
|
||||||
(
|
|
||||||
coords.back().x(),
|
|
||||||
(_lineCount - 0.5) * line_height
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
osg::BoundingBox Text::TextOSG::computeBound() const
|
const std::string Text::TYPE_NAME = "text";
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Text::staticInit()
|
||||||
{
|
{
|
||||||
osg::BoundingBox bb = osgText::Text::computeBound();
|
if( isInit<Text>() )
|
||||||
if( !bb.valid() )
|
return;
|
||||||
return bb;
|
|
||||||
|
|
||||||
// TODO bounding box still doesn't seem always right (eg. with center
|
osg::ref_ptr<TextOSG> Text::*text = &Text::_text;
|
||||||
// horizontal alignment not completely accurate)
|
|
||||||
bb._min.y() += _offset.y();
|
|
||||||
bb._max.y() += _offset.y();
|
|
||||||
|
|
||||||
_text_element->setBoundingBox(bb);
|
addStyle("fill", "color", &TextOSG::setFill, text);
|
||||||
|
addStyle("background", "color", &TextOSG::setBackgroundColor, text);
|
||||||
|
addStyle("stroke", "color", &TextOSG::setStroke, text);
|
||||||
|
addStyle("character-size",
|
||||||
|
"numeric",
|
||||||
|
static_cast<
|
||||||
|
void (TextOSG::*)(float)
|
||||||
|
> (&TextOSG::setCharacterSize),
|
||||||
|
text);
|
||||||
|
addStyle("character-aspect-ratio",
|
||||||
|
"numeric",
|
||||||
|
&TextOSG::setCharacterAspect, text);
|
||||||
|
addStyle("line-height", "numeric", &TextOSG::setLineHeight, text);
|
||||||
|
addStyle("font-resolution", "numeric", &TextOSG::setFontResolution, text);
|
||||||
|
addStyle("padding", "numeric", &TextOSG::setBoundingBoxMargin, text);
|
||||||
|
// TEXT = 1 default
|
||||||
|
// BOUNDINGBOX = 2
|
||||||
|
// FILLEDBOUNDINGBOX = 4
|
||||||
|
// ALIGNMENT = 8
|
||||||
|
addStyle<int>("draw-mode", "", &TextOSG::setDrawMode, text);
|
||||||
|
addStyle("max-width", "numeric", &TextOSG::setMaximumWidth, text);
|
||||||
|
addStyle("font", "", &Text::setFont);
|
||||||
|
addStyle("alignment", "", &Text::setAlignment);
|
||||||
|
addStyle("text", "", &Text::setText, false);
|
||||||
|
|
||||||
return bb;
|
osgDB::Registry* reg = osgDB::Registry::instance();
|
||||||
|
if( !reg->getReaderWriterForExtension("ttf") )
|
||||||
|
SG_LOG(SG_GL, SG_ALERT, "canvas::Text: Missing 'ttf' font reader");
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -183,28 +729,13 @@ namespace canvas
|
|||||||
Element(canvas, node, parent_style, parent),
|
Element(canvas, node, parent_style, parent),
|
||||||
_text( new Text::TextOSG(this) )
|
_text( new Text::TextOSG(this) )
|
||||||
{
|
{
|
||||||
|
staticInit();
|
||||||
|
|
||||||
setDrawable(_text);
|
setDrawable(_text);
|
||||||
_text->setCharacterSizeMode(osgText::Text::OBJECT_COORDS);
|
_text->setCharacterSizeMode(osgText::Text::OBJECT_COORDS);
|
||||||
_text->setAxisAlignment(osgText::Text::USER_DEFINED_ROTATION);
|
_text->setAxisAlignment(osgText::Text::USER_DEFINED_ROTATION);
|
||||||
_text->setRotation(osg::Quat(osg::PI, osg::X_AXIS));
|
_text->setRotation(osg::Quat(osg::PI, osg::X_AXIS));
|
||||||
|
|
||||||
addStyle("fill", &TextOSG::setFill, _text);
|
|
||||||
addStyle("background", &TextOSG::setBackgroundColor, _text);
|
|
||||||
addStyle("character-size",
|
|
||||||
static_cast<void (TextOSG::*)(float)>(&TextOSG::setCharacterSize),
|
|
||||||
_text);
|
|
||||||
addStyle("character-aspect-ratio", &TextOSG::setCharacterAspect, _text);
|
|
||||||
addStyle("padding", &TextOSG::setBoundingBoxMargin, _text);
|
|
||||||
// TEXT = 1 default
|
|
||||||
// BOUNDINGBOX = 2
|
|
||||||
// FILLEDBOUNDINGBOX = 4
|
|
||||||
// ALIGNMENT = 8
|
|
||||||
addStyle<int>("draw-mode", &TextOSG::setDrawMode, _text);
|
|
||||||
addStyle("max-width", &TextOSG::setMaximumWidth, _text);
|
|
||||||
addStyle("font", &Text::setFont, this);
|
|
||||||
addStyle("alignment", &Text::setAlignment, this);
|
|
||||||
addStyle("text", &Text::setText, this);
|
|
||||||
|
|
||||||
setupStyle();
|
setupStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,7 +754,7 @@ namespace canvas
|
|||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Text::setFont(const char* name)
|
void Text::setFont(const char* name)
|
||||||
{
|
{
|
||||||
_text->setFont( _canvas.lock()->getSystemAdapter()->getFont(name) );
|
_text->setFont( Canvas::getSystemAdapter()->getFont(name) );
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -266,10 +797,51 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
int Text::heightForWidth(int w) const
|
||||||
|
{
|
||||||
|
return _text->sizeForWidth(w).y();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
int Text::maxWidth() const
|
||||||
|
{
|
||||||
|
return _text->sizeForWidth(INT_MAX).x();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
size_t Text::lineCount() const
|
||||||
|
{
|
||||||
|
return _text->lineCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
size_t Text::lineLength(size_t line) const
|
||||||
|
{
|
||||||
|
return _text->lineAt(line).size();
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
osg::Vec2 Text::getNearestCursor(const osg::Vec2& pos) const
|
osg::Vec2 Text::getNearestCursor(const osg::Vec2& pos) const
|
||||||
{
|
{
|
||||||
return _text->handleHit(pos);
|
return _text->nearestLine(pos.y()).nearestCursor(pos.x());
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
osg::Vec2 Text::getCursorPos(size_t line, size_t character) const
|
||||||
|
{
|
||||||
|
return _text->lineAt(line).cursorPos(character);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
osg::StateSet* Text::getOrCreateStateSet()
|
||||||
|
{
|
||||||
|
if( !_transform.valid() )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Only check for StateSet on Transform, as the text stateset is shared
|
||||||
|
// between all text instances using the same font (texture).
|
||||||
|
return _transform->getOrCreateStateSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace canvas
|
} // namespace canvas
|
||||||
|
|||||||
@@ -29,10 +29,14 @@ namespace simgear
|
|||||||
namespace canvas
|
namespace canvas
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class TextLine;
|
||||||
class Text:
|
class Text:
|
||||||
public Element
|
public Element
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static const std::string TYPE_NAME;
|
||||||
|
static void staticInit();
|
||||||
|
|
||||||
Text( const CanvasWeakPtr& canvas,
|
Text( const CanvasWeakPtr& canvas,
|
||||||
const SGPropertyNode_ptr& node,
|
const SGPropertyNode_ptr& node,
|
||||||
const Style& parent_style,
|
const Style& parent_style,
|
||||||
@@ -43,13 +47,26 @@ namespace canvas
|
|||||||
void setFont(const char* name);
|
void setFont(const char* name);
|
||||||
void setAlignment(const char* align);
|
void setAlignment(const char* align);
|
||||||
|
|
||||||
|
int heightForWidth(int w) const;
|
||||||
|
int maxWidth() const;
|
||||||
|
|
||||||
|
/// Number of text lines.
|
||||||
|
size_t lineCount() const;
|
||||||
|
|
||||||
|
/// Number of characters in @a line.
|
||||||
|
size_t lineLength(size_t line) const;
|
||||||
|
|
||||||
osg::Vec2 getNearestCursor(const osg::Vec2& pos) const;
|
osg::Vec2 getNearestCursor(const osg::Vec2& pos) const;
|
||||||
|
osg::Vec2 getCursorPos(size_t line, size_t character) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
friend class TextLine;
|
||||||
class TextOSG;
|
class TextOSG;
|
||||||
osg::ref_ptr<TextOSG> _text;
|
osg::ref_ptr<TextOSG> _text;
|
||||||
|
|
||||||
|
virtual osg::StateSet* getOrCreateStateSet();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace canvas
|
} // namespace canvas
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/// Unit tests for canvas::Element
|
||||||
|
#define BOOST_TEST_MODULE canvas
|
||||||
|
#include <BoostTestTargetConfig.h>
|
||||||
|
|
||||||
|
#include "CanvasElement.hxx"
|
||||||
|
#include "CanvasGroup.hxx"
|
||||||
|
|
||||||
|
namespace sc = simgear::canvas;
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE( attr_data )
|
||||||
|
{
|
||||||
|
// http://www.w3.org/TR/html5/dom.html#attr-data-*
|
||||||
|
|
||||||
|
#define SG_CHECK_ATTR2PROP(attr, prop)\
|
||||||
|
BOOST_CHECK_EQUAL(sc::Element::attrToDataPropName(attr), prop)
|
||||||
|
|
||||||
|
// If name starts with "data-", for each "-" (U+002D) character in the name
|
||||||
|
// that is followed by a lowercase ASCII letter, remove the "-" (U+002D)
|
||||||
|
// character and replace the character that followed it by the same character
|
||||||
|
// converted to ASCII uppercase.
|
||||||
|
|
||||||
|
SG_CHECK_ATTR2PROP("no-data", "");
|
||||||
|
SG_CHECK_ATTR2PROP("data-blub", "blub");
|
||||||
|
SG_CHECK_ATTR2PROP("data-blub-x-y", "blubXY");
|
||||||
|
SG_CHECK_ATTR2PROP("data-blub-x-y-", "blubXY-");
|
||||||
|
|
||||||
|
#undef SG_CHECK_ATTR2PROP
|
||||||
|
|
||||||
|
#define SG_CHECK_PROP2ATTR(prop, attr)\
|
||||||
|
BOOST_CHECK_EQUAL(sc::Element::dataPropToAttrName(prop), attr)
|
||||||
|
|
||||||
|
// If name contains a "-" (U+002D) character followed by a lowercase ASCII
|
||||||
|
// letter, throw a SyntaxError exception (empty string) and abort these steps.
|
||||||
|
// For each uppercase ASCII letter in name, insert a "-" (U+002D) character
|
||||||
|
// before the character and replace the character with the same character
|
||||||
|
// converted to ASCII lowercase.
|
||||||
|
// Insert the string "data-" at the front of name.
|
||||||
|
|
||||||
|
SG_CHECK_PROP2ATTR("test", "data-test");
|
||||||
|
SG_CHECK_PROP2ATTR("testIt", "data-test-it");
|
||||||
|
SG_CHECK_PROP2ATTR("testIt-Hyphen", "data-test-it--hyphen");
|
||||||
|
SG_CHECK_PROP2ATTR("-test", "");
|
||||||
|
SG_CHECK_PROP2ATTR("test-", "data-test-");
|
||||||
|
|
||||||
|
#undef SG_CHECK_PROP2ATTR
|
||||||
|
|
||||||
|
SGPropertyNode_ptr node = new SGPropertyNode;
|
||||||
|
sc::ElementPtr el =
|
||||||
|
sc::Element::create<sc::Group>(sc::CanvasWeakPtr(), node);
|
||||||
|
|
||||||
|
el->setDataProp("myData", 3);
|
||||||
|
BOOST_CHECK_EQUAL( el->getDataProp<int>("myData"), 3 );
|
||||||
|
BOOST_CHECK_EQUAL( node->getIntValue("data-my-data"), 3 );
|
||||||
|
|
||||||
|
SGPropertyNode* prop = el->getDataProp<SGPropertyNode*>("notExistingProp");
|
||||||
|
BOOST_CHECK( !prop );
|
||||||
|
prop = el->getDataProp<SGPropertyNode*>("myData");
|
||||||
|
BOOST_CHECK( prop );
|
||||||
|
BOOST_CHECK_EQUAL( prop->getParent(), node );
|
||||||
|
BOOST_CHECK_EQUAL( prop->getIntValue(), 3 );
|
||||||
|
|
||||||
|
BOOST_CHECK( el->hasDataProp("myData") );
|
||||||
|
el->removeDataProp("myData");
|
||||||
|
BOOST_CHECK( !el->hasDataProp("myData") );
|
||||||
|
BOOST_CHECK_EQUAL( el->getDataProp("myData", 5), 5 );
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#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
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
include (SimGearComponent)
|
||||||
|
|
||||||
|
set(HEADERS
|
||||||
|
CustomEvent.hxx
|
||||||
|
DeviceEvent.hxx
|
||||||
|
KeyboardEvent.hxx
|
||||||
|
MouseEvent.hxx
|
||||||
|
)
|
||||||
|
|
||||||
|
set(SOURCES
|
||||||
|
CustomEvent.cxx
|
||||||
|
DeviceEvent.cxx
|
||||||
|
KeyboardEvent.cxx
|
||||||
|
MouseEvent.cxx
|
||||||
|
)
|
||||||
|
|
||||||
|
simgear_scene_component(canvas-events canvas/events "${SOURCES}" "${HEADERS}")
|
||||||
|
|
||||||
|
add_boost_test(canvas_event
|
||||||
|
SOURCES event_test.cpp
|
||||||
|
LIBRARIES ${TEST_LIBS}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(input_event_demo input_event_demo.cxx)
|
||||||
|
target_link_libraries(input_event_demo
|
||||||
|
${TEST_LIBS}
|
||||||
|
${OPENSCENEGRAPH_LIBRARIES}
|
||||||
|
)
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
// Canvas user defined event
|
||||||
|
//
|
||||||
|
// Copyright (C) 2014 Thomas Geymayer <tomgey@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 Library General Public
|
||||||
|
// 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 "CustomEvent.hxx"
|
||||||
|
|
||||||
|
namespace simgear
|
||||||
|
{
|
||||||
|
namespace canvas
|
||||||
|
{
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
CustomEvent::CustomEvent( std::string const& type_str,
|
||||||
|
bool bubbles,
|
||||||
|
StringMap const& data ):
|
||||||
|
detail(data),
|
||||||
|
bubbles(bubbles)
|
||||||
|
{
|
||||||
|
type = getOrRegisterType(type_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
CustomEvent::CustomEvent( int type_id,
|
||||||
|
bool bubbles,
|
||||||
|
StringMap const& data ):
|
||||||
|
detail(data),
|
||||||
|
bubbles(bubbles)
|
||||||
|
{
|
||||||
|
type = type_id;
|
||||||
|
// TypeMap::map_by<id>::type const& type_map = getTypeMap().by<id>();
|
||||||
|
// assert( type_map.find(type_id) != type_map.end() );
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void CustomEvent::setDetail(StringMap const& data)
|
||||||
|
{
|
||||||
|
detail = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace canvas
|
||||||
|
} // namespace simgear
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
///@file
|
||||||
|
/// Canvas user defined event
|
||||||
|
//
|
||||||
|
// Copyright (C) 2014 Thomas Geymayer <tomgey@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 Library General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
|
||||||
|
#ifndef CANVAS_CUSTOM_EVENT_HXX_
|
||||||
|
#define CANVAS_CUSTOM_EVENT_HXX_
|
||||||
|
|
||||||
|
#include <simgear/canvas/CanvasEvent.hxx>
|
||||||
|
#include <simgear/structure/map.hxx>
|
||||||
|
|
||||||
|
namespace simgear
|
||||||
|
{
|
||||||
|
namespace canvas
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User defined event (optionally carrying additional context information or
|
||||||
|
* data).
|
||||||
|
*/
|
||||||
|
class CustomEvent:
|
||||||
|
public Event
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param type_str Event type name (if name does not exist yet it will
|
||||||
|
* be registered as new event type)
|
||||||
|
* @param bubbles If this event should take part in the bubbling phase
|
||||||
|
* @param data Optional user data stored in event
|
||||||
|
*/
|
||||||
|
CustomEvent( std::string const& type_str,
|
||||||
|
bool bubbles = false,
|
||||||
|
StringMap const& data = StringMap() );
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param type_id Event type id
|
||||||
|
* @param bubbles If this event should take part in the bubbling phase
|
||||||
|
* @param data Optional user data stored in event
|
||||||
|
*/
|
||||||
|
CustomEvent( int type_id,
|
||||||
|
bool bubbles = false,
|
||||||
|
StringMap const& data = StringMap() );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set user data
|
||||||
|
*/
|
||||||
|
void setDetail(StringMap const& data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get user data
|
||||||
|
*/
|
||||||
|
StringMap const& getDetail() const { return detail; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get whether this event supports bubbling.
|
||||||
|
*
|
||||||
|
* @see #bubbles
|
||||||
|
* @see CustomEvent()
|
||||||
|
*/
|
||||||
|
virtual bool canBubble() const { return bubbles; }
|
||||||
|
|
||||||
|
StringMap detail; //!< User data map
|
||||||
|
bool bubbles; //!< Whether the event supports bubbling
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace canvas
|
||||||
|
} // namespace simgear
|
||||||
|
|
||||||
|
#endif /* CANVAS_CUSTOM_EVENT_HXX_ */
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// Conversion functions to convert Nasal types to C++ types
|
// Input device event
|
||||||
//
|
//
|
||||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
|
||||||
//
|
//
|
||||||
// This library is free software; you can redistribute it and/or
|
// This library is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU Library General Public
|
// modify it under the terms of the GNU Library General Public
|
||||||
@@ -16,50 +16,51 @@
|
|||||||
// License along with this library; if not, write to the Free Software
|
// License along with this library; if not, write to the Free Software
|
||||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
|
||||||
#include "from_nasal_detail.hxx"
|
#include "DeviceEvent.hxx"
|
||||||
#include "NasalHash.hxx"
|
#include <osgGA/GUIEventAdapter>
|
||||||
|
|
||||||
namespace nasal
|
namespace simgear
|
||||||
{
|
{
|
||||||
|
namespace canvas
|
||||||
|
{
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
bad_nasal_cast::bad_nasal_cast()
|
DeviceEvent::DeviceEvent():
|
||||||
|
modifiers(0)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
bad_nasal_cast::bad_nasal_cast(const std::string& msg):
|
DeviceEvent::DeviceEvent(const osgGA::GUIEventAdapter& ea):
|
||||||
_msg( msg )
|
modifiers(ea.getModKeyMask())
|
||||||
{
|
{
|
||||||
|
time = ea.getTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
bad_nasal_cast::~bad_nasal_cast() throw()
|
bool DeviceEvent::ctrlKey() const
|
||||||
{
|
{
|
||||||
|
return (modifiers & osgGA::GUIEventAdapter::MODKEY_CTRL) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
const char* bad_nasal_cast::what() const throw()
|
bool DeviceEvent::shiftKey() const
|
||||||
{
|
{
|
||||||
return _msg.empty() ? bad_cast::what() : _msg.c_str();
|
return (modifiers & osgGA::GUIEventAdapter::MODKEY_SHIFT) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
std::string from_nasal_helper(naContext c, naRef ref, std::string*)
|
bool DeviceEvent::altKey() const
|
||||||
{
|
{
|
||||||
naRef na_str = naStringValue(c, ref);
|
return (modifiers & osgGA::GUIEventAdapter::MODKEY_ALT) != 0;
|
||||||
return std::string(naStr_data(na_str), naStr_len(na_str));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Hash from_nasal_helper(naContext c, naRef ref, Hash*)
|
bool DeviceEvent::metaKey() const
|
||||||
{
|
{
|
||||||
if( !naIsHash(ref) )
|
return (modifiers & osgGA::GUIEventAdapter::MODKEY_META) != 0;
|
||||||
throw bad_nasal_cast("Not a hash");
|
|
||||||
|
|
||||||
return Hash(ref, c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace nasal
|
} // namespace canvas
|
||||||
|
} // namespace simgear
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
///@file
|
||||||
|
/// Input device event (keyboard/mouse)
|
||||||
|
//
|
||||||
|
// Copyright (C) 2014 Thomas Geymayer <tomgey@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 Library General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
|
||||||
|
#ifndef CANVAS_DEVICE_EVENT_HXX_
|
||||||
|
#define CANVAS_DEVICE_EVENT_HXX_
|
||||||
|
|
||||||
|
#include <simgear/canvas/CanvasEvent.hxx>
|
||||||
|
|
||||||
|
namespace osgGA { class GUIEventAdapter; }
|
||||||
|
|
||||||
|
namespace simgear
|
||||||
|
{
|
||||||
|
namespace canvas
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common interface for input device events.
|
||||||
|
*/
|
||||||
|
class DeviceEvent:
|
||||||
|
public Event
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Default initialization (no active keyboard modifier).
|
||||||
|
DeviceEvent();
|
||||||
|
|
||||||
|
/// Initialize from an OpenSceneGraph event.
|
||||||
|
DeviceEvent(const osgGA::GUIEventAdapter& ea);
|
||||||
|
|
||||||
|
/// Get mask of active keyboard modifiers at the time of the event.
|
||||||
|
int getModifiers() const { return modifiers; }
|
||||||
|
|
||||||
|
/// Get if a Ctrl modifier was active.
|
||||||
|
bool ctrlKey() const;
|
||||||
|
|
||||||
|
/// Get if a Shift modifier was active.
|
||||||
|
bool shiftKey() const;
|
||||||
|
|
||||||
|
/// Get if an Alt modifier was active.
|
||||||
|
bool altKey() const;
|
||||||
|
|
||||||
|
/// Get if a Meta modifier was active.
|
||||||
|
bool metaKey() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int modifiers; //!< Keyboard modifier state
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace canvas
|
||||||
|
} // namespace simgear
|
||||||
|
|
||||||
|
#endif /* CANVAS_DEVICE_EVENT_HXX_ */
|
||||||
@@ -0,0 +1,323 @@
|
|||||||
|
// Keyboard event
|
||||||
|
//
|
||||||
|
// Copyright (C) 2014 Thomas Geymayer <tomgey@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 Library General Public
|
||||||
|
// 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 "KeyboardEvent.hxx"
|
||||||
|
#include "utf8.h"
|
||||||
|
|
||||||
|
#include <osgGA/GUIEventAdapter>
|
||||||
|
|
||||||
|
#include <boost/version.hpp>
|
||||||
|
#if BOOST_VERSION >= 104800
|
||||||
|
# include <boost/container/flat_map.hpp>
|
||||||
|
# include <boost/container/flat_set.hpp>
|
||||||
|
#else
|
||||||
|
# include <map>
|
||||||
|
# include <set>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
|
namespace simgear
|
||||||
|
{
|
||||||
|
namespace canvas
|
||||||
|
{
|
||||||
|
typedef osgGA::GUIEventAdapter EA;
|
||||||
|
|
||||||
|
// TODO check Win/Mac keycode for altgr/ISO Level3 Shift
|
||||||
|
const uint32_t KEY_AltGraph = 0xfe03;
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
KeyboardEvent::KeyboardEvent():
|
||||||
|
_key(0),
|
||||||
|
_unmodified_key(0),
|
||||||
|
_repeat(false),
|
||||||
|
_location(DOM_KEY_LOCATION_STANDARD)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
KeyboardEvent::KeyboardEvent(const osgGA::GUIEventAdapter& ea):
|
||||||
|
DeviceEvent(ea),
|
||||||
|
_key(ea.getKey()),
|
||||||
|
_unmodified_key(ea.getUnmodifiedKey()),
|
||||||
|
_repeat(false),
|
||||||
|
_location(DOM_KEY_LOCATION_STANDARD)
|
||||||
|
{
|
||||||
|
if( ea.getEventType() == EA::KEYDOWN )
|
||||||
|
type = KEY_DOWN;
|
||||||
|
else if( ea.getEventType() == EA::KEYUP )
|
||||||
|
type = KEY_UP;
|
||||||
|
// else
|
||||||
|
// // TODO what to do with wrong event type?
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void KeyboardEvent::setKey(uint32_t key)
|
||||||
|
{
|
||||||
|
_name.clear();
|
||||||
|
_key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void KeyboardEvent::setUnmodifiedKey(uint32_t key)
|
||||||
|
{
|
||||||
|
_name.clear();
|
||||||
|
_unmodified_key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void KeyboardEvent::setRepeat(bool repeat)
|
||||||
|
{
|
||||||
|
_repeat = repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
std::string KeyboardEvent::key() const
|
||||||
|
{
|
||||||
|
if( !_name.empty() )
|
||||||
|
return _name;
|
||||||
|
|
||||||
|
// We need to make sure only valid const char* pointers are passed. The best
|
||||||
|
// way is just to use string constants.
|
||||||
|
// Use an empty string ("") to just use the value reported by the operating
|
||||||
|
// system.
|
||||||
|
typedef std::pair<const char*, uint8_t> InternalKeyInfo;
|
||||||
|
|
||||||
|
#if BOOST_VERSION >= 104800
|
||||||
|
typedef boost::container::flat_map<int, InternalKeyInfo> InternalKeyMap;
|
||||||
|
typedef boost::container::flat_set<int> KeyList;
|
||||||
|
#else
|
||||||
|
# warning "Use Boost >= 1.48 for faster and more memory efficient key lookup"
|
||||||
|
typedef std::map<int, InternalKeyInfo> InternalKeyMap;
|
||||||
|
typedef std::set<int> KeyList;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static InternalKeyMap key_map;
|
||||||
|
static KeyList num_pad_keys;
|
||||||
|
|
||||||
|
if( key_map.empty() )
|
||||||
|
{
|
||||||
|
const uint8_t S = DOM_KEY_LOCATION_STANDARD,
|
||||||
|
L = DOM_KEY_LOCATION_LEFT,
|
||||||
|
R = DOM_KEY_LOCATION_RIGHT,
|
||||||
|
N = DOM_KEY_LOCATION_NUMPAD;
|
||||||
|
|
||||||
|
key_map[ EA::KEY_BackSpace ] = std::make_pair("Backspace", S);
|
||||||
|
key_map[ EA::KEY_Tab ] = std::make_pair("Tab", S);
|
||||||
|
key_map[ EA::KEY_Linefeed ] = std::make_pair("Linefeed", S);
|
||||||
|
key_map[ EA::KEY_Clear ] = std::make_pair("Clear", S);
|
||||||
|
key_map[ EA::KEY_Return ] = std::make_pair("Enter", S);
|
||||||
|
key_map[ EA::KEY_Pause ] = std::make_pair("Pause", S);
|
||||||
|
key_map[ EA::KEY_Scroll_Lock ] = std::make_pair("ScrollLock", S);
|
||||||
|
key_map[ EA::KEY_Sys_Req ] = std::make_pair("SystemRequest", S);
|
||||||
|
key_map[ EA::KEY_Escape ] = std::make_pair("Escape", S);
|
||||||
|
key_map[ EA::KEY_Delete ] = std::make_pair("Delete", S);
|
||||||
|
|
||||||
|
key_map[ EA::KEY_Home ] = std::make_pair("Home", S);
|
||||||
|
key_map[ EA::KEY_Left ] = std::make_pair("Left", S);
|
||||||
|
key_map[ EA::KEY_Up ] = std::make_pair("Up", S);
|
||||||
|
key_map[ EA::KEY_Right ] = std::make_pair("Right", S);
|
||||||
|
key_map[ EA::KEY_Down ] = std::make_pair("Down", S);
|
||||||
|
key_map[ EA::KEY_Page_Up ] = std::make_pair("PageUp", S);
|
||||||
|
key_map[ EA::KEY_Page_Down ] = std::make_pair("PageDown", S);
|
||||||
|
key_map[ EA::KEY_End ] = std::make_pair("End", S);
|
||||||
|
key_map[ EA::KEY_Begin ] = std::make_pair("Begin", S);
|
||||||
|
|
||||||
|
key_map[ EA::KEY_Select ] = std::make_pair("Select", S);
|
||||||
|
key_map[ EA::KEY_Print ] = std::make_pair("PrintScreen", S);
|
||||||
|
key_map[ EA::KEY_Execute ] = std::make_pair("Execute", S);
|
||||||
|
key_map[ EA::KEY_Insert ] = std::make_pair("Insert", S);
|
||||||
|
key_map[ EA::KEY_Undo ] = std::make_pair("Undo", S);
|
||||||
|
key_map[ EA::KEY_Redo ] = std::make_pair("Redo", S);
|
||||||
|
key_map[ EA::KEY_Menu ] = std::make_pair("ContextMenu", S);
|
||||||
|
key_map[ EA::KEY_Find ] = std::make_pair("Find", S);
|
||||||
|
key_map[ EA::KEY_Cancel ] = std::make_pair("Cancel", S);
|
||||||
|
key_map[ EA::KEY_Help ] = std::make_pair("Help", S);
|
||||||
|
key_map[ EA::KEY_Break ] = std::make_pair("Break", S);
|
||||||
|
key_map[ EA::KEY_Mode_switch ] = std::make_pair("ModeChange", S);
|
||||||
|
key_map[ EA::KEY_Num_Lock ] = std::make_pair("NumLock", S);
|
||||||
|
|
||||||
|
key_map[ EA::KEY_KP_Space ] = std::make_pair(" ", N);
|
||||||
|
key_map[ EA::KEY_KP_Tab ] = std::make_pair("Tab", N);
|
||||||
|
key_map[ EA::KEY_KP_Enter ] = std::make_pair("Enter", N);
|
||||||
|
key_map[ EA::KEY_KP_F1 ] = std::make_pair("F1", N);
|
||||||
|
key_map[ EA::KEY_KP_F2 ] = std::make_pair("F2", N);
|
||||||
|
key_map[ EA::KEY_KP_F3 ] = std::make_pair("F3", N);
|
||||||
|
key_map[ EA::KEY_KP_F4 ] = std::make_pair("F4", N);
|
||||||
|
key_map[ EA::KEY_KP_Home ] = std::make_pair("Home", N);
|
||||||
|
key_map[ EA::KEY_KP_Left ] = std::make_pair("Left", N);
|
||||||
|
key_map[ EA::KEY_KP_Up ] = std::make_pair("Up", N);
|
||||||
|
key_map[ EA::KEY_KP_Right ] = std::make_pair("Right", N);
|
||||||
|
key_map[ EA::KEY_KP_Down ] = std::make_pair("Down", N);
|
||||||
|
key_map[ EA::KEY_KP_Page_Up ] = std::make_pair("PageUp", N);
|
||||||
|
key_map[ EA::KEY_KP_Page_Down ] = std::make_pair("PageDown", N);
|
||||||
|
key_map[ EA::KEY_KP_End ] = std::make_pair("End", N);
|
||||||
|
key_map[ EA::KEY_KP_Begin ] = std::make_pair("Begin", N);
|
||||||
|
key_map[ EA::KEY_KP_Insert ] = std::make_pair("Insert", N);
|
||||||
|
key_map[ EA::KEY_KP_Delete ] = std::make_pair("Delete", N);
|
||||||
|
key_map[ EA::KEY_KP_Equal ] = std::make_pair("=", N);
|
||||||
|
key_map[ EA::KEY_KP_Multiply ] = std::make_pair("*", N);
|
||||||
|
key_map[ EA::KEY_KP_Add ] = std::make_pair("+", N);
|
||||||
|
key_map[ EA::KEY_KP_Separator ] = std::make_pair("", N);
|
||||||
|
key_map[ EA::KEY_KP_Subtract ] = std::make_pair("-", N);
|
||||||
|
key_map[ EA::KEY_KP_Decimal ] = std::make_pair("", N);
|
||||||
|
key_map[ EA::KEY_KP_Divide ] = std::make_pair("/", N);
|
||||||
|
|
||||||
|
key_map[ EA::KEY_KP_0 ] = std::make_pair("0", N);
|
||||||
|
key_map[ EA::KEY_KP_1 ] = std::make_pair("1", N);
|
||||||
|
key_map[ EA::KEY_KP_2 ] = std::make_pair("2", N);
|
||||||
|
key_map[ EA::KEY_KP_3 ] = std::make_pair("3", N);
|
||||||
|
key_map[ EA::KEY_KP_4 ] = std::make_pair("4", N);
|
||||||
|
key_map[ EA::KEY_KP_5 ] = std::make_pair("5", N);
|
||||||
|
key_map[ EA::KEY_KP_6 ] = std::make_pair("6", N);
|
||||||
|
key_map[ EA::KEY_KP_7 ] = std::make_pair("7", N);
|
||||||
|
key_map[ EA::KEY_KP_8 ] = std::make_pair("8", N);
|
||||||
|
key_map[ EA::KEY_KP_9 ] = std::make_pair("9", N);
|
||||||
|
|
||||||
|
key_map[ EA::KEY_F1 ] = std::make_pair("F1", S);
|
||||||
|
key_map[ EA::KEY_F2 ] = std::make_pair("F2", S);
|
||||||
|
key_map[ EA::KEY_F3 ] = std::make_pair("F3", S);
|
||||||
|
key_map[ EA::KEY_F4 ] = std::make_pair("F4", S);
|
||||||
|
key_map[ EA::KEY_F5 ] = std::make_pair("F5", S);
|
||||||
|
key_map[ EA::KEY_F6 ] = std::make_pair("F6", S);
|
||||||
|
key_map[ EA::KEY_F7 ] = std::make_pair("F7", S);
|
||||||
|
key_map[ EA::KEY_F8 ] = std::make_pair("F8", S);
|
||||||
|
key_map[ EA::KEY_F9 ] = std::make_pair("F9", S);
|
||||||
|
key_map[ EA::KEY_F10 ] = std::make_pair("F10", S);
|
||||||
|
key_map[ EA::KEY_F11 ] = std::make_pair("F11", S);
|
||||||
|
key_map[ EA::KEY_F12 ] = std::make_pair("F12", S);
|
||||||
|
key_map[ EA::KEY_F13 ] = std::make_pair("F13", S);
|
||||||
|
key_map[ EA::KEY_F14 ] = std::make_pair("F14", S);
|
||||||
|
key_map[ EA::KEY_F15 ] = std::make_pair("F15", S);
|
||||||
|
key_map[ EA::KEY_F16 ] = std::make_pair("F16", S);
|
||||||
|
key_map[ EA::KEY_F17 ] = std::make_pair("F17", S);
|
||||||
|
key_map[ EA::KEY_F18 ] = std::make_pair("F18", S);
|
||||||
|
key_map[ EA::KEY_F19 ] = std::make_pair("F19", S);
|
||||||
|
key_map[ EA::KEY_F20 ] = std::make_pair("F20", S);
|
||||||
|
key_map[ EA::KEY_F21 ] = std::make_pair("F21", S);
|
||||||
|
key_map[ EA::KEY_F22 ] = std::make_pair("F22", S);
|
||||||
|
key_map[ EA::KEY_F23 ] = std::make_pair("F23", S);
|
||||||
|
key_map[ EA::KEY_F24 ] = std::make_pair("F24", S);
|
||||||
|
key_map[ EA::KEY_F25 ] = std::make_pair("F25", S);
|
||||||
|
key_map[ EA::KEY_F26 ] = std::make_pair("F26", S);
|
||||||
|
key_map[ EA::KEY_F27 ] = std::make_pair("F27", S);
|
||||||
|
key_map[ EA::KEY_F28 ] = std::make_pair("F28", S);
|
||||||
|
key_map[ EA::KEY_F29 ] = std::make_pair("F29", S);
|
||||||
|
key_map[ EA::KEY_F30 ] = std::make_pair("F30", S);
|
||||||
|
key_map[ EA::KEY_F31 ] = std::make_pair("F31", S);
|
||||||
|
key_map[ EA::KEY_F32 ] = std::make_pair("F32", S);
|
||||||
|
key_map[ EA::KEY_F33 ] = std::make_pair("F33", S);
|
||||||
|
key_map[ EA::KEY_F34 ] = std::make_pair("F34", S);
|
||||||
|
key_map[ EA::KEY_F35 ] = std::make_pair("F35", S);
|
||||||
|
|
||||||
|
key_map[ KEY_AltGraph ] = std::make_pair("AltGraph", S);
|
||||||
|
key_map[ EA::KEY_Shift_L ] = std::make_pair("Shift", L);
|
||||||
|
key_map[ EA::KEY_Shift_R ] = std::make_pair("Shift", R);
|
||||||
|
key_map[ EA::KEY_Control_L ] = std::make_pair("Control", L);
|
||||||
|
key_map[ EA::KEY_Control_R ] = std::make_pair("Control", R);
|
||||||
|
key_map[ EA::KEY_Caps_Lock ] = std::make_pair("CapsLock", S);
|
||||||
|
key_map[ EA::KEY_Shift_Lock ] = std::make_pair("ShiftLock", S);
|
||||||
|
key_map[ EA::KEY_Meta_L ] = std::make_pair("Meta", L);
|
||||||
|
key_map[ EA::KEY_Meta_R ] = std::make_pair("Meta", R);
|
||||||
|
key_map[ EA::KEY_Alt_L ] = std::make_pair("Alt", L);
|
||||||
|
key_map[ EA::KEY_Alt_R ] = std::make_pair("Alt", R);
|
||||||
|
key_map[ EA::KEY_Super_L ] = std::make_pair("Super", L);
|
||||||
|
key_map[ EA::KEY_Super_R ] = std::make_pair("Super", R);
|
||||||
|
key_map[ EA::KEY_Hyper_L ] = std::make_pair("Hyper", L);
|
||||||
|
key_map[ EA::KEY_Hyper_R ] = std::make_pair("Hyper", R);
|
||||||
|
|
||||||
|
num_pad_keys.insert(EA::KEY_KP_Home );
|
||||||
|
num_pad_keys.insert(EA::KEY_KP_Left );
|
||||||
|
num_pad_keys.insert(EA::KEY_KP_Up );
|
||||||
|
num_pad_keys.insert(EA::KEY_KP_Right );
|
||||||
|
num_pad_keys.insert(EA::KEY_KP_Down );
|
||||||
|
num_pad_keys.insert(EA::KEY_KP_Page_Up );
|
||||||
|
num_pad_keys.insert(EA::KEY_KP_Page_Down);
|
||||||
|
num_pad_keys.insert(EA::KEY_KP_End );
|
||||||
|
num_pad_keys.insert(EA::KEY_KP_Begin );
|
||||||
|
num_pad_keys.insert(EA::KEY_KP_Insert );
|
||||||
|
num_pad_keys.insert(EA::KEY_KP_Delete );
|
||||||
|
}
|
||||||
|
|
||||||
|
_location = DOM_KEY_LOCATION_STANDARD;
|
||||||
|
|
||||||
|
InternalKeyMap::const_iterator it = key_map.find(_key);
|
||||||
|
if( it != key_map.end())
|
||||||
|
{
|
||||||
|
_name = it->second.first;
|
||||||
|
_location = it->second.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty or no mapping -> convert UTF-32 key value to UTF-8
|
||||||
|
if( _name.empty() )
|
||||||
|
{
|
||||||
|
if( !utf8::internal::is_code_point_valid(_key) )
|
||||||
|
_name = "Unidentified";
|
||||||
|
else
|
||||||
|
utf8::unchecked::append(_key, std::back_inserter(_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys on the numpad with NumLock enabled are reported just like their
|
||||||
|
// equivalent keys in the standard key block. Using the unmodified key value
|
||||||
|
// we can detect such keys and set the location accordingly.
|
||||||
|
if( num_pad_keys.find(_unmodified_key) != num_pad_keys.end() )
|
||||||
|
_location = DOM_KEY_LOCATION_NUMPAD;
|
||||||
|
|
||||||
|
return _name;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
KeyboardEvent::DOMKeyLocation KeyboardEvent::location() const
|
||||||
|
{
|
||||||
|
key(); // ensure location is up-to-date
|
||||||
|
return static_cast<DOMKeyLocation>(_location);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
bool KeyboardEvent::isModifier() const
|
||||||
|
{
|
||||||
|
return ( _key >= EA::KEY_Shift_L
|
||||||
|
&& _key <= EA::KEY_Hyper_R
|
||||||
|
)
|
||||||
|
|| _key == KEY_AltGraph;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
bool KeyboardEvent::isPrint() const
|
||||||
|
{
|
||||||
|
const std::string& key_name = key();
|
||||||
|
if( key_name.empty() )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string::const_iterator it = key_name.begin();
|
||||||
|
uint32_t cp = utf8::next(it, key_name.end());
|
||||||
|
|
||||||
|
// Check if _name contains exactly one (UTF-8 encoded) character.
|
||||||
|
if( it != key_name.end() )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// C0 and C1 control characters are not printable.
|
||||||
|
if( cp <= 0x1f || (0x7f <= cp && cp <= 0x9f) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace canvas
|
||||||
|
} // namespace simgear
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
///@file
|
||||||
|
/// Keyboard event
|
||||||
|
//
|
||||||
|
// Copyright (C) 2014 Thomas Geymayer <tomgey@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 Library General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
|
||||||
|
#ifndef CANVAS_KEYBOARD_EVENT_HXX_
|
||||||
|
#define CANVAS_KEYBOARD_EVENT_HXX_
|
||||||
|
|
||||||
|
#include "DeviceEvent.hxx"
|
||||||
|
|
||||||
|
namespace simgear
|
||||||
|
{
|
||||||
|
namespace canvas
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keyboard (button up/down) event
|
||||||
|
*/
|
||||||
|
class KeyboardEvent:
|
||||||
|
public DeviceEvent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum DOMKeyLocation
|
||||||
|
{
|
||||||
|
DOM_KEY_LOCATION_STANDARD = 0,
|
||||||
|
DOM_KEY_LOCATION_LEFT,
|
||||||
|
DOM_KEY_LOCATION_RIGHT,
|
||||||
|
DOM_KEY_LOCATION_NUMPAD
|
||||||
|
};
|
||||||
|
|
||||||
|
KeyboardEvent();
|
||||||
|
KeyboardEvent(const osgGA::GUIEventAdapter& ea);
|
||||||
|
|
||||||
|
void setKey(uint32_t key);
|
||||||
|
void setUnmodifiedKey(uint32_t key);
|
||||||
|
void setRepeat(bool repeat);
|
||||||
|
|
||||||
|
std::string key() const;
|
||||||
|
DOMKeyLocation location() const;
|
||||||
|
bool repeat() const { return _repeat; }
|
||||||
|
|
||||||
|
uint32_t charCode() const { return _key; }
|
||||||
|
uint32_t keyCode() const { return _unmodified_key; }
|
||||||
|
|
||||||
|
/// Whether the key which has triggered this event is a modifier
|
||||||
|
bool isModifier() const;
|
||||||
|
|
||||||
|
/// Whether this events represents an input of a printable character
|
||||||
|
bool isPrint() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint32_t _key, //!< Key identifier for this event
|
||||||
|
_unmodified_key; //!< Virtual key identifier without any
|
||||||
|
// modifiers applied
|
||||||
|
bool _repeat; //!< If key has been depressed long enough to
|
||||||
|
// generate key repetition
|
||||||
|
|
||||||
|
mutable std::string _name; //!< Printable representation/name
|
||||||
|
mutable uint8_t _location; //!< Location of the key on the keyboard
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace canvas
|
||||||
|
} // namespace simgear
|
||||||
|
|
||||||
|
#endif /* CANVAS_KEYBOARD_EVENT_HXX_ */
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// Listener for canvas (GUI) events being passed to a Nasal function/code
|
// Mouse event
|
||||||
//
|
//
|
||||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
|
||||||
//
|
//
|
||||||
// This library is free software; you can redistribute it and/or
|
// This library is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU Library General Public
|
// modify it under the terms of the GNU Library General Public
|
||||||
@@ -16,11 +16,8 @@
|
|||||||
// License along with this library; if not, write to the Free Software
|
// License along with this library; if not, write to the Free Software
|
||||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
|
||||||
#include "CanvasEvent.hxx"
|
#include "MouseEvent.hxx"
|
||||||
#include "CanvasEventListener.hxx"
|
#include <osgGA/GUIEventAdapter>
|
||||||
#include "CanvasSystemAdapter.hxx"
|
|
||||||
|
|
||||||
#include <simgear/nasal/cppbind/Ghost.hxx>
|
|
||||||
|
|
||||||
namespace simgear
|
namespace simgear
|
||||||
{
|
{
|
||||||
@@ -28,43 +25,44 @@ namespace canvas
|
|||||||
{
|
{
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
EventListener::EventListener(naRef code, const SystemAdapterPtr& sys_adapter):
|
MouseEvent::MouseEvent():
|
||||||
_code(code),
|
button(0),
|
||||||
_gc_key(-1),
|
buttons(0),
|
||||||
_sys(sys_adapter)
|
click_count(0)
|
||||||
{
|
{
|
||||||
assert( sys_adapter );
|
|
||||||
if( !naIsCode(code)
|
|
||||||
&& !naIsCCode(code)
|
|
||||||
&& !naIsFunc(code) )
|
|
||||||
throw std::runtime_error
|
|
||||||
(
|
|
||||||
"canvas::EventListener: invalid function argument"
|
|
||||||
);
|
|
||||||
|
|
||||||
_gc_key = sys_adapter->gcSave(_code);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
EventListener::~EventListener()
|
MouseEvent::MouseEvent(const osgGA::GUIEventAdapter& ea):
|
||||||
|
DeviceEvent(ea),
|
||||||
|
button(0),
|
||||||
|
buttons(ea.getButtonMask()),
|
||||||
|
click_count(0)
|
||||||
{
|
{
|
||||||
assert( !_sys.expired() );
|
// Convert button mask to index
|
||||||
_sys.lock()->gcRelease(_gc_key);
|
int button_mask = ea.getButton();
|
||||||
|
while( (button_mask >>= 1) > 0 )
|
||||||
|
button += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void EventListener::call(const canvas::EventPtr& event)
|
bool MouseEvent::canBubble() const
|
||||||
{
|
{
|
||||||
SystemAdapterPtr sys = _sys.lock();
|
// Check if event supports bubbling
|
||||||
|
const Event::Type types_no_bubbling[] = {
|
||||||
naRef args[] = {
|
Event::MOUSE_ENTER,
|
||||||
nasal::Ghost<EventPtr>::create(sys->getNasalContext(), event)
|
Event::MOUSE_LEAVE,
|
||||||
};
|
};
|
||||||
const int num_args = sizeof(args)/sizeof(args[0]);
|
const size_t num_types_no_bubbling = sizeof(types_no_bubbling)
|
||||||
|
/ sizeof(types_no_bubbling[0]);
|
||||||
|
|
||||||
sys->callMethod(_code, naNil(), num_args, args, naNil());
|
for( size_t i = 0; i < num_types_no_bubbling; ++i )
|
||||||
|
if( type == types_no_bubbling[i] )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace canvas
|
} // namespace canvas
|
||||||
} // namespace simgear
|
} // namespace simgear
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
// Mouse event
|
///@file
|
||||||
|
/// Mouse event
|
||||||
//
|
//
|
||||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||||
//
|
//
|
||||||
@@ -19,27 +20,28 @@
|
|||||||
#ifndef CANVAS_MOUSE_EVENT_HXX_
|
#ifndef CANVAS_MOUSE_EVENT_HXX_
|
||||||
#define CANVAS_MOUSE_EVENT_HXX_
|
#define CANVAS_MOUSE_EVENT_HXX_
|
||||||
|
|
||||||
#include "CanvasEvent.hxx"
|
#include "DeviceEvent.hxx"
|
||||||
#include <osgGA/GUIEventAdapter>
|
|
||||||
|
|
||||||
namespace simgear
|
namespace simgear
|
||||||
{
|
{
|
||||||
namespace canvas
|
namespace canvas
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mouse (button/move/wheel) event
|
||||||
|
*/
|
||||||
class MouseEvent:
|
class MouseEvent:
|
||||||
public Event
|
public DeviceEvent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MouseEvent():
|
MouseEvent();
|
||||||
button(-1),
|
MouseEvent(const osgGA::GUIEventAdapter& ea);
|
||||||
state(-1),
|
|
||||||
mod(-1),
|
virtual bool canBubble() const;
|
||||||
click_count(0)
|
|
||||||
{}
|
|
||||||
|
|
||||||
osg::Vec2f getScreenPos() const { return screen_pos; }
|
osg::Vec2f getScreenPos() const { return screen_pos; }
|
||||||
osg::Vec2f getClientPos() const { return client_pos; }
|
osg::Vec2f getClientPos() const { return client_pos; }
|
||||||
|
osg::Vec2f getLocalPos() const { return local_pos; }
|
||||||
osg::Vec2f getDelta() const { return delta; }
|
osg::Vec2f getDelta() const { return delta; }
|
||||||
|
|
||||||
float getScreenX() const { return screen_pos.x(); }
|
float getScreenX() const { return screen_pos.x(); }
|
||||||
@@ -48,18 +50,24 @@ namespace canvas
|
|||||||
float getClientX() const { return client_pos.x(); }
|
float getClientX() const { return client_pos.x(); }
|
||||||
float getClientY() const { return client_pos.y(); }
|
float getClientY() const { return client_pos.y(); }
|
||||||
|
|
||||||
|
float getLocalX() const { return local_pos.x(); }
|
||||||
|
float getLocalY() const { return local_pos.y(); }
|
||||||
|
|
||||||
float getDeltaX() const { return delta.x(); }
|
float getDeltaX() const { return delta.x(); }
|
||||||
float getDeltaY() const { return delta.y(); }
|
float getDeltaY() const { return delta.y(); }
|
||||||
|
|
||||||
|
int getButton() const { return button; }
|
||||||
|
int getButtonMask() const { return buttons; }
|
||||||
|
|
||||||
int getCurrentClickCount() const { return click_count; }
|
int getCurrentClickCount() const { return click_count; }
|
||||||
|
|
||||||
osg::Vec2f screen_pos, //<! Position in screen coordinates
|
osg::Vec2f screen_pos, //!< Position in screen coordinates
|
||||||
client_pos, //<! Position in window/canvas coordinates
|
client_pos, //!< Position in window/canvas coordinates
|
||||||
|
local_pos, //!< Position in local/element coordinates
|
||||||
delta;
|
delta;
|
||||||
int button, //<! Button for this event
|
int button, //!< Button for this event
|
||||||
state, //<! Current button state
|
buttons, //!< Current button state
|
||||||
mod, //<! Keyboard modifier state
|
click_count; //!< Current click count
|
||||||
click_count; //<! Current click count
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace canvas
|
} // namespace canvas
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/// Unit tests for reference counting and smart pointer classes
|
||||||
|
#define BOOST_TEST_MODULE structure
|
||||||
|
#include <BoostTestTargetConfig.h>
|
||||||
|
|
||||||
|
#include "MouseEvent.hxx"
|
||||||
|
#include "CustomEvent.hxx"
|
||||||
|
|
||||||
|
namespace sc = simgear::canvas;
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE( canvas_event_types )
|
||||||
|
{
|
||||||
|
// Register type
|
||||||
|
BOOST_REQUIRE_EQUAL( sc::Event::strToType("test"),
|
||||||
|
sc::Event::UNKNOWN );
|
||||||
|
BOOST_REQUIRE_EQUAL( sc::Event::getOrRegisterType("test"),
|
||||||
|
sc::Event::CUSTOM_EVENT );
|
||||||
|
BOOST_REQUIRE_EQUAL( sc::Event::strToType("test"),
|
||||||
|
sc::Event::CUSTOM_EVENT );
|
||||||
|
BOOST_REQUIRE_EQUAL( sc::Event::typeToStr(sc::Event::CUSTOM_EVENT),
|
||||||
|
"test" );
|
||||||
|
|
||||||
|
// Basic internal type
|
||||||
|
BOOST_REQUIRE_EQUAL( sc::Event::typeToStr(sc::Event::MOUSE_DOWN),
|
||||||
|
"mousedown" );
|
||||||
|
BOOST_REQUIRE_EQUAL( sc::Event::strToType("mousedown"),
|
||||||
|
sc::Event::MOUSE_DOWN );
|
||||||
|
|
||||||
|
// Unknown type
|
||||||
|
BOOST_REQUIRE_EQUAL( sc::Event::typeToStr(123),
|
||||||
|
"unknown" );
|
||||||
|
|
||||||
|
// Register type through custom event instance
|
||||||
|
sc::CustomEvent e("blub");
|
||||||
|
BOOST_REQUIRE_EQUAL( e.getTypeString(), "blub" );
|
||||||
|
BOOST_REQUIRE_NE( e.getType(), sc::Event::UNKNOWN );
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user